aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-12-21 15:13:21 +0000
committerAleksey Kladov <[email protected]>2018-12-21 15:13:21 +0000
commit45232dfa689bafadf98b92ef30fd32ea9a5e9e7a (patch)
tree4652bd1f393540f2f5abdaaab367cfb0b46a10d6 /crates
parentd4ef07b2355df891d4f9b7641f0246ebe5bd6a6b (diff)
organize completion tests better
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/src/completion.rs343
-rw-r--r--crates/ra_analysis/src/completion/completion_item.rs80
-rw-r--r--crates/ra_analysis/src/completion/reference_completion.rs376
-rw-r--r--crates/ra_analysis/tests/tests.rs60
-rw-r--r--crates/test_utils/src/lib.rs22
5 files changed, 488 insertions, 393 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs
index 09894d955..a11e98ac0 100644
--- a/crates/ra_analysis/src/completion.rs
+++ b/crates/ra_analysis/src/completion.rs
@@ -16,7 +16,7 @@ use hir::source_binder;
16use crate::{ 16use crate::{
17 db, 17 db,
18 Cancelable, FilePosition, 18 Cancelable, FilePosition,
19 completion::completion_item::Completions, 19 completion::completion_item::{Completions, CompletionKind},
20}; 20};
21 21
22pub use crate::completion::completion_item::{CompletionItem, InsertText}; 22pub use crate::completion::completion_item::{CompletionItem, InsertText};
@@ -81,7 +81,12 @@ fn param_completions(acc: &mut Completions, ctx: SyntaxNodeRef) {
81 Some((label, lookup)) 81 Some((label, lookup))
82 } 82 }
83 }) 83 })
84 .for_each(|(label, lookup)| CompletionItem::new(label).lookup_by(lookup).add_to(acc)); 84 .for_each(|(label, lookup)| {
85 CompletionItem::new(label)
86 .lookup_by(lookup)
87 .kind(CompletionKind::Magic)
88 .add_to(acc)
89 });
85 90
86 fn process<'a, N: ast::FnDefOwner<'a>>( 91 fn process<'a, N: ast::FnDefOwner<'a>>(
87 node: N, 92 node: N,
@@ -105,341 +110,61 @@ fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool {
105} 110}
106 111
107#[cfg(test)] 112#[cfg(test)]
108mod tests { 113fn check_completion(code: &str, expected_completions: &str, kind: CompletionKind) {
109 use test_utils::assert_eq_dbg; 114 use crate::mock_analysis::{single_file_with_position, analysis_and_position};
110 115 let (analysis, position) = if code.contains("//-") {
111 use crate::mock_analysis::single_file_with_position; 116 analysis_and_position(code)
117 } else {
118 single_file_with_position(code)
119 };
120 let completions = completions(&analysis.imp.db, position).unwrap().unwrap();
121 completions.assert_match(expected_completions, kind);
122}
112 123
124#[cfg(test)]
125mod tests {
113 use super::*; 126 use super::*;
114 127
115 fn is_snippet(completion_item: &CompletionItem) -> bool { 128 fn check_magic_completion(code: &str, expected_completions: &str) {
116 match completion_item.insert_text() { 129 check_completion(code, expected_completions, CompletionKind::Magic);
117 InsertText::Snippet { .. } => true,
118 _ => false,
119 }
120 }
121
122 fn check_scope_completion(code: &str, expected_completions: &str) {
123 let (analysis, position) = single_file_with_position(code);
124 let completions = completions(&analysis.imp.db, position)
125 .unwrap()
126 .unwrap()
127 .into_iter()
128 .filter(|c| !is_snippet(c))
129 .collect::<Vec<_>>();
130 assert_eq_dbg(expected_completions, &completions);
131 }
132
133 fn check_snippet_completion(code: &str, expected_completions: &str) {
134 let (analysis, position) = single_file_with_position(code);
135 let completions = completions(&analysis.imp.db, position)
136 .unwrap()
137 .unwrap()
138 .into_iter()
139 .filter(is_snippet)
140 .collect::<Vec<_>>();
141 assert_eq_dbg(expected_completions, &completions);
142 }
143
144 #[test]
145 fn test_completion_let_scope() {
146 check_scope_completion(
147 r"
148 fn quux(x: i32) {
149 let y = 92;
150 1 + <|>;
151 let z = ();
152 }
153 ",
154 r#"[CompletionItem { label: "y", lookup: None, snippet: None },
155 CompletionItem { label: "x", lookup: None, snippet: None },
156 CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
157 );
158 }
159
160 #[test]
161 fn test_completion_if_let_scope() {
162 check_scope_completion(
163 r"
164 fn quux() {
165 if let Some(x) = foo() {
166 let y = 92;
167 };
168 if let Some(a) = bar() {
169 let b = 62;
170 1 + <|>
171 }
172 }
173 ",
174 r#"[CompletionItem { label: "b", lookup: None, snippet: None },
175 CompletionItem { label: "a", lookup: None, snippet: None },
176 CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
177 );
178 }
179
180 #[test]
181 fn test_completion_for_scope() {
182 check_scope_completion(
183 r"
184 fn quux() {
185 for x in &[1, 2, 3] {
186 <|>
187 }
188 }
189 ",
190 r#"[CompletionItem { label: "x", lookup: None, snippet: None },
191 CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
192 );
193 }
194
195 #[test]
196 fn test_completion_mod_scope() {
197 check_scope_completion(
198 r"
199 struct Foo;
200 enum Baz {}
201 fn quux() {
202 <|>
203 }
204 ",
205 r#"[CompletionItem { label: "quux", lookup: None, snippet: None },
206 CompletionItem { label: "Foo", lookup: None, snippet: None },
207 CompletionItem { label: "Baz", lookup: None, snippet: None }]"#,
208 );
209 }
210
211 #[test]
212 fn test_completion_mod_scope_no_self_use() {
213 check_scope_completion(
214 r"
215 use foo<|>;
216 ",
217 r#"[]"#,
218 );
219 }
220
221 #[test]
222 fn test_completion_self_path() {
223 check_scope_completion(
224 r"
225 use self::m::<|>;
226
227 mod m {
228 struct Bar;
229 }
230 ",
231 r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }]"#,
232 );
233 }
234
235 #[test]
236 fn test_completion_mod_scope_nested() {
237 check_scope_completion(
238 r"
239 struct Foo;
240 mod m {
241 struct Bar;
242 fn quux() { <|> }
243 }
244 ",
245 r#"[CompletionItem { label: "quux", lookup: None, snippet: None },
246 CompletionItem { label: "Bar", lookup: None, snippet: None }]"#,
247 );
248 }
249
250 #[test]
251 fn test_complete_type() {
252 check_scope_completion(
253 r"
254 struct Foo;
255 fn x() -> <|>
256 ",
257 r#"[CompletionItem { label: "Foo", lookup: None, snippet: None },
258 CompletionItem { label: "x", lookup: None, snippet: None }]"#,
259 )
260 }
261
262 #[test]
263 fn test_complete_shadowing() {
264 check_scope_completion(
265 r"
266 fn foo() -> {
267 let bar = 92;
268 {
269 let bar = 62;
270 <|>
271 }
272 }
273 ",
274 r#"[CompletionItem { label: "bar", lookup: None, snippet: None },
275 CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
276 )
277 }
278
279 #[test]
280 fn test_complete_self() {
281 check_scope_completion(
282 r"
283 impl S { fn foo(&self) { <|> } }
284 ",
285 r#"[CompletionItem { label: "self", lookup: None, snippet: None }]"#,
286 )
287 }
288
289 #[test]
290 fn test_completion_kewords() {
291 check_snippet_completion(r"
292 fn quux() {
293 <|>
294 }
295 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
296 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
297 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
298 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
299 CompletionItem { label: "return", lookup: None, snippet: Some("return") },
300 CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
301 CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
302 }
303
304 #[test]
305 fn test_completion_else() {
306 check_snippet_completion(r"
307 fn quux() {
308 if true {
309 ()
310 } <|>
311 }
312 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
313 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
314 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
315 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
316 CompletionItem { label: "else", lookup: None, snippet: Some("else {$0}") },
317 CompletionItem { label: "else if", lookup: None, snippet: Some("else if $0 {}") },
318 CompletionItem { label: "return", lookup: None, snippet: Some("return") },
319 CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
320 CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
321 }
322
323 #[test]
324 fn test_completion_return_value() {
325 check_snippet_completion(r"
326 fn quux() -> i32 {
327 <|>
328 92
329 }
330 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
331 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
332 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
333 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
334 CompletionItem { label: "return", lookup: None, snippet: Some("return $0;") },
335 CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
336 CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
337 check_snippet_completion(r"
338 fn quux() {
339 <|>
340 92
341 }
342 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
343 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
344 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
345 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
346 CompletionItem { label: "return", lookup: None, snippet: Some("return;") },
347 CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
348 CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
349 }
350
351 #[test]
352 fn test_completion_return_no_stmt() {
353 check_snippet_completion(r"
354 fn quux() -> i32 {
355 match () {
356 () => <|>
357 }
358 }
359 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
360 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
361 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
362 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
363 CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
364 CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
365 CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
366 }
367
368 #[test]
369 fn test_continue_break_completion() {
370 check_snippet_completion(r"
371 fn quux() -> i32 {
372 loop { <|> }
373 }
374 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
375 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
376 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
377 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
378 CompletionItem { label: "continue", lookup: None, snippet: Some("continue") },
379 CompletionItem { label: "break", lookup: None, snippet: Some("break") },
380 CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
381 CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
382 CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
383 check_snippet_completion(r"
384 fn quux() -> i32 {
385 loop { || { <|> } }
386 }
387 ", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
388 CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
389 CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
390 CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
391 CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
392 CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
393 CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
394 } 130 }
395 131
396 #[test] 132 #[test]
397 fn test_param_completion_last_param() { 133 fn test_param_completion_last_param() {
398 check_scope_completion(r" 134 check_magic_completion(
135 r"
399 fn foo(file_id: FileId) {} 136 fn foo(file_id: FileId) {}
400 fn bar(file_id: FileId) {} 137 fn bar(file_id: FileId) {}
401 fn baz(file<|>) {} 138 fn baz(file<|>) {}
402 ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#); 139 ",
140 r#"file_id "file_id: FileId""#,
141 );
403 } 142 }
404 143
405 #[test] 144 #[test]
406 fn test_param_completion_nth_param() { 145 fn test_param_completion_nth_param() {
407 check_scope_completion(r" 146 check_magic_completion(
147 r"
408 fn foo(file_id: FileId) {} 148 fn foo(file_id: FileId) {}
409 fn bar(file_id: FileId) {} 149 fn bar(file_id: FileId) {}
410 fn baz(file<|>, x: i32) {} 150 fn baz(file<|>, x: i32) {}
411 ", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#); 151 ",
152 r#"file_id "file_id: FileId""#,
153 );
412 } 154 }
413 155
414 #[test] 156 #[test]
415 fn test_param_completion_trait_param() { 157 fn test_param_completion_trait_param() {
416 check_scope_completion(r" 158 check_magic_completion(
159 r"
417 pub(crate) trait SourceRoot { 160 pub(crate) trait SourceRoot {
418 pub fn contains(&self, file_id: FileId) -> bool; 161 pub fn contains(&self, file_id: FileId) -> bool;
419 pub fn module_map(&self) -> &ModuleMap; 162 pub fn module_map(&self) -> &ModuleMap;
420 pub fn lines(&self, file_id: FileId) -> &LineIndex; 163 pub fn lines(&self, file_id: FileId) -> &LineIndex;
421 pub fn syntax(&self, file<|>) 164 pub fn syntax(&self, file<|>)
422 } 165 }
423 ", r#"[CompletionItem { label: "self", lookup: None, snippet: None },
424 CompletionItem { label: "SourceRoot", lookup: None, snippet: None },
425 CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
426 }
427
428 #[test]
429 fn test_item_snippets() {
430 // check_snippet_completion(r"
431 // <|>
432 // ",
433 // r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##,
434 // );
435 check_snippet_completion(r"
436 #[cfg(test)]
437 mod tests {
438 <|>
439 }
440 ", 166 ",
441 r##"[CompletionItem { label: "Test function", lookup: Some("tfn"), snippet: Some("#[test]\nfn ${1:feature}() {\n $0\n}") }, 167 r#"file_id "file_id: FileId""#,
442 CompletionItem { label: "pub(crate)", lookup: None, snippet: Some("pub(crate) $0") }]"##,
443 ); 168 );
444 } 169 }
445} 170}
diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs
index 8aa9da005..d5d751759 100644
--- a/crates/ra_analysis/src/completion/completion_item.rs
+++ b/crates/ra_analysis/src/completion/completion_item.rs
@@ -6,6 +6,8 @@ pub struct CompletionItem {
6 label: String, 6 label: String,
7 lookup: Option<String>, 7 lookup: Option<String>,
8 snippet: Option<String>, 8 snippet: Option<String>,
9 /// Used only internally in test, to check only specific kind of completion.
10 kind: CompletionKind,
9} 11}
10 12
11pub enum InsertText { 13pub enum InsertText {
@@ -13,6 +15,18 @@ pub enum InsertText {
13 Snippet { text: String }, 15 Snippet { text: String },
14} 16}
15 17
18#[derive(Debug, PartialEq, Eq)]
19pub(crate) enum CompletionKind {
20 /// Parser-based keyword completion.
21 Keyword,
22 /// Your usual "complete all valid identifiers".
23 Reference,
24 /// "Secret sauce" completions.
25 Magic,
26 Snippet,
27 Unspecified,
28}
29
16impl CompletionItem { 30impl CompletionItem {
17 pub(crate) fn new(label: impl Into<String>) -> Builder { 31 pub(crate) fn new(label: impl Into<String>) -> Builder {
18 let label = label.into(); 32 let label = label.into();
@@ -20,6 +34,7 @@ impl CompletionItem {
20 label, 34 label,
21 lookup: None, 35 lookup: None,
22 snippet: None, 36 snippet: None,
37 kind: CompletionKind::Unspecified,
23 } 38 }
24 } 39 }
25 /// What user sees in pop-up in the UI. 40 /// What user sees in pop-up in the UI.
@@ -50,28 +65,34 @@ pub(crate) struct Builder {
50 label: String, 65 label: String,
51 lookup: Option<String>, 66 lookup: Option<String>,
52 snippet: Option<String>, 67 snippet: Option<String>,
68 kind: CompletionKind,
53} 69}
54 70
55impl Builder { 71impl Builder {
56 pub fn add_to(self, acc: &mut Completions) { 72 pub(crate) fn add_to(self, acc: &mut Completions) {
57 acc.add(self.build()) 73 acc.add(self.build())
58 } 74 }
59 75
60 pub fn build(self) -> CompletionItem { 76 pub(crate) fn build(self) -> CompletionItem {
61 CompletionItem { 77 CompletionItem {
62 label: self.label, 78 label: self.label,
63 lookup: self.lookup, 79 lookup: self.lookup,
64 snippet: self.snippet, 80 snippet: self.snippet,
81 kind: self.kind,
65 } 82 }
66 } 83 }
67 pub fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 84 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
68 self.lookup = Some(lookup.into()); 85 self.lookup = Some(lookup.into());
69 self 86 self
70 } 87 }
71 pub fn snippet(mut self, snippet: impl Into<String>) -> Builder { 88 pub(crate) fn snippet(mut self, snippet: impl Into<String>) -> Builder {
72 self.snippet = Some(snippet.into()); 89 self.snippet = Some(snippet.into());
73 self 90 self
74 } 91 }
92 pub(crate) fn kind(mut self, kind: CompletionKind) -> Builder {
93 self.kind = kind;
94 self
95 }
75} 96}
76 97
77impl Into<CompletionItem> for Builder { 98impl Into<CompletionItem> for Builder {
@@ -97,6 +118,57 @@ impl Completions {
97 { 118 {
98 items.into_iter().for_each(|item| self.add(item.into())) 119 items.into_iter().for_each(|item| self.add(item.into()))
99 } 120 }
121
122 #[cfg(test)]
123 pub(crate) fn assert_match(&self, expected: &str, kind: CompletionKind) {
124 let expected = normalize(expected);
125 let actual = self.debug_render(kind);
126 test_utils::assert_eq_text!(expected.as_str(), actual.as_str(),);
127
128 /// Normalize the textual representation of `Completions`:
129 /// replace `;` with newlines, normalize whitespace
130 fn normalize(expected: &str) -> String {
131 use ra_syntax::{tokenize, TextUnit, TextRange, SyntaxKind::SEMI};
132 let mut res = String::new();
133 for line in expected.trim().lines() {
134 let line = line.trim();
135 let mut start_offset: TextUnit = 0.into();
136 // Yep, we use rust tokenize in completion tests :-)
137 for token in tokenize(line) {
138 let range = TextRange::offset_len(start_offset, token.len);
139 start_offset += token.len;
140 if token.kind == SEMI {
141 res.push('\n');
142 } else {
143 res.push_str(&line[range]);
144 }
145 }
146
147 res.push('\n');
148 }
149 res
150 }
151 }
152
153 #[cfg(test)]
154 fn debug_render(&self, kind: CompletionKind) -> String {
155 let mut res = String::new();
156 for c in self.buf.iter() {
157 if c.kind == kind {
158 if let Some(lookup) = &c.lookup {
159 res.push_str(lookup);
160 res.push_str(&format!(" {:?}", c.label));
161 } else {
162 res.push_str(&c.label);
163 }
164 if let Some(snippet) = &c.snippet {
165 res.push_str(&format!(" {:?}", snippet));
166 }
167 res.push('\n');
168 }
169 }
170 res
171 }
100} 172}
101 173
102impl Into<Vec<CompletionItem>> for Completions { 174impl Into<Vec<CompletionItem>> for Completions {
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs
index c578e9e8b..c2a650b6d 100644
--- a/crates/ra_analysis/src/completion/reference_completion.rs
+++ b/crates/ra_analysis/src/completion/reference_completion.rs
@@ -13,7 +13,7 @@ use hir::{
13 13
14use crate::{ 14use crate::{
15 db::RootDatabase, 15 db::RootDatabase,
16 completion::{CompletionItem, Completions}, 16 completion::{CompletionItem, Completions, CompletionKind::*},
17 Cancelable 17 Cancelable
18}; 18};
19 19
@@ -51,7 +51,11 @@ pub(super) fn completions(
51 } 51 }
52 } 52 }
53 }) 53 })
54 .for_each(|(name, _res)| CompletionItem::new(name.to_string()).add_to(acc)); 54 .for_each(|(name, _res)| {
55 CompletionItem::new(name.to_string())
56 .kind(Reference)
57 .add_to(acc)
58 });
55 } 59 }
56 NameRefKind::Path(path) => complete_path(acc, db, module, path)?, 60 NameRefKind::Path(path) => complete_path(acc, db, module, path)?,
57 NameRefKind::BareIdentInMod => { 61 NameRefKind::BareIdentInMod => {
@@ -123,9 +127,13 @@ fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Completions)
123 .scope_chain(name_ref.syntax()) 127 .scope_chain(name_ref.syntax())
124 .flat_map(|scope| scopes.entries(scope).iter()) 128 .flat_map(|scope| scopes.entries(scope).iter())
125 .filter(|entry| shadowed.insert(entry.name())) 129 .filter(|entry| shadowed.insert(entry.name()))
126 .for_each(|entry| CompletionItem::new(entry.name().to_string()).add_to(acc)); 130 .for_each(|entry| {
131 CompletionItem::new(entry.name().to_string())
132 .kind(Reference)
133 .add_to(acc)
134 });
127 if scopes.self_param.is_some() { 135 if scopes.self_param.is_some() {
128 CompletionItem::new("self").add_to(acc); 136 CompletionItem::new("self").kind(Reference).add_to(acc);
129 } 137 }
130} 138}
131 139
@@ -148,9 +156,11 @@ fn complete_path(
148 _ => return Ok(()), 156 _ => return Ok(()),
149 }; 157 };
150 let module_scope = target_module.scope(db)?; 158 let module_scope = target_module.scope(db)?;
151 module_scope 159 module_scope.entries().for_each(|(name, _res)| {
152 .entries() 160 CompletionItem::new(name.to_string())
153 .for_each(|(name, _res)| CompletionItem::new(name.to_string()).add_to(acc)); 161 .kind(Reference)
162 .add_to(acc)
163 });
154 Ok(()) 164 Ok(())
155} 165}
156 166
@@ -164,9 +174,11 @@ fn ${1:feature}() {
164 $0 174 $0
165}", 175}",
166 ) 176 )
177 .kind(Snippet)
167 .add_to(acc); 178 .add_to(acc);
168 CompletionItem::new("pub(crate)") 179 CompletionItem::new("pub(crate)")
169 .snippet("pub(crate) $0") 180 .snippet("pub(crate) $0")
181 .kind(Snippet)
170 .add_to(acc); 182 .add_to(acc);
171} 183}
172 184
@@ -249,14 +261,362 @@ fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<Complet
249} 261}
250 262
251fn keyword(kw: &str, snippet: &str) -> CompletionItem { 263fn keyword(kw: &str, snippet: &str) -> CompletionItem {
252 CompletionItem::new(kw).snippet(snippet).build() 264 CompletionItem::new(kw)
265 .kind(Keyword)
266 .snippet(snippet)
267 .build()
253} 268}
254 269
255fn complete_expr_snippets(acc: &mut Completions) { 270fn complete_expr_snippets(acc: &mut Completions) {
256 CompletionItem::new("pd") 271 CompletionItem::new("pd")
257 .snippet("eprintln!(\"$0 = {:?}\", $0);") 272 .snippet("eprintln!(\"$0 = {:?}\", $0);")
273 .kind(Snippet)
258 .add_to(acc); 274 .add_to(acc);
259 CompletionItem::new("ppd") 275 CompletionItem::new("ppd")
260 .snippet("eprintln!(\"$0 = {:#?}\", $0);") 276 .snippet("eprintln!(\"$0 = {:#?}\", $0);")
277 .kind(Snippet)
261 .add_to(acc); 278 .add_to(acc);
262} 279}
280
281#[cfg(test)]
282mod tests {
283 use crate::completion::{CompletionKind, check_completion};
284
285 fn check_reference_completion(code: &str, expected_completions: &str) {
286 check_completion(code, expected_completions, CompletionKind::Reference);
287 }
288
289 fn check_keyword_completion(code: &str, expected_completions: &str) {
290 check_completion(code, expected_completions, CompletionKind::Keyword);
291 }
292
293 fn check_snippet_completion(code: &str, expected_completions: &str) {
294 check_completion(code, expected_completions, CompletionKind::Snippet);
295 }
296
297 #[test]
298 fn test_completion_let_scope() {
299 check_reference_completion(
300 r"
301 fn quux(x: i32) {
302 let y = 92;
303 1 + <|>;
304 let z = ();
305 }
306 ",
307 "y;x;quux",
308 );
309 }
310
311 #[test]
312 fn test_completion_if_let_scope() {
313 check_reference_completion(
314 r"
315 fn quux() {
316 if let Some(x) = foo() {
317 let y = 92;
318 };
319 if let Some(a) = bar() {
320 let b = 62;
321 1 + <|>
322 }
323 }
324 ",
325 "b;a;quux",
326 );
327 }
328
329 #[test]
330 fn test_completion_for_scope() {
331 check_reference_completion(
332 r"
333 fn quux() {
334 for x in &[1, 2, 3] {
335 <|>
336 }
337 }
338 ",
339 "x;quux",
340 );
341 }
342
343 #[test]
344 fn test_completion_mod_scope() {
345 check_reference_completion(
346 r"
347 struct Foo;
348 enum Baz {}
349 fn quux() {
350 <|>
351 }
352 ",
353 "quux;Foo;Baz",
354 );
355 }
356
357 #[test]
358 fn test_completion_mod_scope_no_self_use() {
359 check_reference_completion(
360 r"
361 use foo<|>;
362 ",
363 "",
364 );
365 }
366
367 #[test]
368 fn test_completion_self_path() {
369 check_reference_completion(
370 r"
371 use self::m::<|>;
372
373 mod m {
374 struct Bar;
375 }
376 ",
377 "Bar",
378 );
379 }
380
381 #[test]
382 fn test_completion_mod_scope_nested() {
383 check_reference_completion(
384 r"
385 struct Foo;
386 mod m {
387 struct Bar;
388 fn quux() { <|> }
389 }
390 ",
391 "quux;Bar",
392 );
393 }
394
395 #[test]
396 fn test_complete_type() {
397 check_reference_completion(
398 r"
399 struct Foo;
400 fn x() -> <|>
401 ",
402 "Foo;x",
403 )
404 }
405
406 #[test]
407 fn test_complete_shadowing() {
408 check_reference_completion(
409 r"
410 fn foo() -> {
411 let bar = 92;
412 {
413 let bar = 62;
414 <|>
415 }
416 }
417 ",
418 "bar;foo",
419 )
420 }
421
422 #[test]
423 fn test_complete_self() {
424 check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self")
425 }
426
427 #[test]
428 fn test_complete_crate_path() {
429 check_reference_completion(
430 "
431 //- /lib.rs
432 mod foo;
433 struct Spam;
434 //- /foo.rs
435 use crate::Sp<|>
436 ",
437 "Spam;foo",
438 );
439 }
440
441 #[test]
442 fn test_complete_crate_path_with_braces() {
443 check_reference_completion(
444 "
445 //- /lib.rs
446 mod foo;
447 struct Spam;
448 //- /foo.rs
449 use crate::{Sp<|>};
450 ",
451 "Spam;foo",
452 );
453 }
454
455 #[test]
456 fn test_complete_crate_path_in_nested_tree() {
457 check_reference_completion(
458 "
459 //- /lib.rs
460 mod foo;
461 pub mod bar {
462 pub mod baz {
463 pub struct Spam;
464 }
465 }
466 //- /foo.rs
467 use crate::{bar::{baz::Sp<|>}};
468 ",
469 "Spam",
470 );
471 }
472
473 #[test]
474 fn test_completion_kewords() {
475 check_keyword_completion(
476 r"
477 fn quux() {
478 <|>
479 }
480 ",
481 r#"
482 if "if $0 {}"
483 match "match $0 {}"
484 while "while $0 {}"
485 loop "loop {$0}"
486 return "return"
487 "#,
488 );
489 }
490
491 #[test]
492 fn test_completion_else() {
493 check_keyword_completion(
494 r"
495 fn quux() {
496 if true {
497 ()
498 } <|>
499 }
500 ",
501 r#"
502 if "if $0 {}"
503 match "match $0 {}"
504 while "while $0 {}"
505 loop "loop {$0}"
506 else "else {$0}"
507 else if "else if $0 {}"
508 return "return"
509 "#,
510 );
511 }
512
513 #[test]
514 fn test_completion_return_value() {
515 check_keyword_completion(
516 r"
517 fn quux() -> i32 {
518 <|>
519 92
520 }
521 ",
522 r#"
523 if "if $0 {}"
524 match "match $0 {}"
525 while "while $0 {}"
526 loop "loop {$0}"
527 return "return $0;"
528 "#,
529 );
530 check_keyword_completion(
531 r"
532 fn quux() {
533 <|>
534 92
535 }
536 ",
537 r#"
538 if "if $0 {}"
539 match "match $0 {}"
540 while "while $0 {}"
541 loop "loop {$0}"
542 return "return;"
543 "#,
544 );
545 }
546
547 #[test]
548 fn test_completion_return_no_stmt() {
549 check_keyword_completion(
550 r"
551 fn quux() -> i32 {
552 match () {
553 () => <|>
554 }
555 }
556 ",
557 r#"
558 if "if $0 {}"
559 match "match $0 {}"
560 while "while $0 {}"
561 loop "loop {$0}"
562 return "return $0"
563 "#,
564 );
565 }
566
567 #[test]
568 fn test_continue_break_completion() {
569 check_keyword_completion(
570 r"
571 fn quux() -> i32 {
572 loop { <|> }
573 }
574 ",
575 r#"
576 if "if $0 {}"
577 match "match $0 {}"
578 while "while $0 {}"
579 loop "loop {$0}"
580 continue "continue"
581 break "break"
582 return "return $0"
583 "#,
584 );
585 check_keyword_completion(
586 r"
587 fn quux() -> i32 {
588 loop { || { <|> } }
589 }
590 ",
591 r#"
592 if "if $0 {}"
593 match "match $0 {}"
594 while "while $0 {}"
595 loop "loop {$0}"
596 return "return $0"
597 "#,
598 );
599 }
600
601 #[test]
602 fn test_item_snippets() {
603 // check_snippet_completion(r"
604 // <|>
605 // ",
606 // r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##,
607 // );
608 check_snippet_completion(
609 r"
610 #[cfg(test)]
611 mod tests {
612 <|>
613 }
614 ",
615 r##"
616 tfn "Test function" "#[test]\nfn ${1:feature}() {\n $0\n}"
617 pub(crate) "pub(crate) $0"
618 "##,
619 );
620 }
621
622}
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index 67738da48..938ca797a 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -452,63 +452,3 @@ fn test_find_all_refs_for_fn_param() {
452 let refs = get_all_refs(code); 452 let refs = get_all_refs(code);
453 assert_eq!(refs.len(), 2); 453 assert_eq!(refs.len(), 2);
454} 454}
455
456#[test]
457fn test_complete_crate_path() {
458 let (analysis, position) = analysis_and_position(
459 "
460 //- /lib.rs
461 mod foo;
462 struct Spam;
463 //- /foo.rs
464 use crate::Sp<|>
465 ",
466 );
467 let completions = analysis.completions(position).unwrap().unwrap();
468 assert_eq_dbg(
469 r#"[CompletionItem { label: "Spam", lookup: None, snippet: None },
470 CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
471 &completions,
472 );
473}
474
475#[test]
476fn test_complete_crate_path_with_braces() {
477 let (analysis, position) = analysis_and_position(
478 "
479 //- /lib.rs
480 mod foo;
481 struct Spam;
482 //- /foo.rs
483 use crate::{Sp<|>};
484 ",
485 );
486 let completions = analysis.completions(position).unwrap().unwrap();
487 assert_eq_dbg(
488 r#"[CompletionItem { label: "Spam", lookup: None, snippet: None },
489 CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
490 &completions,
491 );
492}
493
494#[test]
495fn test_complete_crate_path_in_nested_tree() {
496 let (analysis, position) = analysis_and_position(
497 "
498 //- /lib.rs
499 mod foo;
500 pub mod bar {
501 pub mod baz {
502 pub struct Spam;
503 }
504 }
505 //- /foo.rs
506 use crate::{bar::{baz::Sp<|>}};
507 ",
508 );
509 let completions = analysis.completions(position).unwrap().unwrap();
510 assert_eq_dbg(
511 r#"[CompletionItem { label: "Spam", lookup: None, snippet: None }]"#,
512 &completions,
513 );
514}
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 1ae800d7c..beb936c61 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -10,22 +10,20 @@ pub const CURSOR_MARKER: &str = "<|>";
10 10
11#[macro_export] 11#[macro_export]
12macro_rules! assert_eq_text { 12macro_rules! assert_eq_text {
13 ($expected:expr, $actual:expr) => {{ 13 ($expected:expr, $actual:expr) => {
14 let expected = $expected; 14 assert_eq_text!($expected, $actual,)
15 let actual = $actual; 15 };
16 if expected != actual {
17 let changeset = $crate::__Changeset::new(actual, expected, "\n");
18 println!("Expected:\n{}\n\nActual:\n{}\nDiff:{}\n", expected, actual, changeset);
19 panic!("text differs");
20 }
21 }};
22 ($expected:expr, $actual:expr, $($tt:tt)*) => {{ 16 ($expected:expr, $actual:expr, $($tt:tt)*) => {{
23 let expected = $expected; 17 let expected = $expected;
24 let actual = $actual; 18 let actual = $actual;
25 if expected != actual { 19 if expected != actual {
26 let changeset = $crate::__Changeset::new(actual, expected, "\n"); 20 if expected.trim() == actual.trim() {
27 println!("Expected:\n{}\n\nActual:\n{}\n\nDiff:\n{}\n", expected, actual, changeset); 21 eprintln!("Expected:\n{:?}\n\nActual:\n{:?}\n\nWhitespace difference\n", expected, actual);
28 println!($($tt)*); 22 } else {
23 let changeset = $crate::__Changeset::new(actual, expected, "\n");
24 eprintln!("Expected:\n{}\n\nActual:\n{}\n\nDiff:\n{}\n", expected, actual, changeset);
25 }
26 eprintln!($($tt)*);
29 panic!("text differs"); 27 panic!("text differs");
30 } 28 }
31 }}; 29 }};