use Completions to collect completions

This commit is contained in:
Aleksey Kladov 2018-12-21 15:46:01 +03:00
parent 052e20162a
commit ba0072401c
3 changed files with 38 additions and 26 deletions

View file

@ -15,7 +15,8 @@ use hir::source_binder;
use crate::{ use crate::{
db, db,
Cancelable, FilePosition Cancelable, FilePosition,
completion::completion_item::Completions,
}; };
pub use crate::completion::completion_item::{CompletionItem, InsertText}; pub use crate::completion::completion_item::{CompletionItem, InsertText};
@ -33,15 +34,15 @@ pub(crate) fn completions(
let module = ctry!(source_binder::module_from_position(db, position)?); let module = ctry!(source_binder::module_from_position(db, position)?);
let mut res = Vec::new(); let mut acc = Completions::default();
let mut has_completions = false; let mut has_completions = false;
// First, let's try to complete a reference to some declaration. // First, let's try to complete a reference to some declaration.
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
has_completions = true; has_completions = true;
reference_completion::completions(&mut res, db, &module, &file, name_ref)?; reference_completion::completions(&mut acc, db, &module, &file, name_ref)?;
// special case, `trait T { fn foo(i_am_a_name_ref) {} }` // special case, `trait T { fn foo(i_am_a_name_ref) {} }`
if is_node::<ast::Param>(name_ref.syntax()) { if is_node::<ast::Param>(name_ref.syntax()) {
param_completions(&mut res, name_ref.syntax()); param_completions(&mut acc, name_ref.syntax());
} }
} }
@ -49,10 +50,14 @@ pub(crate) fn completions(
if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) { if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
if is_node::<ast::Param>(name.syntax()) { if is_node::<ast::Param>(name.syntax()) {
has_completions = true; has_completions = true;
param_completions(&mut res, name.syntax()); param_completions(&mut acc, name.syntax());
} }
} }
let res = if has_completions { Some(res) } else { None }; let res = if has_completions {
Some(acc.into())
} else {
None
};
Ok(res) Ok(res)
} }
@ -60,7 +65,7 @@ pub(crate) fn completions(
/// functions in a file have a `spam: &mut Spam` parameter, a completion with /// functions in a file have a `spam: &mut Spam` parameter, a completion with
/// `spam: &mut Spam` insert text/label and `spam` lookup string will be /// `spam: &mut Spam` insert text/label and `spam` lookup string will be
/// suggested. /// suggested.
fn param_completions(acc: &mut Vec<CompletionItem>, ctx: SyntaxNodeRef) { fn param_completions(acc: &mut Completions, ctx: SyntaxNodeRef) {
let mut params = FxHashMap::default(); let mut params = FxHashMap::default();
for node in ctx.ancestors() { for node in ctx.ancestors() {
let _ = visitor_ctx(&mut params) let _ = visitor_ctx(&mut params)

View file

@ -53,8 +53,8 @@ pub(crate) struct Builder {
} }
impl Builder { impl Builder {
pub fn add_to(self, acc: &mut Vec<CompletionItem>) { pub fn add_to(self, acc: &mut Completions) {
acc.push(self.build()) acc.add(self.build())
} }
pub fn build(self) -> CompletionItem { pub fn build(self) -> CompletionItem {
@ -81,7 +81,7 @@ impl Into<CompletionItem> for Builder {
} }
/// Represents an in-progress set of completions being built. /// Represents an in-progress set of completions being built.
#[derive(Debug)] #[derive(Debug, Default)]
pub(crate) struct Completions { pub(crate) struct Completions {
buf: Vec<CompletionItem>, buf: Vec<CompletionItem>,
} }
@ -90,6 +90,13 @@ impl Completions {
pub(crate) fn add(&mut self, item: impl Into<CompletionItem>) { pub(crate) fn add(&mut self, item: impl Into<CompletionItem>) {
self.buf.push(item.into()) self.buf.push(item.into())
} }
pub(crate) fn add_all<I>(&mut self, items: I)
where
I: IntoIterator,
I::Item: Into<CompletionItem>,
{
items.into_iter().for_each(|item| self.add(item.into()))
}
} }
impl Into<Vec<CompletionItem>> for Completions { impl Into<Vec<CompletionItem>> for Completions {

View file

@ -13,12 +13,12 @@ use hir::{
use crate::{ use crate::{
db::RootDatabase, db::RootDatabase,
completion::CompletionItem, completion::{CompletionItem, Completions},
Cancelable Cancelable
}; };
pub(super) fn completions( pub(super) fn completions(
acc: &mut Vec<CompletionItem>, acc: &mut Completions,
db: &RootDatabase, db: &RootDatabase,
module: &hir::Module, module: &hir::Module,
file: &SourceFileNode, file: &SourceFileNode,
@ -117,7 +117,7 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
None None
} }
fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Completions) {
let mut shadowed = FxHashSet::default(); let mut shadowed = FxHashSet::default();
scopes scopes
.scope_chain(name_ref.syntax()) .scope_chain(name_ref.syntax())
@ -130,7 +130,7 @@ fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<Completi
} }
fn complete_path( fn complete_path(
acc: &mut Vec<CompletionItem>, acc: &mut Completions,
db: &RootDatabase, db: &RootDatabase,
module: &hir::Module, module: &hir::Module,
mut path: Path, mut path: Path,
@ -154,7 +154,7 @@ fn complete_path(
Ok(()) Ok(())
} }
fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) { fn complete_mod_item_snippets(acc: &mut Completions) {
CompletionItem::new("Test function") CompletionItem::new("Test function")
.lookup_by("tfn") .lookup_by("tfn")
.snippet( .snippet(
@ -174,26 +174,26 @@ fn complete_expr_keywords(
file: &SourceFileNode, file: &SourceFileNode,
fn_def: ast::FnDef, fn_def: ast::FnDef,
name_ref: ast::NameRef, name_ref: ast::NameRef,
acc: &mut Vec<CompletionItem>, acc: &mut Completions,
) { ) {
acc.push(keyword("if", "if $0 {}")); acc.add(keyword("if", "if $0 {}"));
acc.push(keyword("match", "match $0 {}")); acc.add(keyword("match", "match $0 {}"));
acc.push(keyword("while", "while $0 {}")); acc.add(keyword("while", "while $0 {}"));
acc.push(keyword("loop", "loop {$0}")); acc.add(keyword("loop", "loop {$0}"));
if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) { if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
if if_expr.syntax().range().end() < name_ref.syntax().range().start() { if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
acc.push(keyword("else", "else {$0}")); acc.add(keyword("else", "else {$0}"));
acc.push(keyword("else if", "else if $0 {}")); acc.add(keyword("else if", "else if $0 {}"));
} }
} }
} }
if is_in_loop_body(name_ref) { if is_in_loop_body(name_ref) {
acc.push(keyword("continue", "continue")); acc.add(keyword("continue", "continue"));
acc.push(keyword("break", "break")); acc.add(keyword("break", "break"));
} }
acc.extend(complete_return(fn_def, name_ref)); acc.add_all(complete_return(fn_def, name_ref));
} }
fn is_in_loop_body(name_ref: ast::NameRef) -> bool { fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
@ -252,7 +252,7 @@ fn keyword(kw: &str, snippet: &str) -> CompletionItem {
CompletionItem::new(kw).snippet(snippet).build() CompletionItem::new(kw).snippet(snippet).build()
} }
fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) { fn complete_expr_snippets(acc: &mut Completions) {
CompletionItem::new("pd") CompletionItem::new("pd")
.snippet("eprintln!(\"$0 = {:?}\", $0);") .snippet("eprintln!(\"$0 = {:?}\", $0);")
.add_to(acc); .add_to(acc);