diff options
author | Kirill Bulatov <[email protected]> | 2020-09-18 21:40:11 +0100 |
---|---|---|
committer | Kirill Bulatov <[email protected]> | 2020-11-16 19:19:05 +0000 |
commit | f62e8616c879255e70052ae35ce7f98bffedac11 (patch) | |
tree | f1a21bebdd22483a1a503861bbe55b77fa1d2b37 /crates/completion/src/completions | |
parent | 0a658c4a973d934d622957a6fb03916271496385 (diff) |
Add imports in auto completion
Diffstat (limited to 'crates/completion/src/completions')
-rw-r--r-- | crates/completion/src/completions/complete_magic.rs | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/crates/completion/src/completions/complete_magic.rs b/crates/completion/src/completions/complete_magic.rs new file mode 100644 index 000000000..857a0b620 --- /dev/null +++ b/crates/completion/src/completions/complete_magic.rs | |||
@@ -0,0 +1,114 @@ | |||
1 | //! TODO kb move this into the complete_unqualified_path when starts to work properly | ||
2 | |||
3 | use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour}; | ||
4 | use hir::Query; | ||
5 | use itertools::Itertools; | ||
6 | use syntax::AstNode; | ||
7 | use text_edit::TextEdit; | ||
8 | |||
9 | use crate::{context::CompletionContext, item::CompletionKind, CompletionItem, CompletionItemKind}; | ||
10 | |||
11 | use super::Completions; | ||
12 | |||
13 | pub(crate) fn complete_magic(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | ||
14 | if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { | ||
15 | return None; | ||
16 | } | ||
17 | let current_module = ctx.scope.module()?; | ||
18 | let anchor = ctx.name_ref_syntax.as_ref()?; | ||
19 | let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; | ||
20 | // TODO kb now this is the whole file, which is not disjoint with any other change in the same file, fix it | ||
21 | // otherwise it's impossible to correctly add the use statement and also change the completed text into something more meaningful | ||
22 | let import_syntax = import_scope.as_syntax_node(); | ||
23 | |||
24 | // TODO kb consider heuristics, such as "don't show `hash_map` import if `HashMap` is the import for completion" | ||
25 | // TODO kb module functions are not completed, consider `std::io::stdin` one | ||
26 | let potential_import_name = ctx.token.to_string(); | ||
27 | |||
28 | let possible_imports = ctx | ||
29 | .krate? | ||
30 | // TODO kb use imports_locator instead? | ||
31 | .query_external_importables(ctx.db, Query::new(&potential_import_name).limit(40)) | ||
32 | .unique() | ||
33 | .filter_map(|import_candidate| match import_candidate { | ||
34 | either::Either::Left(module_def) => current_module.find_use_path(ctx.db, module_def), | ||
35 | either::Either::Right(macro_def) => current_module.find_use_path(ctx.db, macro_def), | ||
36 | }) | ||
37 | .filter_map(|mod_path| { | ||
38 | let correct_qualifier = mod_path.segments.last()?.to_string(); | ||
39 | let rewriter = | ||
40 | insert_use(&import_scope, mod_path_to_ast(&mod_path), Some(MergeBehaviour::Full)); | ||
41 | let rewritten_node = rewriter.rewrite(import_syntax); | ||
42 | let insert_use_edit = | ||
43 | TextEdit::replace(import_syntax.text_range(), rewritten_node.to_string()); | ||
44 | let mut completion_edit = | ||
45 | TextEdit::replace(anchor.syntax().text_range(), correct_qualifier); | ||
46 | completion_edit.union(insert_use_edit).expect("TODO kb"); | ||
47 | |||
48 | let completion_item: CompletionItem = CompletionItem::new( | ||
49 | CompletionKind::Magic, | ||
50 | ctx.source_range(), | ||
51 | mod_path.to_string(), | ||
52 | ) | ||
53 | .kind(CompletionItemKind::Struct) | ||
54 | .text_edit(completion_edit) | ||
55 | .into(); | ||
56 | Some(completion_item) | ||
57 | }); | ||
58 | acc.add_all(possible_imports); | ||
59 | |||
60 | Some(()) | ||
61 | } | ||
62 | |||
63 | #[cfg(test)] | ||
64 | mod tests { | ||
65 | use expect_test::{expect, Expect}; | ||
66 | |||
67 | use crate::{ | ||
68 | item::CompletionKind, | ||
69 | test_utils::{check_edit, completion_list}, | ||
70 | }; | ||
71 | |||
72 | fn check(ra_fixture: &str, expect: Expect) { | ||
73 | let actual = completion_list(ra_fixture, CompletionKind::Magic); | ||
74 | expect.assert_eq(&actual) | ||
75 | } | ||
76 | |||
77 | #[test] | ||
78 | fn case_insensitive_magic_completion_works() { | ||
79 | check( | ||
80 | r#" | ||
81 | //- /lib.rs crate:dep | ||
82 | pub struct TestStruct; | ||
83 | |||
84 | //- /main.rs crate:main deps:dep | ||
85 | fn main() { | ||
86 | teru<|> | ||
87 | } | ||
88 | "#, | ||
89 | expect![[r#" | ||
90 | st dep::TestStruct | ||
91 | "#]], | ||
92 | ); | ||
93 | |||
94 | check_edit( | ||
95 | "dep::TestStruct", | ||
96 | r#" | ||
97 | //- /lib.rs crate:dep | ||
98 | pub struct TestStruct; | ||
99 | |||
100 | //- /main.rs crate:main deps:dep | ||
101 | fn main() { | ||
102 | teru<|> | ||
103 | } | ||
104 | "#, | ||
105 | r#" | ||
106 | use dep::TestStruct; | ||
107 | |||
108 | fn main() { | ||
109 | TestStruct | ||
110 | } | ||
111 | "#, | ||
112 | ); | ||
113 | } | ||
114 | } | ||