diff options
Diffstat (limited to 'crates/ide_completion/src/completions')
-rw-r--r-- | crates/ide_completion/src/completions/attribute.rs | 3 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/dot.rs | 2 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/flyimport.rs | 93 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/lifetime.rs | 316 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/pattern.rs | 2 |
5 files changed, 410 insertions, 6 deletions
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs index e846678b4..b1505c74b 100644 --- a/crates/ide_completion/src/completions/attribute.rs +++ b/crates/ide_completion/src/completions/attribute.rs | |||
@@ -246,7 +246,8 @@ fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { | |||
246 | let mut result = FxHashSet::default(); | 246 | let mut result = FxHashSet::default(); |
247 | ctx.scope.process_all_names(&mut |name, scope_def| { | 247 | ctx.scope.process_all_names(&mut |name, scope_def| { |
248 | if let hir::ScopeDef::MacroDef(mac) = scope_def { | 248 | if let hir::ScopeDef::MacroDef(mac) = scope_def { |
249 | if mac.is_derive_macro() { | 249 | // FIXME kind() doesn't check whether proc-macro is a derive |
250 | if mac.kind() == hir::MacroKind::Derive || mac.kind() == hir::MacroKind::ProcMacro { | ||
250 | result.insert(name.to_string()); | 251 | result.insert(name.to_string()); |
251 | } | 252 | } |
252 | } | 253 | } |
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index cec2d0c3a..7e4efe589 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs | |||
@@ -51,7 +51,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T | |||
51 | && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) | 51 | && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) |
52 | && seen_methods.insert(func.name(ctx.db)) | 52 | && seen_methods.insert(func.name(ctx.db)) |
53 | { | 53 | { |
54 | acc.add_function(ctx, func, None); | 54 | acc.add_method(ctx, func, None); |
55 | } | 55 | } |
56 | None::<()> | 56 | None::<()> |
57 | }); | 57 | }); |
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 | ||
944 | fn main() { | 946 | fn 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#" | ||
957 | mod 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 | |||
978 | fn main() { | ||
979 | test_f$0 | ||
980 | }"#, | ||
981 | expect![[]], | ||
982 | ) | ||
983 | } | ||
984 | |||
985 | #[test] | ||
986 | fn case_matters() { | ||
987 | check( | ||
988 | r#" | ||
989 | mod foo { | ||
990 | pub const TEST_CONST: usize = 3; | ||
991 | pub fn test_function() -> i32 { | ||
992 | 4 | ||
993 | } | ||
994 | } | ||
995 | |||
996 | fn main() { | ||
997 | TE$0 | ||
998 | }"#, | ||
999 | expect![[r#" | ||
1000 | ct foo::TEST_CONST | ||
1001 | "#]], | ||
1002 | ); | ||
1003 | |||
1004 | check( | ||
1005 | r#" | ||
1006 | mod foo { | ||
1007 | pub const TEST_CONST: usize = 3; | ||
1008 | pub fn test_function() -> i32 { | ||
1009 | 4 | ||
1010 | } | ||
1011 | } | ||
1012 | |||
1013 | fn 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#" | ||
1024 | mod foo { | ||
1025 | pub const TEST_CONST: usize = 3; | ||
1026 | pub fn test_function() -> i32 { | ||
1027 | 4 | ||
1028 | } | ||
1029 | } | ||
1030 | |||
1031 | fn 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. | ||
2 | use hir::ScopeDef; | ||
3 | |||
4 | use crate::{completions::Completions, context::CompletionContext}; | ||
5 | |||
6 | /// Completes lifetimes. | ||
7 | pub(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. | ||
33 | pub(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)] | ||
45 | mod 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#" | ||
67 | fn func<'lifetime>(foo: &'li$0) {} | ||
68 | "#, | ||
69 | r#" | ||
70 | fn func<'lifetime>(foo: &'lifetime) {} | ||
71 | "#, | ||
72 | ); | ||
73 | cov_mark::check!(completes_if_lifetime_without_idents); | ||
74 | check_edit( | ||
75 | "'lifetime", | ||
76 | r#" | ||
77 | fn func<'lifetime>(foo: &'$0) {} | ||
78 | "#, | ||
79 | r#" | ||
80 | fn func<'lifetime>(foo: &'lifetime) {} | ||
81 | "#, | ||
82 | ); | ||
83 | } | ||
84 | |||
85 | #[test] | ||
86 | fn complete_lifetime_in_ref() { | ||
87 | check( | ||
88 | r#" | ||
89 | fn 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#" | ||
102 | fn 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#" | ||
114 | struct Foo; | ||
115 | impl<'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#" | ||
131 | struct Foo<'lt>; | ||
132 | fn 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#" | ||
145 | fn 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#" | ||
158 | fn foo2<'lifetime, T>() where T: 'a$0 {} | ||
159 | "#, | ||
160 | expect![[r#" | ||
161 | lt 'lifetime | ||
162 | lt 'static | ||
163 | "#]], | ||
164 | ); | ||
165 | check( | ||
166 | r#" | ||
167 | fn 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#" | ||
180 | fn 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#" | ||
190 | fn foo<'a$0>() {} | ||
191 | "#, | ||
192 | expect![[r#""#]], | ||
193 | ); | ||
194 | check( | ||
195 | r#" | ||
196 | fn 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#" | ||
209 | fn foo() { | ||
210 | 'label: loop { | ||
211 | break '$0 | ||
212 | } | ||
213 | } | ||
214 | "#, | ||
215 | r#" | ||
216 | fn 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#" | ||
229 | fn foo() { | ||
230 | 'foop: loop { | ||
231 | break '$0 | ||
232 | } | ||
233 | } | ||
234 | "#, | ||
235 | expect![[r#" | ||
236 | lb 'foop | ||
237 | "#]], | ||
238 | ); | ||
239 | check( | ||
240 | r#" | ||
241 | fn 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#" | ||
257 | fn 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#" | ||
276 | fn 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#" | ||
292 | fn 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#" | ||
307 | fn 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 | ||
3 | use crate::{CompletionContext, Completions}; | 3 | use crate::{CompletionContext, Completions}; |
4 | 4 | ||