From: Aleksey Kladov Date: Sun, 24 Feb 2019 20:49:47 +0000 (+0300) Subject: complete patterns X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=330ce2e26baf6f9ebf808e16140f6ad9c9b05d5e;p=rust.git complete patterns --- diff --git a/crates/ra_ide_api/src/completion.rs b/crates/ra_ide_api/src/completion.rs index fbfd7e3e728..c8022f94fd5 100644 --- a/crates/ra_ide_api/src/completion.rs +++ b/crates/ra_ide_api/src/completion.rs @@ -4,6 +4,7 @@ mod complete_dot; mod complete_struct_literal; +mod complete_pattern; mod complete_fn_param; mod complete_keyword; mod complete_snippet; @@ -65,6 +66,7 @@ pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Opti complete_scope::complete_scope(&mut acc, &ctx); complete_dot::complete_dot(&mut acc, &ctx); complete_struct_literal::complete_struct_literal(&mut acc, &ctx); + complete_pattern::complete_pattern(&mut acc, &ctx); complete_postfix::complete_postfix(&mut acc, &ctx); Some(acc) } diff --git a/crates/ra_ide_api/src/completion/complete_pattern.rs b/crates/ra_ide_api/src/completion/complete_pattern.rs new file mode 100644 index 00000000000..3cf79c080fb --- /dev/null +++ b/crates/ra_ide_api/src/completion/complete_pattern.rs @@ -0,0 +1,87 @@ +use crate::completion::{CompletionContext, Completions}; + +/// Completes constats and paths in patterns. +pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { + if !ctx.is_pat_binding { + return; + } + // TODO: ideally, we should look at the type we are matching against and + // suggest variants + auto-imports + let names = ctx.resolver.all_names(ctx.db); + for (name, res) in names.into_iter() { + let r = res.as_ref(); + let def = match r.take_types().or(r.take_values()) { + Some(hir::Resolution::Def(def)) => def, + _ => continue, + }; + match def { + hir::ModuleDef::Enum(..) + | hir::ModuleDef::EnumVariant(..) + | hir::ModuleDef::Const(..) + | hir::ModuleDef::Module(..) => (), + _ => continue, + } + acc.add_resolution(ctx, name.to_string(), &res) + } +} + +#[cfg(test)] +mod tests { + use insta::assert_debug_snapshot_matches; + use crate::completion::{CompletionItem, CompletionKind, do_completion}; + + fn complete(code: &str) -> Vec { + do_completion(code, CompletionKind::Reference) + } + + #[test] + fn completes_enum_variants_and_modules() { + let completions = complete( + r" + enum E { X } + use self::E::X; + const Z: E = E::X; + mod m {} + + static FOO: E = E::X; + struct Bar { f: u32 } + + fn foo() { + match E::X { + <|> + } + } + ", + ); + assert_debug_snapshot_matches!(completions, @r###"[ + CompletionItem { + label: "E", + source_range: [246; 246), + delete: [246; 246), + insert: "E", + kind: Enum + }, + CompletionItem { + label: "X", + source_range: [246; 246), + delete: [246; 246), + insert: "X", + kind: EnumVariant + }, + CompletionItem { + label: "Z", + source_range: [246; 246), + delete: [246; 246), + insert: "Z", + kind: Const + }, + CompletionItem { + label: "m", + source_range: [246; 246), + delete: [246; 246), + insert: "m", + kind: Module + } +]"###); + } +} diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index d351be054b4..724d0dfbf56 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs @@ -23,6 +23,9 @@ pub(crate) struct CompletionContext<'a> { pub(super) use_item_syntax: Option<&'a ast::UseItem>, pub(super) struct_lit_syntax: Option<&'a ast::StructLit>, pub(super) is_param: bool, + /// If a name-binding or reference to a const in a pattern. + /// Irrefutable patterns (like let) are excluded. + pub(super) is_pat_binding: bool, /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. pub(super) is_trivial_path: bool, /// If not a trivial, path, the prefix (qualifier). @@ -58,6 +61,7 @@ pub(super) fn new( use_item_syntax: None, struct_lit_syntax: None, is_param: false, + is_pat_binding: false, is_trivial_path: false, path_prefix: None, after_if: false, @@ -102,12 +106,22 @@ fn fill(&mut self, original_file: &'a SourceFile, offset: TextUnit) { // Otherwise, see if this is a declaration. We can use heuristics to // suggest declaration names, see `CompletionKind::Magic`. if let Some(name) = find_node_at_offset::(file.syntax(), offset) { + if is_node::(name.syntax()) { + let bind_pat = name.syntax().ancestors().find_map(ast::BindPat::cast).unwrap(); + let parent = bind_pat.syntax().parent(); + if parent.and_then(ast::MatchArm::cast).is_some() + || parent.and_then(ast::Condition::cast).is_some() + { + self.is_pat_binding = true; + } + } if is_node::(name.syntax()) { self.is_param = true; return; } } } + fn classify_name_ref(&mut self, original_file: &'a SourceFile, name_ref: &ast::NameRef) { let name_range = name_ref.syntax().range(); if name_ref.syntax().parent().and_then(ast::NamedField::cast).is_some() {