]> git.lizzy.rs Git - rust.git/blobdiff - crates/ra_ide_api/src/completion/completion_context.rs
complete patterns
[rust.git] / crates / ra_ide_api / src / completion / completion_context.rs
index ca2069e2a74dfe4224d4838fc708fb6b77a0c6cf..724d0dfbf5606c068957736ee9051e3b39f4f39b 100644 (file)
@@ -5,7 +5,7 @@
     algo::{find_leaf_at_offset, find_covering_node, find_node_at_offset},
     SyntaxKind::*,
 };
-use hir::source_binder;
+use hir::{source_binder, Resolver};
 
 use crate::{db, FilePosition};
 
@@ -16,11 +16,16 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) db: &'a db::RootDatabase,
     pub(super) offset: TextUnit,
     pub(super) leaf: &'a SyntaxNode,
+    pub(super) resolver: Resolver,
     pub(super) module: Option<hir::Module>,
     pub(super) function: Option<hir::Function>,
     pub(super) function_syntax: Option<&'a ast::FnDef>,
     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).
@@ -42,17 +47,21 @@ pub(super) fn new(
         original_file: &'a SourceFile,
         position: FilePosition,
     ) -> Option<CompletionContext<'a>> {
+        let resolver = source_binder::resolver_for_position(db, position);
         let module = source_binder::module_from_position(db, position);
         let leaf = find_leaf_at_offset(original_file.syntax(), position.offset).left_biased()?;
         let mut ctx = CompletionContext {
             db,
             leaf,
             offset: position.offset,
+            resolver,
             module,
             function: None,
             function_syntax: None,
             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,
@@ -97,20 +106,30 @@ 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::<ast::Name>(file.syntax(), offset) {
+            if is_node::<ast::BindPat>(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::<ast::Param>(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();
-        let top_node = name_ref
-            .syntax()
-            .ancestors()
-            .take_while(|it| it.range() == name_range)
-            .last()
-            .unwrap();
+        if name_ref.syntax().parent().and_then(ast::NamedField::cast).is_some() {
+            self.struct_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset);
+        }
+
+        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) => {
@@ -127,12 +146,9 @@ fn classify_name_ref(&mut self, original_file: &'a SourceFile, name_ref: &ast::N
             .ancestors()
             .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
             .find_map(ast::FnDef::cast);
-        match (&self.module, self.function_syntax) {
-            (Some(module), Some(fn_def)) => {
-                let function = source_binder::function_from_module(self.db, module, fn_def);
-                self.function = Some(function);
-            }
-            _ => (),
+        if let (Some(module), Some(fn_def)) = (self.module, self.function_syntax) {
+            let function = source_binder::function_from_module(self.db, module, fn_def);
+            self.function = Some(function);
         }
 
         let parent = match name_ref.syntax().parent() {
@@ -141,6 +157,13 @@ fn classify_name_ref(&mut self, original_file: &'a SourceFile, name_ref: &ast::N
         };
         if let Some(segment) = ast::PathSegment::cast(parent) {
             let path = segment.parent_path();
+            self.is_call = path
+                .syntax()
+                .parent()
+                .and_then(ast::PathExpr::cast)
+                .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
+                .is_some();
+
             if let Some(mut path) = hir::Path::from_ast(path) {
                 if !path.is_ident() {
                     path.segments.pop().unwrap();
@@ -180,12 +203,6 @@ fn classify_name_ref(&mut self, original_file: &'a SourceFile, name_ref: &ast::N
                     }
                 }
             }
-            self.is_call = path
-                .syntax()
-                .parent()
-                .and_then(ast::PathExpr::cast)
-                .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
-                .is_some()
         }
         if let Some(field_expr) = ast::FieldExpr::cast(parent) {
             // The receiver comes before the point of insertion of the fake