aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs306
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs317
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs63
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs26
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs57
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs65
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs27
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs7
-rw-r--r--crates/ra_ide/src/completion/presentation.rs142
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs2
10 files changed, 910 insertions, 102 deletions
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index 8bf952798..fb3f0b743 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -3,25 +3,29 @@
3//! This module uses a bit of static metadata to provide completions 3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes. 4//! for built-in attributes.
5 5
6use super::completion_context::CompletionContext; 6use ra_syntax::{ast, AstNode, SyntaxKind};
7use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}; 7use rustc_hash::FxHashSet;
8use ra_syntax::{ 8
9 ast::{Attr, AttrKind}, 9use crate::completion::{
10 AstNode, 10 completion_context::CompletionContext,
11 completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions},
11}; 12};
12 13
13pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) { 14pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
14 if !ctx.is_attribute { 15 let attribute = ctx.attribute_under_caret.as_ref()?;
15 return;
16 }
17 16
18 let is_inner = ctx 17 match (attribute.path(), attribute.input()) {
19 .original_token 18 (Some(path), Some(ast::AttrInput::TokenTree(token_tree)))
20 .ancestors() 19 if path.to_string() == "derive" =>
21 .find_map(Attr::cast) 20 {
22 .map(|attr| attr.kind() == AttrKind::Inner) 21 complete_derive(acc, ctx, token_tree)
23 .unwrap_or(false); 22 }
23 _ => complete_attribute_start(acc, ctx, attribute),
24 }
25 Some(())
26}
24 27
28fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
25 for attr_completion in ATTRIBUTES { 29 for attr_completion in ATTRIBUTES {
26 let mut item = CompletionItem::new( 30 let mut item = CompletionItem::new(
27 CompletionKind::Attribute, 31 CompletionKind::Attribute,
@@ -37,7 +41,7 @@ pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
37 _ => {} 41 _ => {}
38 } 42 }
39 43
40 if is_inner || !attr_completion.should_be_inner { 44 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.should_be_inner {
41 acc.add(item); 45 acc.add(item);
42 } 46 }
43 } 47 }
@@ -108,7 +112,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[
108 AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false }, 112 AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false },
109 AttrCompletion { 113 AttrCompletion {
110 label: "should_panic", 114 label: "should_panic",
111 snippet: Some(r#"expected = "${0:reason}""#), 115 snippet: Some(r#"should_panic(expected = "${0:reason}")"#),
112 should_be_inner: false, 116 should_be_inner: false,
113 }, 117 },
114 AttrCompletion { 118 AttrCompletion {
@@ -126,6 +130,106 @@ const ATTRIBUTES: &[AttrCompletion] = &[
126 }, 130 },
127]; 131];
128 132
133fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
134 if let Ok(existing_derives) = parse_derive_input(derive_input) {
135 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
136 .into_iter()
137 .filter(|completion| !existing_derives.contains(completion.label))
138 {
139 let mut label = derive_completion.label.to_owned();
140 for dependency in derive_completion
141 .dependencies
142 .into_iter()
143 .filter(|&&dependency| !existing_derives.contains(dependency))
144 {
145 label.push_str(", ");
146 label.push_str(dependency);
147 }
148 acc.add(
149 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label)
150 .kind(CompletionItemKind::Attribute),
151 );
152 }
153
154 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
155 acc.add(
156 CompletionItem::new(
157 CompletionKind::Attribute,
158 ctx.source_range(),
159 custom_derive_name,
160 )
161 .kind(CompletionItemKind::Attribute),
162 );
163 }
164 }
165}
166
167fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
168 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
169 (Some(left_paren), Some(right_paren))
170 if left_paren.kind() == SyntaxKind::L_PAREN
171 && right_paren.kind() == SyntaxKind::R_PAREN =>
172 {
173 let mut input_derives = FxHashSet::default();
174 let mut current_derive = String::new();
175 for token in derive_input
176 .syntax()
177 .children_with_tokens()
178 .filter_map(|token| token.into_token())
179 .skip_while(|token| token != &left_paren)
180 .skip(1)
181 .take_while(|token| token != &right_paren)
182 {
183 if SyntaxKind::COMMA == token.kind() {
184 if !current_derive.is_empty() {
185 input_derives.insert(current_derive);
186 current_derive = String::new();
187 }
188 } else {
189 current_derive.push_str(token.to_string().trim());
190 }
191 }
192
193 if !current_derive.is_empty() {
194 input_derives.insert(current_derive);
195 }
196 Ok(input_derives)
197 }
198 _ => Err(()),
199 }
200}
201
202fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
203 let mut result = FxHashSet::default();
204 ctx.scope().process_all_names(&mut |name, scope_def| {
205 if let hir::ScopeDef::MacroDef(mac) = scope_def {
206 if mac.is_derive_macro() {
207 result.insert(name.to_string());
208 }
209 }
210 });
211 result
212}
213
214struct DeriveCompletion {
215 label: &'static str,
216 dependencies: &'static [&'static str],
217}
218
219/// Standard Rust derives and the information about their dependencies
220/// (the dependencies are needed so that the main derive don't break the compilation when added)
221const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
222 DeriveCompletion { label: "Clone", dependencies: &[] },
223 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
224 DeriveCompletion { label: "Debug", dependencies: &[] },
225 DeriveCompletion { label: "Default", dependencies: &[] },
226 DeriveCompletion { label: "Hash", dependencies: &[] },
227 DeriveCompletion { label: "PartialEq", dependencies: &[] },
228 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
229 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
230 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
231];
232
129#[cfg(test)] 233#[cfg(test)]
130mod tests { 234mod tests {
131 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 235 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
@@ -136,6 +240,170 @@ mod tests {
136 } 240 }
137 241
138 #[test] 242 #[test]
243 fn empty_derive_completion() {
244 assert_debug_snapshot!(
245 do_attr_completion(
246 r"
247 #[derive(<|>)]
248 struct Test {}
249 ",
250 ),
251 @r###"
252 [
253 CompletionItem {
254 label: "Clone",
255 source_range: 30..30,
256 delete: 30..30,
257 insert: "Clone",
258 kind: Attribute,
259 },
260 CompletionItem {
261 label: "Copy, Clone",
262 source_range: 30..30,
263 delete: 30..30,
264 insert: "Copy, Clone",
265 kind: Attribute,
266 },
267 CompletionItem {
268 label: "Debug",
269 source_range: 30..30,
270 delete: 30..30,
271 insert: "Debug",
272 kind: Attribute,
273 },
274 CompletionItem {
275 label: "Default",
276 source_range: 30..30,
277 delete: 30..30,
278 insert: "Default",
279 kind: Attribute,
280 },
281 CompletionItem {
282 label: "Eq, PartialEq",
283 source_range: 30..30,
284 delete: 30..30,
285 insert: "Eq, PartialEq",
286 kind: Attribute,
287 },
288 CompletionItem {
289 label: "Hash",
290 source_range: 30..30,
291 delete: 30..30,
292 insert: "Hash",
293 kind: Attribute,
294 },
295 CompletionItem {
296 label: "Ord, PartialOrd, Eq, PartialEq",
297 source_range: 30..30,
298 delete: 30..30,
299 insert: "Ord, PartialOrd, Eq, PartialEq",
300 kind: Attribute,
301 },
302 CompletionItem {
303 label: "PartialEq",
304 source_range: 30..30,
305 delete: 30..30,
306 insert: "PartialEq",
307 kind: Attribute,
308 },
309 CompletionItem {
310 label: "PartialOrd, PartialEq",
311 source_range: 30..30,
312 delete: 30..30,
313 insert: "PartialOrd, PartialEq",
314 kind: Attribute,
315 },
316 ]
317 "###
318 );
319 }
320
321 #[test]
322 fn no_completion_for_incorrect_derive() {
323 assert_debug_snapshot!(
324 do_attr_completion(
325 r"
326 #[derive{<|>)]
327 struct Test {}
328 ",
329 ),
330 @"[]"
331 );
332 }
333
334 #[test]
335 fn derive_with_input_completion() {
336 assert_debug_snapshot!(
337 do_attr_completion(
338 r"
339 #[derive(serde::Serialize, PartialEq, <|>)]
340 struct Test {}
341 ",
342 ),
343 @r###"
344 [
345 CompletionItem {
346 label: "Clone",
347 source_range: 59..59,
348 delete: 59..59,
349 insert: "Clone",
350 kind: Attribute,
351 },
352 CompletionItem {
353 label: "Copy, Clone",
354 source_range: 59..59,
355 delete: 59..59,
356 insert: "Copy, Clone",
357 kind: Attribute,
358 },
359 CompletionItem {
360 label: "Debug",
361 source_range: 59..59,
362 delete: 59..59,
363 insert: "Debug",
364 kind: Attribute,
365 },
366 CompletionItem {
367 label: "Default",
368 source_range: 59..59,
369 delete: 59..59,
370 insert: "Default",
371 kind: Attribute,
372 },
373 CompletionItem {
374 label: "Eq",
375 source_range: 59..59,
376 delete: 59..59,
377 insert: "Eq",
378 kind: Attribute,
379 },
380 CompletionItem {
381 label: "Hash",
382 source_range: 59..59,
383 delete: 59..59,
384 insert: "Hash",
385 kind: Attribute,
386 },
387 CompletionItem {
388 label: "Ord, PartialOrd, Eq",
389 source_range: 59..59,
390 delete: 59..59,
391 insert: "Ord, PartialOrd, Eq",
392 kind: Attribute,
393 },
394 CompletionItem {
395 label: "PartialOrd",
396 source_range: 59..59,
397 delete: 59..59,
398 insert: "PartialOrd",
399 kind: Attribute,
400 },
401 ]
402 "###
403 );
404 }
405
406 #[test]
139 fn test_attribute_completion() { 407 fn test_attribute_completion() {
140 assert_debug_snapshot!( 408 assert_debug_snapshot!(
141 do_attr_completion( 409 do_attr_completion(
@@ -303,7 +571,7 @@ mod tests {
303 label: "should_panic", 571 label: "should_panic",
304 source_range: 19..19, 572 source_range: 19..19,
305 delete: 19..19, 573 delete: 19..19,
306 insert: "expected = \"${0:reason}\"", 574 insert: "should_panic(expected = \"${0:reason}\")",
307 kind: Attribute, 575 kind: Attribute,
308 }, 576 },
309 CompletionItem { 577 CompletionItem {
@@ -542,7 +810,7 @@ mod tests {
542 label: "should_panic", 810 label: "should_panic",
543 source_range: 20..20, 811 source_range: 20..20,
544 delete: 20..20, 812 delete: 20..20,
545 insert: "expected = \"${0:reason}\"", 813 insert: "should_panic(expected = \"${0:reason}\")",
546 kind: Attribute, 814 kind: Attribute,
547 }, 815 },
548 CompletionItem { 816 CompletionItem {
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 6a0f0c72e..59b58bf98 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -1,12 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2use ra_assists::utils::TryEnum;
3use ra_syntax::{ 3use ra_syntax::{
4 ast::{self, AstNode}, 4 ast::{self, AstNode},
5 TextRange, TextSize, 5 TextRange, TextSize,
6}; 6};
7use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
8 8
9use super::completion_config::SnippetCap;
10use crate::{ 9use crate::{
11 completion::{ 10 completion::{
12 completion_context::CompletionContext, 11 completion_context::CompletionContext,
@@ -15,6 +14,8 @@ use crate::{
15 CompletionItem, 14 CompletionItem,
16}; 15};
17 16
17use super::completion_config::SnippetCap;
18
18pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 19pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
19 if !ctx.config.enable_postfix_completions { 20 if !ctx.config.enable_postfix_completions {
20 return; 21 return;
@@ -37,8 +38,53 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
37 Some(it) => it, 38 Some(it) => it,
38 None => return, 39 None => return,
39 }; 40 };
41 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty);
42 if let Some(try_enum) = &try_enum {
43 match try_enum {
44 TryEnum::Result => {
45 postfix_snippet(
46 ctx,
47 cap,
48 &dot_receiver,
49 "ifl",
50 "if let Ok {}",
51 &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text),
52 )
53 .add_to(acc);
54
55 postfix_snippet(
56 ctx,
57 cap,
58 &dot_receiver,
59 "while",
60 "while let Ok {}",
61 &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text),
62 )
63 .add_to(acc);
64 }
65 TryEnum::Option => {
66 postfix_snippet(
67 ctx,
68 cap,
69 &dot_receiver,
70 "ifl",
71 "if let Some {}",
72 &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text),
73 )
74 .add_to(acc);
40 75
41 if receiver_ty.is_bool() || receiver_ty.is_unknown() { 76 postfix_snippet(
77 ctx,
78 cap,
79 &dot_receiver,
80 "while",
81 "while let Some {}",
82 &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text),
83 )
84 .add_to(acc);
85 }
86 }
87 } else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
42 postfix_snippet( 88 postfix_snippet(
43 ctx, 89 ctx,
44 cap, 90 cap,
@@ -58,7 +104,6 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
58 ) 104 )
59 .add_to(acc); 105 .add_to(acc);
60 } 106 }
61
62 // !&&&42 is a compiler error, ergo process it before considering the references 107 // !&&&42 is a compiler error, ergo process it before considering the references
63 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)) 108 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
64 .add_to(acc); 109 .add_to(acc);
@@ -80,14 +125,53 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
80 let dot_receiver = include_references(dot_receiver); 125 let dot_receiver = include_references(dot_receiver);
81 let receiver_text = 126 let receiver_text =
82 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal); 127 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
128 match try_enum {
129 Some(try_enum) => {
130 match try_enum {
131 TryEnum::Result => {
132 postfix_snippet(
133 ctx,
134 cap,
135 &dot_receiver,
136 "match",
137 "match expr {}",
138 &format!("match {} {{\n Ok(${{1:_}}) => {{$2\\}},\n Err(${{3:_}}) => {{$0\\}},\n}}", receiver_text),
139 )
140 .add_to(acc);
141 }
142 TryEnum::Option => {
143 postfix_snippet(
144 ctx,
145 cap,
146 &dot_receiver,
147 "match",
148 "match expr {}",
149 &format!("match {} {{\n Some(${{1:_}}) => {{$2\\}},\n None => {{$0\\}},\n}}", receiver_text),
150 )
151 .add_to(acc);
152 }
153 }
154 }
155 None => {
156 postfix_snippet(
157 ctx,
158 cap,
159 &dot_receiver,
160 "match",
161 "match expr {}",
162 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text),
163 )
164 .add_to(acc);
165 }
166 }
83 167
84 postfix_snippet( 168 postfix_snippet(
85 ctx, 169 ctx,
86 cap, 170 cap,
87 &dot_receiver, 171 &dot_receiver,
88 "match", 172 "box",
89 "match expr {}", 173 "Box::new(expr)",
90 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), 174 &format!("Box::new({})", receiver_text),
91 ) 175 )
92 .add_to(acc); 176 .add_to(acc);
93 177
@@ -95,9 +179,9 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
95 ctx, 179 ctx,
96 cap, 180 cap,
97 &dot_receiver, 181 &dot_receiver,
98 "box", 182 "dbg",
99 "Box::new(expr)", 183 "dbg!(expr)",
100 &format!("Box::new({})", receiver_text), 184 &format!("dbg!({})", receiver_text),
101 ) 185 )
102 .add_to(acc); 186 .add_to(acc);
103 187
@@ -105,9 +189,9 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
105 ctx, 189 ctx,
106 cap, 190 cap,
107 &dot_receiver, 191 &dot_receiver,
108 "dbg", 192 "call",
109 "dbg!(expr)", 193 "function(expr)",
110 &format!("dbg!({})", receiver_text), 194 &format!("${{1}}({})", receiver_text),
111 ) 195 )
112 .add_to(acc); 196 .add_to(acc);
113} 197}
@@ -182,6 +266,13 @@ mod tests {
182 detail: "Box::new(expr)", 266 detail: "Box::new(expr)",
183 }, 267 },
184 CompletionItem { 268 CompletionItem {
269 label: "call",
270 source_range: 89..89,
271 delete: 85..89,
272 insert: "${1}(bar)",
273 detail: "function(expr)",
274 },
275 CompletionItem {
185 label: "dbg", 276 label: "dbg",
186 source_range: 89..89, 277 source_range: 89..89,
187 delete: 85..89, 278 delete: 85..89,
@@ -236,6 +327,178 @@ mod tests {
236 } 327 }
237 328
238 #[test] 329 #[test]
330 fn postfix_completion_works_for_option() {
331 assert_debug_snapshot!(
332 do_postfix_completion(
333 r#"
334 enum Option<T> {
335 Some(T),
336 None,
337 }
338
339 fn main() {
340 let bar = Option::Some(true);
341 bar.<|>
342 }
343 "#,
344 ),
345 @r###"
346 [
347 CompletionItem {
348 label: "box",
349 source_range: 210..210,
350 delete: 206..210,
351 insert: "Box::new(bar)",
352 detail: "Box::new(expr)",
353 },
354 CompletionItem {
355 label: "call",
356 source_range: 210..210,
357 delete: 206..210,
358 insert: "${1}(bar)",
359 detail: "function(expr)",
360 },
361 CompletionItem {
362 label: "dbg",
363 source_range: 210..210,
364 delete: 206..210,
365 insert: "dbg!(bar)",
366 detail: "dbg!(expr)",
367 },
368 CompletionItem {
369 label: "ifl",
370 source_range: 210..210,
371 delete: 206..210,
372 insert: "if let Some($1) = bar {\n $0\n}",
373 detail: "if let Some {}",
374 },
375 CompletionItem {
376 label: "match",
377 source_range: 210..210,
378 delete: 206..210,
379 insert: "match bar {\n Some(${1:_}) => {$2\\},\n None => {$0\\},\n}",
380 detail: "match expr {}",
381 },
382 CompletionItem {
383 label: "not",
384 source_range: 210..210,
385 delete: 206..210,
386 insert: "!bar",
387 detail: "!expr",
388 },
389 CompletionItem {
390 label: "ref",
391 source_range: 210..210,
392 delete: 206..210,
393 insert: "&bar",
394 detail: "&expr",
395 },
396 CompletionItem {
397 label: "refm",
398 source_range: 210..210,
399 delete: 206..210,
400 insert: "&mut bar",
401 detail: "&mut expr",
402 },
403 CompletionItem {
404 label: "while",
405 source_range: 210..210,
406 delete: 206..210,
407 insert: "while let Some($1) = bar {\n $0\n}",
408 detail: "while let Some {}",
409 },
410 ]
411 "###
412 );
413 }
414
415 #[test]
416 fn postfix_completion_works_for_result() {
417 assert_debug_snapshot!(
418 do_postfix_completion(
419 r#"
420 enum Result<T, E> {
421 Ok(T),
422 Err(E),
423 }
424
425 fn main() {
426 let bar = Result::Ok(true);
427 bar.<|>
428 }
429 "#,
430 ),
431 @r###"
432 [
433 CompletionItem {
434 label: "box",
435 source_range: 211..211,
436 delete: 207..211,
437 insert: "Box::new(bar)",
438 detail: "Box::new(expr)",
439 },
440 CompletionItem {
441 label: "call",
442 source_range: 211..211,
443 delete: 207..211,
444 insert: "${1}(bar)",
445 detail: "function(expr)",
446 },
447 CompletionItem {
448 label: "dbg",
449 source_range: 211..211,
450 delete: 207..211,
451 insert: "dbg!(bar)",
452 detail: "dbg!(expr)",
453 },
454 CompletionItem {
455 label: "ifl",
456 source_range: 211..211,
457 delete: 207..211,
458 insert: "if let Ok($1) = bar {\n $0\n}",
459 detail: "if let Ok {}",
460 },
461 CompletionItem {
462 label: "match",
463 source_range: 211..211,
464 delete: 207..211,
465 insert: "match bar {\n Ok(${1:_}) => {$2\\},\n Err(${3:_}) => {$0\\},\n}",
466 detail: "match expr {}",
467 },
468 CompletionItem {
469 label: "not",
470 source_range: 211..211,
471 delete: 207..211,
472 insert: "!bar",
473 detail: "!expr",
474 },
475 CompletionItem {
476 label: "ref",
477 source_range: 211..211,
478 delete: 207..211,
479 insert: "&bar",
480 detail: "&expr",
481 },
482 CompletionItem {
483 label: "refm",
484 source_range: 211..211,
485 delete: 207..211,
486 insert: "&mut bar",
487 detail: "&mut expr",
488 },
489 CompletionItem {
490 label: "while",
491 source_range: 211..211,
492 delete: 207..211,
493 insert: "while let Ok($1) = bar {\n $0\n}",
494 detail: "while let Ok {}",
495 },
496 ]
497 "###
498 );
499 }
500
501 #[test]
239 fn some_postfix_completions_ignored() { 502 fn some_postfix_completions_ignored() {
240 assert_debug_snapshot!( 503 assert_debug_snapshot!(
241 do_postfix_completion( 504 do_postfix_completion(
@@ -256,6 +519,13 @@ mod tests {
256 detail: "Box::new(expr)", 519 detail: "Box::new(expr)",
257 }, 520 },
258 CompletionItem { 521 CompletionItem {
522 label: "call",
523 source_range: 91..91,
524 delete: 87..91,
525 insert: "${1}(bar)",
526 detail: "function(expr)",
527 },
528 CompletionItem {
259 label: "dbg", 529 label: "dbg",
260 source_range: 91..91, 530 source_range: 91..91,
261 delete: 87..91, 531 delete: 87..91,
@@ -315,6 +585,13 @@ mod tests {
315 detail: "Box::new(expr)", 585 detail: "Box::new(expr)",
316 }, 586 },
317 CompletionItem { 587 CompletionItem {
588 label: "call",
589 source_range: 52..52,
590 delete: 49..52,
591 insert: "${1}(42)",
592 detail: "function(expr)",
593 },
594 CompletionItem {
318 label: "dbg", 595 label: "dbg",
319 source_range: 52..52, 596 source_range: 52..52,
320 delete: 49..52, 597 delete: 49..52,
@@ -376,6 +653,13 @@ mod tests {
376 detail: "Box::new(expr)", 653 detail: "Box::new(expr)",
377 }, 654 },
378 CompletionItem { 655 CompletionItem {
656 label: "call",
657 source_range: 149..150,
658 delete: 145..150,
659 insert: "${1}(bar)",
660 detail: "function(expr)",
661 },
662 CompletionItem {
379 label: "dbg", 663 label: "dbg",
380 source_range: 149..150, 664 source_range: 149..150,
381 delete: 145..150, 665 delete: 145..150,
@@ -435,6 +719,13 @@ mod tests {
435 detail: "Box::new(expr)", 719 detail: "Box::new(expr)",
436 }, 720 },
437 CompletionItem { 721 CompletionItem {
722 label: "call",
723 source_range: 56..56,
724 delete: 49..56,
725 insert: "${1}(&&&&42)",
726 detail: "function(expr)",
727 },
728 CompletionItem {
438 label: "dbg", 729 label: "dbg",
439 source_range: 56..56, 730 source_range: 56..56,
440 delete: 49..56, 731 delete: 49..56,
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index aa56a5cd8..02ac0166b 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -2,20 +2,25 @@
2 2
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; 3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use ra_syntax::AstNode; 4use ra_syntax::AstNode;
5use test_utils::tested_by; 5use rustc_hash::FxHashSet;
6use test_utils::mark;
6 7
7use crate::completion::{CompletionContext, Completions}; 8use crate::completion::{CompletionContext, Completions};
8use rustc_hash::FxHashSet;
9 9
10pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 let path = match &ctx.path_prefix { 11 let path = match &ctx.path_prefix {
12 Some(path) => path.clone(), 12 Some(path) => path.clone(),
13 _ => return, 13 None => return,
14 }; 14 };
15
16 if ctx.attribute_under_caret.is_some() {
17 return;
18 }
19
15 let scope = ctx.scope(); 20 let scope = ctx.scope();
16 let context_module = scope.module(); 21 let context_module = scope.module();
17 22
18 let res = match scope.resolve_hir_path(&path) { 23 let res = match scope.resolve_hir_path_qualifier(&path) {
19 Some(res) => res, 24 Some(res) => res,
20 None => return, 25 None => return,
21 }; 26 };
@@ -35,7 +40,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
35 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { 40 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
36 if name_ref.syntax().text() == name.to_string().as_str() { 41 if name_ref.syntax().text() == name.to_string().as_str() {
37 // for `use self::foo<|>`, don't suggest `foo` as a completion 42 // for `use self::foo<|>`, don't suggest `foo` as a completion
38 tested_by!(dont_complete_current_use); 43 mark::hit!(dont_complete_current_use);
39 continue; 44 continue;
40 } 45 }
41 } 46 }
@@ -79,7 +84,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
79 }); 84 });
80 85
81 // Iterate assoc types separately 86 // Iterate assoc types separately
82 ty.iterate_impl_items(ctx.db, krate, |item| { 87 ty.iterate_assoc_items(ctx.db, krate, |item| {
83 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 88 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
84 return None; 89 return None;
85 } 90 }
@@ -142,7 +147,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
142 147
143#[cfg(test)] 148#[cfg(test)]
144mod tests { 149mod tests {
145 use test_utils::covers; 150 use test_utils::mark;
146 151
147 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 152 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
148 use insta::assert_debug_snapshot; 153 use insta::assert_debug_snapshot;
@@ -153,7 +158,7 @@ mod tests {
153 158
154 #[test] 159 #[test]
155 fn dont_complete_current_use() { 160 fn dont_complete_current_use() {
156 covers!(dont_complete_current_use); 161 mark::check!(dont_complete_current_use);
157 let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); 162 let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference);
158 assert!(completions.is_empty()); 163 assert!(completions.is_empty());
159 } 164 }
@@ -221,6 +226,34 @@ mod tests {
221 } 226 }
222 227
223 #[test] 228 #[test]
229 fn completes_mod_with_same_name_as_function() {
230 assert_debug_snapshot!(
231 do_reference_completion(
232 r"
233 use self::my::<|>;
234
235 mod my {
236 pub struct Bar;
237 }
238
239 fn my() {}
240 "
241 ),
242 @r###"
243 [
244 CompletionItem {
245 label: "Bar",
246 source_range: 31..31,
247 delete: 31..31,
248 insert: "Bar",
249 kind: Struct,
250 },
251 ]
252 "###
253 );
254 }
255
256 #[test]
224 fn path_visibility() { 257 fn path_visibility() {
225 assert_debug_snapshot!( 258 assert_debug_snapshot!(
226 do_reference_completion( 259 do_reference_completion(
@@ -1325,4 +1358,18 @@ mod tests {
1325 "### 1358 "###
1326 ); 1359 );
1327 } 1360 }
1361
1362 #[test]
1363 fn dont_complete_attr() {
1364 assert_debug_snapshot!(
1365 do_reference_completion(
1366 r"
1367 mod foo { pub struct Foo; }
1368 #[foo::<|>]
1369 fn f() {}
1370 "
1371 ),
1372 @r###"[]"###
1373 )
1374 }
1328} 1375}
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs
index a3f5d1b6a..0568d9ccf 100644
--- a/crates/ra_ide/src/completion/complete_snippet.rs
+++ b/crates/ra_ide/src/completion/complete_snippet.rs
@@ -36,6 +36,24 @@ pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte
36 snippet( 36 snippet(
37 ctx, 37 ctx,
38 cap, 38 cap,
39 "Test module",
40 "\
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn ${1:test_name}() {
47 $0
48 }
49}",
50 )
51 .lookup_by("tmod")
52 .add_to(acc);
53
54 snippet(
55 ctx,
56 cap,
39 "Test function", 57 "Test function",
40 "\ 58 "\
41#[test] 59#[test]
@@ -118,6 +136,14 @@ mod tests {
118 lookup: "tfn", 136 lookup: "tfn",
119 }, 137 },
120 CompletionItem { 138 CompletionItem {
139 label: "Test module",
140 source_range: 78..78,
141 delete: 78..78,
142 insert: "#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn ${1:test_name}() {\n $0\n }\n}",
143 kind: Snippet,
144 lookup: "tmod",
145 },
146 CompletionItem {
121 label: "macro_rules", 147 label: "macro_rules",
122 source_range: 78..78, 148 source_range: 78..78,
123 delete: 78..78, 149 delete: 78..78,
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index ee32d1ff6..21c9316e6 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -32,7 +32,7 @@
32//! ``` 32//! ```
33 33
34use hir::{self, Docs, HasSource}; 34use hir::{self, Docs, HasSource};
35use ra_assists::utils::get_missing_impl_items; 35use ra_assists::utils::get_missing_assoc_items;
36use ra_syntax::{ 36use ra_syntax::{
37 ast::{self, edit, ImplDef}, 37 ast::{self, edit, ImplDef},
38 AstNode, SyntaxKind, SyntaxNode, TextRange, T, 38 AstNode, SyntaxKind, SyntaxNode, TextRange, T,
@@ -49,50 +49,53 @@ use crate::{
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
50 if let Some((trigger, impl_def)) = completion_match(ctx) { 50 if let Some((trigger, impl_def)) = completion_match(ctx) {
51 match trigger.kind() { 51 match trigger.kind() {
52 SyntaxKind::NAME_REF => { 52 SyntaxKind::NAME_REF => get_missing_assoc_items(&ctx.sema, &impl_def)
53 get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { 53 .into_iter()
54 .for_each(|item| match item {
54 hir::AssocItem::Function(fn_item) => { 55 hir::AssocItem::Function(fn_item) => {
55 add_function_impl(&trigger, acc, ctx, &fn_item) 56 add_function_impl(&trigger, acc, ctx, fn_item)
56 } 57 }
57 hir::AssocItem::TypeAlias(type_item) => { 58 hir::AssocItem::TypeAlias(type_item) => {
58 add_type_alias_impl(&trigger, acc, ctx, &type_item) 59 add_type_alias_impl(&trigger, acc, ctx, type_item)
59 } 60 }
60 hir::AssocItem::Const(const_item) => { 61 hir::AssocItem::Const(const_item) => {
61 add_const_impl(&trigger, acc, ctx, &const_item) 62 add_const_impl(&trigger, acc, ctx, const_item)
62 } 63 }
63 }) 64 }),
64 }
65 65
66 SyntaxKind::FN_DEF => { 66 SyntaxKind::FN_DEF => {
67 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( 67 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
68 |item| match item { 68 .into_iter()
69 .filter_map(|item| match item {
69 hir::AssocItem::Function(fn_item) => Some(fn_item), 70 hir::AssocItem::Function(fn_item) => Some(fn_item),
70 _ => None, 71 _ => None,
71 }, 72 })
72 ) { 73 {
73 add_function_impl(&trigger, acc, ctx, &missing_fn); 74 add_function_impl(&trigger, acc, ctx, missing_fn);
74 } 75 }
75 } 76 }
76 77
77 SyntaxKind::TYPE_ALIAS_DEF => { 78 SyntaxKind::TYPE_ALIAS_DEF => {
78 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( 79 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
79 |item| match item { 80 .into_iter()
81 .filter_map(|item| match item {
80 hir::AssocItem::TypeAlias(type_item) => Some(type_item), 82 hir::AssocItem::TypeAlias(type_item) => Some(type_item),
81 _ => None, 83 _ => None,
82 }, 84 })
83 ) { 85 {
84 add_type_alias_impl(&trigger, acc, ctx, &missing_fn); 86 add_type_alias_impl(&trigger, acc, ctx, missing_fn);
85 } 87 }
86 } 88 }
87 89
88 SyntaxKind::CONST_DEF => { 90 SyntaxKind::CONST_DEF => {
89 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( 91 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
90 |item| match item { 92 .into_iter()
93 .filter_map(|item| match item {
91 hir::AssocItem::Const(const_item) => Some(const_item), 94 hir::AssocItem::Const(const_item) => Some(const_item),
92 _ => None, 95 _ => None,
93 }, 96 })
94 ) { 97 {
95 add_const_impl(&trigger, acc, ctx, &missing_fn); 98 add_const_impl(&trigger, acc, ctx, missing_fn);
96 } 99 }
97 } 100 }
98 101
@@ -120,9 +123,9 @@ fn add_function_impl(
120 fn_def_node: &SyntaxNode, 123 fn_def_node: &SyntaxNode,
121 acc: &mut Completions, 124 acc: &mut Completions,
122 ctx: &CompletionContext, 125 ctx: &CompletionContext,
123 func: &hir::Function, 126 func: hir::Function,
124) { 127) {
125 let signature = FunctionSignature::from_hir(ctx.db, *func); 128 let signature = FunctionSignature::from_hir(ctx.db, func);
126 129
127 let fn_name = func.name(ctx.db).to_string(); 130 let fn_name = func.name(ctx.db).to_string();
128 131
@@ -161,7 +164,7 @@ fn add_type_alias_impl(
161 type_def_node: &SyntaxNode, 164 type_def_node: &SyntaxNode,
162 acc: &mut Completions, 165 acc: &mut Completions,
163 ctx: &CompletionContext, 166 ctx: &CompletionContext,
164 type_alias: &hir::TypeAlias, 167 type_alias: hir::TypeAlias,
165) { 168) {
166 let alias_name = type_alias.name(ctx.db).to_string(); 169 let alias_name = type_alias.name(ctx.db).to_string();
167 170
@@ -181,7 +184,7 @@ fn add_const_impl(
181 const_def_node: &SyntaxNode, 184 const_def_node: &SyntaxNode,
182 acc: &mut Completions, 185 acc: &mut Completions,
183 ctx: &CompletionContext, 186 ctx: &CompletionContext,
184 const_: &hir::Const, 187 const_: hir::Const,
185) { 188) {
186 let const_name = const_.name(ctx.db).map(|n| n.to_string()); 189 let const_name = const_.name(ctx.db).map(|n| n.to_string());
187 190
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index a6a5568de..68032c37e 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -1,16 +1,19 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef; 3use hir::ScopeDef;
4use test_utils::tested_by; 4use test_utils::mark;
5 5
6use crate::completion::{CompletionContext, Completions}; 6use crate::completion::{CompletionContext, Completions};
7use hir::{Adt, ModuleDef, Type}; 7use hir::{Adt, ModuleDef, Type};
8use ra_syntax::AstNode; 8use ra_syntax::AstNode;
9 9
10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 if (!ctx.is_trivial_path && !ctx.is_pat_binding_or_const) 11 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
12 || ctx.record_lit_syntax.is_some() 12 return;
13 }
14 if ctx.record_lit_syntax.is_some()
13 || ctx.record_pat_syntax.is_some() 15 || ctx.record_pat_syntax.is_some()
16 || ctx.attribute_under_caret.is_some()
14 { 17 {
15 return; 18 return;
16 } 19 }
@@ -27,7 +30,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
27 if ctx.use_item_syntax.is_some() { 30 if ctx.use_item_syntax.is_some() {
28 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 31 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
29 if name_ref.syntax().text() == name.to_string().as_str() { 32 if name_ref.syntax().text() == name.to_string().as_str() {
30 tested_by!(self_fulfilling_completion); 33 mark::hit!(self_fulfilling_completion);
31 return; 34 return;
32 } 35 }
33 } 36 }
@@ -63,7 +66,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
63#[cfg(test)] 66#[cfg(test)]
64mod tests { 67mod tests {
65 use insta::assert_debug_snapshot; 68 use insta::assert_debug_snapshot;
66 use test_utils::covers; 69 use test_utils::mark;
67 70
68 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 71 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
69 72
@@ -73,7 +76,7 @@ mod tests {
73 76
74 #[test] 77 #[test]
75 fn self_fulfilling_completion() { 78 fn self_fulfilling_completion() {
76 covers!(self_fulfilling_completion); 79 mark::check!(self_fulfilling_completion);
77 assert_debug_snapshot!( 80 assert_debug_snapshot!(
78 do_reference_completion( 81 do_reference_completion(
79 r#" 82 r#"
@@ -295,6 +298,42 @@ mod tests {
295 } 298 }
296 299
297 #[test] 300 #[test]
301 fn completes_bindings_from_for_with_in_prefix() {
302 mark::check!(completes_bindings_from_for_with_in_prefix);
303 assert_debug_snapshot!(
304 do_reference_completion(
305 r"
306 fn test() {
307 for index in &[1, 2, 3] {
308 let t = in<|>
309 }
310 }
311 "
312 ),
313 @r###"
314 [
315 CompletionItem {
316 label: "index",
317 source_range: 107..107,
318 delete: 107..107,
319 insert: "index",
320 kind: Binding,
321 },
322 CompletionItem {
323 label: "test()",
324 source_range: 107..107,
325 delete: 107..107,
326 insert: "test()$0",
327 kind: Function,
328 lookup: "test",
329 detail: "fn test()",
330 },
331 ]
332 "###
333 );
334 }
335
336 #[test]
298 fn completes_generic_params() { 337 fn completes_generic_params() {
299 assert_debug_snapshot!( 338 assert_debug_snapshot!(
300 do_reference_completion( 339 do_reference_completion(
@@ -1369,4 +1408,18 @@ mod tests {
1369 "### 1408 "###
1370 ) 1409 )
1371 } 1410 }
1411
1412 #[test]
1413 fn dont_complete_attr() {
1414 assert_debug_snapshot!(
1415 do_reference_completion(
1416 r"
1417 struct Foo;
1418 #[<|>]
1419 fn f() {}
1420 "
1421 ),
1422 @r###"[]"###
1423 )
1424 }
1372} 1425}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 118fceb2e..c4646b727 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -9,9 +9,10 @@ use ra_syntax::{
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, 10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::Indel;
13 13
14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; 14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
15use test_utils::mark;
15 16
16/// `CompletionContext` is created early during completion to figure out, where 17/// `CompletionContext` is created early during completion to figure out, where
17/// exactly is the cursor, syntax-wise. 18/// exactly is the cursor, syntax-wise.
@@ -34,7 +35,7 @@ pub(crate) struct CompletionContext<'a> {
34 pub(super) record_pat_syntax: Option<ast::RecordPat>, 35 pub(super) record_pat_syntax: Option<ast::RecordPat>,
35 pub(super) record_field_syntax: Option<ast::RecordField>, 36 pub(super) record_field_syntax: Option<ast::RecordField>,
36 pub(super) impl_def: Option<ast::ImplDef>, 37 pub(super) impl_def: Option<ast::ImplDef>,
37 /// FIXME: `ActiveParameter` is string-based, which is very wrong 38 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
38 pub(super) active_parameter: Option<ActiveParameter>, 39 pub(super) active_parameter: Option<ActiveParameter>,
39 pub(super) is_param: bool, 40 pub(super) is_param: bool,
40 /// If a name-binding or reference to a const in a pattern. 41 /// If a name-binding or reference to a const in a pattern.
@@ -58,7 +59,7 @@ pub(crate) struct CompletionContext<'a> {
58 pub(super) is_macro_call: bool, 59 pub(super) is_macro_call: bool,
59 pub(super) is_path_type: bool, 60 pub(super) is_path_type: bool,
60 pub(super) has_type_args: bool, 61 pub(super) has_type_args: bool,
61 pub(super) is_attribute: bool, 62 pub(super) attribute_under_caret: Option<ast::Attr>,
62} 63}
63 64
64impl<'a> CompletionContext<'a> { 65impl<'a> CompletionContext<'a> {
@@ -76,7 +77,7 @@ impl<'a> CompletionContext<'a> {
76 // actual completion. 77 // actual completion.
77 let file_with_fake_ident = { 78 let file_with_fake_ident = {
78 let parse = db.parse(position.file_id); 79 let parse = db.parse(position.file_id);
79 let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); 80 let edit = Indel::insert(position.offset, "intellijRulezz".to_string());
80 parse.reparse(&edit).tree() 81 parse.reparse(&edit).tree()
81 }; 82 };
82 let fake_ident_token = 83 let fake_ident_token =
@@ -116,7 +117,7 @@ impl<'a> CompletionContext<'a> {
116 is_path_type: false, 117 is_path_type: false,
117 has_type_args: false, 118 has_type_args: false,
118 dot_receiver_is_ambiguous_float_literal: false, 119 dot_receiver_is_ambiguous_float_literal: false,
119 is_attribute: false, 120 attribute_under_caret: None,
120 }; 121 };
121 122
122 let mut original_file = original_file.syntax().clone(); 123 let mut original_file = original_file.syntax().clone();
@@ -169,7 +170,17 @@ impl<'a> CompletionContext<'a> {
169 match self.token.kind() { 170 match self.token.kind() {
170 // workaroud when completion is triggered by trigger characters. 171 // workaroud when completion is triggered by trigger characters.
171 IDENT => self.original_token.text_range(), 172 IDENT => self.original_token.text_range(),
172 _ => TextRange::empty(self.offset), 173 _ => {
174 // If we haven't characters between keyword and our cursor we take the keyword start range to edit
175 if self.token.kind().is_keyword()
176 && self.offset == self.original_token.text_range().end()
177 {
178 mark::hit!(completes_bindings_from_for_with_in_prefix);
179 TextRange::empty(self.original_token.text_range().start())
180 } else {
181 TextRange::empty(self.offset)
182 }
183 }
173 } 184 }
174 } 185 }
175 186
@@ -200,6 +211,7 @@ impl<'a> CompletionContext<'a> {
200 Some(ty) 211 Some(ty)
201 }) 212 })
202 .flatten(); 213 .flatten();
214 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
203 215
204 // First, let's try to complete a reference to some declaration. 216 // First, let's try to complete a reference to some declaration.
205 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { 217 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
@@ -318,7 +330,6 @@ impl<'a> CompletionContext<'a> {
318 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 330 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
319 .is_some(); 331 .is_some();
320 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); 332 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
321 self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some();
322 333
323 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 334 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
324 self.has_type_args = segment.type_arg_list().is_some(); 335 self.has_type_args = segment.type_arg_list().is_some();
@@ -344,7 +355,7 @@ impl<'a> CompletionContext<'a> {
344 stmt.syntax().text_range() == name_ref.syntax().text_range(), 355 stmt.syntax().text_range() == name_ref.syntax().text_range(),
345 ); 356 );
346 } 357 }
347 if let Some(block) = ast::Block::cast(node) { 358 if let Some(block) = ast::BlockExpr::cast(node) {
348 return Some( 359 return Some(
349 block.expr().map(|e| e.syntax().text_range()) 360 block.expr().map(|e| e.syntax().text_range())
350 == Some(name_ref.syntax().text_range()), 361 == Some(name_ref.syntax().text_range()),
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index 5936fb8f7..cfb7c1e38 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -2,11 +2,12 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use super::completion_config::SnippetCap;
6use hir::Documentation; 5use hir::Documentation;
7use ra_syntax::TextRange; 6use ra_syntax::TextRange;
8use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
9 8
9use crate::completion::completion_config::SnippetCap;
10
10/// `CompletionItem` describes a single completion variant in the editor pop-up. 11/// `CompletionItem` describes a single completion variant in the editor pop-up.
11/// It is basically a POD with various properties. To construct a 12/// It is basically a POD with various properties. To construct a
12/// `CompletionItem`, use `new` method and the `Builder` struct. 13/// `CompletionItem`, use `new` method and the `Builder` struct.
@@ -62,8 +63,8 @@ impl fmt::Debug for CompletionItem {
62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63 let mut s = f.debug_struct("CompletionItem"); 64 let mut s = f.debug_struct("CompletionItem");
64 s.field("label", &self.label()).field("source_range", &self.source_range()); 65 s.field("label", &self.label()).field("source_range", &self.source_range());
65 if self.text_edit().as_atoms().len() == 1 { 66 if self.text_edit().len() == 1 {
66 let atom = &self.text_edit().as_atoms()[0]; 67 let atom = &self.text_edit().iter().next().unwrap();
67 s.field("delete", &atom.delete); 68 s.field("delete", &atom.delete);
68 s.field("insert", &atom.insert); 69 s.field("insert", &atom.insert);
69 } else { 70 } else {
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 2edb130cf..61565c84f 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -3,7 +3,7 @@
3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; 3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
4use ra_syntax::ast::NameOwner; 4use ra_syntax::ast::NameOwner;
5use stdx::SepBy; 5use stdx::SepBy;
6use test_utils::tested_by; 6use test_utils::mark;
7 7
8use crate::{ 8use crate::{
9 completion::{ 9 completion::{
@@ -17,12 +17,11 @@ use crate::{
17impl Completions { 17impl Completions {
18 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) { 18 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) {
19 let is_deprecated = is_deprecated(field, ctx.db); 19 let is_deprecated = is_deprecated(field, ctx.db);
20 let ty = ty.display(ctx.db).to_string();
21 let name = field.name(ctx.db); 20 let name = field.name(ctx.db);
22 let mut completion_item = 21 let mut completion_item =
23 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) 22 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
24 .kind(CompletionItemKind::Field) 23 .kind(CompletionItemKind::Field)
25 .detail(ty.clone()) 24 .detail(ty.display(ctx.db).to_string())
26 .set_documentation(field.docs(ctx.db)) 25 .set_documentation(field.docs(ctx.db))
27 .set_deprecated(is_deprecated); 26 .set_deprecated(is_deprecated);
28 27
@@ -107,6 +106,12 @@ impl Completions {
107 } 106 }
108 }; 107 };
109 108
109 if let ScopeDef::Local(local) = resolution {
110 if let Some(score) = compute_score(ctx, &local.ty(ctx.db), &local_name) {
111 completion_item = completion_item.set_score(score);
112 }
113 }
114
110 // Add `<>` for generic types 115 // Add `<>` for generic types
111 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis { 116 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
112 if let Some(cap) = ctx.config.snippet_cap { 117 if let Some(cap) = ctx.config.snippet_cap {
@@ -116,7 +121,7 @@ impl Completions {
116 _ => false, 121 _ => false,
117 }; 122 };
118 if has_non_default_type_params { 123 if has_non_default_type_params {
119 tested_by!(inserts_angle_brackets_for_generics); 124 mark::hit!(inserts_angle_brackets_for_generics);
120 completion_item = completion_item 125 completion_item = completion_item
121 .lookup_by(local_name.clone()) 126 .lookup_by(local_name.clone())
122 .label(format!("{}<…>", local_name)) 127 .label(format!("{}<…>", local_name))
@@ -171,7 +176,7 @@ impl Completions {
171 } 176 }
172 None if needs_bang => builder.insert_text(format!("{}!", name)), 177 None if needs_bang => builder.insert_text(format!("{}!", name)),
173 _ => { 178 _ => {
174 tested_by!(dont_insert_macro_call_parens_unncessary); 179 mark::hit!(dont_insert_macro_call_parens_unncessary);
175 builder.insert_text(name) 180 builder.insert_text(name)
176 } 181 }
177 }; 182 };
@@ -206,7 +211,7 @@ impl Completions {
206 .parameter_names 211 .parameter_names
207 .iter() 212 .iter()
208 .skip(if function_signature.has_self_param { 1 } else { 0 }) 213 .skip(if function_signature.has_self_param { 1 } else { 0 })
209 .cloned() 214 .map(|name| name.trim_start_matches('_').into())
210 .collect(); 215 .collect();
211 216
212 builder = builder.add_call_parens(ctx, name, Params::Named(params)); 217 builder = builder.add_call_parens(ctx, name, Params::Named(params));
@@ -319,19 +324,20 @@ impl Completions {
319 324
320pub(crate) fn compute_score( 325pub(crate) fn compute_score(
321 ctx: &CompletionContext, 326 ctx: &CompletionContext,
322 // FIXME: this definitely should be a `Type` 327 ty: &Type,
323 ty: &str,
324 name: &str, 328 name: &str,
325) -> Option<CompletionScore> { 329) -> Option<CompletionScore> {
330 // FIXME: this should not fall back to string equality.
331 let ty = &ty.display(ctx.db).to_string();
326 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { 332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
327 tested_by!(test_struct_field_completion_in_record_lit); 333 mark::hit!(test_struct_field_completion_in_record_lit);
328 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; 334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
329 ( 335 (
330 struct_field.name(ctx.db).to_string(), 336 struct_field.name(ctx.db).to_string(),
331 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), 337 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
332 ) 338 )
333 } else if let Some(active_parameter) = &ctx.active_parameter { 339 } else if let Some(active_parameter) = &ctx.active_parameter {
334 tested_by!(test_struct_field_completion_in_func_call); 340 mark::hit!(test_struct_field_completion_in_func_call);
335 (active_parameter.name.clone(), active_parameter.ty.clone()) 341 (active_parameter.name.clone(), active_parameter.ty.clone())
336 } else { 342 } else {
337 return None; 343 return None;
@@ -392,7 +398,7 @@ impl Builder {
392 None => return self, 398 None => return self,
393 }; 399 };
394 // If not an import, add parenthesis automatically. 400 // If not an import, add parenthesis automatically.
395 tested_by!(inserts_parens_for_function_calls); 401 mark::hit!(inserts_parens_for_function_calls);
396 402
397 let (snippet, label) = if params.is_empty() { 403 let (snippet, label) = if params.is_empty() {
398 (format!("{}()$0", name), format!("{}()", name)) 404 (format!("{}()$0", name), format!("{}()", name))
@@ -451,7 +457,7 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
451#[cfg(test)] 457#[cfg(test)]
452mod tests { 458mod tests {
453 use insta::assert_debug_snapshot; 459 use insta::assert_debug_snapshot;
454 use test_utils::covers; 460 use test_utils::mark;
455 461
456 use crate::completion::{ 462 use crate::completion::{
457 test_utils::{do_completion, do_completion_with_options}, 463 test_utils::{do_completion, do_completion_with_options},
@@ -601,7 +607,7 @@ mod tests {
601 607
602 #[test] 608 #[test]
603 fn inserts_parens_for_function_calls() { 609 fn inserts_parens_for_function_calls() {
604 covers!(inserts_parens_for_function_calls); 610 mark::check!(inserts_parens_for_function_calls);
605 assert_debug_snapshot!( 611 assert_debug_snapshot!(
606 do_reference_completion( 612 do_reference_completion(
607 r" 613 r"
@@ -666,6 +672,37 @@ mod tests {
666 assert_debug_snapshot!( 672 assert_debug_snapshot!(
667 do_reference_completion( 673 do_reference_completion(
668 r" 674 r"
675 fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String) {}
676 fn main() { with_<|> }
677 "
678 ),
679 @r###"
680 [
681 CompletionItem {
682 label: "main()",
683 source_range: 110..115,
684 delete: 110..115,
685 insert: "main()$0",
686 kind: Function,
687 lookup: "main",
688 detail: "fn main()",
689 },
690 CompletionItem {
691 label: "with_ignored_args(…)",
692 source_range: 110..115,
693 delete: 110..115,
694 insert: "with_ignored_args(${1:foo}, ${2:bar}, ${3:ho_ge_})$0",
695 kind: Function,
696 lookup: "with_ignored_args",
697 detail: "fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String)",
698 trigger_call_info: true,
699 },
700 ]
701 "###
702 );
703 assert_debug_snapshot!(
704 do_reference_completion(
705 r"
669 struct S {} 706 struct S {}
670 impl S { 707 impl S {
671 fn foo(&self) {} 708 fn foo(&self) {}
@@ -689,6 +726,33 @@ mod tests {
689 ] 726 ]
690 "### 727 "###
691 ); 728 );
729 assert_debug_snapshot!(
730 do_reference_completion(
731 r"
732 struct S {}
733 impl S {
734 fn foo_ignored_args(&self, _a: bool, b: i32) {}
735 }
736 fn bar(s: &S) {
737 s.f<|>
738 }
739 "
740 ),
741 @r###"
742 [
743 CompletionItem {
744 label: "foo_ignored_args(…)",
745 source_range: 194..195,
746 delete: 194..195,
747 insert: "foo_ignored_args(${1:a}, ${2:b})$0",
748 kind: Method,
749 lookup: "foo_ignored_args",
750 detail: "fn foo_ignored_args(&self, _a: bool, b: i32)",
751 trigger_call_info: true,
752 },
753 ]
754 "###
755 );
692 } 756 }
693 757
694 #[test] 758 #[test]
@@ -986,7 +1050,7 @@ mod tests {
986 1050
987 #[test] 1051 #[test]
988 fn inserts_angle_brackets_for_generics() { 1052 fn inserts_angle_brackets_for_generics() {
989 covers!(inserts_angle_brackets_for_generics); 1053 mark::check!(inserts_angle_brackets_for_generics);
990 assert_debug_snapshot!( 1054 assert_debug_snapshot!(
991 do_reference_completion( 1055 do_reference_completion(
992 r" 1056 r"
@@ -1109,7 +1173,7 @@ mod tests {
1109 1173
1110 #[test] 1174 #[test]
1111 fn dont_insert_macro_call_parens_unncessary() { 1175 fn dont_insert_macro_call_parens_unncessary() {
1112 covers!(dont_insert_macro_call_parens_unncessary); 1176 mark::check!(dont_insert_macro_call_parens_unncessary);
1113 assert_debug_snapshot!( 1177 assert_debug_snapshot!(
1114 do_reference_completion( 1178 do_reference_completion(
1115 r" 1179 r"
@@ -1175,7 +1239,7 @@ mod tests {
1175 1239
1176 #[test] 1240 #[test]
1177 fn test_struct_field_completion_in_func_call() { 1241 fn test_struct_field_completion_in_func_call() {
1178 covers!(test_struct_field_completion_in_func_call); 1242 mark::check!(test_struct_field_completion_in_func_call);
1179 assert_debug_snapshot!( 1243 assert_debug_snapshot!(
1180 do_reference_completion( 1244 do_reference_completion(
1181 r" 1245 r"
@@ -1265,7 +1329,7 @@ mod tests {
1265 1329
1266 #[test] 1330 #[test]
1267 fn test_struct_field_completion_in_record_lit() { 1331 fn test_struct_field_completion_in_record_lit() {
1268 covers!(test_struct_field_completion_in_record_lit); 1332 mark::check!(test_struct_field_completion_in_record_lit);
1269 assert_debug_snapshot!( 1333 assert_debug_snapshot!(
1270 do_reference_completion( 1334 do_reference_completion(
1271 r" 1335 r"
@@ -1405,4 +1469,48 @@ mod tests {
1405 "### 1469 "###
1406 ); 1470 );
1407 } 1471 }
1472
1473 #[test]
1474 fn prioritize_exact_ref_match() {
1475 assert_debug_snapshot!(
1476 do_reference_completion(
1477 r"
1478 struct WorldSnapshot { _f: () };
1479 fn go(world: &WorldSnapshot) {
1480 go(w<|>)
1481 }
1482 ",
1483 ),
1484 @r###"
1485 [
1486 CompletionItem {
1487 label: "WorldSnapshot",
1488 source_range: 132..133,
1489 delete: 132..133,
1490 insert: "WorldSnapshot",
1491 kind: Struct,
1492 },
1493 CompletionItem {
1494 label: "go(…)",
1495 source_range: 132..133,
1496 delete: 132..133,
1497 insert: "go(${1:world})$0",
1498 kind: Function,
1499 lookup: "go",
1500 detail: "fn go(world: &WorldSnapshot)",
1501 trigger_call_info: true,
1502 },
1503 CompletionItem {
1504 label: "world",
1505 source_range: 132..133,
1506 delete: 132..133,
1507 insert: "world",
1508 kind: Binding,
1509 detail: "&WorldSnapshot",
1510 score: TypeAndNameMatch,
1511 },
1512 ]
1513 "###
1514 );
1515 }
1408} 1516}
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index eb90b5279..bf22452a2 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -20,7 +20,7 @@ pub(crate) fn do_completion_with_options(
20 } else { 20 } else {
21 single_file_with_position(code) 21 single_file_with_position(code)
22 }; 22 };
23 let completions = analysis.completions(position, options).unwrap().unwrap(); 23 let completions = analysis.completions(options, position).unwrap().unwrap();
24 let completion_items: Vec<CompletionItem> = completions.into(); 24 let completion_items: Vec<CompletionItem> = completions.into();
25 let mut kind_completions: Vec<CompletionItem> = 25 let mut kind_completions: Vec<CompletionItem> =
26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); 26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect();