aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/completions
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/completions')
-rw-r--r--crates/completion/src/completions/complete_magic.rs114
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
3use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour};
4use hir::Query;
5use itertools::Itertools;
6use syntax::AstNode;
7use text_edit::TextEdit;
8
9use crate::{context::CompletionContext, item::CompletionKind, CompletionItem, CompletionItemKind};
10
11use super::Completions;
12
13pub(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)]
64mod 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
82pub struct TestStruct;
83
84//- /main.rs crate:main deps:dep
85fn 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
98pub struct TestStruct;
99
100//- /main.rs crate:main deps:dep
101fn main() {
102 teru<|>
103}
104"#,
105 r#"
106use dep::TestStruct;
107
108fn main() {
109 TestStruct
110}
111"#,
112 );
113 }
114}