From ccca5aae43a27c94180bb099bcc09bb6c29c2ea3 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 22 Dec 2018 00:52:02 +0300 Subject: scope-based copmletions on original file --- crates/ra_analysis/src/completion/complete_path.rs | 2 +- .../ra_analysis/src/completion/complete_scope.rs | 171 ++++++++++++ .../src/completion/reference_completion.rs | 307 --------------------- 3 files changed, 172 insertions(+), 308 deletions(-) create mode 100644 crates/ra_analysis/src/completion/complete_scope.rs delete mode 100644 crates/ra_analysis/src/completion/reference_completion.rs (limited to 'crates/ra_analysis/src/completion') diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs index d04503e46..8374ec346 100644 --- a/crates/ra_analysis/src/completion/complete_path.rs +++ b/crates/ra_analysis/src/completion/complete_path.rs @@ -9,8 +9,8 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &SyntaxContext) -> Cance _ => return Ok(()), }; let def_id = match module.resolve_path(ctx.db, path)? { - None => return Ok(()), Some(it) => it, + None => return Ok(()), }; let target_module = match def_id.resolve(ctx.db)? { hir::Def::Module(it) => it, diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs new file mode 100644 index 000000000..4ffd63016 --- /dev/null +++ b/crates/ra_analysis/src/completion/complete_scope.rs @@ -0,0 +1,171 @@ +use rustc_hash::FxHashSet; +use ra_syntax::TextUnit; + +use crate::{ + completion::{CompletionItem, Completions, CompletionKind::*, SyntaxContext}, + Cancelable +}; + +pub(super) fn complete_scope(acc: &mut Completions, ctx: &SyntaxContext) -> Cancelable<()> { + if !ctx.is_trivial_path { + return Ok(()); + } + if let Some(fn_def) = ctx.enclosing_fn { + let scopes = hir::FnScopes::new(fn_def); + complete_fn(acc, &scopes, ctx.offset); + } + + if let Some(module) = &ctx.module { + let module_scope = module.scope(ctx.db)?; + module_scope + .entries() + .filter(|(_name, res)| { + // Don't expose this item + match res.import { + None => true, + Some(import) => { + let range = import.range(ctx.db, module.source().file_id()); + !range.is_subrange(&ctx.leaf.range()) + } + } + }) + .for_each(|(name, _res)| { + CompletionItem::new(name.to_string()) + .kind(Reference) + .add_to(acc) + }); + } + + Ok(()) +} + +fn complete_fn(acc: &mut Completions, scopes: &hir::FnScopes, offset: TextUnit) { + let mut shadowed = FxHashSet::default(); + scopes + .scope_chain_for_offset(offset) + .flat_map(|scope| scopes.entries(scope).iter()) + .filter(|entry| shadowed.insert(entry.name())) + .for_each(|entry| { + CompletionItem::new(entry.name().to_string()) + .kind(Reference) + .add_to(acc) + }); + if scopes.self_param.is_some() { + CompletionItem::new("self").kind(Reference).add_to(acc); + } +} + +#[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 test_completion_let_scope() { + check_reference_completion( + r" + fn quux(x: i32) { + let y = 92; + 1 + <|>; + let z = (); + } + ", + "y;x;quux", + ); + } + + #[test] + fn test_completion_if_let_scope() { + check_reference_completion( + r" + fn quux() { + if let Some(x) = foo() { + let y = 92; + }; + if let Some(a) = bar() { + let b = 62; + 1 + <|> + } + } + ", + "b;a;quux", + ); + } + + #[test] + fn test_completion_for_scope() { + check_reference_completion( + r" + fn quux() { + for x in &[1, 2, 3] { + <|> + } + } + ", + "x;quux", + ); + } + + #[test] + fn test_completion_mod_scope() { + check_reference_completion( + r" + struct Foo; + enum Baz {} + fn quux() { + <|> + } + ", + "quux;Foo;Baz", + ); + } + + #[test] + fn test_completion_mod_scope_nested() { + check_reference_completion( + r" + struct Foo; + mod m { + struct Bar; + fn quux() { <|> } + } + ", + "quux;Bar", + ); + } + + #[test] + fn test_complete_type() { + check_reference_completion( + r" + struct Foo; + fn x() -> <|> + ", + "Foo;x", + ) + } + + #[test] + fn test_complete_shadowing() { + check_reference_completion( + r" + fn foo() -> { + let bar = 92; + { + let bar = 62; + <|> + } + } + ", + "bar;foo", + ) + } + + #[test] + fn test_complete_self() { + check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self") + } +} diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs deleted file mode 100644 index 459ed8f6f..000000000 --- a/crates/ra_analysis/src/completion/reference_completion.rs +++ /dev/null @@ -1,307 +0,0 @@ -use rustc_hash::FxHashSet; -use ra_syntax::{ - SourceFileNode, AstNode, - ast, - SyntaxKind::*, -}; -use hir::{ - self, - FnScopes, Path -}; - -use crate::{ - db::RootDatabase, - completion::{CompletionItem, Completions, CompletionKind::*}, - Cancelable -}; - -pub(super) fn completions( - acc: &mut Completions, - db: &RootDatabase, - module: &hir::Module, - _file: &SourceFileNode, - name_ref: ast::NameRef, -) -> Cancelable<()> { - let kind = match classify_name_ref(name_ref) { - Some(it) => it, - None => return Ok(()), - }; - - match kind { - NameRefKind::LocalRef { enclosing_fn } => { - if let Some(fn_def) = enclosing_fn { - let scopes = FnScopes::new(fn_def); - complete_fn(name_ref, &scopes, acc); - } - - let module_scope = module.scope(db)?; - module_scope - .entries() - .filter(|(_name, res)| { - // Don't expose this item - match res.import { - None => true, - Some(import) => { - let range = import.range(db, module.source().file_id()); - !range.is_subrange(&name_ref.syntax().range()) - } - } - }) - .for_each(|(name, _res)| { - CompletionItem::new(name.to_string()) - .kind(Reference) - .add_to(acc) - }); - } - NameRefKind::Path(_) => (), - NameRefKind::BareIdentInMod => (), - } - Ok(()) -} - -enum NameRefKind<'a> { - /// NameRef is a part of single-segment path, for example, a refernece to a - /// local variable. - LocalRef { - enclosing_fn: Option>, - }, - /// NameRef is the last segment in some path - Path(Path), - /// NameRef is bare identifier at the module's root. - /// Used for keyword completion - BareIdentInMod, -} - -fn classify_name_ref(name_ref: ast::NameRef) -> Option { - let name_range = name_ref.syntax().range(); - let top_node = name_ref - .syntax() - .ancestors() - .take_while(|it| it.range() == name_range) - .last() - .unwrap(); - match top_node.parent().map(|it| it.kind()) { - Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod), - _ => (), - } - - let parent = name_ref.syntax().parent()?; - 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 path.qualifier().is_none() { - let enclosing_fn = name_ref - .syntax() - .ancestors() - .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) - .find_map(ast::FnDef::cast); - return Some(NameRefKind::LocalRef { enclosing_fn }); - } - } - None -} - -fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Completions) { - let mut shadowed = FxHashSet::default(); - scopes - .scope_chain(name_ref.syntax()) - .flat_map(|scope| scopes.entries(scope).iter()) - .filter(|entry| shadowed.insert(entry.name())) - .for_each(|entry| { - CompletionItem::new(entry.name().to_string()) - .kind(Reference) - .add_to(acc) - }); - if scopes.self_param.is_some() { - CompletionItem::new("self").kind(Reference).add_to(acc); - } -} - -#[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 test_completion_let_scope() { - check_reference_completion( - r" - fn quux(x: i32) { - let y = 92; - 1 + <|>; - let z = (); - } - ", - "y;x;quux", - ); - } - - #[test] - fn test_completion_if_let_scope() { - check_reference_completion( - r" - fn quux() { - if let Some(x) = foo() { - let y = 92; - }; - if let Some(a) = bar() { - let b = 62; - 1 + <|> - } - } - ", - "b;a;quux", - ); - } - - #[test] - fn test_completion_for_scope() { - check_reference_completion( - r" - fn quux() { - for x in &[1, 2, 3] { - <|> - } - } - ", - "x;quux", - ); - } - - #[test] - fn test_completion_mod_scope() { - check_reference_completion( - r" - struct Foo; - enum Baz {} - fn quux() { - <|> - } - ", - "quux;Foo;Baz", - ); - } - - #[test] - fn test_completion_mod_scope_no_self_use() { - check_reference_completion( - r" - use foo<|>; - ", - "", - ); - } - - #[test] - fn test_completion_self_path() { - check_reference_completion( - r" - use self::m::<|>; - - mod m { - struct Bar; - } - ", - "Bar", - ); - } - - #[test] - fn test_completion_mod_scope_nested() { - check_reference_completion( - r" - struct Foo; - mod m { - struct Bar; - fn quux() { <|> } - } - ", - "quux;Bar", - ); - } - - #[test] - fn test_complete_type() { - check_reference_completion( - r" - struct Foo; - fn x() -> <|> - ", - "Foo;x", - ) - } - - #[test] - fn test_complete_shadowing() { - check_reference_completion( - r" - fn foo() -> { - let bar = 92; - { - let bar = 62; - <|> - } - } - ", - "bar;foo", - ) - } - - #[test] - fn test_complete_self() { - check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self") - } - - #[test] - fn test_complete_crate_path() { - check_reference_completion( - " - //- /lib.rs - mod foo; - struct Spam; - //- /foo.rs - use crate::Sp<|> - ", - "Spam;foo", - ); - } - - #[test] - fn test_complete_crate_path_with_braces() { - check_reference_completion( - " - //- /lib.rs - mod foo; - struct Spam; - //- /foo.rs - use crate::{Sp<|>}; - ", - "Spam;foo", - ); - } - - #[test] - fn test_complete_crate_path_in_nested_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", - ); - } -} -- cgit v1.2.3