diff options
Diffstat (limited to 'crates/ra_ide/src/completion')
-rw-r--r-- | crates/ra_ide/src/completion/complete_attribute.rs | 300 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_dot.rs | 38 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_qualified_path.rs | 295 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_trait_impl.rs | 46 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_unqualified_path.rs | 75 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 41 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/presentation.rs | 104 |
7 files changed, 801 insertions, 98 deletions
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs index 8bf952798..f17266221 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 | ||
6 | use super::completion_context::CompletionContext; | 6 | use ra_syntax::{ast, AstNode, SyntaxKind}; |
7 | use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}; | 7 | use rustc_hash::FxHashSet; |
8 | use ra_syntax::{ | 8 | |
9 | ast::{Attr, AttrKind}, | 9 | use crate::completion::{ |
10 | AstNode, | 10 | completion_context::CompletionContext, |
11 | completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}, | ||
11 | }; | 12 | }; |
12 | 13 | ||
13 | pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) { | 14 | pub(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 | ||
28 | fn 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 | } |
@@ -126,6 +130,106 @@ const ATTRIBUTES: &[AttrCompletion] = &[ | |||
126 | }, | 130 | }, |
127 | ]; | 131 | ]; |
128 | 132 | ||
133 | fn 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 | |||
167 | fn 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 | |||
202 | fn 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 | |||
214 | struct 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) | ||
221 | const 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)] |
130 | mod tests { | 234 | mod 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( |
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 814354ffa..05f825c6f 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -250,6 +250,44 @@ mod tests { | |||
250 | } | 250 | } |
251 | 251 | ||
252 | #[test] | 252 | #[test] |
253 | fn test_union_field_completion() { | ||
254 | assert_debug_snapshot!( | ||
255 | do_ref_completion( | ||
256 | r" | ||
257 | union Un { | ||
258 | field: u8, | ||
259 | other: u16, | ||
260 | } | ||
261 | |||
262 | fn foo(u: Un) { | ||
263 | u.<|> | ||
264 | } | ||
265 | ", | ||
266 | ), | ||
267 | @r###" | ||
268 | [ | ||
269 | CompletionItem { | ||
270 | label: "field", | ||
271 | source_range: 140..140, | ||
272 | delete: 140..140, | ||
273 | insert: "field", | ||
274 | kind: Field, | ||
275 | detail: "u8", | ||
276 | }, | ||
277 | CompletionItem { | ||
278 | label: "other", | ||
279 | source_range: 140..140, | ||
280 | delete: 140..140, | ||
281 | insert: "other", | ||
282 | kind: Field, | ||
283 | detail: "u16", | ||
284 | }, | ||
285 | ] | ||
286 | "### | ||
287 | ); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
253 | fn test_method_completion() { | 291 | fn test_method_completion() { |
254 | assert_debug_snapshot!( | 292 | assert_debug_snapshot!( |
255 | do_ref_completion( | 293 | do_ref_completion( |
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index dd10f74e6..7fcd22525 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; | 3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::AstNode; |
5 | use rustc_hash::FxHashSet; | ||
5 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
6 | 7 | ||
7 | use crate::completion::{CompletionContext, Completions}; | 8 | use crate::completion::{CompletionContext, Completions}; |
@@ -9,15 +10,29 @@ use crate::completion::{CompletionContext, Completions}; | |||
9 | pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
10 | let path = match &ctx.path_prefix { | 11 | let path = match &ctx.path_prefix { |
11 | Some(path) => path.clone(), | 12 | Some(path) => path.clone(), |
12 | _ => return, | 13 | None => return, |
13 | }; | 14 | }; |
14 | let def = match ctx.scope().resolve_hir_path(&path) { | 15 | |
15 | Some(PathResolution::Def(def)) => def, | 16 | if ctx.attribute_under_caret.is_some() { |
16 | _ => return, | 17 | return; |
18 | } | ||
19 | |||
20 | let scope = ctx.scope(); | ||
21 | let context_module = scope.module(); | ||
22 | |||
23 | let res = match scope.resolve_hir_path(&path) { | ||
24 | Some(res) => res, | ||
25 | None => return, | ||
17 | }; | 26 | }; |
18 | let context_module = ctx.scope().module(); | 27 | |
19 | match def { | 28 | // Add associated types on type parameters and `Self`. |
20 | hir::ModuleDef::Module(module) => { | 29 | res.assoc_type_shorthand_candidates(ctx.db, |alias| { |
30 | acc.add_type_alias(ctx, alias); | ||
31 | None::<()> | ||
32 | }); | ||
33 | |||
34 | match res { | ||
35 | PathResolution::Def(hir::ModuleDef::Module(module)) => { | ||
21 | let module_scope = module.scope(ctx.db, context_module); | 36 | let module_scope = module.scope(ctx.db, context_module); |
22 | for (name, def) in module_scope { | 37 | for (name, def) in module_scope { |
23 | if ctx.use_item_syntax.is_some() { | 38 | if ctx.use_item_syntax.is_some() { |
@@ -35,7 +50,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
35 | acc.add_resolution(ctx, name.to_string(), &def); | 50 | acc.add_resolution(ctx, name.to_string(), &def); |
36 | } | 51 | } |
37 | } | 52 | } |
38 | hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { | 53 | PathResolution::Def(def @ hir::ModuleDef::Adt(_)) |
54 | | PathResolution::Def(def @ hir::ModuleDef::TypeAlias(_)) => { | ||
39 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { | 55 | if let hir::ModuleDef::Adt(Adt::Enum(e)) = def { |
40 | for variant in e.variants(ctx.db) { | 56 | for variant in e.variants(ctx.db) { |
41 | acc.add_enum_variant(ctx, variant, None); | 57 | acc.add_enum_variant(ctx, variant, None); |
@@ -46,8 +62,10 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
46 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), | 62 | hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db), |
47 | _ => unreachable!(), | 63 | _ => unreachable!(), |
48 | }; | 64 | }; |
49 | // Iterate assoc types separately | 65 | |
50 | // FIXME: complete T::AssocType | 66 | // XXX: For parity with Rust bug #22519, this does not complete Ty::AssocType. |
67 | // (where AssocType is defined on a trait, not an inherent impl) | ||
68 | |||
51 | let krate = ctx.krate; | 69 | let krate = ctx.krate; |
52 | if let Some(krate) = krate { | 70 | if let Some(krate) = krate { |
53 | let traits_in_scope = ctx.scope().traits_in_scope(); | 71 | let traits_in_scope = ctx.scope().traits_in_scope(); |
@@ -65,7 +83,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
65 | None::<()> | 83 | None::<()> |
66 | }); | 84 | }); |
67 | 85 | ||
68 | ty.iterate_impl_items(ctx.db, krate, |item| { | 86 | // Iterate assoc types separately |
87 | ty.iterate_assoc_items(ctx.db, krate, |item| { | ||
69 | 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)) { |
70 | return None; | 89 | return None; |
71 | } | 90 | } |
@@ -77,7 +96,8 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
77 | }); | 96 | }); |
78 | } | 97 | } |
79 | } | 98 | } |
80 | hir::ModuleDef::Trait(t) => { | 99 | PathResolution::Def(hir::ModuleDef::Trait(t)) => { |
100 | // Handles `Trait::assoc` as well as `<Ty as Trait>::assoc`. | ||
81 | for item in t.items(ctx.db) { | 101 | for item in t.items(ctx.db) { |
82 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | 102 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { |
83 | continue; | 103 | continue; |
@@ -91,8 +111,38 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
91 | } | 111 | } |
92 | } | 112 | } |
93 | } | 113 | } |
114 | PathResolution::TypeParam(_) | PathResolution::SelfType(_) => { | ||
115 | if let Some(krate) = ctx.krate { | ||
116 | let ty = match res { | ||
117 | PathResolution::TypeParam(param) => param.ty(ctx.db), | ||
118 | PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db), | ||
119 | _ => return, | ||
120 | }; | ||
121 | |||
122 | let traits_in_scope = ctx.scope().traits_in_scope(); | ||
123 | let mut seen = FxHashSet::default(); | ||
124 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | ||
125 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
126 | return None; | ||
127 | } | ||
128 | |||
129 | // We might iterate candidates of a trait multiple times here, so deduplicate | ||
130 | // them. | ||
131 | if seen.insert(item) { | ||
132 | match item { | ||
133 | hir::AssocItem::Function(func) => { | ||
134 | acc.add_function(ctx, func, None); | ||
135 | } | ||
136 | hir::AssocItem::Const(ct) => acc.add_const(ctx, ct), | ||
137 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | ||
138 | } | ||
139 | } | ||
140 | None::<()> | ||
141 | }); | ||
142 | } | ||
143 | } | ||
94 | _ => {} | 144 | _ => {} |
95 | }; | 145 | } |
96 | } | 146 | } |
97 | 147 | ||
98 | #[cfg(test)] | 148 | #[cfg(test)] |
@@ -844,6 +894,211 @@ mod tests { | |||
844 | } | 894 | } |
845 | 895 | ||
846 | #[test] | 896 | #[test] |
897 | fn completes_ty_param_assoc_ty() { | ||
898 | assert_debug_snapshot!( | ||
899 | do_reference_completion( | ||
900 | " | ||
901 | //- /lib.rs | ||
902 | trait Super { | ||
903 | type Ty; | ||
904 | const CONST: u8; | ||
905 | fn func() {} | ||
906 | fn method(&self) {} | ||
907 | } | ||
908 | |||
909 | trait Sub: Super { | ||
910 | type SubTy; | ||
911 | const C2: (); | ||
912 | fn subfunc() {} | ||
913 | fn submethod(&self) {} | ||
914 | } | ||
915 | |||
916 | fn foo<T: Sub>() { | ||
917 | T::<|> | ||
918 | } | ||
919 | " | ||
920 | ), | ||
921 | @r###" | ||
922 | [ | ||
923 | CompletionItem { | ||
924 | label: "C2", | ||
925 | source_range: 219..219, | ||
926 | delete: 219..219, | ||
927 | insert: "C2", | ||
928 | kind: Const, | ||
929 | detail: "const C2: ();", | ||
930 | }, | ||
931 | CompletionItem { | ||
932 | label: "CONST", | ||
933 | source_range: 219..219, | ||
934 | delete: 219..219, | ||
935 | insert: "CONST", | ||
936 | kind: Const, | ||
937 | detail: "const CONST: u8;", | ||
938 | }, | ||
939 | CompletionItem { | ||
940 | label: "SubTy", | ||
941 | source_range: 219..219, | ||
942 | delete: 219..219, | ||
943 | insert: "SubTy", | ||
944 | kind: TypeAlias, | ||
945 | detail: "type SubTy;", | ||
946 | }, | ||
947 | CompletionItem { | ||
948 | label: "Ty", | ||
949 | source_range: 219..219, | ||
950 | delete: 219..219, | ||
951 | insert: "Ty", | ||
952 | kind: TypeAlias, | ||
953 | detail: "type Ty;", | ||
954 | }, | ||
955 | CompletionItem { | ||
956 | label: "func()", | ||
957 | source_range: 219..219, | ||
958 | delete: 219..219, | ||
959 | insert: "func()$0", | ||
960 | kind: Function, | ||
961 | lookup: "func", | ||
962 | detail: "fn func()", | ||
963 | }, | ||
964 | CompletionItem { | ||
965 | label: "method()", | ||
966 | source_range: 219..219, | ||
967 | delete: 219..219, | ||
968 | insert: "method()$0", | ||
969 | kind: Method, | ||
970 | lookup: "method", | ||
971 | detail: "fn method(&self)", | ||
972 | }, | ||
973 | CompletionItem { | ||
974 | label: "subfunc()", | ||
975 | source_range: 219..219, | ||
976 | delete: 219..219, | ||
977 | insert: "subfunc()$0", | ||
978 | kind: Function, | ||
979 | lookup: "subfunc", | ||
980 | detail: "fn subfunc()", | ||
981 | }, | ||
982 | CompletionItem { | ||
983 | label: "submethod()", | ||
984 | source_range: 219..219, | ||
985 | delete: 219..219, | ||
986 | insert: "submethod()$0", | ||
987 | kind: Method, | ||
988 | lookup: "submethod", | ||
989 | detail: "fn submethod(&self)", | ||
990 | }, | ||
991 | ] | ||
992 | "### | ||
993 | ); | ||
994 | } | ||
995 | |||
996 | #[test] | ||
997 | fn completes_self_param_assoc_ty() { | ||
998 | assert_debug_snapshot!( | ||
999 | do_reference_completion( | ||
1000 | " | ||
1001 | //- /lib.rs | ||
1002 | trait Super { | ||
1003 | type Ty; | ||
1004 | const CONST: u8 = 0; | ||
1005 | fn func() {} | ||
1006 | fn method(&self) {} | ||
1007 | } | ||
1008 | |||
1009 | trait Sub: Super { | ||
1010 | type SubTy; | ||
1011 | const C2: () = (); | ||
1012 | fn subfunc() {} | ||
1013 | fn submethod(&self) {} | ||
1014 | } | ||
1015 | |||
1016 | struct Wrap<T>(T); | ||
1017 | impl<T> Super for Wrap<T> {} | ||
1018 | impl<T> Sub for Wrap<T> { | ||
1019 | fn subfunc() { | ||
1020 | // Should be able to assume `Self: Sub + Super` | ||
1021 | Self::<|> | ||
1022 | } | ||
1023 | } | ||
1024 | " | ||
1025 | ), | ||
1026 | @r###" | ||
1027 | [ | ||
1028 | CompletionItem { | ||
1029 | label: "C2", | ||
1030 | source_range: 365..365, | ||
1031 | delete: 365..365, | ||
1032 | insert: "C2", | ||
1033 | kind: Const, | ||
1034 | detail: "const C2: () = ();", | ||
1035 | }, | ||
1036 | CompletionItem { | ||
1037 | label: "CONST", | ||
1038 | source_range: 365..365, | ||
1039 | delete: 365..365, | ||
1040 | insert: "CONST", | ||
1041 | kind: Const, | ||
1042 | detail: "const CONST: u8 = 0;", | ||
1043 | }, | ||
1044 | CompletionItem { | ||
1045 | label: "SubTy", | ||
1046 | source_range: 365..365, | ||
1047 | delete: 365..365, | ||
1048 | insert: "SubTy", | ||
1049 | kind: TypeAlias, | ||
1050 | detail: "type SubTy;", | ||
1051 | }, | ||
1052 | CompletionItem { | ||
1053 | label: "Ty", | ||
1054 | source_range: 365..365, | ||
1055 | delete: 365..365, | ||
1056 | insert: "Ty", | ||
1057 | kind: TypeAlias, | ||
1058 | detail: "type Ty;", | ||
1059 | }, | ||
1060 | CompletionItem { | ||
1061 | label: "func()", | ||
1062 | source_range: 365..365, | ||
1063 | delete: 365..365, | ||
1064 | insert: "func()$0", | ||
1065 | kind: Function, | ||
1066 | lookup: "func", | ||
1067 | detail: "fn func()", | ||
1068 | }, | ||
1069 | CompletionItem { | ||
1070 | label: "method()", | ||
1071 | source_range: 365..365, | ||
1072 | delete: 365..365, | ||
1073 | insert: "method()$0", | ||
1074 | kind: Method, | ||
1075 | lookup: "method", | ||
1076 | detail: "fn method(&self)", | ||
1077 | }, | ||
1078 | CompletionItem { | ||
1079 | label: "subfunc()", | ||
1080 | source_range: 365..365, | ||
1081 | delete: 365..365, | ||
1082 | insert: "subfunc()$0", | ||
1083 | kind: Function, | ||
1084 | lookup: "subfunc", | ||
1085 | detail: "fn subfunc()", | ||
1086 | }, | ||
1087 | CompletionItem { | ||
1088 | label: "submethod()", | ||
1089 | source_range: 365..365, | ||
1090 | delete: 365..365, | ||
1091 | insert: "submethod()$0", | ||
1092 | kind: Method, | ||
1093 | lookup: "submethod", | ||
1094 | detail: "fn submethod(&self)", | ||
1095 | }, | ||
1096 | ] | ||
1097 | "### | ||
1098 | ); | ||
1099 | } | ||
1100 | |||
1101 | #[test] | ||
847 | fn completes_type_alias() { | 1102 | fn completes_type_alias() { |
848 | assert_debug_snapshot!( | 1103 | assert_debug_snapshot!( |
849 | do_reference_completion( | 1104 | do_reference_completion( |
@@ -1075,4 +1330,18 @@ mod tests { | |||
1075 | "### | 1330 | "### |
1076 | ); | 1331 | ); |
1077 | } | 1332 | } |
1333 | |||
1334 | #[test] | ||
1335 | fn dont_complete_attr() { | ||
1336 | assert_debug_snapshot!( | ||
1337 | do_reference_completion( | ||
1338 | r" | ||
1339 | mod foo { pub struct Foo; } | ||
1340 | #[foo::<|>] | ||
1341 | fn f() {} | ||
1342 | " | ||
1343 | ), | ||
1344 | @r###"[]"### | ||
1345 | ) | ||
1346 | } | ||
1078 | } | 1347 | } |
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index ee32d1ff6..039df03e0 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 | ||
34 | use hir::{self, Docs, HasSource}; | 34 | use hir::{self, Docs, HasSource}; |
35 | use ra_assists::utils::get_missing_impl_items; | 35 | use ra_assists::utils::get_missing_assoc_items; |
36 | use ra_syntax::{ | 36 | use ra_syntax::{ |
37 | ast::{self, edit, ImplDef}, | 37 | ast::{self, edit, ImplDef}, |
38 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, | 38 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, |
@@ -50,7 +50,7 @@ pub(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 => { |
53 | get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { | 53 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { |
54 | hir::AssocItem::Function(fn_item) => { | 54 | hir::AssocItem::Function(fn_item) => { |
55 | add_function_impl(&trigger, acc, ctx, &fn_item) | 55 | add_function_impl(&trigger, acc, ctx, &fn_item) |
56 | } | 56 | } |
@@ -64,34 +64,40 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext | |||
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 |
68 | |item| match item { | 68 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
69 | hir::AssocItem::Function(fn_item) => Some(fn_item), | 69 | match item { |
70 | _ => None, | 70 | hir::AssocItem::Function(fn_item) => Some(fn_item), |
71 | }, | 71 | _ => None, |
72 | ) { | 72 | } |
73 | }) | ||
74 | { | ||
73 | add_function_impl(&trigger, acc, ctx, &missing_fn); | 75 | add_function_impl(&trigger, acc, ctx, &missing_fn); |
74 | } | 76 | } |
75 | } | 77 | } |
76 | 78 | ||
77 | SyntaxKind::TYPE_ALIAS_DEF => { | 79 | SyntaxKind::TYPE_ALIAS_DEF => { |
78 | for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( | 80 | for missing_fn in |
79 | |item| match item { | 81 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
80 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), | 82 | match item { |
81 | _ => None, | 83 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), |
82 | }, | 84 | _ => None, |
83 | ) { | 85 | } |
86 | }) | ||
87 | { | ||
84 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); | 88 | add_type_alias_impl(&trigger, acc, ctx, &missing_fn); |
85 | } | 89 | } |
86 | } | 90 | } |
87 | 91 | ||
88 | SyntaxKind::CONST_DEF => { | 92 | SyntaxKind::CONST_DEF => { |
89 | for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( | 93 | for missing_fn in |
90 | |item| match item { | 94 | get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { |
91 | hir::AssocItem::Const(const_item) => Some(const_item), | 95 | match item { |
92 | _ => None, | 96 | hir::AssocItem::Const(const_item) => Some(const_item), |
93 | }, | 97 | _ => None, |
94 | ) { | 98 | } |
99 | }) | ||
100 | { | ||
95 | add_const_impl(&trigger, acc, ctx, &missing_fn); | 101 | add_const_impl(&trigger, acc, ctx, &missing_fn); |
96 | } | 102 | } |
97 | } | 103 | } |
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index 56cd086c6..bd40af1cb 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs | |||
@@ -4,18 +4,23 @@ use hir::ScopeDef; | |||
4 | use test_utils::tested_by; | 4 | use test_utils::tested_by; |
5 | 5 | ||
6 | use crate::completion::{CompletionContext, Completions}; | 6 | use crate::completion::{CompletionContext, Completions}; |
7 | use hir::{Adt, ModuleDef}; | 7 | use hir::{Adt, ModuleDef, Type}; |
8 | use ra_syntax::AstNode; | 8 | use ra_syntax::AstNode; |
9 | 9 | ||
10 | pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(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 | } |
17 | 20 | ||
18 | complete_enum_variants(acc, ctx); | 21 | if let Some(ty) = &ctx.expected_type { |
22 | complete_enum_variants(acc, ctx, ty); | ||
23 | } | ||
19 | 24 | ||
20 | if ctx.is_pat_binding_or_const { | 25 | if ctx.is_pat_binding_or_const { |
21 | return; | 26 | return; |
@@ -34,26 +39,24 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
34 | }); | 39 | }); |
35 | } | 40 | } |
36 | 41 | ||
37 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext) { | 42 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { |
38 | if let Some(ty) = ctx.expected_type_of(&ctx.token.parent()) { | 43 | if let Some(Adt::Enum(enum_data)) = ty.as_adt() { |
39 | if let Some(Adt::Enum(enum_data)) = ty.as_adt() { | 44 | let variants = enum_data.variants(ctx.db); |
40 | let variants = enum_data.variants(ctx.db); | 45 | |
41 | 46 | let module = if let Some(module) = ctx.scope().module() { | |
42 | let module = if let Some(module) = ctx.scope().module() { | 47 | // Compute path from the completion site if available. |
43 | // Compute path from the completion site if available. | 48 | module |
44 | module | 49 | } else { |
45 | } else { | 50 | // Otherwise fall back to the enum's definition site. |
46 | // Otherwise fall back to the enum's definition site. | 51 | enum_data.module(ctx.db) |
47 | enum_data.module(ctx.db) | 52 | }; |
48 | }; | 53 | |
49 | 54 | for variant in variants { | |
50 | for variant in variants { | 55 | if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { |
51 | if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { | 56 | // Variants with trivial paths are already added by the existing completion logic, |
52 | // Variants with trivial paths are already added by the existing completion logic, | 57 | // so we should avoid adding these twice |
53 | // so we should avoid adding these twice | 58 | if path.segments.len() > 1 { |
54 | if path.segments.len() > 1 { | 59 | acc.add_qualified_enum_variant(ctx, variant, path); |
55 | acc.add_enum_variant(ctx, variant, Some(path.to_string())); | ||
56 | } | ||
57 | } | 60 | } |
58 | } | 61 | } |
59 | } | 62 | } |
@@ -1173,6 +1176,7 @@ mod tests { | |||
1173 | delete: 248..250, | 1176 | delete: 248..250, |
1174 | insert: "Foo::Bar", | 1177 | insert: "Foo::Bar", |
1175 | kind: EnumVariant, | 1178 | kind: EnumVariant, |
1179 | lookup: "Bar", | ||
1176 | detail: "()", | 1180 | detail: "()", |
1177 | }, | 1181 | }, |
1178 | CompletionItem { | 1182 | CompletionItem { |
@@ -1181,6 +1185,7 @@ mod tests { | |||
1181 | delete: 248..250, | 1185 | delete: 248..250, |
1182 | insert: "Foo::Baz", | 1186 | insert: "Foo::Baz", |
1183 | kind: EnumVariant, | 1187 | kind: EnumVariant, |
1188 | lookup: "Baz", | ||
1184 | detail: "()", | 1189 | detail: "()", |
1185 | }, | 1190 | }, |
1186 | CompletionItem { | 1191 | CompletionItem { |
@@ -1189,6 +1194,7 @@ mod tests { | |||
1189 | delete: 248..250, | 1194 | delete: 248..250, |
1190 | insert: "Foo::Quux", | 1195 | insert: "Foo::Quux", |
1191 | kind: EnumVariant, | 1196 | kind: EnumVariant, |
1197 | lookup: "Quux", | ||
1192 | detail: "()", | 1198 | detail: "()", |
1193 | }, | 1199 | }, |
1194 | ] | 1200 | ] |
@@ -1231,6 +1237,7 @@ mod tests { | |||
1231 | delete: 219..221, | 1237 | delete: 219..221, |
1232 | insert: "Foo::Bar", | 1238 | insert: "Foo::Bar", |
1233 | kind: EnumVariant, | 1239 | kind: EnumVariant, |
1240 | lookup: "Bar", | ||
1234 | detail: "()", | 1241 | detail: "()", |
1235 | }, | 1242 | }, |
1236 | CompletionItem { | 1243 | CompletionItem { |
@@ -1239,6 +1246,7 @@ mod tests { | |||
1239 | delete: 219..221, | 1246 | delete: 219..221, |
1240 | insert: "Foo::Baz", | 1247 | insert: "Foo::Baz", |
1241 | kind: EnumVariant, | 1248 | kind: EnumVariant, |
1249 | lookup: "Baz", | ||
1242 | detail: "()", | 1250 | detail: "()", |
1243 | }, | 1251 | }, |
1244 | CompletionItem { | 1252 | CompletionItem { |
@@ -1247,6 +1255,7 @@ mod tests { | |||
1247 | delete: 219..221, | 1255 | delete: 219..221, |
1248 | insert: "Foo::Quux", | 1256 | insert: "Foo::Quux", |
1249 | kind: EnumVariant, | 1257 | kind: EnumVariant, |
1258 | lookup: "Quux", | ||
1250 | detail: "()", | 1259 | detail: "()", |
1251 | }, | 1260 | }, |
1252 | ] | 1261 | ] |
@@ -1285,6 +1294,7 @@ mod tests { | |||
1285 | delete: 185..186, | 1294 | delete: 185..186, |
1286 | insert: "Foo::Bar", | 1295 | insert: "Foo::Bar", |
1287 | kind: EnumVariant, | 1296 | kind: EnumVariant, |
1297 | lookup: "Bar", | ||
1288 | detail: "()", | 1298 | detail: "()", |
1289 | }, | 1299 | }, |
1290 | CompletionItem { | 1300 | CompletionItem { |
@@ -1293,6 +1303,7 @@ mod tests { | |||
1293 | delete: 185..186, | 1303 | delete: 185..186, |
1294 | insert: "Foo::Baz", | 1304 | insert: "Foo::Baz", |
1295 | kind: EnumVariant, | 1305 | kind: EnumVariant, |
1306 | lookup: "Baz", | ||
1296 | detail: "()", | 1307 | detail: "()", |
1297 | }, | 1308 | }, |
1298 | CompletionItem { | 1309 | CompletionItem { |
@@ -1301,6 +1312,7 @@ mod tests { | |||
1301 | delete: 185..186, | 1312 | delete: 185..186, |
1302 | insert: "Foo::Quux", | 1313 | insert: "Foo::Quux", |
1303 | kind: EnumVariant, | 1314 | kind: EnumVariant, |
1315 | lookup: "Quux", | ||
1304 | detail: "()", | 1316 | detail: "()", |
1305 | }, | 1317 | }, |
1306 | CompletionItem { | 1318 | CompletionItem { |
@@ -1353,10 +1365,25 @@ mod tests { | |||
1353 | delete: 98..99, | 1365 | delete: 98..99, |
1354 | insert: "m::E::V", | 1366 | insert: "m::E::V", |
1355 | kind: EnumVariant, | 1367 | kind: EnumVariant, |
1368 | lookup: "V", | ||
1356 | detail: "()", | 1369 | detail: "()", |
1357 | }, | 1370 | }, |
1358 | ] | 1371 | ] |
1359 | "### | 1372 | "### |
1360 | ) | 1373 | ) |
1361 | } | 1374 | } |
1375 | |||
1376 | #[test] | ||
1377 | fn dont_complete_attr() { | ||
1378 | assert_debug_snapshot!( | ||
1379 | do_reference_completion( | ||
1380 | r" | ||
1381 | struct Foo; | ||
1382 | #[<|>] | ||
1383 | fn f() {} | ||
1384 | " | ||
1385 | ), | ||
1386 | @r###"[]"### | ||
1387 | ) | ||
1388 | } | ||
1362 | } | 1389 | } |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 5f2797e41..dd87bd119 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -5,7 +5,7 @@ use ra_db::SourceDatabase; | |||
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{find_covering_element, find_node_at_offset}, |
8 | ast, AstNode, | 8 | ast, match_ast, AstNode, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNode, SyntaxToken, TextRange, TextSize, | 10 | SyntaxNode, SyntaxToken, TextRange, TextSize, |
11 | }; | 11 | }; |
@@ -26,6 +26,7 @@ pub(crate) struct CompletionContext<'a> { | |||
26 | /// The token before the cursor, in the macro-expanded file. | 26 | /// The token before the cursor, in the macro-expanded file. |
27 | pub(super) token: SyntaxToken, | 27 | pub(super) token: SyntaxToken, |
28 | pub(super) krate: Option<hir::Crate>, | 28 | pub(super) krate: Option<hir::Crate>, |
29 | pub(super) expected_type: Option<Type>, | ||
29 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 30 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
30 | pub(super) function_syntax: Option<ast::FnDef>, | 31 | pub(super) function_syntax: Option<ast::FnDef>, |
31 | pub(super) use_item_syntax: Option<ast::UseItem>, | 32 | pub(super) use_item_syntax: Option<ast::UseItem>, |
@@ -57,7 +58,7 @@ pub(crate) struct CompletionContext<'a> { | |||
57 | pub(super) is_macro_call: bool, | 58 | pub(super) is_macro_call: bool, |
58 | pub(super) is_path_type: bool, | 59 | pub(super) is_path_type: bool, |
59 | pub(super) has_type_args: bool, | 60 | pub(super) has_type_args: bool, |
60 | pub(super) is_attribute: bool, | 61 | pub(super) attribute_under_caret: Option<ast::Attr>, |
61 | } | 62 | } |
62 | 63 | ||
63 | impl<'a> CompletionContext<'a> { | 64 | impl<'a> CompletionContext<'a> { |
@@ -93,6 +94,7 @@ impl<'a> CompletionContext<'a> { | |||
93 | token, | 94 | token, |
94 | offset: position.offset, | 95 | offset: position.offset, |
95 | krate, | 96 | krate, |
97 | expected_type: None, | ||
96 | name_ref_syntax: None, | 98 | name_ref_syntax: None, |
97 | function_syntax: None, | 99 | function_syntax: None, |
98 | use_item_syntax: None, | 100 | use_item_syntax: None, |
@@ -114,7 +116,7 @@ impl<'a> CompletionContext<'a> { | |||
114 | is_path_type: false, | 116 | is_path_type: false, |
115 | has_type_args: false, | 117 | has_type_args: false, |
116 | dot_receiver_is_ambiguous_float_literal: false, | 118 | dot_receiver_is_ambiguous_float_literal: false, |
117 | is_attribute: false, | 119 | attribute_under_caret: None, |
118 | }; | 120 | }; |
119 | 121 | ||
120 | let mut original_file = original_file.syntax().clone(); | 122 | let mut original_file = original_file.syntax().clone(); |
@@ -175,23 +177,31 @@ impl<'a> CompletionContext<'a> { | |||
175 | self.sema.scope_at_offset(&self.token.parent(), self.offset) | 177 | self.sema.scope_at_offset(&self.token.parent(), self.offset) |
176 | } | 178 | } |
177 | 179 | ||
178 | pub(crate) fn expected_type_of(&self, node: &SyntaxNode) -> Option<Type> { | ||
179 | for ancestor in node.ancestors() { | ||
180 | if let Some(pat) = ast::Pat::cast(ancestor.clone()) { | ||
181 | return self.sema.type_of_pat(&pat); | ||
182 | } else if let Some(expr) = ast::Expr::cast(ancestor) { | ||
183 | return self.sema.type_of_expr(&expr); | ||
184 | } | ||
185 | } | ||
186 | None | ||
187 | } | ||
188 | |||
189 | fn fill( | 180 | fn fill( |
190 | &mut self, | 181 | &mut self, |
191 | original_file: &SyntaxNode, | 182 | original_file: &SyntaxNode, |
192 | file_with_fake_ident: SyntaxNode, | 183 | file_with_fake_ident: SyntaxNode, |
193 | offset: TextSize, | 184 | offset: TextSize, |
194 | ) { | 185 | ) { |
186 | // FIXME: this is wrong in at least two cases: | ||
187 | // * when there's no token `foo(<|>)` | ||
188 | // * when there is a token, but it happens to have type of it's own | ||
189 | self.expected_type = self | ||
190 | .token | ||
191 | .ancestors() | ||
192 | .find_map(|node| { | ||
193 | let ty = match_ast! { | ||
194 | match node { | ||
195 | ast::Pat(it) => self.sema.type_of_pat(&it), | ||
196 | ast::Expr(it) => self.sema.type_of_expr(&it), | ||
197 | _ => return None, | ||
198 | } | ||
199 | }; | ||
200 | Some(ty) | ||
201 | }) | ||
202 | .flatten(); | ||
203 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); | ||
204 | |||
195 | // First, let's try to complete a reference to some declaration. | 205 | // First, let's try to complete a reference to some declaration. |
196 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { | 206 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { |
197 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | 207 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. |
@@ -309,7 +319,6 @@ impl<'a> CompletionContext<'a> { | |||
309 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) | 319 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) |
310 | .is_some(); | 320 | .is_some(); |
311 | self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); | 321 | self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); |
312 | self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some(); | ||
313 | 322 | ||
314 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | 323 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); |
315 | self.has_type_args = segment.type_arg_list().is_some(); | 324 | self.has_type_args = segment.type_arg_list().is_some(); |
@@ -335,7 +344,7 @@ impl<'a> CompletionContext<'a> { | |||
335 | stmt.syntax().text_range() == name_ref.syntax().text_range(), | 344 | stmt.syntax().text_range() == name_ref.syntax().text_range(), |
336 | ); | 345 | ); |
337 | } | 346 | } |
338 | if let Some(block) = ast::Block::cast(node) { | 347 | if let Some(block) = ast::BlockExpr::cast(node) { |
339 | return Some( | 348 | return Some( |
340 | block.expr().map(|e| e.syntax().text_range()) | 349 | block.expr().map(|e| e.syntax().text_range()) |
341 | == Some(name_ref.syntax().text_range()), | 350 | == Some(name_ref.syntax().text_range()), |
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 7633cd7fd..2edb130cf 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! This modules takes care of rendering various definitions as completion items. | 1 | //! This modules takes care of rendering various definitions as completion items. |
2 | 2 | ||
3 | use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type}; | 3 | use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; |
4 | use ra_syntax::ast::NameOwner; | 4 | use ra_syntax::ast::NameOwner; |
5 | use stdx::SepBy; | 5 | use stdx::SepBy; |
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
@@ -246,14 +246,37 @@ impl Completions { | |||
246 | .add_to(self); | 246 | .add_to(self); |
247 | } | 247 | } |
248 | 248 | ||
249 | pub(crate) fn add_qualified_enum_variant( | ||
250 | &mut self, | ||
251 | ctx: &CompletionContext, | ||
252 | variant: hir::EnumVariant, | ||
253 | path: ModPath, | ||
254 | ) { | ||
255 | self.add_enum_variant_impl(ctx, variant, None, Some(path)) | ||
256 | } | ||
257 | |||
249 | pub(crate) fn add_enum_variant( | 258 | pub(crate) fn add_enum_variant( |
250 | &mut self, | 259 | &mut self, |
251 | ctx: &CompletionContext, | 260 | ctx: &CompletionContext, |
252 | variant: hir::EnumVariant, | 261 | variant: hir::EnumVariant, |
253 | local_name: Option<String>, | 262 | local_name: Option<String>, |
254 | ) { | 263 | ) { |
264 | self.add_enum_variant_impl(ctx, variant, local_name, None) | ||
265 | } | ||
266 | |||
267 | fn add_enum_variant_impl( | ||
268 | &mut self, | ||
269 | ctx: &CompletionContext, | ||
270 | variant: hir::EnumVariant, | ||
271 | local_name: Option<String>, | ||
272 | path: Option<ModPath>, | ||
273 | ) { | ||
255 | let is_deprecated = is_deprecated(variant, ctx.db); | 274 | let is_deprecated = is_deprecated(variant, ctx.db); |
256 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); | 275 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db).to_string()); |
276 | let qualified_name = match &path { | ||
277 | Some(it) => it.to_string(), | ||
278 | None => name.to_string(), | ||
279 | }; | ||
257 | let detail_types = variant | 280 | let detail_types = variant |
258 | .fields(ctx.db) | 281 | .fields(ctx.db) |
259 | .into_iter() | 282 | .into_iter() |
@@ -271,16 +294,23 @@ impl Completions { | |||
271 | .surround_with("{ ", " }") | 294 | .surround_with("{ ", " }") |
272 | .to_string(), | 295 | .to_string(), |
273 | }; | 296 | }; |
274 | let mut res = | 297 | let mut res = CompletionItem::new( |
275 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) | 298 | CompletionKind::Reference, |
276 | .kind(CompletionItemKind::EnumVariant) | 299 | ctx.source_range(), |
277 | .set_documentation(variant.docs(ctx.db)) | 300 | qualified_name.clone(), |
278 | .set_deprecated(is_deprecated) | 301 | ) |
279 | .detail(detail); | 302 | .kind(CompletionItemKind::EnumVariant) |
303 | .set_documentation(variant.docs(ctx.db)) | ||
304 | .set_deprecated(is_deprecated) | ||
305 | .detail(detail); | ||
306 | |||
307 | if path.is_some() { | ||
308 | res = res.lookup_by(name); | ||
309 | } | ||
280 | 310 | ||
281 | if variant_kind == StructKind::Tuple { | 311 | if variant_kind == StructKind::Tuple { |
282 | let params = Params::Anonymous(variant.fields(ctx.db).len()); | 312 | let params = Params::Anonymous(variant.fields(ctx.db).len()); |
283 | res = res.add_call_parens(ctx, name, params) | 313 | res = res.add_call_parens(ctx, qualified_name, params) |
284 | } | 314 | } |
285 | 315 | ||
286 | res.add_to(self); | 316 | res.add_to(self); |
@@ -349,6 +379,14 @@ impl Builder { | |||
349 | if ctx.use_item_syntax.is_some() || ctx.is_call { | 379 | if ctx.use_item_syntax.is_some() || ctx.is_call { |
350 | return self; | 380 | return self; |
351 | } | 381 | } |
382 | |||
383 | // Don't add parentheses if the expected type is some function reference. | ||
384 | if let Some(ty) = &ctx.expected_type { | ||
385 | if ty.is_fn() { | ||
386 | return self; | ||
387 | } | ||
388 | } | ||
389 | |||
352 | let cap = match ctx.config.snippet_cap { | 390 | let cap = match ctx.config.snippet_cap { |
353 | Some(it) => it, | 391 | Some(it) => it, |
354 | None => return self, | 392 | None => return self, |
@@ -749,6 +787,54 @@ mod tests { | |||
749 | } | 787 | } |
750 | 788 | ||
751 | #[test] | 789 | #[test] |
790 | fn no_call_parens_if_fn_ptr_needed() { | ||
791 | assert_debug_snapshot!( | ||
792 | do_reference_completion( | ||
793 | r" | ||
794 | fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8) {} | ||
795 | |||
796 | struct ManualVtable { | ||
797 | method: fn(u8, u8, u8, u8, u8), | ||
798 | } | ||
799 | |||
800 | fn main() -> ManualVtable { | ||
801 | ManualVtable { | ||
802 | method: some<|> | ||
803 | } | ||
804 | } | ||
805 | " | ||
806 | ), | ||
807 | @r###" | ||
808 | [ | ||
809 | CompletionItem { | ||
810 | label: "ManualVtable", | ||
811 | source_range: 295..299, | ||
812 | delete: 295..299, | ||
813 | insert: "ManualVtable", | ||
814 | kind: Struct, | ||
815 | }, | ||
816 | CompletionItem { | ||
817 | label: "main", | ||
818 | source_range: 295..299, | ||
819 | delete: 295..299, | ||
820 | insert: "main", | ||
821 | kind: Function, | ||
822 | detail: "fn main() -> ManualVtable", | ||
823 | }, | ||
824 | CompletionItem { | ||
825 | label: "somefn", | ||
826 | source_range: 295..299, | ||
827 | delete: 295..299, | ||
828 | insert: "somefn", | ||
829 | kind: Function, | ||
830 | detail: "fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8)", | ||
831 | }, | ||
832 | ] | ||
833 | "### | ||
834 | ); | ||
835 | } | ||
836 | |||
837 | #[test] | ||
752 | fn arg_snippets_for_method_call() { | 838 | fn arg_snippets_for_method_call() { |
753 | assert_debug_snapshot!( | 839 | assert_debug_snapshot!( |
754 | do_reference_completion( | 840 | do_reference_completion( |
@@ -1179,7 +1265,7 @@ mod tests { | |||
1179 | 1265 | ||
1180 | #[test] | 1266 | #[test] |
1181 | fn test_struct_field_completion_in_record_lit() { | 1267 | fn test_struct_field_completion_in_record_lit() { |
1182 | covers!(test_struct_field_completion_in_func_call); | 1268 | covers!(test_struct_field_completion_in_record_lit); |
1183 | assert_debug_snapshot!( | 1269 | assert_debug_snapshot!( |
1184 | do_reference_completion( | 1270 | do_reference_completion( |
1185 | r" | 1271 | r" |