From 1894db49b1e4c3200e9f561feb7f9891e54e1453 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 28 May 2021 03:20:55 +0200 Subject: Complete keywords in (Assoc)ItemList with leading attribute --- crates/ide_completion/src/completions/keyword.rs | 15 +++++++++++++++ crates/ide_completion/src/patterns.rs | 22 ++++++++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) (limited to 'crates/ide_completion/src') 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 @@ -394,6 +394,21 @@ fn quux() -> i32 { ); } + #[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( 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 { } pub(crate) fn determine_location(tok: SyntaxToken) -> Option { - // 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 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() { -- cgit v1.2.3