diff options
Diffstat (limited to 'crates/ide_completion')
-rw-r--r-- | crates/ide_completion/src/completions/keyword.rs | 15 | ||||
-rw-r--r-- | crates/ide_completion/src/patterns.rs | 22 |
2 files changed, 33 insertions, 4 deletions
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 96447a603..c9673df85 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -395,6 +395,21 @@ fn quux() -> i32 { | |||
395 | } | 395 | } |
396 | 396 | ||
397 | #[test] | 397 | #[test] |
398 | fn test_keywords_in_impl_def_with_attr() { | ||
399 | check( | ||
400 | r"impl My { #[foo] $0 }", | ||
401 | expect![[r#" | ||
402 | kw fn | ||
403 | kw const | ||
404 | kw type | ||
405 | kw unsafe | ||
406 | kw pub(crate) | ||
407 | kw pub | ||
408 | "#]], | ||
409 | ); | ||
410 | } | ||
411 | |||
412 | #[test] | ||
398 | fn test_keywords_in_loop() { | 413 | fn test_keywords_in_loop() { |
399 | check( | 414 | check( |
400 | r"fn my() { loop { $0 } }", | 415 | r"fn my() { loop { $0 } }", |
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 7bae7d12c..c8a88367d 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs | |||
@@ -25,9 +25,10 @@ pub(crate) enum ImmediateLocation { | |||
25 | } | 25 | } |
26 | 26 | ||
27 | pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { | 27 | pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { |
28 | // First "expand" the element we are completing to its maximum so that we can check in what | 28 | // First walk the element we are completing up to its highest node that has the same text range |
29 | // context it immediately lies. This for example means if the token is a NameRef at the end of | 29 | // as the element so that we can check in what context it immediately lies. We only do this for |
30 | // a path, we want to look at where the path is in the tree. | 30 | // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically. |
31 | // We only wanna do this if the NameRef is the last segment of the path. | ||
31 | let node = match tok.parent().and_then(ast::NameLike::cast)? { | 32 | let node = match tok.parent().and_then(ast::NameLike::cast)? { |
32 | ast::NameLike::NameRef(name_ref) => { | 33 | ast::NameLike::NameRef(name_ref) => { |
33 | if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) { | 34 | 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> | |||
47 | it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(), | 48 | it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(), |
48 | }; | 49 | }; |
49 | let parent = match node.parent() { | 50 | let parent = match node.parent() { |
50 | Some(parent) => parent, | 51 | Some(parent) => match ast::MacroCall::cast(parent.clone()) { |
52 | // When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call. | ||
53 | // This is usually fine as the node expansion code above already accounts for that with | ||
54 | // the ancestors call, but there is one exception to this which is that when an attribute | ||
55 | // precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ. | ||
56 | Some(call) | ||
57 | if call.excl_token().is_none() | ||
58 | && call.token_tree().is_none() | ||
59 | && call.semicolon_token().is_none() => | ||
60 | { | ||
61 | call.syntax().parent()? | ||
62 | } | ||
63 | _ => parent, | ||
64 | }, | ||
51 | // SourceFile | 65 | // SourceFile |
52 | None => { | 66 | None => { |
53 | return match node.kind() { | 67 | return match node.kind() { |