aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion')
-rw-r--r--crates/ide_completion/src/completions/keyword.rs15
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs30
-rw-r--r--crates/ide_completion/src/context.rs4
-rw-r--r--crates/ide_completion/src/patterns.rs29
-rw-r--r--crates/ide_completion/src/render/macro_.rs6
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
3use hir::ScopeDef; 3use hir::ScopeDef;
4use syntax::AstNode;
5 4
6use crate::{CompletionContext, Completions}; 5use 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#"
75use foo$0 75use f$0
76use std::collections; 76
77struct Foo;
78mod 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)]
16pub(crate) enum ImmediateLocation { 16pub(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
26pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { 27pub(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]
107fn test_has_use_parent() {
108 check_location(r"use f$0", ImmediateLocation::Use);
109}
110
111#[test]
91fn test_has_impl_parent() { 112fn 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