diff options
-rw-r--r-- | crates/base_db/src/fixture.rs | 10 | ||||
-rw-r--r-- | crates/ide/src/fixture.rs | 17 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/keyword.rs | 192 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 9 | ||||
-rw-r--r-- | crates/ide_completion/src/patterns.rs | 12 | ||||
-rw-r--r-- | crates/ide_completion/src/test_utils.rs | 16 | ||||
-rw-r--r-- | crates/ide_db/src/call_info/tests.rs | 6 | ||||
-rw-r--r-- | crates/ide_db/src/traits/tests.rs | 6 | ||||
-rw-r--r-- | crates/test_utils/src/lib.rs | 15 |
9 files changed, 140 insertions, 143 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 0132565e4..69ceba735 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs | |||
@@ -34,19 +34,13 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
34 | 34 | ||
35 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { | 35 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { |
36 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 36 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); |
37 | let offset = match range_or_offset { | 37 | let offset = range_or_offset.expect_offset(); |
38 | RangeOrOffset::Range(_) => panic!("Expected a cursor position, got a range instead"), | ||
39 | RangeOrOffset::Offset(it) => it, | ||
40 | }; | ||
41 | (db, FilePosition { file_id, offset }) | 38 | (db, FilePosition { file_id, offset }) |
42 | } | 39 | } |
43 | 40 | ||
44 | fn with_range(ra_fixture: &str) -> (Self, FileRange) { | 41 | fn with_range(ra_fixture: &str) -> (Self, FileRange) { |
45 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 42 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); |
46 | let range = match range_or_offset { | 43 | let range = range_or_offset.expect_range(); |
47 | RangeOrOffset::Range(it) => it, | ||
48 | RangeOrOffset::Offset(_) => panic!("Expected a cursor range, got a position instead"), | ||
49 | }; | ||
50 | (db, FileRange { file_id, range }) | 44 | (db, FileRange { file_id, range }) |
51 | } | 45 | } |
52 | 46 | ||
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs index cc6641ba1..6780af617 100644 --- a/crates/ide/src/fixture.rs +++ b/crates/ide/src/fixture.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Utilities for creating `Analysis` instances for tests. | 1 | //! Utilities for creating `Analysis` instances for tests. |
2 | use ide_db::base_db::fixture::ChangeFixture; | 2 | use ide_db::base_db::fixture::ChangeFixture; |
3 | use syntax::{TextRange, TextSize}; | 3 | use syntax::{TextRange, TextSize}; |
4 | use test_utils::{extract_annotations, RangeOrOffset}; | 4 | use test_utils::extract_annotations; |
5 | 5 | ||
6 | use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; | 6 | use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; |
7 | 7 | ||
@@ -27,10 +27,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { | |||
27 | let change_fixture = ChangeFixture::parse(ra_fixture); | 27 | let change_fixture = ChangeFixture::parse(ra_fixture); |
28 | host.db.apply_change(change_fixture.change); | 28 | host.db.apply_change(change_fixture.change); |
29 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 29 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
30 | let offset = match range_or_offset { | 30 | let offset = range_or_offset.expect_offset(); |
31 | RangeOrOffset::Range(_) => panic!(), | ||
32 | RangeOrOffset::Offset(it) => it, | ||
33 | }; | ||
34 | (host.analysis(), FilePosition { file_id, offset }) | 31 | (host.analysis(), FilePosition { file_id, offset }) |
35 | } | 32 | } |
36 | 33 | ||
@@ -40,10 +37,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) { | |||
40 | let change_fixture = ChangeFixture::parse(ra_fixture); | 37 | let change_fixture = ChangeFixture::parse(ra_fixture); |
41 | host.db.apply_change(change_fixture.change); | 38 | host.db.apply_change(change_fixture.change); |
42 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 39 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
43 | let range = match range_or_offset { | 40 | let range = range_or_offset.expect_range(); |
44 | RangeOrOffset::Range(it) => it, | ||
45 | RangeOrOffset::Offset(_) => panic!(), | ||
46 | }; | ||
47 | (host.analysis(), FileRange { file_id, range }) | 41 | (host.analysis(), FileRange { file_id, range }) |
48 | } | 42 | } |
49 | 43 | ||
@@ -53,10 +47,7 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil | |||
53 | let change_fixture = ChangeFixture::parse(ra_fixture); | 47 | let change_fixture = ChangeFixture::parse(ra_fixture); |
54 | host.db.apply_change(change_fixture.change); | 48 | host.db.apply_change(change_fixture.change); |
55 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 49 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
56 | let offset = match range_or_offset { | 50 | let offset = range_or_offset.expect_offset(); |
57 | RangeOrOffset::Range(_) => panic!(), | ||
58 | RangeOrOffset::Offset(it) => it, | ||
59 | }; | ||
60 | 51 | ||
61 | let annotations = change_fixture | 52 | let annotations = change_fixture |
62 | .files | 53 | .files |
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index c9673df85..662c389fe 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -39,6 +39,8 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC | |||
39 | } | 39 | } |
40 | } | 40 | } |
41 | 41 | ||
42 | trait Foo {} | ||
43 | |||
42 | pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { | 44 | pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { |
43 | if ctx.token.kind() == SyntaxKind::COMMENT { | 45 | if ctx.token.kind() == SyntaxKind::COMMENT { |
44 | cov_mark::hit!(no_keyword_completion_in_comments); | 46 | cov_mark::hit!(no_keyword_completion_in_comments); |
@@ -48,91 +50,92 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
48 | cov_mark::hit!(no_keyword_completion_in_record_lit); | 50 | cov_mark::hit!(no_keyword_completion_in_record_lit); |
49 | return; | 51 | return; |
50 | } | 52 | } |
53 | let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet); | ||
51 | 54 | ||
52 | let expects_assoc_item = ctx.expects_assoc_item(); | 55 | let expects_assoc_item = ctx.expects_assoc_item(); |
53 | let has_block_expr_parent = ctx.has_block_expr_parent(); | 56 | let has_block_expr_parent = ctx.has_block_expr_parent(); |
54 | let expects_item = ctx.expects_item(); | 57 | let expects_item = ctx.expects_item(); |
58 | |||
55 | if ctx.has_impl_or_trait_prev_sibling() { | 59 | if ctx.has_impl_or_trait_prev_sibling() { |
56 | add_keyword(ctx, acc, "where", "where "); | 60 | // FIXME this also incorrectly shows up after a complete trait/impl |
61 | add_keyword("where", "where "); | ||
57 | return; | 62 | return; |
58 | } | 63 | } |
59 | if ctx.previous_token_is(T![unsafe]) { | 64 | if ctx.previous_token_is(T![unsafe]) { |
60 | if expects_item || has_block_expr_parent { | 65 | if expects_item || expects_assoc_item || has_block_expr_parent { |
61 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}") | 66 | add_keyword("fn", "fn $1($2) {\n $0\n}") |
62 | } | 67 | } |
63 | 68 | ||
64 | if expects_item || has_block_expr_parent { | 69 | if expects_item || has_block_expr_parent { |
65 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); | 70 | add_keyword("trait", "trait $1 {\n $0\n}"); |
66 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); | 71 | add_keyword("impl", "impl $1 {\n $0\n}"); |
67 | } | 72 | } |
68 | 73 | ||
69 | return; | 74 | return; |
70 | } | 75 | } |
76 | |||
77 | if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() { | ||
78 | add_keyword("pub(crate)", "pub(crate) "); | ||
79 | add_keyword("pub", "pub "); | ||
80 | } | ||
81 | |||
82 | if expects_item || expects_assoc_item || has_block_expr_parent || ctx.is_match_arm { | ||
83 | add_keyword("unsafe", "unsafe "); | ||
84 | } | ||
85 | |||
71 | if expects_item || expects_assoc_item || has_block_expr_parent { | 86 | if expects_item || expects_assoc_item || has_block_expr_parent { |
72 | add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}"); | 87 | add_keyword("fn", "fn $1($2) {\n $0\n}"); |
88 | add_keyword("const", "const $0"); | ||
89 | add_keyword("type", "type $0"); | ||
73 | } | 90 | } |
91 | |||
74 | if expects_item || has_block_expr_parent { | 92 | if expects_item || has_block_expr_parent { |
75 | add_keyword(ctx, acc, "use", "use "); | 93 | add_keyword("use", "use $0"); |
76 | add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); | 94 | add_keyword("impl", "impl $1 {\n $0\n}"); |
77 | add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); | 95 | add_keyword("trait", "trait $1 {\n $0\n}"); |
96 | add_keyword("static", "static $0"); | ||
97 | add_keyword("extern", "extern $0"); | ||
98 | add_keyword("mod", "mod $0"); | ||
78 | } | 99 | } |
79 | 100 | ||
80 | if expects_item { | 101 | if expects_item { |
81 | add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}"); | 102 | add_keyword("enum", "enum $1 {\n $0\n}"); |
82 | add_keyword(ctx, acc, "struct", "struct $0"); | 103 | add_keyword("struct", "struct $0"); |
83 | add_keyword(ctx, acc, "union", "union $1 {\n $0\n}"); | 104 | add_keyword("union", "union $1 {\n $0\n}"); |
84 | } | 105 | } |
85 | 106 | ||
86 | if ctx.is_expr { | 107 | if ctx.expects_expression() { |
87 | add_keyword(ctx, acc, "match", "match $1 {\n $0\n}"); | 108 | add_keyword("match", "match $1 {\n $0\n}"); |
88 | add_keyword(ctx, acc, "while", "while $1 {\n $0\n}"); | 109 | add_keyword("while", "while $1 {\n $0\n}"); |
89 | add_keyword(ctx, acc, "while let", "while let $1 = $2 {\n $0\n}"); | 110 | add_keyword("while let", "while let $1 = $2 {\n $0\n}"); |
90 | add_keyword(ctx, acc, "loop", "loop {\n $0\n}"); | 111 | add_keyword("loop", "loop {\n $0\n}"); |
91 | add_keyword(ctx, acc, "if", "if $1 {\n $0\n}"); | 112 | add_keyword("if", "if $1 {\n $0\n}"); |
92 | add_keyword(ctx, acc, "if let", "if let $1 = $2 {\n $0\n}"); | 113 | add_keyword("if let", "if let $1 = $2 {\n $0\n}"); |
93 | add_keyword(ctx, acc, "for", "for $1 in $2 {\n $0\n}"); | 114 | add_keyword("for", "for $1 in $2 {\n $0\n}"); |
94 | } | 115 | } |
95 | 116 | ||
96 | if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent { | 117 | if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent { |
97 | add_keyword(ctx, acc, "let", "let "); | 118 | add_keyword("let", "let "); |
98 | } | 119 | } |
99 | 120 | ||
100 | if ctx.after_if { | 121 | if ctx.after_if { |
101 | add_keyword(ctx, acc, "else", "else {\n $0\n}"); | 122 | add_keyword("else", "else {\n $0\n}"); |
102 | add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}"); | 123 | 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 | } | 124 | } |
125 | |||
107 | if ctx.expects_ident_pat_or_ref_expr() { | 126 | if ctx.expects_ident_pat_or_ref_expr() { |
108 | add_keyword(ctx, acc, "mut", "mut "); | 127 | 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 | } | 128 | } |
129 | |||
123 | if ctx.in_loop_body { | 130 | if ctx.in_loop_body { |
124 | if ctx.can_be_stmt { | 131 | if ctx.can_be_stmt { |
125 | add_keyword(ctx, acc, "continue", "continue;"); | 132 | add_keyword("continue", "continue;"); |
126 | add_keyword(ctx, acc, "break", "break;"); | 133 | add_keyword("break", "break;"); |
127 | } else { | 134 | } else { |
128 | add_keyword(ctx, acc, "continue", "continue"); | 135 | add_keyword("continue", "continue"); |
129 | add_keyword(ctx, acc, "break", "break"); | 136 | add_keyword("break", "break"); |
130 | } | 137 | } |
131 | } | 138 | } |
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 | 139 | ||
137 | if !ctx.is_trivial_path { | 140 | if !ctx.is_trivial_path { |
138 | return; | 141 | return; |
@@ -143,8 +146,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
143 | }; | 146 | }; |
144 | 147 | ||
145 | add_keyword( | 148 | add_keyword( |
146 | ctx, | ||
147 | acc, | ||
148 | "return", | 149 | "return", |
149 | match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { | 150 | match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { |
150 | (true, true) => "return $0;", | 151 | (true, true) => "return $0;", |
@@ -161,15 +162,12 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet | |||
161 | 162 | ||
162 | match ctx.config.snippet_cap { | 163 | match ctx.config.snippet_cap { |
163 | Some(cap) => { | 164 | Some(cap) => { |
164 | let tmp; | 165 | if snippet.ends_with('}') && ctx.incomplete_let { |
165 | let snippet = if snippet.ends_with('}') && ctx.incomplete_let { | ||
166 | cov_mark::hit!(let_semi); | 166 | cov_mark::hit!(let_semi); |
167 | tmp = format!("{};", snippet); | 167 | item.insert_snippet(cap, format!("{};", snippet)); |
168 | &tmp | ||
169 | } else { | 168 | } else { |
170 | snippet | 169 | item.insert_snippet(cap, snippet); |
171 | }; | 170 | } |
172 | item.insert_snippet(cap, snippet); | ||
173 | } | 171 | } |
174 | None => { | 172 | None => { |
175 | item.insert_text(if snippet.contains('$') { kw } else { snippet }); | 173 | item.insert_text(if snippet.contains('$') { kw } else { snippet }); |
@@ -232,21 +230,21 @@ mod tests { | |||
232 | check( | 230 | check( |
233 | r"m$0", | 231 | r"m$0", |
234 | expect![[r#" | 232 | expect![[r#" |
233 | kw pub(crate) | ||
234 | kw pub | ||
235 | kw unsafe | ||
235 | kw fn | 236 | kw fn |
237 | kw const | ||
238 | kw type | ||
236 | kw use | 239 | kw use |
237 | kw impl | 240 | kw impl |
238 | kw trait | 241 | kw trait |
242 | kw static | ||
243 | kw extern | ||
244 | kw mod | ||
239 | kw enum | 245 | kw enum |
240 | kw struct | 246 | kw struct |
241 | kw union | 247 | 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 | "#]], | 248 | "#]], |
251 | ); | 249 | ); |
252 | } | 250 | } |
@@ -256,10 +254,16 @@ mod tests { | |||
256 | check( | 254 | check( |
257 | r"fn quux() { $0 }", | 255 | r"fn quux() { $0 }", |
258 | expect![[r#" | 256 | expect![[r#" |
257 | kw unsafe | ||
259 | kw fn | 258 | kw fn |
259 | kw const | ||
260 | kw type | ||
260 | kw use | 261 | kw use |
261 | kw impl | 262 | kw impl |
262 | kw trait | 263 | kw trait |
264 | kw static | ||
265 | kw extern | ||
266 | kw mod | ||
263 | kw match | 267 | kw match |
264 | kw while | 268 | kw while |
265 | kw while let | 269 | kw while let |
@@ -268,12 +272,6 @@ mod tests { | |||
268 | kw if let | 272 | kw if let |
269 | kw for | 273 | kw for |
270 | kw let | 274 | kw let |
271 | kw mod | ||
272 | kw const | ||
273 | kw type | ||
274 | kw static | ||
275 | kw extern | ||
276 | kw unsafe | ||
277 | kw return | 275 | kw return |
278 | "#]], | 276 | "#]], |
279 | ); | 277 | ); |
@@ -284,10 +282,16 @@ mod tests { | |||
284 | check( | 282 | check( |
285 | r"fn quux() { if true { $0 } }", | 283 | r"fn quux() { if true { $0 } }", |
286 | expect![[r#" | 284 | expect![[r#" |
285 | kw unsafe | ||
287 | kw fn | 286 | kw fn |
287 | kw const | ||
288 | kw type | ||
288 | kw use | 289 | kw use |
289 | kw impl | 290 | kw impl |
290 | kw trait | 291 | kw trait |
292 | kw static | ||
293 | kw extern | ||
294 | kw mod | ||
291 | kw match | 295 | kw match |
292 | kw while | 296 | kw while |
293 | kw while let | 297 | kw while let |
@@ -296,12 +300,6 @@ mod tests { | |||
296 | kw if let | 300 | kw if let |
297 | kw for | 301 | kw for |
298 | kw let | 302 | kw let |
299 | kw mod | ||
300 | kw const | ||
301 | kw type | ||
302 | kw static | ||
303 | kw extern | ||
304 | kw unsafe | ||
305 | kw return | 303 | kw return |
306 | "#]], | 304 | "#]], |
307 | ); | 305 | ); |
@@ -312,10 +310,16 @@ mod tests { | |||
312 | check( | 310 | check( |
313 | r#"fn quux() { if true { () } $0 }"#, | 311 | r#"fn quux() { if true { () } $0 }"#, |
314 | expect![[r#" | 312 | expect![[r#" |
313 | kw unsafe | ||
315 | kw fn | 314 | kw fn |
315 | kw const | ||
316 | kw type | ||
316 | kw use | 317 | kw use |
317 | kw impl | 318 | kw impl |
318 | kw trait | 319 | kw trait |
320 | kw static | ||
321 | kw extern | ||
322 | kw mod | ||
319 | kw match | 323 | kw match |
320 | kw while | 324 | kw while |
321 | kw while let | 325 | kw while let |
@@ -326,12 +330,6 @@ mod tests { | |||
326 | kw let | 330 | kw let |
327 | kw else | 331 | kw else |
328 | kw else if | 332 | kw else if |
329 | kw mod | ||
330 | kw const | ||
331 | kw type | ||
332 | kw static | ||
333 | kw extern | ||
334 | kw unsafe | ||
335 | kw return | 333 | kw return |
336 | "#]], | 334 | "#]], |
337 | ); | 335 | ); |
@@ -353,6 +351,7 @@ fn quux() -> i32 { | |||
353 | } | 351 | } |
354 | "#, | 352 | "#, |
355 | expect![[r#" | 353 | expect![[r#" |
354 | kw unsafe | ||
356 | kw match | 355 | kw match |
357 | kw while | 356 | kw while |
358 | kw while let | 357 | kw while let |
@@ -360,7 +359,6 @@ fn quux() -> i32 { | |||
360 | kw if | 359 | kw if |
361 | kw if let | 360 | kw if let |
362 | kw for | 361 | kw for |
363 | kw unsafe | ||
364 | kw return | 362 | kw return |
365 | "#]], | 363 | "#]], |
366 | ); | 364 | ); |
@@ -371,10 +369,10 @@ fn quux() -> i32 { | |||
371 | check( | 369 | check( |
372 | r"trait My { $0 }", | 370 | r"trait My { $0 }", |
373 | expect![[r#" | 371 | expect![[r#" |
372 | kw unsafe | ||
374 | kw fn | 373 | kw fn |
375 | kw const | 374 | kw const |
376 | kw type | 375 | kw type |
377 | kw unsafe | ||
378 | "#]], | 376 | "#]], |
379 | ); | 377 | ); |
380 | } | 378 | } |
@@ -384,12 +382,12 @@ fn quux() -> i32 { | |||
384 | check( | 382 | check( |
385 | r"impl My { $0 }", | 383 | r"impl My { $0 }", |
386 | expect![[r#" | 384 | expect![[r#" |
385 | kw pub(crate) | ||
386 | kw pub | ||
387 | kw unsafe | ||
387 | kw fn | 388 | kw fn |
388 | kw const | 389 | kw const |
389 | kw type | 390 | kw type |
390 | kw unsafe | ||
391 | kw pub(crate) | ||
392 | kw pub | ||
393 | "#]], | 391 | "#]], |
394 | ); | 392 | ); |
395 | } | 393 | } |
@@ -399,12 +397,12 @@ fn quux() -> i32 { | |||
399 | check( | 397 | check( |
400 | r"impl My { #[foo] $0 }", | 398 | r"impl My { #[foo] $0 }", |
401 | expect![[r#" | 399 | expect![[r#" |
400 | kw pub(crate) | ||
401 | kw pub | ||
402 | kw unsafe | ||
402 | kw fn | 403 | kw fn |
403 | kw const | 404 | kw const |
404 | kw type | 405 | kw type |
405 | kw unsafe | ||
406 | kw pub(crate) | ||
407 | kw pub | ||
408 | "#]], | 406 | "#]], |
409 | ); | 407 | ); |
410 | } | 408 | } |
@@ -414,10 +412,16 @@ fn quux() -> i32 { | |||
414 | check( | 412 | check( |
415 | r"fn my() { loop { $0 } }", | 413 | r"fn my() { loop { $0 } }", |
416 | expect![[r#" | 414 | expect![[r#" |
415 | kw unsafe | ||
417 | kw fn | 416 | kw fn |
417 | kw const | ||
418 | kw type | ||
418 | kw use | 419 | kw use |
419 | kw impl | 420 | kw impl |
420 | kw trait | 421 | kw trait |
422 | kw static | ||
423 | kw extern | ||
424 | kw mod | ||
421 | kw match | 425 | kw match |
422 | kw while | 426 | kw while |
423 | kw while let | 427 | kw while let |
@@ -426,12 +430,6 @@ fn quux() -> i32 { | |||
426 | kw if let | 430 | kw if let |
427 | kw for | 431 | kw for |
428 | kw let | 432 | kw let |
429 | kw mod | ||
430 | kw const | ||
431 | kw type | ||
432 | kw static | ||
433 | kw extern | ||
434 | kw unsafe | ||
435 | kw continue | 433 | kw continue |
436 | kw break | 434 | kw break |
437 | kw return | 435 | kw return |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 923e35dbb..faf8469a5 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -288,6 +288,10 @@ impl<'a> CompletionContext<'a> { | |||
288 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | 288 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) |
289 | } | 289 | } |
290 | 290 | ||
291 | pub(crate) fn expects_expression(&self) -> bool { | ||
292 | self.is_expr | ||
293 | } | ||
294 | |||
291 | pub(crate) fn has_block_expr_parent(&self) -> bool { | 295 | pub(crate) fn has_block_expr_parent(&self) -> bool { |
292 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | 296 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) |
293 | } | 297 | } |
@@ -316,7 +320,7 @@ impl<'a> CompletionContext<'a> { | |||
316 | 320 | ||
317 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { | 321 | 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(); | 322 | 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()); | 323 | let syntax_element = NodeOrToken::Token(fake_ident_token); |
320 | self.previous_token = previous_token(syntax_element.clone()); | 324 | self.previous_token = previous_token(syntax_element.clone()); |
321 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | 325 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); |
322 | self.is_match_arm = is_match_arm(syntax_element.clone()); | 326 | self.is_match_arm = is_match_arm(syntax_element.clone()); |
@@ -338,8 +342,6 @@ impl<'a> CompletionContext<'a> { | |||
338 | let fn_is_prev = self.previous_token_is(T![fn]); | 342 | let fn_is_prev = self.previous_token_is(T![fn]); |
339 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); | 343 | 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; | 344 | 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 | } | 345 | } |
344 | 346 | ||
345 | fn fill_impl_def(&mut self) { | 347 | fn fill_impl_def(&mut self) { |
@@ -465,6 +467,7 @@ impl<'a> CompletionContext<'a> { | |||
465 | Some(it) => it, | 467 | Some(it) => it, |
466 | None => return, | 468 | None => return, |
467 | }; | 469 | }; |
470 | self.completion_location = determine_location(&name_like); | ||
468 | match name_like { | 471 | match name_like { |
469 | ast::NameLike::Lifetime(lifetime) => { | 472 | ast::NameLike::Lifetime(lifetime) => { |
470 | self.classify_lifetime(original_file, lifetime, offset); | 473 | self.classify_lifetime(original_file, lifetime, offset); |
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index c8a88367d..f04471b57 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs | |||
@@ -24,12 +24,12 @@ pub(crate) enum ImmediateLocation { | |||
24 | ItemList, | 24 | ItemList, |
25 | } | 25 | } |
26 | 26 | ||
27 | pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { | 27 | pub(crate) fn determine_location(name_like: &ast::NameLike) -> Option<ImmediateLocation> { |
28 | // First walk the element we are completing up to its highest node that has the same text range | 28 | // First walk the element we are completing up to its highest node that has the same text range |
29 | // as the element so that we can check in what context it immediately lies. We only do this for | 29 | // as the element so that we can check in what context it immediately lies. We only do this for |
30 | // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically. | 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. | 31 | // We only wanna do this if the NameRef is the last segment of the path. |
32 | let node = match tok.parent().and_then(ast::NameLike::cast)? { | 32 | let node = match name_like { |
33 | ast::NameLike::NameRef(name_ref) => { | 33 | ast::NameLike::NameRef(name_ref) => { |
34 | 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) { |
35 | let p = segment.parent_path(); | 35 | let p = segment.parent_path(); |
@@ -93,7 +93,8 @@ pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> | |||
93 | #[cfg(test)] | 93 | #[cfg(test)] |
94 | fn check_location(code: &str, loc: ImmediateLocation) { | 94 | fn check_location(code: &str, loc: ImmediateLocation) { |
95 | check_pattern_is_applicable(code, |e| { | 95 | check_pattern_is_applicable(code, |e| { |
96 | assert_eq!(determine_location(e.into_token().expect("Expected a token")), Some(loc)); | 96 | let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike"); |
97 | assert_eq!(determine_location(name), Some(loc)); | ||
97 | true | 98 | true |
98 | }); | 99 | }); |
99 | } | 100 | } |
@@ -199,6 +200,11 @@ fn test_has_impl_as_prev_sibling() { | |||
199 | check_pattern_is_applicable(r"impl A w$0 {}", |it| has_prev_sibling(it, IMPL)); | 200 | check_pattern_is_applicable(r"impl A w$0 {}", |it| has_prev_sibling(it, IMPL)); |
200 | } | 201 | } |
201 | 202 | ||
203 | #[test] | ||
204 | fn test_has_trait_as_prev_sibling() { | ||
205 | check_pattern_is_applicable(r"trait A w$0 {}", |it| has_prev_sibling(it, TRAIT)); | ||
206 | } | ||
207 | |||
202 | pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { | 208 | pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { |
203 | element | 209 | element |
204 | .ancestors() | 210 | .ancestors() |
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 { |
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs index 1aeda08e5..b585085f3 100644 --- a/crates/ide_db/src/call_info/tests.rs +++ b/crates/ide_db/src/call_info/tests.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use base_db::{fixture::ChangeFixture, FilePosition}; | 1 | use base_db::{fixture::ChangeFixture, FilePosition}; |
2 | use expect_test::{expect, Expect}; | 2 | use expect_test::{expect, Expect}; |
3 | use test_utils::RangeOrOffset; | ||
4 | 3 | ||
5 | use crate::RootDatabase; | 4 | use crate::RootDatabase; |
6 | 5 | ||
@@ -10,10 +9,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { | |||
10 | let mut database = RootDatabase::default(); | 9 | let mut database = RootDatabase::default(); |
11 | database.apply_change(change_fixture.change); | 10 | database.apply_change(change_fixture.change); |
12 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 11 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
13 | let offset = match range_or_offset { | 12 | let offset = range_or_offset.expect_offset(); |
14 | RangeOrOffset::Range(_) => panic!(), | ||
15 | RangeOrOffset::Offset(it) => it, | ||
16 | }; | ||
17 | (database, FilePosition { file_id, offset }) | 13 | (database, FilePosition { file_id, offset }) |
18 | } | 14 | } |
19 | 15 | ||
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs index 2a5482024..de994407c 100644 --- a/crates/ide_db/src/traits/tests.rs +++ b/crates/ide_db/src/traits/tests.rs | |||
@@ -2,7 +2,6 @@ use base_db::{fixture::ChangeFixture, FilePosition}; | |||
2 | use expect_test::{expect, Expect}; | 2 | use expect_test::{expect, Expect}; |
3 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use syntax::ast::{self, AstNode}; | 4 | use syntax::ast::{self, AstNode}; |
5 | use test_utils::RangeOrOffset; | ||
6 | 5 | ||
7 | use crate::RootDatabase; | 6 | use crate::RootDatabase; |
8 | 7 | ||
@@ -12,10 +11,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { | |||
12 | let mut database = RootDatabase::default(); | 11 | let mut database = RootDatabase::default(); |
13 | database.apply_change(change_fixture.change); | 12 | database.apply_change(change_fixture.change); |
14 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); | 13 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); |
15 | let offset = match range_or_offset { | 14 | let offset = range_or_offset.expect_offset(); |
16 | RangeOrOffset::Range(_) => panic!(), | ||
17 | RangeOrOffset::Offset(it) => it, | ||
18 | }; | ||
19 | (database, FilePosition { file_id, offset }) | 15 | (database, FilePosition { file_id, offset }) |
20 | } | 16 | } |
21 | 17 | ||
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index fce4fd6bf..bd017567c 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -96,6 +96,21 @@ pub enum RangeOrOffset { | |||
96 | Offset(TextSize), | 96 | Offset(TextSize), |
97 | } | 97 | } |
98 | 98 | ||
99 | impl RangeOrOffset { | ||
100 | pub fn expect_offset(self) -> TextSize { | ||
101 | match self { | ||
102 | RangeOrOffset::Offset(it) => it, | ||
103 | RangeOrOffset::Range(_) => panic!("expected an offset but got a range instead"), | ||
104 | } | ||
105 | } | ||
106 | pub fn expect_range(self) -> TextRange { | ||
107 | match self { | ||
108 | RangeOrOffset::Range(it) => it, | ||
109 | RangeOrOffset::Offset(_) => panic!("expected a range but got an offset"), | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | |||
99 | impl From<RangeOrOffset> for TextRange { | 114 | impl From<RangeOrOffset> for TextRange { |
100 | fn from(selection: RangeOrOffset) -> Self { | 115 | fn from(selection: RangeOrOffset) -> Self { |
101 | match selection { | 116 | match selection { |