From 2136e75c0bce090d104bb5b5006e48e42fb22a0a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 21 Dec 2018 23:04:56 +0300 Subject: move path completion to a separate component --- crates/ra_analysis/src/completion.rs | 43 ++++++---- crates/ra_analysis/src/completion/complete_path.rs | 95 ++++++++++++++++++++++ .../src/completion/reference_completion.rs | 33 +------- 3 files changed, 127 insertions(+), 44 deletions(-) create mode 100644 crates/ra_analysis/src/completion/complete_path.rs (limited to 'crates/ra_analysis') diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs index 883b3e851..d91304bc2 100644 --- a/crates/ra_analysis/src/completion.rs +++ b/crates/ra_analysis/src/completion.rs @@ -4,13 +4,12 @@ mod reference_completion; mod complete_fn_param; mod complete_keyword; mod complete_snippet; +mod complete_path; use ra_editor::find_node_at_offset; use ra_text_edit::AtomTextEdit; use ra_syntax::{ - algo::{ - find_leaf_at_offset, - }, + algo::find_leaf_at_offset, ast, AstNode, SyntaxNodeRef, @@ -48,11 +47,12 @@ pub(crate) fn completions( reference_completion::completions(&mut acc, db, &module, &file, name_ref)?; } - let ctx = ctry!(SyntaxContext::new(&original_file, position.offset)); + let ctx = ctry!(SyntaxContext::new(db, &original_file, position)?); complete_fn_param::complete_fn_param(&mut acc, &ctx); complete_keyword::complete_expr_keyword(&mut acc, &ctx); complete_snippet::complete_expr_snippet(&mut acc, &ctx); complete_snippet::complete_item_snippet(&mut acc, &ctx); + complete_path::complete_path(&mut acc, &ctx)?; Ok(Some(acc)) } @@ -61,31 +61,44 @@ pub(crate) fn completions( /// exactly is the cursor, syntax-wise. #[derive(Debug)] pub(super) struct SyntaxContext<'a> { + db: &'a db::RootDatabase, leaf: SyntaxNodeRef<'a>, + module: Option, enclosing_fn: Option>, is_param: bool, /// A single-indent path, like `foo`. is_trivial_path: bool, + /// If not a trivial, path, the prefix (qualifier). + path_prefix: Option, after_if: bool, is_stmt: bool, /// Something is typed at the "top" level, in module or impl/trait. is_new_item: bool, } -impl SyntaxContext<'_> { - pub(super) fn new(original_file: &SourceFileNode, offset: TextUnit) -> Option { - let leaf = find_leaf_at_offset(original_file.syntax(), offset).left_biased()?; +impl<'a> SyntaxContext<'a> { + pub(super) fn new( + db: &'a db::RootDatabase, + original_file: &'a SourceFileNode, + position: FilePosition, + ) -> Cancelable>> { + let module = source_binder::module_from_position(db, position)?; + let leaf = + ctry!(find_leaf_at_offset(original_file.syntax(), position.offset).left_biased()); let mut ctx = SyntaxContext { + db, leaf, + module, enclosing_fn: None, is_param: false, is_trivial_path: false, + path_prefix: None, after_if: false, is_stmt: false, is_new_item: false, }; - ctx.fill(original_file, offset); - Some(ctx) + ctx.fill(original_file, position.offset); + Ok(Some(ctx)) } fn fill(&mut self, original_file: &SourceFileNode, offset: TextUnit) { @@ -140,11 +153,13 @@ impl SyntaxContext<'_> { }; if let Some(segment) = ast::PathSegment::cast(parent) { let path = segment.parent_path(); - // if let Some(path) = Path::from_ast(path) { - // if !path.is_ident() { - // return Some(NameRefKind::Path(path)); - // } - // } + if let Some(mut path) = hir::Path::from_ast(path) { + if !path.is_ident() { + path.segments.pop().unwrap(); + self.path_prefix = Some(path); + return; + } + } if path.qualifier().is_none() { self.is_trivial_path = true; self.enclosing_fn = self diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs new file mode 100644 index 000000000..d04503e46 --- /dev/null +++ b/crates/ra_analysis/src/completion/complete_path.rs @@ -0,0 +1,95 @@ +use crate::{ + completion::{CompletionItem, Completions, CompletionKind::*, SyntaxContext}, + Cancelable, +}; + +pub(super) fn complete_path(acc: &mut Completions, ctx: &SyntaxContext) -> Cancelable<()> { + let (path, module) = match (&ctx.path_prefix, &ctx.module) { + (Some(path), Some(module)) => (path.clone(), module), + _ => return Ok(()), + }; + let def_id = match module.resolve_path(ctx.db, path)? { + None => return Ok(()), + Some(it) => it, + }; + let target_module = match def_id.resolve(ctx.db)? { + hir::Def::Module(it) => it, + _ => return Ok(()), + }; + let module_scope = target_module.scope(ctx.db)?; + module_scope.entries().for_each(|(name, _res)| { + CompletionItem::new(name.to_string()) + .kind(Reference) + .add_to(acc) + }); + Ok(()) +} + +#[cfg(test)] +mod tests { + use crate::completion::{CompletionKind, check_completion}; + + fn check_reference_completion(code: &str, expected_completions: &str) { + check_completion(code, expected_completions, CompletionKind::Reference); + } + + #[test] + fn completes_use_item_starting_with_self() { + check_reference_completion( + r" + use self::m::<|>; + + mod m { + struct Bar; + } + ", + "Bar", + ); + } + + #[test] + fn completes_use_item_starting_with_crate() { + check_reference_completion( + " + //- /lib.rs + mod foo; + struct Spam; + //- /foo.rs + use crate::Sp<|> + ", + "Spam;foo", + ); + } + + #[test] + fn completes_nested_use_tree() { + check_reference_completion( + " + //- /lib.rs + mod foo; + struct Spam; + //- /foo.rs + use crate::{Sp<|>}; + ", + "Spam;foo", + ); + } + + #[test] + fn completes_deeply_nested_use_tree() { + check_reference_completion( + " + //- /lib.rs + mod foo; + pub mod bar { + pub mod baz { + pub struct Spam; + } + } + //- /foo.rs + use crate::{bar::{baz::Sp<|>}}; + ", + "Spam", + ); + } +} diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs index 46d381927..459ed8f6f 100644 --- a/crates/ra_analysis/src/completion/reference_completion.rs +++ b/crates/ra_analysis/src/completion/reference_completion.rs @@ -1,4 +1,4 @@ -use rustc_hash::{FxHashSet}; +use rustc_hash::FxHashSet; use ra_syntax::{ SourceFileNode, AstNode, ast, @@ -6,7 +6,7 @@ use ra_syntax::{ }; use hir::{ self, - FnScopes, Def, Path + FnScopes, Path }; use crate::{ @@ -53,7 +53,7 @@ pub(super) fn completions( .add_to(acc) }); } - NameRefKind::Path(path) => complete_path(acc, db, module, path)?, + NameRefKind::Path(_) => (), NameRefKind::BareIdentInMod => (), } Ok(()) @@ -121,33 +121,6 @@ fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Completions) } } -fn complete_path( - acc: &mut Completions, - db: &RootDatabase, - module: &hir::Module, - mut path: Path, -) -> Cancelable<()> { - if path.segments.is_empty() { - return Ok(()); - } - path.segments.pop(); - let def_id = match module.resolve_path(db, path)? { - None => return Ok(()), - Some(it) => it, - }; - let target_module = match def_id.resolve(db)? { - Def::Module(it) => it, - _ => return Ok(()), - }; - let module_scope = target_module.scope(db)?; - module_scope.entries().for_each(|(name, _res)| { - CompletionItem::new(name.to_string()) - .kind(Reference) - .add_to(acc) - }); - Ok(()) -} - #[cfg(test)] mod tests { use crate::completion::{CompletionKind, check_completion}; -- cgit v1.2.3