aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorLukas Wirth <[email protected]>2021-05-28 02:20:55 +0100
committerLukas Wirth <[email protected]>2021-05-28 02:20:55 +0100
commit1894db49b1e4c3200e9f561feb7f9891e54e1453 (patch)
tree8c5bab7e516a4cd1f326faac127240d0678ad624 /crates
parent9e71dd9799879fc9070f8717f8711fba5dae490a (diff)
Complete keywords in (Assoc)ItemList with leading attribute
Diffstat (limited to 'crates')
-rw-r--r--crates/ide_completion/src/completions/keyword.rs15
-rw-r--r--crates/ide_completion/src/patterns.rs22
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
27pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { 27pub(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() {