diff options
Diffstat (limited to 'crates/ide/src/completion')
-rw-r--r-- | crates/ide/src/completion/complete_attribute.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/completion/complete_keyword.rs | 22 | ||||
-rw-r--r-- | crates/ide/src/completion/complete_mod.rs | 324 | ||||
-rw-r--r-- | crates/ide/src/completion/complete_qualified_path.rs | 24 | ||||
-rw-r--r-- | crates/ide/src/completion/complete_trait_impl.rs | 377 | ||||
-rw-r--r-- | crates/ide/src/completion/complete_unqualified_path.rs | 1 | ||||
-rw-r--r-- | crates/ide/src/completion/completion_context.rs | 7 | ||||
-rw-r--r-- | crates/ide/src/completion/patterns.rs | 1 |
8 files changed, 692 insertions, 68 deletions
diff --git a/crates/ide/src/completion/complete_attribute.rs b/crates/ide/src/completion/complete_attribute.rs index 0abfaebcb..f4a9864d1 100644 --- a/crates/ide/src/completion/complete_attribute.rs +++ b/crates/ide/src/completion/complete_attribute.rs | |||
@@ -13,6 +13,10 @@ use crate::completion::{ | |||
13 | }; | 13 | }; |
14 | 14 | ||
15 | pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 15 | pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
16 | if ctx.mod_declaration_under_caret.is_some() { | ||
17 | return None; | ||
18 | } | ||
19 | |||
16 | let attribute = ctx.attribute_under_caret.as_ref()?; | 20 | let attribute = ctx.attribute_under_caret.as_ref()?; |
17 | match (attribute.path(), attribute.token_tree()) { | 21 | match (attribute.path(), attribute.token_tree()) { |
18 | (Some(path), Some(token_tree)) if path.to_string() == "derive" => { | 22 | (Some(path), Some(token_tree)) if path.to_string() == "derive" => { |
diff --git a/crates/ide/src/completion/complete_keyword.rs b/crates/ide/src/completion/complete_keyword.rs index 53ba76e0e..5645b41fa 100644 --- a/crates/ide/src/completion/complete_keyword.rs +++ b/crates/ide/src/completion/complete_keyword.rs | |||
@@ -510,6 +510,28 @@ pub mod future { | |||
510 | expect![[r#" | 510 | expect![[r#" |
511 | kw await expr.await | 511 | kw await expr.await |
512 | "#]], | 512 | "#]], |
513 | ); | ||
514 | |||
515 | check( | ||
516 | r#" | ||
517 | //- /main.rs | ||
518 | use std::future::*; | ||
519 | fn foo() { | ||
520 | let a = async {}; | ||
521 | a.<|> | ||
522 | } | ||
523 | |||
524 | //- /std/lib.rs | ||
525 | pub mod future { | ||
526 | #[lang = "future_trait"] | ||
527 | pub trait Future { | ||
528 | type Output; | ||
529 | } | ||
530 | } | ||
531 | "#, | ||
532 | expect![[r#" | ||
533 | kw await expr.await | ||
534 | "#]], | ||
513 | ) | 535 | ) |
514 | } | 536 | } |
515 | 537 | ||
diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs new file mode 100644 index 000000000..3cfc2e131 --- /dev/null +++ b/crates/ide/src/completion/complete_mod.rs | |||
@@ -0,0 +1,324 @@ | |||
1 | //! Completes mod declarations. | ||
2 | |||
3 | use base_db::{SourceDatabaseExt, VfsPath}; | ||
4 | use hir::{Module, ModuleSource}; | ||
5 | use ide_db::RootDatabase; | ||
6 | use rustc_hash::FxHashSet; | ||
7 | |||
8 | use crate::{CompletionItem, CompletionItemKind}; | ||
9 | |||
10 | use super::{ | ||
11 | completion_context::CompletionContext, completion_item::CompletionKind, | ||
12 | completion_item::Completions, | ||
13 | }; | ||
14 | |||
15 | /// Complete mod declaration, i.e. `mod <|> ;` | ||
16 | pub(super) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | ||
17 | let mod_under_caret = match &ctx.mod_declaration_under_caret { | ||
18 | Some(mod_under_caret) if mod_under_caret.item_list().is_some() => return None, | ||
19 | Some(mod_under_caret) => mod_under_caret, | ||
20 | None => return None, | ||
21 | }; | ||
22 | |||
23 | let _p = profile::span("completion::complete_mod"); | ||
24 | |||
25 | let current_module = ctx.scope.module()?; | ||
26 | |||
27 | let module_definition_file = | ||
28 | current_module.definition_source(ctx.db).file_id.original_file(ctx.db); | ||
29 | let source_root = ctx.db.source_root(ctx.db.file_source_root(module_definition_file)); | ||
30 | let directory_to_look_for_submodules = directory_to_look_for_submodules( | ||
31 | current_module, | ||
32 | ctx.db, | ||
33 | source_root.path_for_file(&module_definition_file)?, | ||
34 | )?; | ||
35 | |||
36 | let existing_mod_declarations = current_module | ||
37 | .children(ctx.db) | ||
38 | .filter_map(|module| Some(module.name(ctx.db)?.to_string())) | ||
39 | .collect::<FxHashSet<_>>(); | ||
40 | |||
41 | let module_declaration_file = | ||
42 | current_module.declaration_source(ctx.db).map(|module_declaration_source_file| { | ||
43 | module_declaration_source_file.file_id.original_file(ctx.db) | ||
44 | }); | ||
45 | |||
46 | source_root | ||
47 | .iter() | ||
48 | .filter(|submodule_candidate_file| submodule_candidate_file != &module_definition_file) | ||
49 | .filter(|submodule_candidate_file| { | ||
50 | Some(submodule_candidate_file) != module_declaration_file.as_ref() | ||
51 | }) | ||
52 | .filter_map(|submodule_file| { | ||
53 | let submodule_path = source_root.path_for_file(&submodule_file)?; | ||
54 | let directory_with_submodule = submodule_path.parent()?; | ||
55 | match submodule_path.name_and_extension()? { | ||
56 | ("lib", Some("rs")) | ("main", Some("rs")) => None, | ||
57 | ("mod", Some("rs")) => { | ||
58 | if directory_with_submodule.parent()? == directory_to_look_for_submodules { | ||
59 | match directory_with_submodule.name_and_extension()? { | ||
60 | (directory_name, None) => Some(directory_name.to_owned()), | ||
61 | _ => None, | ||
62 | } | ||
63 | } else { | ||
64 | None | ||
65 | } | ||
66 | } | ||
67 | (file_name, Some("rs")) | ||
68 | if directory_with_submodule == directory_to_look_for_submodules => | ||
69 | { | ||
70 | Some(file_name.to_owned()) | ||
71 | } | ||
72 | _ => None, | ||
73 | } | ||
74 | }) | ||
75 | .filter(|name| !existing_mod_declarations.contains(name)) | ||
76 | .for_each(|submodule_name| { | ||
77 | let mut label = submodule_name; | ||
78 | if mod_under_caret.semicolon_token().is_none() { | ||
79 | label.push(';') | ||
80 | } | ||
81 | acc.add( | ||
82 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) | ||
83 | .kind(CompletionItemKind::Module), | ||
84 | ) | ||
85 | }); | ||
86 | |||
87 | Some(()) | ||
88 | } | ||
89 | |||
90 | fn directory_to_look_for_submodules( | ||
91 | module: Module, | ||
92 | db: &RootDatabase, | ||
93 | module_file_path: &VfsPath, | ||
94 | ) -> Option<VfsPath> { | ||
95 | let directory_with_module_path = module_file_path.parent()?; | ||
96 | let base_directory = match module_file_path.name_and_extension()? { | ||
97 | ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => { | ||
98 | Some(directory_with_module_path) | ||
99 | } | ||
100 | (regular_rust_file_name, Some("rs")) => { | ||
101 | if matches!( | ||
102 | ( | ||
103 | directory_with_module_path | ||
104 | .parent() | ||
105 | .as_ref() | ||
106 | .and_then(|path| path.name_and_extension()), | ||
107 | directory_with_module_path.name_and_extension(), | ||
108 | ), | ||
109 | (Some(("src", None)), Some(("bin", None))) | ||
110 | ) { | ||
111 | // files in /src/bin/ can import each other directly | ||
112 | Some(directory_with_module_path) | ||
113 | } else { | ||
114 | directory_with_module_path.join(regular_rust_file_name) | ||
115 | } | ||
116 | } | ||
117 | _ => None, | ||
118 | }?; | ||
119 | |||
120 | let mut resulting_path = base_directory; | ||
121 | for module in module_chain_to_containing_module_file(module, db) { | ||
122 | if let Some(name) = module.name(db) { | ||
123 | resulting_path = resulting_path.join(&name.to_string())?; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | Some(resulting_path) | ||
128 | } | ||
129 | |||
130 | fn module_chain_to_containing_module_file( | ||
131 | current_module: Module, | ||
132 | db: &RootDatabase, | ||
133 | ) -> Vec<Module> { | ||
134 | let mut path = Vec::new(); | ||
135 | |||
136 | let mut current_module = Some(current_module); | ||
137 | while let Some(ModuleSource::Module(_)) = | ||
138 | current_module.map(|module| module.definition_source(db).value) | ||
139 | { | ||
140 | if let Some(module) = current_module { | ||
141 | path.insert(0, module); | ||
142 | current_module = module.parent(db); | ||
143 | } else { | ||
144 | current_module = None; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | path | ||
149 | } | ||
150 | |||
151 | #[cfg(test)] | ||
152 | mod tests { | ||
153 | use crate::completion::{test_utils::completion_list, CompletionKind}; | ||
154 | use expect_test::{expect, Expect}; | ||
155 | |||
156 | fn check(ra_fixture: &str, expect: Expect) { | ||
157 | let actual = completion_list(ra_fixture, CompletionKind::Magic); | ||
158 | expect.assert_eq(&actual); | ||
159 | } | ||
160 | |||
161 | #[test] | ||
162 | fn lib_module_completion() { | ||
163 | check( | ||
164 | r#" | ||
165 | //- /lib.rs | ||
166 | mod <|> | ||
167 | //- /foo.rs | ||
168 | fn foo() {} | ||
169 | //- /foo/ignored_foo.rs | ||
170 | fn ignored_foo() {} | ||
171 | //- /bar/mod.rs | ||
172 | fn bar() {} | ||
173 | //- /bar/ignored_bar.rs | ||
174 | fn ignored_bar() {} | ||
175 | "#, | ||
176 | expect![[r#" | ||
177 | md bar; | ||
178 | md foo; | ||
179 | "#]], | ||
180 | ); | ||
181 | } | ||
182 | |||
183 | #[test] | ||
184 | fn no_module_completion_with_module_body() { | ||
185 | check( | ||
186 | r#" | ||
187 | //- /lib.rs | ||
188 | mod <|> { | ||
189 | |||
190 | } | ||
191 | //- /foo.rs | ||
192 | fn foo() {} | ||
193 | "#, | ||
194 | expect![[r#""#]], | ||
195 | ); | ||
196 | } | ||
197 | |||
198 | #[test] | ||
199 | fn main_module_completion() { | ||
200 | check( | ||
201 | r#" | ||
202 | //- /main.rs | ||
203 | mod <|> | ||
204 | //- /foo.rs | ||
205 | fn foo() {} | ||
206 | //- /foo/ignored_foo.rs | ||
207 | fn ignored_foo() {} | ||
208 | //- /bar/mod.rs | ||
209 | fn bar() {} | ||
210 | //- /bar/ignored_bar.rs | ||
211 | fn ignored_bar() {} | ||
212 | "#, | ||
213 | expect![[r#" | ||
214 | md bar; | ||
215 | md foo; | ||
216 | "#]], | ||
217 | ); | ||
218 | } | ||
219 | |||
220 | #[test] | ||
221 | fn main_test_module_completion() { | ||
222 | check( | ||
223 | r#" | ||
224 | //- /main.rs | ||
225 | mod tests { | ||
226 | mod <|>; | ||
227 | } | ||
228 | //- /tests/foo.rs | ||
229 | fn foo() {} | ||
230 | "#, | ||
231 | expect![[r#" | ||
232 | md foo | ||
233 | "#]], | ||
234 | ); | ||
235 | } | ||
236 | |||
237 | #[test] | ||
238 | fn directly_nested_module_completion() { | ||
239 | check( | ||
240 | r#" | ||
241 | //- /lib.rs | ||
242 | mod foo; | ||
243 | //- /foo.rs | ||
244 | mod <|>; | ||
245 | //- /foo/bar.rs | ||
246 | fn bar() {} | ||
247 | //- /foo/bar/ignored_bar.rs | ||
248 | fn ignored_bar() {} | ||
249 | //- /foo/baz/mod.rs | ||
250 | fn baz() {} | ||
251 | //- /foo/moar/ignored_moar.rs | ||
252 | fn ignored_moar() {} | ||
253 | "#, | ||
254 | expect![[r#" | ||
255 | md bar | ||
256 | md baz | ||
257 | "#]], | ||
258 | ); | ||
259 | } | ||
260 | |||
261 | #[test] | ||
262 | fn nested_in_source_module_completion() { | ||
263 | check( | ||
264 | r#" | ||
265 | //- /lib.rs | ||
266 | mod foo; | ||
267 | //- /foo.rs | ||
268 | mod bar { | ||
269 | mod <|> | ||
270 | } | ||
271 | //- /foo/bar/baz.rs | ||
272 | fn baz() {} | ||
273 | "#, | ||
274 | expect![[r#" | ||
275 | md baz; | ||
276 | "#]], | ||
277 | ); | ||
278 | } | ||
279 | |||
280 | // FIXME binary modules are not supported in tests properly | ||
281 | // Binary modules are a bit special, they allow importing the modules from `/src/bin` | ||
282 | // and that's why are good to test two things: | ||
283 | // * no cycles are allowed in mod declarations | ||
284 | // * no modules from the parent directory are proposed | ||
285 | // Unfortunately, binary modules support is in cargo not rustc, | ||
286 | // hence the test does not work now | ||
287 | // | ||
288 | // #[test] | ||
289 | // fn regular_bin_module_completion() { | ||
290 | // check( | ||
291 | // r#" | ||
292 | // //- /src/bin.rs | ||
293 | // fn main() {} | ||
294 | // //- /src/bin/foo.rs | ||
295 | // mod <|> | ||
296 | // //- /src/bin/bar.rs | ||
297 | // fn bar() {} | ||
298 | // //- /src/bin/bar/bar_ignored.rs | ||
299 | // fn bar_ignored() {} | ||
300 | // "#, | ||
301 | // expect![[r#" | ||
302 | // md bar; | ||
303 | // "#]], | ||
304 | // ); | ||
305 | // } | ||
306 | |||
307 | #[test] | ||
308 | fn already_declared_bin_module_completion_omitted() { | ||
309 | check( | ||
310 | r#" | ||
311 | //- /src/bin.rs | ||
312 | fn main() {} | ||
313 | //- /src/bin/foo.rs | ||
314 | mod <|> | ||
315 | //- /src/bin/bar.rs | ||
316 | mod foo; | ||
317 | fn bar() {} | ||
318 | //- /src/bin/bar/bar_ignored.rs | ||
319 | fn bar_ignored() {} | ||
320 | "#, | ||
321 | expect![[r#""#]], | ||
322 | ); | ||
323 | } | ||
324 | } | ||
diff --git a/crates/ide/src/completion/complete_qualified_path.rs b/crates/ide/src/completion/complete_qualified_path.rs index accb09f7e..00e89f0fd 100644 --- a/crates/ide/src/completion/complete_qualified_path.rs +++ b/crates/ide/src/completion/complete_qualified_path.rs | |||
@@ -13,7 +13,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
13 | None => return, | 13 | None => return, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | if ctx.attribute_under_caret.is_some() { | 16 | if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() { |
17 | return; | 17 | return; |
18 | } | 18 | } |
19 | 19 | ||
@@ -730,4 +730,26 @@ fn f() {} | |||
730 | expect![[""]], | 730 | expect![[""]], |
731 | ); | 731 | ); |
732 | } | 732 | } |
733 | |||
734 | #[test] | ||
735 | fn completes_function() { | ||
736 | check( | ||
737 | r#" | ||
738 | fn foo( | ||
739 | a: i32, | ||
740 | b: i32 | ||
741 | ) { | ||
742 | |||
743 | } | ||
744 | |||
745 | fn main() { | ||
746 | fo<|> | ||
747 | } | ||
748 | "#, | ||
749 | expect![[r#" | ||
750 | fn foo(…) fn foo(a: i32, b: i32) | ||
751 | fn main() fn main() | ||
752 | "#]], | ||
753 | ); | ||
754 | } | ||
733 | } | 755 | } |
diff --git a/crates/ide/src/completion/complete_trait_impl.rs b/crates/ide/src/completion/complete_trait_impl.rs index 26f268bd1..ff115df92 100644 --- a/crates/ide/src/completion/complete_trait_impl.rs +++ b/crates/ide/src/completion/complete_trait_impl.rs | |||
@@ -46,76 +46,86 @@ use crate::{ | |||
46 | display::function_declaration, | 46 | display::function_declaration, |
47 | }; | 47 | }; |
48 | 48 | ||
49 | #[derive(Debug, PartialEq, Eq)] | ||
50 | enum ImplCompletionKind { | ||
51 | All, | ||
52 | Fn, | ||
53 | TypeAlias, | ||
54 | Const, | ||
55 | } | ||
56 | |||
49 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { | 57 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { |
50 | if let Some((trigger, impl_def)) = completion_match(ctx) { | 58 | if let Some((kind, trigger, impl_def)) = completion_match(ctx) { |
51 | match trigger.kind() { | 59 | get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item { |
52 | SyntaxKind::NAME_REF => get_missing_assoc_items(&ctx.sema, &impl_def) | 60 | hir::AssocItem::Function(fn_item) |
53 | .into_iter() | 61 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => |
54 | .for_each(|item| match item { | 62 | { |
55 | hir::AssocItem::Function(fn_item) => { | 63 | add_function_impl(&trigger, acc, ctx, fn_item) |
56 | add_function_impl(&trigger, acc, ctx, fn_item) | ||
57 | } | ||
58 | hir::AssocItem::TypeAlias(type_item) => { | ||
59 | add_type_alias_impl(&trigger, acc, ctx, type_item) | ||
60 | } | ||
61 | hir::AssocItem::Const(const_item) => { | ||
62 | add_const_impl(&trigger, acc, ctx, const_item) | ||
63 | } | ||
64 | }), | ||
65 | |||
66 | SyntaxKind::FN => { | ||
67 | for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) | ||
68 | .into_iter() | ||
69 | .filter_map(|item| match item { | ||
70 | hir::AssocItem::Function(fn_item) => Some(fn_item), | ||
71 | _ => None, | ||
72 | }) | ||
73 | { | ||
74 | add_function_impl(&trigger, acc, ctx, missing_fn); | ||
75 | } | ||
76 | } | 64 | } |
77 | 65 | hir::AssocItem::TypeAlias(type_item) | |
78 | SyntaxKind::TYPE_ALIAS => { | 66 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias => |
79 | for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) | 67 | { |
80 | .into_iter() | 68 | add_type_alias_impl(&trigger, acc, ctx, type_item) |
81 | .filter_map(|item| match item { | ||
82 | hir::AssocItem::TypeAlias(type_item) => Some(type_item), | ||
83 | _ => None, | ||
84 | }) | ||
85 | { | ||
86 | add_type_alias_impl(&trigger, acc, ctx, missing_fn); | ||
87 | } | ||
88 | } | 69 | } |
89 | 70 | hir::AssocItem::Const(const_item) | |
90 | SyntaxKind::CONST => { | 71 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const => |
91 | for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) | 72 | { |
92 | .into_iter() | 73 | add_const_impl(&trigger, acc, ctx, const_item) |
93 | .filter_map(|item| match item { | ||
94 | hir::AssocItem::Const(const_item) => Some(const_item), | ||
95 | _ => None, | ||
96 | }) | ||
97 | { | ||
98 | add_const_impl(&trigger, acc, ctx, missing_fn); | ||
99 | } | ||
100 | } | 74 | } |
101 | |||
102 | _ => {} | 75 | _ => {} |
103 | } | 76 | }); |
104 | } | 77 | } |
105 | } | 78 | } |
106 | 79 | ||
107 | fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, Impl)> { | 80 | fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> { |
108 | let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() { | 81 | let mut token = ctx.token.clone(); |
109 | SyntaxKind::FN | SyntaxKind::TYPE_ALIAS | SyntaxKind::CONST | SyntaxKind::BLOCK_EXPR => { | 82 | // For keywork without name like `impl .. { fn <|> }`, the current position is inside |
110 | Some((p, 2)) | 83 | // the whitespace token, which is outside `FN` syntax node. |
111 | } | 84 | // We need to follow the previous token in this case. |
112 | SyntaxKind::NAME_REF => Some((p, 5)), | 85 | if token.kind() == SyntaxKind::WHITESPACE { |
113 | _ => None, | 86 | token = token.prev_token()?; |
114 | })?; | 87 | } |
115 | let impl_def = (0..impl_def_offset - 1) | 88 | |
116 | .try_fold(trigger.parent()?, |t, _| t.parent()) | 89 | let impl_item_offset = match token.kind() { |
117 | .and_then(ast::Impl::cast)?; | 90 | // `impl .. { const <|> }` |
118 | Some((trigger, impl_def)) | 91 | // ERROR 0 |
92 | // CONST_KW <- * | ||
93 | SyntaxKind::CONST_KW => 0, | ||
94 | // `impl .. { fn/type <|> }` | ||
95 | // FN/TYPE_ALIAS 0 | ||
96 | // FN_KW <- * | ||
97 | SyntaxKind::FN_KW | SyntaxKind::TYPE_KW => 0, | ||
98 | // `impl .. { fn/type/const foo<|> }` | ||
99 | // FN/TYPE_ALIAS/CONST 1 | ||
100 | // NAME 0 | ||
101 | // IDENT <- * | ||
102 | SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME => 1, | ||
103 | // `impl .. { foo<|> }` | ||
104 | // MACRO_CALL 3 | ||
105 | // PATH 2 | ||
106 | // PATH_SEGMENT 1 | ||
107 | // NAME_REF 0 | ||
108 | // IDENT <- * | ||
109 | SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME_REF => 3, | ||
110 | _ => return None, | ||
111 | }; | ||
112 | |||
113 | let impl_item = token.ancestors().nth(impl_item_offset)?; | ||
114 | // Must directly belong to an impl block. | ||
115 | // IMPL | ||
116 | // ASSOC_ITEM_LIST | ||
117 | // <item> | ||
118 | let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?; | ||
119 | let kind = match impl_item.kind() { | ||
120 | // `impl ... { const <|> fn/type/const }` | ||
121 | _ if token.kind() == SyntaxKind::CONST_KW => ImplCompletionKind::Const, | ||
122 | SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const, | ||
123 | SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias, | ||
124 | SyntaxKind::FN => ImplCompletionKind::Fn, | ||
125 | SyntaxKind::MACRO_CALL => ImplCompletionKind::All, | ||
126 | _ => return None, | ||
127 | }; | ||
128 | Some((kind, impl_item, impl_def)) | ||
119 | } | 129 | } |
120 | 130 | ||
121 | fn add_function_impl( | 131 | fn add_function_impl( |
@@ -261,19 +271,191 @@ ta type TestType = \n\ | |||
261 | } | 271 | } |
262 | 272 | ||
263 | #[test] | 273 | #[test] |
264 | fn no_nested_fn_completions() { | 274 | fn no_completion_inside_fn() { |
265 | check( | 275 | check( |
266 | r" | 276 | r" |
267 | trait Test { | 277 | trait Test { fn test(); fn test2(); } |
268 | fn test(); | 278 | struct T; |
269 | fn test2(); | 279 | |
280 | impl Test for T { | ||
281 | fn test() { | ||
282 | t<|> | ||
283 | } | ||
284 | } | ||
285 | ", | ||
286 | expect![[""]], | ||
287 | ); | ||
288 | |||
289 | check( | ||
290 | r" | ||
291 | trait Test { fn test(); fn test2(); } | ||
292 | struct T; | ||
293 | |||
294 | impl Test for T { | ||
295 | fn test() { | ||
296 | fn t<|> | ||
297 | } | ||
298 | } | ||
299 | ", | ||
300 | expect![[""]], | ||
301 | ); | ||
302 | |||
303 | check( | ||
304 | r" | ||
305 | trait Test { fn test(); fn test2(); } | ||
306 | struct T; | ||
307 | |||
308 | impl Test for T { | ||
309 | fn test() { | ||
310 | fn <|> | ||
311 | } | ||
270 | } | 312 | } |
313 | ", | ||
314 | expect![[""]], | ||
315 | ); | ||
316 | |||
317 | // https://github.com/rust-analyzer/rust-analyzer/pull/5976#issuecomment-692332191 | ||
318 | check( | ||
319 | r" | ||
320 | trait Test { fn test(); fn test2(); } | ||
271 | struct T; | 321 | struct T; |
272 | 322 | ||
273 | impl Test for T { | 323 | impl Test for T { |
274 | fn test() { | 324 | fn test() { |
325 | foo.<|> | ||
326 | } | ||
327 | } | ||
328 | ", | ||
329 | expect![[""]], | ||
330 | ); | ||
331 | |||
332 | check( | ||
333 | r" | ||
334 | trait Test { fn test(_: i32); fn test2(); } | ||
335 | struct T; | ||
336 | |||
337 | impl Test for T { | ||
338 | fn test(t<|>) | ||
339 | } | ||
340 | ", | ||
341 | expect![[""]], | ||
342 | ); | ||
343 | |||
344 | check( | ||
345 | r" | ||
346 | trait Test { fn test(_: fn()); fn test2(); } | ||
347 | struct T; | ||
348 | |||
349 | impl Test for T { | ||
350 | fn test(f: fn <|>) | ||
351 | } | ||
352 | ", | ||
353 | expect![[""]], | ||
354 | ); | ||
355 | } | ||
356 | |||
357 | #[test] | ||
358 | fn no_completion_inside_const() { | ||
359 | check( | ||
360 | r" | ||
361 | trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); } | ||
362 | struct T; | ||
363 | |||
364 | impl Test for T { | ||
365 | const TEST: fn <|> | ||
366 | } | ||
367 | ", | ||
368 | expect![[""]], | ||
369 | ); | ||
370 | |||
371 | check( | ||
372 | r" | ||
373 | trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } | ||
374 | struct T; | ||
375 | |||
376 | impl Test for T { | ||
377 | const TEST: T<|> | ||
378 | } | ||
379 | ", | ||
380 | expect![[""]], | ||
381 | ); | ||
382 | |||
383 | check( | ||
384 | r" | ||
385 | trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } | ||
386 | struct T; | ||
387 | |||
388 | impl Test for T { | ||
389 | const TEST: u32 = f<|> | ||
390 | } | ||
391 | ", | ||
392 | expect![[""]], | ||
393 | ); | ||
394 | |||
395 | check( | ||
396 | r" | ||
397 | trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } | ||
398 | struct T; | ||
399 | |||
400 | impl Test for T { | ||
401 | const TEST: u32 = { | ||
275 | t<|> | 402 | t<|> |
403 | }; | ||
404 | } | ||
405 | ", | ||
406 | expect![[""]], | ||
407 | ); | ||
408 | |||
409 | check( | ||
410 | r" | ||
411 | trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } | ||
412 | struct T; | ||
413 | |||
414 | impl Test for T { | ||
415 | const TEST: u32 = { | ||
416 | fn <|> | ||
417 | }; | ||
418 | } | ||
419 | ", | ||
420 | expect![[""]], | ||
421 | ); | ||
422 | |||
423 | check( | ||
424 | r" | ||
425 | trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } | ||
426 | struct T; | ||
427 | |||
428 | impl Test for T { | ||
429 | const TEST: u32 = { | ||
430 | fn t<|> | ||
431 | }; | ||
432 | } | ||
433 | ", | ||
434 | expect![[""]], | ||
435 | ); | ||
276 | } | 436 | } |
437 | |||
438 | #[test] | ||
439 | fn no_completion_inside_type() { | ||
440 | check( | ||
441 | r" | ||
442 | trait Test { type Test; type Test2; fn test(); } | ||
443 | struct T; | ||
444 | |||
445 | impl Test for T { | ||
446 | type Test = T<|>; | ||
447 | } | ||
448 | ", | ||
449 | expect![[""]], | ||
450 | ); | ||
451 | |||
452 | check( | ||
453 | r" | ||
454 | trait Test { type Test; type Test2; fn test(); } | ||
455 | struct T; | ||
456 | |||
457 | impl Test for T { | ||
458 | type Test = fn <|>; | ||
277 | } | 459 | } |
278 | ", | 460 | ", |
279 | expect![[""]], | 461 | expect![[""]], |
@@ -485,4 +667,67 @@ impl Test for () { | |||
485 | ", | 667 | ", |
486 | ); | 668 | ); |
487 | } | 669 | } |
670 | |||
671 | #[test] | ||
672 | fn complete_without_name() { | ||
673 | let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| { | ||
674 | println!( | ||
675 | "completion='{}', hint='{}', next_sibling='{}'", | ||
676 | completion, hint, next_sibling | ||
677 | ); | ||
678 | |||
679 | check_edit( | ||
680 | completion, | ||
681 | &format!( | ||
682 | r#" | ||
683 | trait Test {{ | ||
684 | type Foo; | ||
685 | const CONST: u16; | ||
686 | fn bar(); | ||
687 | }} | ||
688 | struct T; | ||
689 | |||
690 | impl Test for T {{ | ||
691 | {} | ||
692 | {} | ||
693 | }} | ||
694 | "#, | ||
695 | hint, next_sibling | ||
696 | ), | ||
697 | &format!( | ||
698 | r#" | ||
699 | trait Test {{ | ||
700 | type Foo; | ||
701 | const CONST: u16; | ||
702 | fn bar(); | ||
703 | }} | ||
704 | struct T; | ||
705 | |||
706 | impl Test for T {{ | ||
707 | {} | ||
708 | {} | ||
709 | }} | ||
710 | "#, | ||
711 | completed, next_sibling | ||
712 | ), | ||
713 | ) | ||
714 | }; | ||
715 | |||
716 | // Enumerate some possible next siblings. | ||
717 | for next_sibling in &[ | ||
718 | "", | ||
719 | "fn other_fn() {}", // `const <|> fn` -> `const fn` | ||
720 | "type OtherType = i32;", | ||
721 | "const OTHER_CONST: i32 = 0;", | ||
722 | "async fn other_fn() {}", | ||
723 | "unsafe fn other_fn() {}", | ||
724 | "default fn other_fn() {}", | ||
725 | "default type OtherType = i32;", | ||
726 | "default const OTHER_CONST: i32 = 0;", | ||
727 | ] { | ||
728 | test("bar", "fn <|>", "fn bar() {\n $0\n}", next_sibling); | ||
729 | test("Foo", "type <|>", "type Foo = ", next_sibling); | ||
730 | test("CONST", "const <|>", "const CONST: u16 = ", next_sibling); | ||
731 | } | ||
732 | } | ||
488 | } | 733 | } |
diff --git a/crates/ide/src/completion/complete_unqualified_path.rs b/crates/ide/src/completion/complete_unqualified_path.rs index 1f1b682a7..8eda4b64d 100644 --- a/crates/ide/src/completion/complete_unqualified_path.rs +++ b/crates/ide/src/completion/complete_unqualified_path.rs | |||
@@ -13,6 +13,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
13 | if ctx.record_lit_syntax.is_some() | 13 | if ctx.record_lit_syntax.is_some() |
14 | || ctx.record_pat_syntax.is_some() | 14 | || ctx.record_pat_syntax.is_some() |
15 | || ctx.attribute_under_caret.is_some() | 15 | || ctx.attribute_under_caret.is_some() |
16 | || ctx.mod_declaration_under_caret.is_some() | ||
16 | { | 17 | { |
17 | return; | 18 | return; |
18 | } | 19 | } |
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 3ef1b97cf..671b13328 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs | |||
@@ -77,6 +77,7 @@ pub(crate) struct CompletionContext<'a> { | |||
77 | pub(super) is_path_type: bool, | 77 | pub(super) is_path_type: bool, |
78 | pub(super) has_type_args: bool, | 78 | pub(super) has_type_args: bool, |
79 | pub(super) attribute_under_caret: Option<ast::Attr>, | 79 | pub(super) attribute_under_caret: Option<ast::Attr>, |
80 | pub(super) mod_declaration_under_caret: Option<ast::Module>, | ||
80 | pub(super) unsafe_is_prev: bool, | 81 | pub(super) unsafe_is_prev: bool, |
81 | pub(super) if_is_prev: bool, | 82 | pub(super) if_is_prev: bool, |
82 | pub(super) block_expr_parent: bool, | 83 | pub(super) block_expr_parent: bool, |
@@ -159,6 +160,7 @@ impl<'a> CompletionContext<'a> { | |||
159 | has_type_args: false, | 160 | has_type_args: false, |
160 | dot_receiver_is_ambiguous_float_literal: false, | 161 | dot_receiver_is_ambiguous_float_literal: false, |
161 | attribute_under_caret: None, | 162 | attribute_under_caret: None, |
163 | mod_declaration_under_caret: None, | ||
162 | unsafe_is_prev: false, | 164 | unsafe_is_prev: false, |
163 | in_loop_body: false, | 165 | in_loop_body: false, |
164 | ref_pat_parent: false, | 166 | ref_pat_parent: false, |
@@ -246,7 +248,10 @@ impl<'a> CompletionContext<'a> { | |||
246 | self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); | 248 | self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); |
247 | self.is_match_arm = is_match_arm(syntax_element.clone()); | 249 | self.is_match_arm = is_match_arm(syntax_element.clone()); |
248 | self.has_item_list_or_source_file_parent = | 250 | self.has_item_list_or_source_file_parent = |
249 | has_item_list_or_source_file_parent(syntax_element); | 251 | has_item_list_or_source_file_parent(syntax_element.clone()); |
252 | self.mod_declaration_under_caret = | ||
253 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) | ||
254 | .filter(|module| module.item_list().is_none()); | ||
250 | } | 255 | } |
251 | 256 | ||
252 | fn fill( | 257 | fn fill( |
diff --git a/crates/ide/src/completion/patterns.rs b/crates/ide/src/completion/patterns.rs index c6ae589db..b17ddf133 100644 --- a/crates/ide/src/completion/patterns.rs +++ b/crates/ide/src/completion/patterns.rs | |||
@@ -115,6 +115,7 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { | |||
115 | .filter(|it| it.kind() == IF_KW) | 115 | .filter(|it| it.kind() == IF_KW) |
116 | .is_some() | 116 | .is_some() |
117 | } | 117 | } |
118 | |||
118 | #[test] | 119 | #[test] |
119 | fn test_if_is_prev() { | 120 | fn test_if_is_prev() { |
120 | check_pattern_is_applicable(r"if l<|>", if_is_prev); | 121 | check_pattern_is_applicable(r"if l<|>", if_is_prev); |