diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-05-28 22:21:37 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-05-28 22:21:37 +0100 |
commit | 7869b01b702d1f12acb03ac131b6eb90ad82e9bb (patch) | |
tree | e6d76df5212db7589ef8dc6583b32d6c64871a33 /crates/ide_completion/src | |
parent | d5f7b2e52a41a7d3b841f4d0e2225eb703f6a50a (diff) | |
parent | e42c448077ca2b7675320da3c5294bf4bebaedeb (diff) |
Merge #9041
9041: internal: Implement prev sibling determination for `CompletionContext ` r=Veykril a=Veykril
bors r+
Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r-- | crates/ide_completion/src/completions/keyword.rs | 192 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 49 | ||||
-rw-r--r-- | crates/ide_completion/src/patterns.rs | 265 | ||||
-rw-r--r-- | crates/ide_completion/src/test_utils.rs | 16 |
4 files changed, 305 insertions, 217 deletions
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index c9673df85..e71a04b6e 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -48,91 +48,92 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
48 | cov_mark::hit!(no_keyword_completion_in_record_lit); | 48 | cov_mark::hit!(no_keyword_completion_in_record_lit); |
49 | return; | 49 | return; |
50 | } | 50 | } |
51 | let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet); | ||
51 | 52 | ||
52 | let expects_assoc_item = ctx.expects_assoc_item(); | 53 | let expects_assoc_item = ctx.expects_assoc_item(); |
53 | let has_block_expr_parent = ctx.has_block_expr_parent(); | 54 | let has_block_expr_parent = ctx.has_block_expr_parent(); |
54 | let expects_item = ctx.expects_item(); | 55 | let expects_item = ctx.expects_item(); |
56 | |||
55 | if ctx.has_impl_or_trait_prev_sibling() { | 57 | if ctx.has_impl_or_trait_prev_sibling() { |
56 | add_keyword(ctx, acc, "where", "where "); | 58 | // FIXME this also incorrectly shows up after a complete trait/impl |
59 | add_keyword("where", "where "); | ||
57 | return; | 60 | return; |
58 | } | 61 | } |
59 | if ctx.previous_token_is(T![unsafe]) { | 62 | if ctx.previous_token_is(T![unsafe]) { |
60 | if expects_item || has_block_expr_parent { | 63 | if expects_item || expects_assoc_item || has_block_expr_parent { |
61 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}") | 64 | add_keyword("fn", "fn $1($2) {\n $0\n}") |
62 | } | 65 | } |
63 | 66 | ||
64 | if expects_item || has_block_expr_parent { | 67 | if expects_item || has_block_expr_parent { |
65 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); | 68 | add_keyword("trait", "trait $1 {\n $0\n}"); |
66 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); | 69 | add_keyword("impl", "impl $1 {\n $0\n}"); |
67 | } | 70 | } |
68 | 71 | ||
69 | return; | 72 | return; |
70 | } | 73 | } |
74 | |||
75 | if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() { | ||
76 | add_keyword("pub(crate)", "pub(crate) "); | ||
77 | add_keyword("pub", "pub "); | ||
78 | } | ||
79 | |||
80 | if expects_item || expects_assoc_item || has_block_expr_parent || ctx.is_match_arm { | ||
81 | add_keyword("unsafe", "unsafe "); | ||
82 | } | ||
83 | |||
71 | if expects_item || expects_assoc_item || has_block_expr_parent { | 84 | if expects_item || expects_assoc_item || has_block_expr_parent { |
72 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}"); | 85 | add_keyword("fn", "fn $1($2) {\n $0\n}"); |
86 | add_keyword("const", "const $0"); | ||
87 | add_keyword("type", "type $0"); | ||
73 | } | 88 | } |
89 | |||
74 | if expects_item || has_block_expr_parent { | 90 | if expects_item || has_block_expr_parent { |
75 | add_keyword(ctx, acc, "use", "use "); | 91 | add_keyword("use", "use $0"); |
76 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); | 92 | add_keyword("impl", "impl $1 {\n $0\n}"); |
77 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); | 93 | add_keyword("trait", "trait $1 {\n $0\n}"); |
94 | add_keyword("static", "static $0"); | ||
95 | add_keyword("extern", "extern $0"); | ||
96 | add_keyword("mod", "mod $0"); | ||
78 | } | 97 | } |
79 | 98 | ||
80 | if expects_item { | 99 | if expects_item { |
81 | add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}"); | 100 | add_keyword("enum", "enum $1 {\n $0\n}"); |
82 | add_keyword(ctx, acc, "struct", "struct $0"); | 101 | add_keyword("struct", "struct $0"); |
83 | add_keyword(ctx, acc, "union", "union $1 {\n $0\n}"); | 102 | add_keyword("union", "union $1 {\n $0\n}"); |
84 | } | 103 | } |
85 | 104 | ||
86 | if ctx.is_expr { | 105 | if ctx.expects_expression() { |
87 | add_keyword(ctx, acc, "match", "match $1 {\n $0\n}"); | 106 | add_keyword("match", "match $1 {\n $0\n}"); |
88 | add_keyword(ctx, acc, "while", "while $1 {\n $0\n}"); | 107 | add_keyword("while", "while $1 {\n $0\n}"); |
89 | add_keyword(ctx, acc, "while let", "while let $1 = $2 {\n $0\n}"); | 108 | add_keyword("while let", "while let $1 = $2 {\n $0\n}"); |
90 | add_keyword(ctx, acc, "loop", "loop {\n $0\n}"); | 109 | add_keyword("loop", "loop {\n $0\n}"); |
91 | add_keyword(ctx, acc, "if", "if $1 {\n $0\n}"); | 110 | add_keyword("if", "if $1 {\n $0\n}"); |
92 | add_keyword(ctx, acc, "if let", "if let $1 = $2 {\n $0\n}"); | 111 | add_keyword("if let", "if let $1 = $2 {\n $0\n}"); |
93 | add_keyword(ctx, acc, "for", "for $1 in $2 {\n $0\n}"); | 112 | add_keyword("for", "for $1 in $2 {\n $0\n}"); |
94 | } | 113 | } |
95 | 114 | ||
96 | if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent { | 115 | if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent { |
97 | add_keyword(ctx, acc, "let", "let "); | 116 | add_keyword("let", "let "); |
98 | } | 117 | } |
99 | 118 | ||
100 | if ctx.after_if { | 119 | if ctx.after_if() { |
101 | add_keyword(ctx, acc, "else", "else {\n $0\n}"); | 120 | add_keyword("else", "else {\n $0\n}"); |
102 | add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}"); | 121 | add_keyword("else if", "else if $1 {\n $0\n}"); |
103 | } | ||
104 | if expects_item || has_block_expr_parent { | ||
105 | add_keyword(ctx, acc, "mod", "mod $0"); | ||
106 | } | 122 | } |
123 | |||
107 | if ctx.expects_ident_pat_or_ref_expr() { | 124 | if ctx.expects_ident_pat_or_ref_expr() { |
108 | add_keyword(ctx, acc, "mut", "mut "); | 125 | add_keyword("mut", "mut "); |
109 | } | ||
110 | if expects_item || expects_assoc_item || has_block_expr_parent { | ||
111 | add_keyword(ctx, acc, "const", "const "); | ||
112 | add_keyword(ctx, acc, "type", "type "); | ||
113 | } | ||
114 | if expects_item || has_block_expr_parent { | ||
115 | add_keyword(ctx, acc, "static", "static "); | ||
116 | }; | ||
117 | if expects_item || has_block_expr_parent { | ||
118 | add_keyword(ctx, acc, "extern", "extern "); | ||
119 | } | ||
120 | if expects_item || expects_assoc_item || has_block_expr_parent || ctx.is_match_arm { | ||
121 | add_keyword(ctx, acc, "unsafe", "unsafe "); | ||
122 | } | 126 | } |
127 | |||
123 | if ctx.in_loop_body { | 128 | if ctx.in_loop_body { |
124 | if ctx.can_be_stmt { | 129 | if ctx.can_be_stmt { |
125 | add_keyword(ctx, acc, "continue", "continue;"); | 130 | add_keyword("continue", "continue;"); |
126 | add_keyword(ctx, acc, "break", "break;"); | 131 | add_keyword("break", "break;"); |
127 | } else { | 132 | } else { |
128 | add_keyword(ctx, acc, "continue", "continue"); | 133 | add_keyword("continue", "continue"); |
129 | add_keyword(ctx, acc, "break", "break"); | 134 | add_keyword("break", "break"); |
130 | } | 135 | } |
131 | } | 136 | } |
132 | if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() { | ||
133 | add_keyword(ctx, acc, "pub(crate)", "pub(crate) "); | ||
134 | add_keyword(ctx, acc, "pub", "pub "); | ||
135 | } | ||
136 | 137 | ||
137 | if !ctx.is_trivial_path { | 138 | if !ctx.is_trivial_path { |
138 | return; | 139 | return; |
@@ -143,8 +144,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
143 | }; | 144 | }; |
144 | 145 | ||
145 | add_keyword( | 146 | add_keyword( |
146 | ctx, | ||
147 | acc, | ||
148 | "return", | 147 | "return", |
149 | match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { | 148 | match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { |
150 | (true, true) => "return $0;", | 149 | (true, true) => "return $0;", |
@@ -161,15 +160,12 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet | |||
161 | 160 | ||
162 | match ctx.config.snippet_cap { | 161 | match ctx.config.snippet_cap { |
163 | Some(cap) => { | 162 | Some(cap) => { |
164 | let tmp; | 163 | if snippet.ends_with('}') && ctx.incomplete_let { |
165 | let snippet = if snippet.ends_with('}') && ctx.incomplete_let { | ||
166 | cov_mark::hit!(let_semi); | 164 | cov_mark::hit!(let_semi); |
167 | tmp = format!("{};", snippet); | 165 | item.insert_snippet(cap, format!("{};", snippet)); |
168 | &tmp | ||
169 | } else { | 166 | } else { |
170 | snippet | 167 | item.insert_snippet(cap, snippet); |
171 | }; | 168 | } |
172 | item.insert_snippet(cap, snippet); | ||
173 | } | 169 | } |
174 | None => { | 170 | None => { |
175 | item.insert_text(if snippet.contains('$') { kw } else { snippet }); | 171 | item.insert_text(if snippet.contains('$') { kw } else { snippet }); |
@@ -232,21 +228,21 @@ mod tests { | |||
232 | check( | 228 | check( |
233 | r"m$0", | 229 | r"m$0", |
234 | expect![[r#" | 230 | expect![[r#" |
231 | kw pub(crate) | ||
232 | kw pub | ||
233 | kw unsafe | ||
235 | kw fn | 234 | kw fn |
235 | kw const | ||
236 | kw type | ||
236 | kw use | 237 | kw use |
237 | kw impl | 238 | kw impl |
238 | kw trait | 239 | kw trait |
240 | kw static | ||
241 | kw extern | ||
242 | kw mod | ||
239 | kw enum | 243 | kw enum |
240 | kw struct | 244 | kw struct |
241 | kw union | 245 | kw union |
242 | kw mod | ||
243 | kw const | ||
244 | kw type | ||
245 | kw static | ||
246 | kw extern | ||
247 | kw unsafe | ||
248 | kw pub(crate) | ||
249 | kw pub | ||
250 | "#]], | 246 | "#]], |
251 | ); | 247 | ); |
252 | } | 248 | } |
@@ -256,10 +252,16 @@ mod tests { | |||
256 | check( | 252 | check( |
257 | r"fn quux() { $0 }", | 253 | r"fn quux() { $0 }", |
258 | expect![[r#" | 254 | expect![[r#" |
255 | kw unsafe | ||
259 | kw fn | 256 | kw fn |
257 | kw const | ||
258 | kw type | ||
260 | kw use | 259 | kw use |
261 | kw impl | 260 | kw impl |
262 | kw trait | 261 | kw trait |
262 | kw static | ||
263 | kw extern | ||
264 | kw mod | ||
263 | kw match | 265 | kw match |
264 | kw while | 266 | kw while |
265 | kw while let | 267 | kw while let |
@@ -268,12 +270,6 @@ mod tests { | |||
268 | kw if let | 270 | kw if let |
269 | kw for | 271 | kw for |
270 | kw let | 272 | kw let |
271 | kw mod | ||
272 | kw const | ||
273 | kw type | ||
274 | kw static | ||
275 | kw extern | ||
276 | kw unsafe | ||
277 | kw return | 273 | kw return |
278 | "#]], | 274 | "#]], |
279 | ); | 275 | ); |
@@ -284,10 +280,16 @@ mod tests { | |||
284 | check( | 280 | check( |
285 | r"fn quux() { if true { $0 } }", | 281 | r"fn quux() { if true { $0 } }", |
286 | expect![[r#" | 282 | expect![[r#" |
283 | kw unsafe | ||
287 | kw fn | 284 | kw fn |
285 | kw const | ||
286 | kw type | ||
288 | kw use | 287 | kw use |
289 | kw impl | 288 | kw impl |
290 | kw trait | 289 | kw trait |
290 | kw static | ||
291 | kw extern | ||
292 | kw mod | ||
291 | kw match | 293 | kw match |
292 | kw while | 294 | kw while |
293 | kw while let | 295 | kw while let |
@@ -296,12 +298,6 @@ mod tests { | |||
296 | kw if let | 298 | kw if let |
297 | kw for | 299 | kw for |
298 | kw let | 300 | kw let |
299 | kw mod | ||
300 | kw const | ||
301 | kw type | ||
302 | kw static | ||
303 | kw extern | ||
304 | kw unsafe | ||
305 | kw return | 301 | kw return |
306 | "#]], | 302 | "#]], |
307 | ); | 303 | ); |
@@ -312,10 +308,16 @@ mod tests { | |||
312 | check( | 308 | check( |
313 | r#"fn quux() { if true { () } $0 }"#, | 309 | r#"fn quux() { if true { () } $0 }"#, |
314 | expect![[r#" | 310 | expect![[r#" |
311 | kw unsafe | ||
315 | kw fn | 312 | kw fn |
313 | kw const | ||
314 | kw type | ||
316 | kw use | 315 | kw use |
317 | kw impl | 316 | kw impl |
318 | kw trait | 317 | kw trait |
318 | kw static | ||
319 | kw extern | ||
320 | kw mod | ||
319 | kw match | 321 | kw match |
320 | kw while | 322 | kw while |
321 | kw while let | 323 | kw while let |
@@ -326,12 +328,6 @@ mod tests { | |||
326 | kw let | 328 | kw let |
327 | kw else | 329 | kw else |
328 | kw else if | 330 | kw else if |
329 | kw mod | ||
330 | kw const | ||
331 | kw type | ||
332 | kw static | ||
333 | kw extern | ||
334 | kw unsafe | ||
335 | kw return | 331 | kw return |
336 | "#]], | 332 | "#]], |
337 | ); | 333 | ); |
@@ -353,6 +349,7 @@ fn quux() -> i32 { | |||
353 | } | 349 | } |
354 | "#, | 350 | "#, |
355 | expect![[r#" | 351 | expect![[r#" |
352 | kw unsafe | ||
356 | kw match | 353 | kw match |
357 | kw while | 354 | kw while |
358 | kw while let | 355 | kw while let |
@@ -360,7 +357,6 @@ fn quux() -> i32 { | |||
360 | kw if | 357 | kw if |
361 | kw if let | 358 | kw if let |
362 | kw for | 359 | kw for |
363 | kw unsafe | ||
364 | kw return | 360 | kw return |
365 | "#]], | 361 | "#]], |
366 | ); | 362 | ); |
@@ -371,10 +367,10 @@ fn quux() -> i32 { | |||
371 | check( | 367 | check( |
372 | r"trait My { $0 }", | 368 | r"trait My { $0 }", |
373 | expect![[r#" | 369 | expect![[r#" |
370 | kw unsafe | ||
374 | kw fn | 371 | kw fn |
375 | kw const | 372 | kw const |
376 | kw type | 373 | kw type |
377 | kw unsafe | ||
378 | "#]], | 374 | "#]], |
379 | ); | 375 | ); |
380 | } | 376 | } |
@@ -384,12 +380,12 @@ fn quux() -> i32 { | |||
384 | check( | 380 | check( |
385 | r"impl My { $0 }", | 381 | r"impl My { $0 }", |
386 | expect![[r#" | 382 | expect![[r#" |
383 | kw pub(crate) | ||
384 | kw pub | ||
385 | kw unsafe | ||
387 | kw fn | 386 | kw fn |
388 | kw const | 387 | kw const |
389 | kw type | 388 | kw type |
390 | kw unsafe | ||
391 | kw pub(crate) | ||
392 | kw pub | ||
393 | "#]], | 389 | "#]], |
394 | ); | 390 | ); |
395 | } | 391 | } |
@@ -399,12 +395,12 @@ fn quux() -> i32 { | |||
399 | check( | 395 | check( |
400 | r"impl My { #[foo] $0 }", | 396 | r"impl My { #[foo] $0 }", |
401 | expect![[r#" | 397 | expect![[r#" |
398 | kw pub(crate) | ||
399 | kw pub | ||
400 | kw unsafe | ||
402 | kw fn | 401 | kw fn |
403 | kw const | 402 | kw const |
404 | kw type | 403 | kw type |
405 | kw unsafe | ||
406 | kw pub(crate) | ||
407 | kw pub | ||
408 | "#]], | 404 | "#]], |
409 | ); | 405 | ); |
410 | } | 406 | } |
@@ -414,10 +410,16 @@ fn quux() -> i32 { | |||
414 | check( | 410 | check( |
415 | r"fn my() { loop { $0 } }", | 411 | r"fn my() { loop { $0 } }", |
416 | expect![[r#" | 412 | expect![[r#" |
413 | kw unsafe | ||
417 | kw fn | 414 | kw fn |
415 | kw const | ||
416 | kw type | ||
418 | kw use | 417 | kw use |
419 | kw impl | 418 | kw impl |
420 | kw trait | 419 | kw trait |
420 | kw static | ||
421 | kw extern | ||
422 | kw mod | ||
421 | kw match | 423 | kw match |
422 | kw while | 424 | kw while |
423 | kw while let | 425 | kw while let |
@@ -426,12 +428,6 @@ fn quux() -> i32 { | |||
426 | kw if let | 428 | kw if let |
427 | kw for | 429 | kw for |
428 | kw let | 430 | kw let |
429 | kw mod | ||
430 | kw const | ||
431 | kw type | ||
432 | kw static | ||
433 | kw extern | ||
434 | kw unsafe | ||
435 | kw continue | 431 | kw continue |
436 | kw break | 432 | kw break |
437 | kw return | 433 | kw return |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 923e35dbb..8d6440cb2 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -17,8 +17,8 @@ use text_edit::Indel; | |||
17 | 17 | ||
18 | use crate::{ | 18 | use crate::{ |
19 | patterns::{ | 19 | patterns::{ |
20 | determine_location, for_is_prev2, has_prev_sibling, inside_impl_trait_block, | 20 | determine_location, determine_prev_sibling, for_is_prev2, inside_impl_trait_block, |
21 | is_in_loop_body, is_match_arm, previous_token, ImmediateLocation, | 21 | is_in_loop_body, is_match_arm, previous_token, ImmediateLocation, ImmediatePrevSibling, |
22 | }, | 22 | }, |
23 | CompletionConfig, | 23 | CompletionConfig, |
24 | }; | 24 | }; |
@@ -29,12 +29,6 @@ pub(crate) enum PatternRefutability { | |||
29 | Irrefutable, | 29 | Irrefutable, |
30 | } | 30 | } |
31 | 31 | ||
32 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
33 | pub(crate) enum PrevSibling { | ||
34 | Trait, | ||
35 | Impl, | ||
36 | } | ||
37 | |||
38 | /// `CompletionContext` is created early during completion to figure out, where | 32 | /// `CompletionContext` is created early during completion to figure out, where |
39 | /// exactly is the cursor, syntax-wise. | 33 | /// exactly is the cursor, syntax-wise. |
40 | #[derive(Debug)] | 34 | #[derive(Debug)] |
@@ -76,6 +70,7 @@ pub(crate) struct CompletionContext<'a> { | |||
76 | pub(super) is_param: bool, | 70 | pub(super) is_param: bool, |
77 | 71 | ||
78 | pub(super) completion_location: Option<ImmediateLocation>, | 72 | pub(super) completion_location: Option<ImmediateLocation>, |
73 | pub(super) prev_sibling: Option<ImmediatePrevSibling>, | ||
79 | 74 | ||
80 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong | 75 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong |
81 | pub(super) active_parameter: Option<ActiveParameter>, | 76 | pub(super) active_parameter: Option<ActiveParameter>, |
@@ -83,7 +78,6 @@ pub(crate) struct CompletionContext<'a> { | |||
83 | pub(super) is_trivial_path: bool, | 78 | pub(super) is_trivial_path: bool, |
84 | /// If not a trivial path, the prefix (qualifier). | 79 | /// If not a trivial path, the prefix (qualifier). |
85 | pub(super) path_qual: Option<ast::Path>, | 80 | pub(super) path_qual: Option<ast::Path>, |
86 | pub(super) after_if: bool, | ||
87 | /// `true` if we are a statement or a last expr in the block. | 81 | /// `true` if we are a statement or a last expr in the block. |
88 | pub(super) can_be_stmt: bool, | 82 | pub(super) can_be_stmt: bool, |
89 | /// `true` if we expect an expression at the cursor position. | 83 | /// `true` if we expect an expression at the cursor position. |
@@ -107,7 +101,6 @@ pub(crate) struct CompletionContext<'a> { | |||
107 | 101 | ||
108 | // keyword patterns | 102 | // keyword patterns |
109 | pub(super) previous_token: Option<SyntaxToken>, | 103 | pub(super) previous_token: Option<SyntaxToken>, |
110 | pub(super) prev_sibling: Option<PrevSibling>, | ||
111 | pub(super) in_loop_body: bool, | 104 | pub(super) in_loop_body: bool, |
112 | pub(super) is_match_arm: bool, | 105 | pub(super) is_match_arm: bool, |
113 | pub(super) incomplete_let: bool, | 106 | pub(super) incomplete_let: bool, |
@@ -173,7 +166,6 @@ impl<'a> CompletionContext<'a> { | |||
173 | is_pat_or_const: None, | 166 | is_pat_or_const: None, |
174 | is_trivial_path: false, | 167 | is_trivial_path: false, |
175 | path_qual: None, | 168 | path_qual: None, |
176 | after_if: false, | ||
177 | can_be_stmt: false, | 169 | can_be_stmt: false, |
178 | is_expr: false, | 170 | is_expr: false, |
179 | is_new_item: false, | 171 | is_new_item: false, |
@@ -288,6 +280,10 @@ impl<'a> CompletionContext<'a> { | |||
288 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | 280 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) |
289 | } | 281 | } |
290 | 282 | ||
283 | pub(crate) fn expects_expression(&self) -> bool { | ||
284 | self.is_expr | ||
285 | } | ||
286 | |||
291 | pub(crate) fn has_block_expr_parent(&self) -> bool { | 287 | pub(crate) fn has_block_expr_parent(&self) -> bool { |
292 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | 288 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) |
293 | } | 289 | } |
@@ -304,7 +300,14 @@ impl<'a> CompletionContext<'a> { | |||
304 | } | 300 | } |
305 | 301 | ||
306 | pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { | 302 | pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { |
307 | self.prev_sibling.is_some() | 303 | matches!( |
304 | self.prev_sibling, | ||
305 | Some(ImmediatePrevSibling::ImplDefType) | Some(ImmediatePrevSibling::TraitDefName) | ||
306 | ) | ||
307 | } | ||
308 | |||
309 | pub(crate) fn after_if(&self) -> bool { | ||
310 | matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr)) | ||
308 | } | 311 | } |
309 | 312 | ||
310 | pub(crate) fn is_path_disallowed(&self) -> bool { | 313 | pub(crate) fn is_path_disallowed(&self) -> bool { |
@@ -316,15 +319,10 @@ impl<'a> CompletionContext<'a> { | |||
316 | 319 | ||
317 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { | 320 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { |
318 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); | 321 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); |
319 | let syntax_element = NodeOrToken::Token(fake_ident_token.clone()); | 322 | let syntax_element = NodeOrToken::Token(fake_ident_token); |
320 | self.previous_token = previous_token(syntax_element.clone()); | 323 | self.previous_token = previous_token(syntax_element.clone()); |
321 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | 324 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); |
322 | self.is_match_arm = is_match_arm(syntax_element.clone()); | 325 | self.is_match_arm = is_match_arm(syntax_element.clone()); |
323 | if has_prev_sibling(syntax_element.clone(), IMPL) { | ||
324 | self.prev_sibling = Some(PrevSibling::Impl) | ||
325 | } else if has_prev_sibling(syntax_element.clone(), TRAIT) { | ||
326 | self.prev_sibling = Some(PrevSibling::Trait) | ||
327 | } | ||
328 | 326 | ||
329 | self.mod_declaration_under_caret = | 327 | self.mod_declaration_under_caret = |
330 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) | 328 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) |
@@ -338,8 +336,6 @@ impl<'a> CompletionContext<'a> { | |||
338 | let fn_is_prev = self.previous_token_is(T![fn]); | 336 | let fn_is_prev = self.previous_token_is(T![fn]); |
339 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); | 337 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); |
340 | self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2; | 338 | self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2; |
341 | |||
342 | self.completion_location = determine_location(fake_ident_token); | ||
343 | } | 339 | } |
344 | 340 | ||
345 | fn fill_impl_def(&mut self) { | 341 | fn fill_impl_def(&mut self) { |
@@ -465,6 +461,8 @@ impl<'a> CompletionContext<'a> { | |||
465 | Some(it) => it, | 461 | Some(it) => it, |
466 | None => return, | 462 | None => return, |
467 | }; | 463 | }; |
464 | self.completion_location = determine_location(&name_like); | ||
465 | self.prev_sibling = determine_prev_sibling(&name_like); | ||
468 | match name_like { | 466 | match name_like { |
469 | ast::NameLike::Lifetime(lifetime) => { | 467 | ast::NameLike::Lifetime(lifetime) => { |
470 | self.classify_lifetime(original_file, lifetime, offset); | 468 | self.classify_lifetime(original_file, lifetime, offset); |
@@ -653,17 +651,6 @@ impl<'a> CompletionContext<'a> { | |||
653 | }) | 651 | }) |
654 | .unwrap_or(false); | 652 | .unwrap_or(false); |
655 | self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); | 653 | self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); |
656 | |||
657 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { | ||
658 | if let Some(if_expr) = | ||
659 | self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off) | ||
660 | { | ||
661 | if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start() | ||
662 | { | ||
663 | self.after_if = true; | ||
664 | } | ||
665 | } | ||
666 | } | ||
667 | } | 654 | } |
668 | 655 | ||
669 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 656 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { |
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index c8a88367d..caf0ef39f 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs | |||
@@ -4,12 +4,19 @@ use syntax::{ | |||
4 | algo::non_trivia_sibling, | 4 | algo::non_trivia_sibling, |
5 | ast::{self, LoopBodyOwner}, | 5 | ast::{self, LoopBodyOwner}, |
6 | match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, | 6 | match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, |
7 | SyntaxKind::{self, *}, | 7 | SyntaxKind::*, |
8 | SyntaxNode, SyntaxToken, T, | 8 | SyntaxNode, SyntaxToken, T, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | #[cfg(test)] | 11 | #[cfg(test)] |
12 | use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; | 12 | use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; |
13 | /// Direct parent container of the cursor position | ||
14 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
15 | pub(crate) enum ImmediatePrevSibling { | ||
16 | IfExpr, | ||
17 | TraitDefName, | ||
18 | ImplDefType, | ||
19 | } | ||
13 | 20 | ||
14 | /// Direct parent container of the cursor position | 21 | /// Direct parent container of the cursor position |
15 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | 22 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
@@ -24,35 +31,61 @@ pub(crate) enum ImmediateLocation { | |||
24 | ItemList, | 31 | ItemList, |
25 | } | 32 | } |
26 | 33 | ||
27 | pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { | 34 | pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<ImmediatePrevSibling> { |
28 | // First walk the element we are completing up to its highest node that has the same text range | 35 | let node = maximize_name_ref(name_like)?; |
29 | // as the element so that we can check in what context it immediately lies. We only do this for | 36 | let node = match node.parent().and_then(ast::MacroCall::cast) { |
30 | // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically. | 37 | // When a path is being typed after the name of a trait/type of an impl it is being |
31 | // We only wanna do this if the NameRef is the last segment of the path. | 38 | // parsed as a macro, so when the trait/impl has a block following it an we are between the |
32 | let node = match tok.parent().and_then(ast::NameLike::cast)? { | 39 | // name and block the macro will attach the block to itself so maximizing fails to take |
33 | ast::NameLike::NameRef(name_ref) => { | 40 | // that into account |
34 | if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) { | 41 | // FIXME path expr and statement have a similar problem with attrs |
35 | let p = segment.parent_path(); | 42 | Some(call) |
36 | if p.parent_path().is_none() { | 43 | if call.excl_token().is_none() |
37 | p.syntax() | 44 | && call.token_tree().map_or(false, |t| t.l_curly_token().is_some()) |
38 | .ancestors() | 45 | && call.semicolon_token().is_none() => |
39 | .take_while(|it| it.text_range() == p.syntax().text_range()) | 46 | { |
40 | .last()? | 47 | call.syntax().clone() |
41 | } else { | 48 | } |
42 | return None; | 49 | _ => node, |
50 | }; | ||
51 | let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; | ||
52 | let res = match_ast! { | ||
53 | match prev_sibling { | ||
54 | ast::ExprStmt(it) => { | ||
55 | let node = it.expr().filter(|_| it.semicolon_token().is_none())?.syntax().clone(); | ||
56 | match_ast! { | ||
57 | match node { | ||
58 | ast::IfExpr(_it) => ImmediatePrevSibling::IfExpr, | ||
59 | _ => return None, | ||
60 | } | ||
43 | } | 61 | } |
44 | } else { | 62 | }, |
45 | return None; | 63 | ast::Trait(it) => if it.assoc_item_list().is_none() { |
46 | } | 64 | ImmediatePrevSibling::TraitDefName |
65 | } else { | ||
66 | return None | ||
67 | }, | ||
68 | ast::Impl(it) => if it.assoc_item_list().is_none() | ||
69 | && (it.for_token().is_none() || it.self_ty().is_some()) { | ||
70 | ImmediatePrevSibling::ImplDefType | ||
71 | } else { | ||
72 | return None | ||
73 | }, | ||
74 | _ => return None, | ||
47 | } | 75 | } |
48 | it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(), | ||
49 | }; | 76 | }; |
77 | Some(res) | ||
78 | } | ||
79 | |||
80 | pub(crate) fn determine_location(name_like: &ast::NameLike) -> Option<ImmediateLocation> { | ||
81 | let node = maximize_name_ref(name_like)?; | ||
50 | let parent = match node.parent() { | 82 | let parent = match node.parent() { |
51 | Some(parent) => match ast::MacroCall::cast(parent.clone()) { | 83 | 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. | 84 | // 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 | 85 | // 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 | 86 | // 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. | 87 | // precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ. |
88 | // FIXME path expr and statement have a similar problem | ||
56 | Some(call) | 89 | Some(call) |
57 | if call.excl_token().is_none() | 90 | if call.excl_token().is_none() |
58 | && call.token_tree().is_none() | 91 | && call.token_tree().is_none() |
@@ -90,56 +123,30 @@ pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> | |||
90 | Some(res) | 123 | Some(res) |
91 | } | 124 | } |
92 | 125 | ||
93 | #[cfg(test)] | 126 | fn maximize_name_ref(name_like: &ast::NameLike) -> Option<SyntaxNode> { |
94 | fn check_location(code: &str, loc: ImmediateLocation) { | 127 | // First walk the element we are completing up to its highest node that has the same text range |
95 | check_pattern_is_applicable(code, |e| { | 128 | // as the element so that we can check in what context it immediately lies. We only do this for |
96 | assert_eq!(determine_location(e.into_token().expect("Expected a token")), Some(loc)); | 129 | // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically. |
97 | true | 130 | // We only wanna do this if the NameRef is the last segment of the path. |
98 | }); | 131 | let node = match name_like { |
99 | } | 132 | ast::NameLike::NameRef(name_ref) => { |
100 | 133 | if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) { | |
101 | #[test] | 134 | let p = segment.parent_path(); |
102 | fn test_has_trait_parent() { | 135 | if p.parent_path().is_none() { |
103 | check_location(r"trait A { f$0 }", ImmediateLocation::Trait); | 136 | p.syntax() |
104 | } | 137 | .ancestors() |
105 | 138 | .take_while(|it| it.text_range() == p.syntax().text_range()) | |
106 | #[test] | 139 | .last()? |
107 | fn test_has_use_parent() { | 140 | } else { |
108 | check_location(r"use f$0", ImmediateLocation::Use); | 141 | return None; |
109 | } | 142 | } |
110 | 143 | } else { | |
111 | #[test] | 144 | return None; |
112 | fn test_has_impl_parent() { | 145 | } |
113 | check_location(r"impl A { f$0 }", ImmediateLocation::Impl); | 146 | } |
114 | } | 147 | it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(), |
115 | #[test] | 148 | }; |
116 | fn test_has_field_list_parent() { | 149 | Some(node) |
117 | check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField); | ||
118 | check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField); | ||
119 | } | ||
120 | |||
121 | #[test] | ||
122 | fn test_has_block_expr_parent() { | ||
123 | check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr); | ||
124 | } | ||
125 | |||
126 | #[test] | ||
127 | fn test_has_ident_pat_parent() { | ||
128 | check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat); | ||
129 | check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat); | ||
130 | check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat); | ||
131 | check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat); | ||
132 | } | ||
133 | |||
134 | #[test] | ||
135 | fn test_has_ref_expr_parent() { | ||
136 | check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr); | ||
137 | } | ||
138 | |||
139 | #[test] | ||
140 | fn test_has_item_list_or_source_file_parent() { | ||
141 | check_location(r"i$0", ImmediateLocation::ItemList); | ||
142 | check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList); | ||
143 | } | 150 | } |
144 | 151 | ||
145 | pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { | 152 | pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { |
@@ -191,14 +198,6 @@ fn test_for_is_prev2() { | |||
191 | check_pattern_is_applicable(r"for i i$0", for_is_prev2); | 198 | check_pattern_is_applicable(r"for i i$0", for_is_prev2); |
192 | } | 199 | } |
193 | 200 | ||
194 | pub(crate) fn has_prev_sibling(element: SyntaxElement, kind: SyntaxKind) -> bool { | ||
195 | previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == kind).is_some() | ||
196 | } | ||
197 | #[test] | ||
198 | fn test_has_impl_as_prev_sibling() { | ||
199 | check_pattern_is_applicable(r"impl A w$0 {}", |it| has_prev_sibling(it, IMPL)); | ||
200 | } | ||
201 | |||
202 | pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { | 201 | pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { |
203 | element | 202 | element |
204 | .ancestors() | 203 | .ancestors() |
@@ -247,3 +246,111 @@ fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<Syntax | |||
247 | non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev) | 246 | non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev) |
248 | } | 247 | } |
249 | } | 248 | } |
249 | |||
250 | #[cfg(test)] | ||
251 | mod tests { | ||
252 | use super::*; | ||
253 | |||
254 | fn check_location(code: &str, loc: impl Into<Option<ImmediateLocation>>) { | ||
255 | check_pattern_is_applicable(code, |e| { | ||
256 | let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike"); | ||
257 | assert_eq!(determine_location(name), loc.into()); | ||
258 | true | ||
259 | }); | ||
260 | } | ||
261 | |||
262 | fn check_prev_sibling(code: &str, sibling: impl Into<Option<ImmediatePrevSibling>>) { | ||
263 | check_pattern_is_applicable(code, |e| { | ||
264 | let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike"); | ||
265 | assert_eq!(determine_prev_sibling(name), sibling.into()); | ||
266 | true | ||
267 | }); | ||
268 | } | ||
269 | |||
270 | #[test] | ||
271 | fn test_trait_loc() { | ||
272 | check_location(r"trait A { f$0 }", ImmediateLocation::Trait); | ||
273 | check_location(r"trait A { #[attr] f$0 }", ImmediateLocation::Trait); | ||
274 | check_location(r"trait A { f$0 fn f() {} }", ImmediateLocation::Trait); | ||
275 | check_location(r"trait A { fn f() {} f$0 }", ImmediateLocation::Trait); | ||
276 | check_location(r"trait A$0 {}", None); | ||
277 | check_location(r"trait A { fn f$0 }", None); | ||
278 | } | ||
279 | |||
280 | #[test] | ||
281 | fn test_impl_loc() { | ||
282 | check_location(r"impl A { f$0 }", ImmediateLocation::Impl); | ||
283 | check_location(r"impl A { #[attr] f$0 }", ImmediateLocation::Impl); | ||
284 | check_location(r"impl A { f$0 fn f() {} }", ImmediateLocation::Impl); | ||
285 | check_location(r"impl A { fn f() {} f$0 }", ImmediateLocation::Impl); | ||
286 | check_location(r"impl A$0 {}", None); | ||
287 | check_location(r"impl A { fn f$0 }", None); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
291 | fn test_use_loc() { | ||
292 | check_location(r"use f$0", ImmediateLocation::Use); | ||
293 | check_location(r"use f$0;", ImmediateLocation::Use); | ||
294 | check_location(r"use f::{f$0}", None); | ||
295 | check_location(r"use {f$0}", None); | ||
296 | } | ||
297 | |||
298 | #[test] | ||
299 | fn test_record_field_loc() { | ||
300 | check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField); | ||
301 | check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField); | ||
302 | check_location(r"struct Foo { pub f: i32, f$0 }", ImmediateLocation::RecordField); | ||
303 | } | ||
304 | |||
305 | #[test] | ||
306 | fn test_block_expr_loc() { | ||
307 | check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr); | ||
308 | check_location(r"fn my_fn() { f$0 f }", ImmediateLocation::BlockExpr); | ||
309 | } | ||
310 | |||
311 | #[test] | ||
312 | fn test_ident_pat_loc() { | ||
313 | check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat); | ||
314 | check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat); | ||
315 | check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat); | ||
316 | check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat); | ||
317 | } | ||
318 | |||
319 | #[test] | ||
320 | fn test_ref_expr_loc() { | ||
321 | check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr); | ||
322 | } | ||
323 | |||
324 | #[test] | ||
325 | fn test_item_list_loc() { | ||
326 | check_location(r"i$0", ImmediateLocation::ItemList); | ||
327 | check_location(r"#[attr] i$0", ImmediateLocation::ItemList); | ||
328 | check_location(r"fn f() {} i$0", ImmediateLocation::ItemList); | ||
329 | check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList); | ||
330 | check_location(r"mod foo { #[attr] f$0 }", ImmediateLocation::ItemList); | ||
331 | check_location(r"mod foo { fn f() {} f$0 }", ImmediateLocation::ItemList); | ||
332 | check_location(r"mod foo$0 {}", None); | ||
333 | } | ||
334 | |||
335 | #[test] | ||
336 | fn test_impl_prev_sibling() { | ||
337 | check_prev_sibling(r"impl A w$0 ", ImmediatePrevSibling::ImplDefType); | ||
338 | check_prev_sibling(r"impl A w$0 {}", ImmediatePrevSibling::ImplDefType); | ||
339 | check_prev_sibling(r"impl A for A w$0 ", ImmediatePrevSibling::ImplDefType); | ||
340 | check_prev_sibling(r"impl A for A w$0 {}", ImmediatePrevSibling::ImplDefType); | ||
341 | check_prev_sibling(r"impl A for w$0 {}", None); | ||
342 | check_prev_sibling(r"impl A for w$0", None); | ||
343 | } | ||
344 | |||
345 | #[test] | ||
346 | fn test_trait_prev_sibling() { | ||
347 | check_prev_sibling(r"trait A w$0 ", ImmediatePrevSibling::TraitDefName); | ||
348 | check_prev_sibling(r"trait A w$0 {}", ImmediatePrevSibling::TraitDefName); | ||
349 | } | ||
350 | |||
351 | #[test] | ||
352 | fn test_if_expr_prev_sibling() { | ||
353 | check_prev_sibling(r"fn foo() { if true {} w$0", ImmediatePrevSibling::IfExpr); | ||
354 | check_prev_sibling(r"fn foo() { if true {}; w$0", None); | ||
355 | } | ||
356 | } | ||
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs index 6656fd725..93c7c872c 100644 --- a/crates/ide_completion/src/test_utils.rs +++ b/crates/ide_completion/src/test_utils.rs | |||
@@ -12,7 +12,7 @@ use ide_db::{ | |||
12 | use itertools::Itertools; | 12 | use itertools::Itertools; |
13 | use stdx::{format_to, trim_indent}; | 13 | use stdx::{format_to, trim_indent}; |
14 | use syntax::{AstNode, NodeOrToken, SyntaxElement}; | 14 | use syntax::{AstNode, NodeOrToken, SyntaxElement}; |
15 | use test_utils::{assert_eq_text, RangeOrOffset}; | 15 | use test_utils::assert_eq_text; |
16 | 16 | ||
17 | use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; | 17 | use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; |
18 | 18 | ||
@@ -36,10 +36,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { | |||
36 | let mut database = RootDatabase::default(); | 36 | let mut database = RootDatabase::default(); |
37 | database.apply_change(change_fixture.change); | 37 | database.apply_change(change_fixture.change); |
38 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 38 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
39 | let offset = match range_or_offset { | 39 | let offset = range_or_offset.expect_offset(); |
40 | RangeOrOffset::Range(_) => panic!(), | ||
41 | RangeOrOffset::Offset(it) => it, | ||
42 | }; | ||
43 | (database, FilePosition { file_id, offset }) | 40 | (database, FilePosition { file_id, offset }) |
44 | } | 41 | } |
45 | 42 | ||
@@ -52,10 +49,11 @@ pub(crate) fn do_completion_with_config( | |||
52 | code: &str, | 49 | code: &str, |
53 | kind: CompletionKind, | 50 | kind: CompletionKind, |
54 | ) -> Vec<CompletionItem> { | 51 | ) -> Vec<CompletionItem> { |
55 | let mut kind_completions: Vec<CompletionItem> = | 52 | get_all_items(config, code) |
56 | get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect(); | 53 | .into_iter() |
57 | kind_completions.sort_by(|l, r| l.label().cmp(r.label())); | 54 | .filter(|c| c.completion_kind == kind) |
58 | kind_completions | 55 | .sorted_by(|l, r| l.label().cmp(r.label())) |
56 | .collect() | ||
59 | } | 57 | } |
60 | 58 | ||
61 | pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { | 59 | pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { |