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/completions/unqualified_path.rs | 30 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 4 | ||||
-rw-r--r-- | crates/ide_completion/src/patterns.rs | 29 | ||||
-rw-r--r-- | crates/ide_completion/src/render/macro_.rs | 6 |
5 files changed, 65 insertions, 19 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/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index c901b358b..ede07f605 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use hir::ScopeDef; | 3 | use hir::ScopeDef; |
4 | use syntax::AstNode; | ||
5 | 4 | ||
6 | use crate::{CompletionContext, Completions}; | 5 | use crate::{CompletionContext, Completions}; |
7 | 6 | ||
@@ -24,6 +23,15 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
24 | return; | 23 | return; |
25 | } | 24 | } |
26 | 25 | ||
26 | if ctx.expects_use_tree() { | ||
27 | cov_mark::hit!(only_completes_modules_in_import); | ||
28 | ctx.scope.process_all_names(&mut |name, res| { | ||
29 | if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { | ||
30 | acc.add_resolution(ctx, name.to_string(), &res); | ||
31 | } | ||
32 | }); | ||
33 | return; | ||
34 | } | ||
27 | if let Some(hir::Adt::Enum(e)) = | 35 | if let Some(hir::Adt::Enum(e)) = |
28 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) | 36 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
29 | { | 37 | { |
@@ -37,14 +45,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
37 | cov_mark::hit!(skip_lifetime_completion); | 45 | cov_mark::hit!(skip_lifetime_completion); |
38 | return; | 46 | return; |
39 | } | 47 | } |
40 | if ctx.use_item_syntax.is_some() { | ||
41 | if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { | ||
42 | if name_ref.syntax().text() == name.to_string().as_str() { | ||
43 | cov_mark::hit!(self_fulfilling_completion); | ||
44 | return; | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | acc.add_resolution(ctx, name.to_string(), &res); | 48 | acc.add_resolution(ctx, name.to_string(), &res); |
49 | }); | 49 | }); |
50 | } | 50 | } |
@@ -68,15 +68,17 @@ mod tests { | |||
68 | } | 68 | } |
69 | 69 | ||
70 | #[test] | 70 | #[test] |
71 | fn self_fulfilling_completion() { | 71 | fn only_completes_modules_in_import() { |
72 | cov_mark::check!(self_fulfilling_completion); | 72 | cov_mark::check!(only_completes_modules_in_import); |
73 | check( | 73 | check( |
74 | r#" | 74 | r#" |
75 | use foo$0 | 75 | use f$0 |
76 | use std::collections; | 76 | |
77 | struct Foo; | ||
78 | mod foo {} | ||
77 | "#, | 79 | "#, |
78 | expect![[r#" | 80 | expect![[r#" |
79 | ?? collections | 81 | md foo |
80 | "#]], | 82 | "#]], |
81 | ); | 83 | ); |
82 | } | 84 | } |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index fbef54408..923e35dbb 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -276,6 +276,10 @@ impl<'a> CompletionContext<'a> { | |||
276 | ) | 276 | ) |
277 | } | 277 | } |
278 | 278 | ||
279 | pub(crate) fn expects_use_tree(&self) -> bool { | ||
280 | matches!(self.completion_location, Some(ImmediateLocation::Use)) | ||
281 | } | ||
282 | |||
279 | pub(crate) fn expects_non_trait_assoc_item(&self) -> bool { | 283 | pub(crate) fn expects_non_trait_assoc_item(&self) -> bool { |
280 | matches!(self.completion_location, Some(ImmediateLocation::Impl)) | 284 | matches!(self.completion_location, Some(ImmediateLocation::Impl)) |
281 | } | 285 | } |
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 19e42ba43..c8a88367d 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs | |||
@@ -14,6 +14,7 @@ use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applic | |||
14 | /// Direct parent container of the cursor position | 14 | /// Direct parent container of the cursor position |
15 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | 15 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
16 | pub(crate) enum ImmediateLocation { | 16 | pub(crate) enum ImmediateLocation { |
17 | Use, | ||
17 | Impl, | 18 | Impl, |
18 | Trait, | 19 | Trait, |
19 | RecordField, | 20 | RecordField, |
@@ -24,9 +25,10 @@ pub(crate) enum ImmediateLocation { | |||
24 | } | 25 | } |
25 | 26 | ||
26 | pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { | 27 | pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { |
27 | // 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 |
28 | // 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 |
29 | // 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. | ||
30 | let node = match tok.parent().and_then(ast::NameLike::cast)? { | 32 | let node = match tok.parent().and_then(ast::NameLike::cast)? { |
31 | ast::NameLike::NameRef(name_ref) => { | 33 | ast::NameLike::NameRef(name_ref) => { |
32 | 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) { |
@@ -46,7 +48,20 @@ pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> | |||
46 | it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(), | 48 | it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(), |
47 | }; | 49 | }; |
48 | let parent = match node.parent() { | 50 | let parent = match node.parent() { |
49 | 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 | }, | ||
50 | // SourceFile | 65 | // SourceFile |
51 | None => { | 66 | None => { |
52 | return match node.kind() { | 67 | return match node.kind() { |
@@ -58,6 +73,7 @@ pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> | |||
58 | let res = match_ast! { | 73 | let res = match_ast! { |
59 | match parent { | 74 | match parent { |
60 | ast::IdentPat(_it) => ImmediateLocation::IdentPat, | 75 | ast::IdentPat(_it) => ImmediateLocation::IdentPat, |
76 | ast::Use(_it) => ImmediateLocation::Use, | ||
61 | ast::BlockExpr(_it) => ImmediateLocation::BlockExpr, | 77 | ast::BlockExpr(_it) => ImmediateLocation::BlockExpr, |
62 | ast::SourceFile(_it) => ImmediateLocation::ItemList, | 78 | ast::SourceFile(_it) => ImmediateLocation::ItemList, |
63 | ast::ItemList(_it) => ImmediateLocation::ItemList, | 79 | ast::ItemList(_it) => ImmediateLocation::ItemList, |
@@ -88,6 +104,11 @@ fn test_has_trait_parent() { | |||
88 | } | 104 | } |
89 | 105 | ||
90 | #[test] | 106 | #[test] |
107 | fn test_has_use_parent() { | ||
108 | check_location(r"use f$0", ImmediateLocation::Use); | ||
109 | } | ||
110 | |||
111 | #[test] | ||
91 | fn test_has_impl_parent() { | 112 | fn test_has_impl_parent() { |
92 | check_location(r"impl A { f$0 }", ImmediateLocation::Impl); | 113 | check_location(r"impl A { f$0 }", ImmediateLocation::Impl); |
93 | } | 114 | } |
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs index 7578ad50b..b90fd3890 100644 --- a/crates/ide_completion/src/render/macro_.rs +++ b/crates/ide_completion/src/render/macro_.rs | |||
@@ -74,7 +74,11 @@ impl<'a> MacroRender<'a> { | |||
74 | if self.needs_bang() && self.ctx.snippet_cap().is_some() { | 74 | if self.needs_bang() && self.ctx.snippet_cap().is_some() { |
75 | format!("{}!{}…{}", self.name, self.bra, self.ket) | 75 | format!("{}!{}…{}", self.name, self.bra, self.ket) |
76 | } else { | 76 | } else { |
77 | self.banged_name() | 77 | if self.macro_.kind() == hir::MacroKind::Derive { |
78 | self.name.to_string() | ||
79 | } else { | ||
80 | self.banged_name() | ||
81 | } | ||
78 | } | 82 | } |
79 | } | 83 | } |
80 | 84 | ||