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.rs29
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs93
-rw-r--r--crates/ide_completion/src/completions/lifetime.rs316
-rw-r--r--crates/ide_completion/src/completions/pattern.rs2
-rw-r--r--crates/ide_completion/src/context.rs90
-rw-r--r--crates/ide_completion/src/lib.rs29
-rw-r--r--crates/ide_completion/src/patterns.rs2
-rw-r--r--crates/ide_completion/src/render.rs6
-rw-r--r--crates/ide_completion/src/render/macro_.rs2
9 files changed, 521 insertions, 48 deletions
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index 09882c4f3..cdac4e41a 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -2,25 +2,27 @@
2 2
3pub(crate) mod attribute; 3pub(crate) mod attribute;
4pub(crate) mod dot; 4pub(crate) mod dot;
5pub(crate) mod record; 5pub(crate) mod flyimport;
6pub(crate) mod pattern;
7pub(crate) mod fn_param; 6pub(crate) mod fn_param;
8pub(crate) mod keyword; 7pub(crate) mod keyword;
9pub(crate) mod snippet; 8pub(crate) mod lifetime;
10pub(crate) mod qualified_path;
11pub(crate) mod unqualified_path;
12pub(crate) mod postfix;
13pub(crate) mod macro_in_item_position; 9pub(crate) mod macro_in_item_position;
14pub(crate) mod trait_impl;
15pub(crate) mod mod_; 10pub(crate) mod mod_;
16pub(crate) mod flyimport; 11pub(crate) mod pattern;
12pub(crate) mod postfix;
13pub(crate) mod qualified_path;
14pub(crate) mod record;
15pub(crate) mod snippet;
16pub(crate) mod trait_impl;
17pub(crate) mod unqualified_path;
17 18
18use std::iter; 19use std::iter;
19 20
20use hir::{known, ModPath, ScopeDef, Type}; 21use hir::{known, ModPath, ScopeDef, Type};
22use ide_db::SymbolKind;
21 23
22use crate::{ 24use crate::{
23 item::Builder, 25 item::{Builder, CompletionKind},
24 render::{ 26 render::{
25 const_::render_const, 27 const_::render_const,
26 enum_variant::render_variant, 28 enum_variant::render_variant,
@@ -31,7 +33,7 @@ use crate::{
31 type_alias::render_type_alias, 33 type_alias::render_type_alias,
32 RenderContext, 34 RenderContext,
33 }, 35 },
34 CompletionContext, CompletionItem, 36 CompletionContext, CompletionItem, CompletionItemKind,
35}; 37};
36 38
37/// Represents an in-progress set of completions being built. 39/// Represents an in-progress set of completions being built.
@@ -77,6 +79,13 @@ impl Completions {
77 self.add(item); 79 self.add(item);
78 } 80 }
79 81
82 pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) {
83 let mut item =
84 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static");
85 item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam));
86 self.add(item.build());
87 }
88
80 pub(crate) fn add_resolution( 89 pub(crate) fn add_resolution(
81 &mut self, 90 &mut self,
82 ctx: &CompletionContext, 91 ctx: &CompletionContext,
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 08df2df3f..1ad017198 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -1,8 +1,10 @@
1//! Feature: completion with imports-on-the-fly 1//! Feature: completion with imports-on-the-fly
2//! 2//!
3//! When completing names in the current scope, proposes additional imports from other modules or crates, 3//! When completing names in the current scope, proposes additional imports from other modules or crates,
4//! if they can be qualified in the scope and their name contains all symbols from the completion input 4//! if they can be qualified in the scope and their name contains all symbols from the completion input.
5//! (case-insensitive, in any order or places). 5//!
6//! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent.
7//! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the contaning is checked case-insensitively.
6//! 8//!
7//! ``` 9//! ```
8//! fn main() { 10//! fn main() {
@@ -942,9 +944,94 @@ mod foo {
942} 944}
943 945
944fn main() { 946fn main() {
945 bar::Ass$0 947 bar::ASS$0
946}"#, 948}"#,
947 expect![[]], 949 expect![[]],
948 ) 950 )
949 } 951 }
952
953 #[test]
954 fn unqualified_assoc_items_are_omitted() {
955 check(
956 r#"
957mod something {
958 pub trait BaseTrait {
959 fn test_function() -> i32;
960 }
961
962 pub struct Item1;
963 pub struct Item2;
964
965 impl BaseTrait for Item1 {
966 fn test_function() -> i32 {
967 1
968 }
969 }
970
971 impl BaseTrait for Item2 {
972 fn test_function() -> i32 {
973 2
974 }
975 }
976}
977
978fn main() {
979 test_f$0
980}"#,
981 expect![[]],
982 )
983 }
984
985 #[test]
986 fn case_matters() {
987 check(
988 r#"
989mod foo {
990 pub const TEST_CONST: usize = 3;
991 pub fn test_function() -> i32 {
992 4
993 }
994}
995
996fn main() {
997 TE$0
998}"#,
999 expect![[r#"
1000 ct foo::TEST_CONST
1001 "#]],
1002 );
1003
1004 check(
1005 r#"
1006mod foo {
1007 pub const TEST_CONST: usize = 3;
1008 pub fn test_function() -> i32 {
1009 4
1010 }
1011}
1012
1013fn main() {
1014 te$0
1015}"#,
1016 expect![[r#"
1017 ct foo::TEST_CONST
1018 fn test_function() (foo::test_function) fn() -> i32
1019 "#]],
1020 );
1021
1022 check(
1023 r#"
1024mod foo {
1025 pub const TEST_CONST: usize = 3;
1026 pub fn test_function() -> i32 {
1027 4
1028 }
1029}
1030
1031fn main() {
1032 Te$0
1033}"#,
1034 expect![[]],
1035 );
1036 }
950} 1037}
diff --git a/crates/ide_completion/src/completions/lifetime.rs b/crates/ide_completion/src/completions/lifetime.rs
new file mode 100644
index 000000000..5eeddf7a4
--- /dev/null
+++ b/crates/ide_completion/src/completions/lifetime.rs
@@ -0,0 +1,316 @@
1//! Completes lifetimes and labels.
2use hir::ScopeDef;
3
4use crate::{completions::Completions, context::CompletionContext};
5
6/// Completes lifetimes.
7pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) {
8 if !ctx.lifetime_allowed {
9 return;
10 }
11 let param_lifetime = match (
12 &ctx.lifetime_syntax,
13 ctx.lifetime_param_syntax.as_ref().and_then(|lp| lp.lifetime()),
14 ) {
15 (Some(lt), Some(lp)) if lp == lt.clone() => return,
16 (Some(_), Some(lp)) => Some(lp.to_string()),
17 _ => None,
18 };
19
20 ctx.scope.process_all_names(&mut |name, res| {
21 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
22 if param_lifetime != Some(name.to_string()) {
23 acc.add_resolution(ctx, name.to_string(), &res);
24 }
25 }
26 });
27 if param_lifetime.is_none() {
28 acc.add_static_lifetime(ctx);
29 }
30}
31
32/// Completes labels.
33pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
34 if !ctx.is_label_ref {
35 return;
36 }
37 ctx.scope.process_all_names(&mut |name, res| {
38 if let ScopeDef::Label(_) = res {
39 acc.add_resolution(ctx, name.to_string(), &res);
40 }
41 });
42}
43
44#[cfg(test)]
45mod tests {
46 use expect_test::{expect, Expect};
47
48 use crate::{
49 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG},
50 CompletionConfig, CompletionKind,
51 };
52
53 fn check(ra_fixture: &str, expect: Expect) {
54 check_with_config(TEST_CONFIG, ra_fixture, expect);
55 }
56
57 fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) {
58 let actual = completion_list_with_config(config, ra_fixture, CompletionKind::Reference);
59 expect.assert_eq(&actual)
60 }
61
62 #[test]
63 fn check_lifetime_edit() {
64 check_edit(
65 "'lifetime",
66 r#"
67fn func<'lifetime>(foo: &'li$0) {}
68"#,
69 r#"
70fn func<'lifetime>(foo: &'lifetime) {}
71"#,
72 );
73 cov_mark::check!(completes_if_lifetime_without_idents);
74 check_edit(
75 "'lifetime",
76 r#"
77fn func<'lifetime>(foo: &'$0) {}
78"#,
79 r#"
80fn func<'lifetime>(foo: &'lifetime) {}
81"#,
82 );
83 }
84
85 #[test]
86 fn complete_lifetime_in_ref() {
87 check(
88 r#"
89fn foo<'lifetime>(foo: &'a$0 usize) {}
90"#,
91 expect![[r#"
92 lt 'lifetime
93 lt 'static
94 "#]],
95 );
96 }
97
98 #[test]
99 fn complete_lifetime_in_ref_missing_ty() {
100 check(
101 r#"
102fn foo<'lifetime>(foo: &'a$0) {}
103"#,
104 expect![[r#"
105 lt 'lifetime
106 lt 'static
107 "#]],
108 );
109 }
110 #[test]
111 fn complete_lifetime_in_self_ref() {
112 check(
113 r#"
114struct Foo;
115impl<'impl> Foo {
116 fn foo<'func>(&'a$0 self) {}
117}
118"#,
119 expect![[r#"
120 lt 'func
121 lt 'impl
122 lt 'static
123 "#]],
124 );
125 }
126
127 #[test]
128 fn complete_lifetime_in_arg_list() {
129 check(
130 r#"
131struct Foo<'lt>;
132fn foo<'lifetime>(_: Foo<'a$0>) {}
133"#,
134 expect![[r#"
135 lt 'lifetime
136 lt 'static
137 "#]],
138 );
139 }
140
141 #[test]
142 fn complete_lifetime_in_where_pred() {
143 check(
144 r#"
145fn foo2<'lifetime, T>() where 'a$0 {}
146"#,
147 expect![[r#"
148 lt 'lifetime
149 lt 'static
150 "#]],
151 );
152 }
153
154 #[test]
155 fn complete_lifetime_in_ty_bound() {
156 check(
157 r#"
158fn foo2<'lifetime, T>() where T: 'a$0 {}
159"#,
160 expect![[r#"
161 lt 'lifetime
162 lt 'static
163 "#]],
164 );
165 check(
166 r#"
167fn foo2<'lifetime, T>() where T: Trait<'a$0> {}
168"#,
169 expect![[r#"
170 lt 'lifetime
171 lt 'static
172 "#]],
173 );
174 }
175
176 #[test]
177 fn dont_complete_lifetime_in_assoc_ty_bound() {
178 check(
179 r#"
180fn foo2<'lifetime, T>() where T: Trait<Item = 'a$0> {}
181"#,
182 expect![[r#""#]],
183 );
184 }
185
186 #[test]
187 fn complete_lifetime_in_param_list() {
188 check(
189 r#"
190fn foo<'a$0>() {}
191"#,
192 expect![[r#""#]],
193 );
194 check(
195 r#"
196fn foo<'footime, 'lifetime: 'a$0>() {}
197"#,
198 expect![[r#"
199 lt 'footime
200 "#]],
201 );
202 }
203
204 #[test]
205 fn check_label_edit() {
206 check_edit(
207 "'label",
208 r#"
209fn foo() {
210 'label: loop {
211 break '$0
212 }
213}
214"#,
215 r#"
216fn foo() {
217 'label: loop {
218 break 'label
219 }
220}
221"#,
222 );
223 }
224
225 #[test]
226 fn complete_label_in_loop() {
227 check(
228 r#"
229fn foo() {
230 'foop: loop {
231 break '$0
232 }
233}
234"#,
235 expect![[r#"
236 lb 'foop
237 "#]],
238 );
239 check(
240 r#"
241fn foo() {
242 'foop: loop {
243 continue '$0
244 }
245}
246"#,
247 expect![[r#"
248 lb 'foop
249 "#]],
250 );
251 }
252
253 #[test]
254 fn complete_label_in_block_nested() {
255 check(
256 r#"
257fn foo() {
258 'foop: {
259 'baap: {
260 break '$0
261 }
262 }
263}
264"#,
265 expect![[r#"
266 lb 'baap
267 lb 'foop
268 "#]],
269 );
270 }
271
272 #[test]
273 fn complete_label_in_loop_with_value() {
274 check(
275 r#"
276fn foo() {
277 'foop: loop {
278 break '$0 i32;
279 }
280}
281"#,
282 expect![[r#"
283 lb 'foop
284 "#]],
285 );
286 }
287
288 #[test]
289 fn complete_label_in_while_cond() {
290 check(
291 r#"
292fn foo() {
293 'outer: while { 'inner: loop { break '$0 } } {}
294}
295"#,
296 expect![[r#"
297 lb 'inner
298 lb 'outer
299 "#]],
300 );
301 }
302
303 #[test]
304 fn complete_label_in_for_iterable() {
305 check(
306 r#"
307fn foo() {
308 'outer: for _ in [{ 'inner: loop { break '$0 } }] {}
309}
310"#,
311 expect![[r#"
312 lb 'inner
313 "#]],
314 );
315 }
316}
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 476eecff0..b06498e6d 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -1,4 +1,4 @@
1//! Completes constats and paths in patterns. 1//! Completes constants and paths in patterns.
2 2
3use crate::{CompletionContext, Completions}; 3use crate::{CompletionContext, Completions};
4 4
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 6d57da06a..32f81aec1 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -4,8 +4,11 @@ use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type};
4use ide_db::base_db::{FilePosition, SourceDatabase}; 4use ide_db::base_db::{FilePosition, SourceDatabase};
5use ide_db::{call_info::ActiveParameter, RootDatabase}; 5use ide_db::{call_info::ActiveParameter, RootDatabase};
6use syntax::{ 6use syntax::{
7 algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode, 7 algo::find_node_at_offset,
8 SyntaxToken, TextRange, TextSize, 8 ast::{self, NameOrNameRef, NameOwner},
9 match_ast, AstNode, NodeOrToken,
10 SyntaxKind::*,
11 SyntaxNode, SyntaxToken, TextRange, TextSize,
9}; 12};
10 13
11use text_edit::Indel; 14use text_edit::Indel;
@@ -35,18 +38,22 @@ pub(crate) struct CompletionContext<'a> {
35 /// The token before the cursor, in the macro-expanded file. 38 /// The token before the cursor, in the macro-expanded file.
36 pub(super) token: SyntaxToken, 39 pub(super) token: SyntaxToken,
37 pub(super) krate: Option<hir::Crate>, 40 pub(super) krate: Option<hir::Crate>,
38 pub(super) expected_name: Option<String>, 41 pub(super) expected_name: Option<NameOrNameRef>,
39 pub(super) expected_type: Option<Type>, 42 pub(super) expected_type: Option<Type>,
40 pub(super) name_ref_syntax: Option<ast::NameRef>, 43 pub(super) name_ref_syntax: Option<ast::NameRef>,
44 pub(super) lifetime_syntax: Option<ast::Lifetime>,
45 pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
41 pub(super) function_syntax: Option<ast::Fn>, 46 pub(super) function_syntax: Option<ast::Fn>,
42 pub(super) use_item_syntax: Option<ast::Use>, 47 pub(super) use_item_syntax: Option<ast::Use>,
43 pub(super) record_lit_syntax: Option<ast::RecordExpr>, 48 pub(super) record_lit_syntax: Option<ast::RecordExpr>,
44 pub(super) record_pat_syntax: Option<ast::RecordPat>, 49 pub(super) record_pat_syntax: Option<ast::RecordPat>,
45 pub(super) record_field_syntax: Option<ast::RecordExprField>, 50 pub(super) record_field_syntax: Option<ast::RecordExprField>,
46 pub(super) impl_def: Option<ast::Impl>, 51 pub(super) impl_def: Option<ast::Impl>,
52 pub(super) lifetime_allowed: bool,
47 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 53 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
48 pub(super) active_parameter: Option<ActiveParameter>, 54 pub(super) active_parameter: Option<ActiveParameter>,
49 pub(super) is_param: bool, 55 pub(super) is_param: bool,
56 pub(super) is_label_ref: bool,
50 /// If a name-binding or reference to a const in a pattern. 57 /// If a name-binding or reference to a const in a pattern.
51 /// Irrefutable patterns (like let) are excluded. 58 /// Irrefutable patterns (like let) are excluded.
52 pub(super) is_pat_binding_or_const: bool, 59 pub(super) is_pat_binding_or_const: bool,
@@ -136,9 +143,12 @@ impl<'a> CompletionContext<'a> {
136 original_token, 143 original_token,
137 token, 144 token,
138 krate, 145 krate,
146 lifetime_allowed: false,
139 expected_name: None, 147 expected_name: None,
140 expected_type: None, 148 expected_type: None,
141 name_ref_syntax: None, 149 name_ref_syntax: None,
150 lifetime_syntax: None,
151 lifetime_param_syntax: None,
142 function_syntax: None, 152 function_syntax: None,
143 use_item_syntax: None, 153 use_item_syntax: None,
144 record_lit_syntax: None, 154 record_lit_syntax: None,
@@ -146,6 +156,7 @@ impl<'a> CompletionContext<'a> {
146 record_field_syntax: None, 156 record_field_syntax: None,
147 impl_def: None, 157 impl_def: None,
148 active_parameter: ActiveParameter::at(db, position), 158 active_parameter: ActiveParameter::at(db, position),
159 is_label_ref: false,
149 is_param: false, 160 is_param: false,
150 is_pat_binding_or_const: false, 161 is_pat_binding_or_const: false,
151 is_irrefutable_pat_binding: false, 162 is_irrefutable_pat_binding: false,
@@ -241,9 +252,13 @@ impl<'a> CompletionContext<'a> {
241 pub(crate) fn source_range(&self) -> TextRange { 252 pub(crate) fn source_range(&self) -> TextRange {
242 // check kind of macro-expanded token, but use range of original token 253 // check kind of macro-expanded token, but use range of original token
243 let kind = self.token.kind(); 254 let kind = self.token.kind();
244 if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() { 255 if kind == IDENT || kind == LIFETIME_IDENT || kind == UNDERSCORE || kind.is_keyword() {
245 cov_mark::hit!(completes_if_prefix_is_keyword); 256 cov_mark::hit!(completes_if_prefix_is_keyword);
246 self.original_token.text_range() 257 self.original_token.text_range()
258 } else if kind == CHAR {
259 // assume we are completing a lifetime but the user has only typed the '
260 cov_mark::hit!(completes_if_lifetime_without_idents);
261 TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
247 } else { 262 } else {
248 TextRange::empty(self.position.offset) 263 TextRange::empty(self.position.offset)
249 } 264 }
@@ -292,13 +307,13 @@ impl<'a> CompletionContext<'a> {
292 file_with_fake_ident: SyntaxNode, 307 file_with_fake_ident: SyntaxNode,
293 offset: TextSize, 308 offset: TextSize,
294 ) { 309 ) {
295 let expected = { 310 let (expected_type, expected_name) = {
296 let mut node = match self.token.parent() { 311 let mut node = match self.token.parent() {
297 Some(it) => it, 312 Some(it) => it,
298 None => return, 313 None => return,
299 }; 314 };
300 loop { 315 loop {
301 let ret = match_ast! { 316 break match_ast! {
302 match node { 317 match node {
303 ast::LetStmt(it) => { 318 ast::LetStmt(it) => {
304 cov_mark::hit!(expected_type_let_with_leading_char); 319 cov_mark::hit!(expected_type_let_with_leading_char);
@@ -306,7 +321,7 @@ impl<'a> CompletionContext<'a> {
306 let ty = it.pat() 321 let ty = it.pat()
307 .and_then(|pat| self.sema.type_of_pat(&pat)); 322 .and_then(|pat| self.sema.type_of_pat(&pat));
308 let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { 323 let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
309 Some(ident.syntax().text().to_string()) 324 ident.name().map(NameOrNameRef::Name)
310 } else { 325 } else {
311 None 326 None
312 }; 327 };
@@ -319,7 +334,10 @@ impl<'a> CompletionContext<'a> {
319 ActiveParameter::at_token( 334 ActiveParameter::at_token(
320 &self.sema, 335 &self.sema,
321 self.token.clone(), 336 self.token.clone(),
322 ).map(|ap| (Some(ap.ty), Some(ap.name))) 337 ).map(|ap| {
338 let name = ap.ident().map(NameOrNameRef::Name);
339 (Some(ap.ty), name)
340 })
323 .unwrap_or((None, None)) 341 .unwrap_or((None, None))
324 }, 342 },
325 ast::RecordExprFieldList(_it) => { 343 ast::RecordExprFieldList(_it) => {
@@ -327,10 +345,10 @@ impl<'a> CompletionContext<'a> {
327 self.token.prev_sibling_or_token() 345 self.token.prev_sibling_or_token()
328 .and_then(|se| se.into_node()) 346 .and_then(|se| se.into_node())
329 .and_then(|node| ast::RecordExprField::cast(node)) 347 .and_then(|node| ast::RecordExprField::cast(node))
330 .and_then(|rf| self.sema.resolve_record_field(&rf)) 348 .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
331 .map(|f|( 349 .map(|(f, rf)|(
332 Some(f.0.signature_ty(self.db)), 350 Some(f.0.signature_ty(self.db)),
333 Some(f.0.name(self.db).to_string()), 351 rf.field_name().map(NameOrNameRef::NameRef),
334 )) 352 ))
335 .unwrap_or((None, None)) 353 .unwrap_or((None, None))
336 }, 354 },
@@ -340,7 +358,7 @@ impl<'a> CompletionContext<'a> {
340 .resolve_record_field(&it) 358 .resolve_record_field(&it)
341 .map(|f|( 359 .map(|f|(
342 Some(f.0.signature_ty(self.db)), 360 Some(f.0.signature_ty(self.db)),
343 Some(f.0.name(self.db).to_string()), 361 it.field_name().map(NameOrNameRef::NameRef),
344 )) 362 ))
345 .unwrap_or((None, None)) 363 .unwrap_or((None, None))
346 }, 364 },
@@ -378,14 +396,17 @@ impl<'a> CompletionContext<'a> {
378 }, 396 },
379 } 397 }
380 }; 398 };
381
382 break ret;
383 } 399 }
384 }; 400 };
385 self.expected_type = expected.0; 401 self.expected_type = expected_type;
386 self.expected_name = expected.1; 402 self.expected_name = expected_name;
387 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); 403 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
388 404
405 if let Some(lifetime) = find_node_at_offset::<ast::Lifetime>(&file_with_fake_ident, offset)
406 {
407 self.classify_lifetime(original_file, lifetime, offset);
408 }
409
389 // First, let's try to complete a reference to some declaration. 410 // First, let's try to complete a reference to some declaration.
390 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { 411 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
391 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. 412 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
@@ -445,6 +466,35 @@ impl<'a> CompletionContext<'a> {
445 } 466 }
446 } 467 }
447 468
469 fn classify_lifetime(
470 &mut self,
471 original_file: &SyntaxNode,
472 lifetime: ast::Lifetime,
473 offset: TextSize,
474 ) {
475 self.lifetime_syntax =
476 find_node_at_offset(original_file, lifetime.syntax().text_range().start());
477 if let Some(parent) = lifetime.syntax().parent() {
478 if parent.kind() == ERROR {
479 return;
480 }
481
482 match_ast! {
483 match parent {
484 ast::LifetimeParam(_it) => {
485 self.lifetime_allowed = true;
486 self.lifetime_param_syntax =
487 self.sema.find_node_at_offset_with_macros(original_file, offset);
488 },
489 ast::BreakExpr(_it) => self.is_label_ref = true,
490 ast::ContinueExpr(_it) => self.is_label_ref = true,
491 ast::Label(_it) => (),
492 _ => self.lifetime_allowed = true,
493 }
494 }
495 }
496 }
497
448 fn classify_name_ref( 498 fn classify_name_ref(
449 &mut self, 499 &mut self,
450 original_file: &SyntaxNode, 500 original_file: &SyntaxNode,
@@ -452,11 +502,11 @@ impl<'a> CompletionContext<'a> {
452 offset: TextSize, 502 offset: TextSize,
453 ) { 503 ) {
454 self.name_ref_syntax = 504 self.name_ref_syntax =
455 find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); 505 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
456 let name_range = name_ref.syntax().text_range(); 506 let name_range = name_ref.syntax().text_range();
457 if ast::RecordExprField::for_field_name(&name_ref).is_some() { 507 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
458 self.record_lit_syntax = 508 self.record_lit_syntax =
459 self.sema.find_node_at_offset_with_macros(&original_file, offset); 509 self.sema.find_node_at_offset_with_macros(original_file, offset);
460 } 510 }
461 511
462 self.fill_impl_def(); 512 self.fill_impl_def();
@@ -631,7 +681,9 @@ mod tests {
631 .map(|t| t.display_test(&db).to_string()) 681 .map(|t| t.display_test(&db).to_string())
632 .unwrap_or("?".to_owned()); 682 .unwrap_or("?".to_owned());
633 683
634 let name = completion_context.expected_name.unwrap_or("?".to_owned()); 684 let name = completion_context
685 .expected_name
686 .map_or_else(|| "?".to_owned(), |name| name.to_string());
635 687
636 expect.assert_eq(&format!("ty: {}, name: {}", ty, name)); 688 expect.assert_eq(&format!("ty: {}, name: {}", ty, name));
637 } 689 }
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index d9ea7b7ea..5ac1cb48d 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -14,7 +14,10 @@ mod completions;
14use completions::flyimport::position_for_import; 14use completions::flyimport::position_for_import;
15use ide_db::{ 15use ide_db::{
16 base_db::FilePosition, 16 base_db::FilePosition,
17 helpers::{import_assets::LocatedImport, insert_use::ImportScope}, 17 helpers::{
18 import_assets::{LocatedImport, NameToImport},
19 insert_use::ImportScope,
20 },
18 items_locator, RootDatabase, 21 items_locator, RootDatabase,
19}; 22};
20use text_edit::TextEdit; 23use text_edit::TextEdit;
@@ -130,6 +133,8 @@ pub fn completions(
130 completions::trait_impl::complete_trait_impl(&mut acc, &ctx); 133 completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
131 completions::mod_::complete_mod(&mut acc, &ctx); 134 completions::mod_::complete_mod(&mut acc, &ctx);
132 completions::flyimport::import_on_the_fly(&mut acc, &ctx); 135 completions::flyimport::import_on_the_fly(&mut acc, &ctx);
136 completions::lifetime::complete_lifetime(&mut acc, &ctx);
137 completions::lifetime::complete_label(&mut acc, &ctx);
133 138
134 Some(acc) 139 Some(acc)
135} 140}
@@ -149,15 +154,19 @@ pub fn resolve_completion_edits(
149 let current_module = ctx.sema.scope(position_for_import).module()?; 154 let current_module = ctx.sema.scope(position_for_import).module()?;
150 let current_crate = current_module.krate(); 155 let current_crate = current_module.krate();
151 156
152 let (import_path, item_to_import) = 157 let (import_path, item_to_import) = items_locator::items_with_name(
153 items_locator::with_exact_name(&ctx.sema, current_crate, imported_name) 158 &ctx.sema,
154 .into_iter() 159 current_crate,
155 .filter_map(|candidate| { 160 NameToImport::Exact(imported_name),
156 current_module 161 items_locator::AssocItemSearch::Include,
157 .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind) 162 Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT),
158 .zip(Some(candidate)) 163 )
159 }) 164 .filter_map(|candidate| {
160 .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?; 165 current_module
166 .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
167 .zip(Some(candidate))
168 })
169 .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
161 let import = 170 let import =
162 LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path)); 171 LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path));
163 172
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index cf5ef07b7..d82564381 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -71,7 +71,7 @@ fn test_has_block_expr_parent() {
71} 71}
72 72
73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { 73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
74 element.ancestors().find(|it| it.kind() == IDENT_PAT).is_some() 74 element.ancestors().any(|it| it.kind() == IDENT_PAT)
75} 75}
76#[test] 76#[test]
77fn test_has_bind_pat_parent() { 77fn test_has_bind_pat_parent() {
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 12921e12b..23e00aa47 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -219,6 +219,7 @@ impl<'a> Render<'a> {
219 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, 219 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,
220 }), 220 }),
221 ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), 221 ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
222 ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),
222 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => { 223 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {
223 CompletionItemKind::SymbolKind(SymbolKind::SelfParam) 224 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
224 } 225 }
@@ -243,7 +244,7 @@ impl<'a> Render<'a> {
243 244
244 item.set_relevance(CompletionRelevance { 245 item.set_relevance(CompletionRelevance {
245 exact_type_match: compute_exact_type_match(self.ctx.completion, &ty), 246 exact_type_match: compute_exact_type_match(self.ctx.completion, &ty),
246 exact_name_match: compute_exact_name_match(self.ctx.completion, local_name.clone()), 247 exact_name_match: compute_exact_name_match(self.ctx.completion, &local_name),
247 is_local: true, 248 is_local: true,
248 ..CompletionRelevance::default() 249 ..CompletionRelevance::default()
249 }); 250 });
@@ -319,8 +320,7 @@ fn compute_exact_type_match(ctx: &CompletionContext, completion_ty: &hir::Type)
319 320
320fn compute_exact_name_match(ctx: &CompletionContext, completion_name: impl Into<String>) -> bool { 321fn compute_exact_name_match(ctx: &CompletionContext, completion_name: impl Into<String>) -> bool {
321 let completion_name = completion_name.into(); 322 let completion_name = completion_name.into();
322 323 ctx.expected_name.as_ref().map_or(false, |name| name.text() == completion_name)
323 Some(&completion_name) == ctx.expected_name.as_ref()
324} 324}
325 325
326fn compute_ref_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> Option<Mutability> { 326fn compute_ref_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> Option<Mutability> {
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index 3fa21ba7c..7578ad50b 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -91,7 +91,7 @@ impl<'a> MacroRender<'a> {
91 } 91 }
92 92
93 fn detail(&self) -> Option<String> { 93 fn detail(&self) -> Option<String> {
94 let ast_node = self.macro_.source(self.ctx.db())?.value; 94 let ast_node = self.macro_.source(self.ctx.db())?.value.left()?;
95 Some(macro_label(&ast_node)) 95 Some(macro_label(&ast_node))
96 } 96 }
97} 97}