1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
//! TODO kb move this into the complete_unqualified_path when starts to work properly
use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour};
use hir::Query;
use itertools::Itertools;
use syntax::{algo, AstNode};
use text_edit::TextEdit;
use crate::{context::CompletionContext, item::CompletionKind, CompletionItem, CompletionItemKind};
use super::Completions;
// TODO kb when typing, completes partial results, need to rerun manually to see the proper ones
pub(crate) fn complete_magic(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
return None;
}
let current_module = ctx.scope.module()?;
let anchor = ctx.name_ref_syntax.as_ref()?;
let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
// TODO kb consider heuristics, such as "don't show `hash_map` import if `HashMap` is the import for completion"
// also apply completion ordering
let potential_import_name = ctx.token.to_string();
let possible_imports = ctx
.krate?
// TODO kb use imports_locator instead?
.query_external_importables(ctx.db, Query::new(&potential_import_name).limit(40))
.unique()
.filter_map(|import_candidate| match import_candidate {
either::Either::Left(module_def) => current_module.find_use_path(ctx.db, module_def),
either::Either::Right(macro_def) => current_module.find_use_path(ctx.db, macro_def),
})
.filter_map(|mod_path| {
let mut builder = TextEdit::builder();
let correct_qualifier = mod_path.segments.last()?.to_string();
builder.replace(anchor.syntax().text_range(), correct_qualifier);
// TODO kb: assists already have the merge behaviour setting, need to unite both
// also consider a settings toggle for this particular feature?
let rewriter =
insert_use(&import_scope, mod_path_to_ast(&mod_path), Some(MergeBehaviour::Full));
let old_ast = rewriter.rewrite_root()?;
algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut builder);
let completion_item: CompletionItem = CompletionItem::new(
CompletionKind::Magic,
ctx.source_range(),
mod_path.to_string(),
)
.kind(CompletionItemKind::Struct)
.text_edit(builder.finish())
.into();
Some(completion_item)
});
acc.add_all(possible_imports);
Some(())
}
#[cfg(test)]
mod tests {
use crate::test_utils::check_edit;
#[test]
fn function_magic_completion() {
check_edit(
"dep::io::stdin",
r#"
//- /lib.rs crate:dep
pub mod io {
pub fn stdin() {}
};
//- /main.rs crate:main deps:dep
fn main() {
stdi<|>
}
"#,
r#"
use dep::io::stdin;
fn main() {
stdin
}
"#,
);
}
#[test]
fn case_insensitive_magic_completion_works() {
check_edit(
"dep::some_module::ThirdStruct",
r#"
//- /lib.rs crate:dep
pub struct FirstStruct;
pub mod some_module {
pub struct SecondStruct;
pub struct ThirdStruct;
}
//- /main.rs crate:main deps:dep
use dep::{FirstStruct, some_module::SecondStruct};
fn main() {
this<|>
}
"#,
r#"
use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
fn main() {
ThirdStruct
}
"#,
);
}
}
|