]> git.lizzy.rs Git - rust.git/commitdiff
Complete keywords in (Assoc)ItemList with leading attribute
authorLukas Wirth <lukastw97@gmail.com>
Fri, 28 May 2021 01:20:55 +0000 (03:20 +0200)
committerLukas Wirth <lukastw97@gmail.com>
Fri, 28 May 2021 01:20:55 +0000 (03:20 +0200)
crates/ide_completion/src/completions/keyword.rs
crates/ide_completion/src/patterns.rs

index 96447a603cb7f38d7b2946a15d484137faae3fa3..c9673df85d2d8ee7aaf4d8ea56f41dc86311a637 100644 (file)
@@ -394,6 +394,21 @@ fn test_keywords_in_impl_def() {
         );
     }
 
+    #[test]
+    fn test_keywords_in_impl_def_with_attr() {
+        check(
+            r"impl My { #[foo] $0 }",
+            expect![[r#"
+                kw fn
+                kw const
+                kw type
+                kw unsafe
+                kw pub(crate)
+                kw pub
+            "#]],
+        );
+    }
+
     #[test]
     fn test_keywords_in_loop() {
         check(
index 7bae7d12c37d916be86eec2ee6af5757a1e7453b..c8a88367d784fab96b2f1afc762661b0acb6fd26 100644 (file)
@@ -25,9 +25,10 @@ pub(crate) enum ImmediateLocation {
 }
 
 pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> {
-    // First "expand" the element we are completing to its maximum so that we can check in what
-    // context it immediately lies. This for example means if the token is a NameRef at the end of
-    // a path, we want to look at where the path is in the tree.
+    // First walk the element we are completing up to its highest node that has the same text range
+    // as the element so that we can check in what context it immediately lies. We only do this for
+    // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically.
+    // We only wanna do this if the NameRef is the last segment of the path.
     let node = match tok.parent().and_then(ast::NameLike::cast)? {
         ast::NameLike::NameRef(name_ref) => {
             if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
@@ -47,7 +48,20 @@ pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation>
         it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(),
     };
     let parent = match node.parent() {
-        Some(parent) => parent,
+        Some(parent) => match ast::MacroCall::cast(parent.clone()) {
+            // When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call.
+            // This is usually fine as the node expansion code above already accounts for that with
+            // the ancestors call, but there is one exception to this which is that when an attribute
+            // precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ.
+            Some(call)
+                if call.excl_token().is_none()
+                    && call.token_tree().is_none()
+                    && call.semicolon_token().is_none() =>
+            {
+                call.syntax().parent()?
+            }
+            _ => parent,
+        },
         // SourceFile
         None => {
             return match node.kind() {