aboutsummaryrefslogtreecommitdiff
path: root/crates/completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion')
-rw-r--r--crates/completion/src/completions/unqualified_path.rs92
-rw-r--r--crates/completion/src/config.rs26
-rw-r--r--crates/completion/src/item.rs64
-rw-r--r--crates/completion/src/lib.rs47
-rw-r--r--crates/completion/src/render.rs38
-rw-r--r--crates/completion/src/render/enum_variant.rs6
-rw-r--r--crates/completion/src/render/function.rs6
-rw-r--r--crates/completion/src/render/macro_.rs6
-rw-r--r--crates/completion/src/test_utils.rs11
9 files changed, 203 insertions, 93 deletions
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 81691cd7f..4e4e2b36f 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -9,7 +9,7 @@ use test_utils::mark;
9 9
10use crate::{ 10use crate::{
11 render::{render_resolution_with_import, RenderContext}, 11 render::{render_resolution_with_import, RenderContext},
12 CompletionContext, Completions, 12 CompletionContext, Completions, ImportEdit,
13}; 13};
14 14
15pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 15pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
@@ -44,7 +44,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
44 acc.add_resolution(ctx, name.to_string(), &res) 44 acc.add_resolution(ctx, name.to_string(), &res)
45 }); 45 });
46 46
47 if ctx.config.enable_experimental_completions { 47 if ctx.config.enable_autoimport_completions && ctx.config.resolve_additional_edits_lazily() {
48 fuzzy_completion(acc, ctx).unwrap_or_default() 48 fuzzy_completion(acc, ctx).unwrap_or_default()
49 } 49 }
50} 50}
@@ -73,19 +73,64 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
73 } 73 }
74} 74}
75 75
76// Feature: Fuzzy Completion and Autoimports
77//
78// When completing names in the current scope, proposes additional imports from other modules or crates,
79// if they can be qualified in the scope and their name contains all symbols from the completion input
80// (case-insensitive, in any order or places).
81//
82// ```
83// fn main() {
84// pda<|>
85// }
86// # pub mod std { pub mod marker { pub struct PhantomData { } } }
87// ```
88// ->
89// ```
90// use std::marker::PhantomData;
91//
92// fn main() {
93// PhantomData
94// }
95// # pub mod std { pub mod marker { pub struct PhantomData { } } }
96// ```
97//
98// .Fuzzy search details
99//
100// To avoid an excessive amount of the results returned, completion input is checked for inclusion in the identifiers only
101// (i.e. in `HashMap` in the `std::collections::HashMap` path), also not in the module indentifiers.
102//
103// .Merge Behaviour
104//
105// It is possible to configure how use-trees are merged with the `importMergeBehaviour` setting.
106// Mimics the corresponding behaviour of the `Auto Import` feature.
107//
108// .LSP and performance implications
109//
110// The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
111// (case sensitive) resolve client capability in its client capabilities.
112// This way the server is able to defer the costly computations, doing them for a selected completion item only.
113// For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
114// which might be slow ergo the feature is automatically disabled.
115//
116// .Feature toggle
117//
118// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag.
119// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
120// capability enabled.
76fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 121fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
77 let _p = profile::span("fuzzy_completion"); 122 let _p = profile::span("fuzzy_completion");
123 let potential_import_name = ctx.token.to_string();
124
78 let current_module = ctx.scope.module()?; 125 let current_module = ctx.scope.module()?;
79 let anchor = ctx.name_ref_syntax.as_ref()?; 126 let anchor = ctx.name_ref_syntax.as_ref()?;
80 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; 127 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
81 128
82 let potential_import_name = ctx.token.to_string();
83
84 let possible_imports = imports_locator::find_similar_imports( 129 let possible_imports = imports_locator::find_similar_imports(
85 &ctx.sema, 130 &ctx.sema,
86 ctx.krate?, 131 ctx.krate?,
132 Some(100),
87 &potential_import_name, 133 &potential_import_name,
88 50,
89 true, 134 true,
90 ) 135 )
91 .filter_map(|import_candidate| { 136 .filter_map(|import_candidate| {
@@ -99,13 +144,14 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()
99 }) 144 })
100 }) 145 })
101 .filter(|(mod_path, _)| mod_path.len() > 1) 146 .filter(|(mod_path, _)| mod_path.len() > 1)
102 .take(20)
103 .filter_map(|(import_path, definition)| { 147 .filter_map(|(import_path, definition)| {
104 render_resolution_with_import( 148 render_resolution_with_import(
105 RenderContext::new(ctx), 149 RenderContext::new(ctx),
106 import_path.clone(), 150 ImportEdit {
107 import_scope.clone(), 151 import_path: import_path.clone(),
108 ctx.config.merge, 152 import_scope: import_scope.clone(),
153 merge_behaviour: ctx.config.merge,
154 },
109 &definition, 155 &definition,
110 ) 156 )
111 }); 157 });
@@ -120,8 +166,8 @@ mod tests {
120 use test_utils::mark; 166 use test_utils::mark;
121 167
122 use crate::{ 168 use crate::{
123 test_utils::{check_edit, completion_list}, 169 test_utils::{check_edit, check_edit_with_config, completion_list},
124 CompletionKind, 170 CompletionConfig, CompletionKind,
125 }; 171 };
126 172
127 fn check(ra_fixture: &str, expect: Expect) { 173 fn check(ra_fixture: &str, expect: Expect) {
@@ -730,7 +776,13 @@ impl My<|>
730 776
731 #[test] 777 #[test]
732 fn function_fuzzy_completion() { 778 fn function_fuzzy_completion() {
733 check_edit( 779 let mut completion_config = CompletionConfig::default();
780 completion_config
781 .active_resolve_capabilities
782 .insert(crate::CompletionResolveCapability::AdditionalTextEdits);
783
784 check_edit_with_config(
785 completion_config,
734 "stdin", 786 "stdin",
735 r#" 787 r#"
736//- /lib.rs crate:dep 788//- /lib.rs crate:dep
@@ -755,7 +807,13 @@ fn main() {
755 807
756 #[test] 808 #[test]
757 fn macro_fuzzy_completion() { 809 fn macro_fuzzy_completion() {
758 check_edit( 810 let mut completion_config = CompletionConfig::default();
811 completion_config
812 .active_resolve_capabilities
813 .insert(crate::CompletionResolveCapability::AdditionalTextEdits);
814
815 check_edit_with_config(
816 completion_config,
759 "macro_with_curlies!", 817 "macro_with_curlies!",
760 r#" 818 r#"
761//- /lib.rs crate:dep 819//- /lib.rs crate:dep
@@ -782,7 +840,13 @@ fn main() {
782 840
783 #[test] 841 #[test]
784 fn struct_fuzzy_completion() { 842 fn struct_fuzzy_completion() {
785 check_edit( 843 let mut completion_config = CompletionConfig::default();
844 completion_config
845 .active_resolve_capabilities
846 .insert(crate::CompletionResolveCapability::AdditionalTextEdits);
847
848 check_edit_with_config(
849 completion_config,
786 "ThirdStruct", 850 "ThirdStruct",
787 r#" 851 r#"
788//- /lib.rs crate:dep 852//- /lib.rs crate:dep
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index 654a76f7b..5175b9d69 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -5,21 +5,42 @@
5//! completions if we are allowed to. 5//! completions if we are allowed to.
6 6
7use ide_db::helpers::insert_use::MergeBehaviour; 7use ide_db::helpers::insert_use::MergeBehaviour;
8use rustc_hash::FxHashSet;
8 9
9#[derive(Clone, Debug, PartialEq, Eq)] 10#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct CompletionConfig { 11pub struct CompletionConfig {
11 pub enable_postfix_completions: bool, 12 pub enable_postfix_completions: bool,
12 pub enable_experimental_completions: bool, 13 pub enable_autoimport_completions: bool,
13 pub add_call_parenthesis: bool, 14 pub add_call_parenthesis: bool,
14 pub add_call_argument_snippets: bool, 15 pub add_call_argument_snippets: bool,
15 pub snippet_cap: Option<SnippetCap>, 16 pub snippet_cap: Option<SnippetCap>,
16 pub merge: Option<MergeBehaviour>, 17 pub merge: Option<MergeBehaviour>,
18 /// A set of capabilities, enabled on the client and supported on the server.
19 pub active_resolve_capabilities: FxHashSet<CompletionResolveCapability>,
20}
21
22/// A resolve capability, supported on the server.
23/// If the client registers any completion resolve capabilities,
24/// the server is able to render completion items' corresponding fields later,
25/// not during an initial completion item request.
26/// See https://github.com/rust-analyzer/rust-analyzer/issues/6366 for more details.
27#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
28pub enum CompletionResolveCapability {
29 Documentation,
30 Detail,
31 AdditionalTextEdits,
17} 32}
18 33
19impl CompletionConfig { 34impl CompletionConfig {
20 pub fn allow_snippets(&mut self, yes: bool) { 35 pub fn allow_snippets(&mut self, yes: bool) {
21 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None } 36 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
22 } 37 }
38
39 /// Whether the completions' additional edits are calculated when sending an initional completions list
40 /// or later, in a separate resolve request.
41 pub fn resolve_additional_edits_lazily(&self) -> bool {
42 self.active_resolve_capabilities.contains(&CompletionResolveCapability::AdditionalTextEdits)
43 }
23} 44}
24 45
25#[derive(Clone, Copy, Debug, PartialEq, Eq)] 46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -31,11 +52,12 @@ impl Default for CompletionConfig {
31 fn default() -> Self { 52 fn default() -> Self {
32 CompletionConfig { 53 CompletionConfig {
33 enable_postfix_completions: true, 54 enable_postfix_completions: true,
34 enable_experimental_completions: true, 55 enable_autoimport_completions: true,
35 add_call_parenthesis: true, 56 add_call_parenthesis: true,
36 add_call_argument_snippets: true, 57 add_call_argument_snippets: true,
37 snippet_cap: Some(SnippetCap { _private: () }), 58 snippet_cap: Some(SnippetCap { _private: () }),
38 merge: Some(MergeBehaviour::Full), 59 merge: Some(MergeBehaviour::Full),
60 active_resolve_capabilities: FxHashSet::default(),
39 } 61 }
40 } 62 }
41} 63}
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index e85549fef..bd94402d7 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -15,6 +15,7 @@ use crate::config::SnippetCap;
15/// `CompletionItem` describes a single completion variant in the editor pop-up. 15/// `CompletionItem` describes a single completion variant in the editor pop-up.
16/// It is basically a POD with various properties. To construct a 16/// It is basically a POD with various properties. To construct a
17/// `CompletionItem`, use `new` method and the `Builder` struct. 17/// `CompletionItem`, use `new` method and the `Builder` struct.
18#[derive(Clone)]
18pub struct CompletionItem { 19pub struct CompletionItem {
19 /// Used only internally in tests, to check only specific kind of 20 /// Used only internally in tests, to check only specific kind of
20 /// completion (postfix, keyword, reference, etc). 21 /// completion (postfix, keyword, reference, etc).
@@ -65,6 +66,9 @@ pub struct CompletionItem {
65 /// Indicates that a reference or mutable reference to this variable is a 66 /// Indicates that a reference or mutable reference to this variable is a
66 /// possible match. 67 /// possible match.
67 ref_match: Option<(Mutability, CompletionScore)>, 68 ref_match: Option<(Mutability, CompletionScore)>,
69
70 /// The import data to add to completion's edits.
71 import_to_add: Option<ImportEdit>,
68} 72}
69 73
70// We use custom debug for CompletionItem to make snapshot tests more readable. 74// We use custom debug for CompletionItem to make snapshot tests more readable.
@@ -256,14 +260,37 @@ impl CompletionItem {
256 pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> { 260 pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> {
257 self.ref_match 261 self.ref_match
258 } 262 }
263
264 pub fn import_to_add(&self) -> Option<&ImportEdit> {
265 self.import_to_add.as_ref()
266 }
259} 267}
260 268
261/// An extra import to add after the completion is applied. 269/// An extra import to add after the completion is applied.
262#[derive(Clone)] 270#[derive(Debug, Clone)]
263pub(crate) struct ImportToAdd { 271pub struct ImportEdit {
264 pub(crate) import_path: ModPath, 272 pub import_path: ModPath,
265 pub(crate) import_scope: ImportScope, 273 pub import_scope: ImportScope,
266 pub(crate) merge_behaviour: Option<MergeBehaviour>, 274 pub merge_behaviour: Option<MergeBehaviour>,
275}
276
277impl ImportEdit {
278 /// Attempts to insert the import to the given scope, producing a text edit.
279 /// May return no edit in edge cases, such as scope already containing the import.
280 pub fn to_text_edit(&self) -> Option<TextEdit> {
281 let _p = profile::span("ImportEdit::to_text_edit");
282
283 let rewriter = insert_use::insert_use(
284 &self.import_scope,
285 mod_path_to_ast(&self.import_path),
286 self.merge_behaviour,
287 );
288 let old_ast = rewriter.rewrite_root()?;
289 let mut import_insert = TextEdit::builder();
290 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
291
292 Some(import_insert.finish())
293 }
267} 294}
268 295
269/// A helper to make `CompletionItem`s. 296/// A helper to make `CompletionItem`s.
@@ -272,7 +299,7 @@ pub(crate) struct ImportToAdd {
272pub(crate) struct Builder { 299pub(crate) struct Builder {
273 source_range: TextRange, 300 source_range: TextRange,
274 completion_kind: CompletionKind, 301 completion_kind: CompletionKind,
275 import_to_add: Option<ImportToAdd>, 302 import_to_add: Option<ImportEdit>,
276 label: String, 303 label: String,
277 insert_text: Option<String>, 304 insert_text: Option<String>,
278 insert_text_format: InsertTextFormat, 305 insert_text_format: InsertTextFormat,
@@ -294,11 +321,9 @@ impl Builder {
294 let mut label = self.label; 321 let mut label = self.label;
295 let mut lookup = self.lookup; 322 let mut lookup = self.lookup;
296 let mut insert_text = self.insert_text; 323 let mut insert_text = self.insert_text;
297 let mut text_edits = TextEdit::builder();
298 324
299 if let Some(import_data) = self.import_to_add { 325 if let Some(import_to_add) = self.import_to_add.as_ref() {
300 let import = mod_path_to_ast(&import_data.import_path); 326 let mut import_path_without_last_segment = import_to_add.import_path.to_owned();
301 let mut import_path_without_last_segment = import_data.import_path;
302 let _ = import_path_without_last_segment.segments.pop(); 327 let _ = import_path_without_last_segment.segments.pop();
303 328
304 if !import_path_without_last_segment.segments.is_empty() { 329 if !import_path_without_last_segment.segments.is_empty() {
@@ -310,32 +335,20 @@ impl Builder {
310 } 335 }
311 label = format!("{}::{}", import_path_without_last_segment, label); 336 label = format!("{}::{}", import_path_without_last_segment, label);
312 } 337 }
313
314 let rewriter = insert_use::insert_use(
315 &import_data.import_scope,
316 import,
317 import_data.merge_behaviour,
318 );
319 if let Some(old_ast) = rewriter.rewrite_root() {
320 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits);
321 }
322 } 338 }
323 339
324 let original_edit = match self.text_edit { 340 let text_edit = match self.text_edit {
325 Some(it) => it, 341 Some(it) => it,
326 None => { 342 None => {
327 TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone())) 343 TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone()))
328 } 344 }
329 }; 345 };
330 346
331 let mut resulting_edit = text_edits.finish();
332 resulting_edit.union(original_edit).expect("Failed to unite text edits");
333
334 CompletionItem { 347 CompletionItem {
335 source_range: self.source_range, 348 source_range: self.source_range,
336 label, 349 label,
337 insert_text_format: self.insert_text_format, 350 insert_text_format: self.insert_text_format,
338 text_edit: resulting_edit, 351 text_edit,
339 detail: self.detail, 352 detail: self.detail,
340 documentation: self.documentation, 353 documentation: self.documentation,
341 lookup, 354 lookup,
@@ -345,6 +358,7 @@ impl Builder {
345 trigger_call_info: self.trigger_call_info.unwrap_or(false), 358 trigger_call_info: self.trigger_call_info.unwrap_or(false),
346 score: self.score, 359 score: self.score,
347 ref_match: self.ref_match, 360 ref_match: self.ref_match,
361 import_to_add: self.import_to_add,
348 } 362 }
349 } 363 }
350 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 364 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
@@ -407,7 +421,7 @@ impl Builder {
407 self.trigger_call_info = Some(true); 421 self.trigger_call_info = Some(true);
408 self 422 self
409 } 423 }
410 pub(crate) fn add_import(mut self, import_to_add: Option<ImportToAdd>) -> Builder { 424 pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder {
411 self.import_to_add = import_to_add; 425 self.import_to_add = import_to_add;
412 self 426 self
413 } 427 }
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index 1ec2e9be7..f60f87243 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -11,14 +11,17 @@ mod render;
11 11
12mod completions; 12mod completions;
13 13
14use ide_db::base_db::FilePosition; 14use ide_db::{
15use ide_db::RootDatabase; 15 base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase,
16};
17use syntax::AstNode;
18use text_edit::TextEdit;
16 19
17use crate::{completions::Completions, context::CompletionContext, item::CompletionKind}; 20use crate::{completions::Completions, context::CompletionContext, item::CompletionKind};
18 21
19pub use crate::{ 22pub use crate::{
20 config::CompletionConfig, 23 config::{CompletionConfig, CompletionResolveCapability},
21 item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat}, 24 item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat},
22}; 25};
23 26
24//FIXME: split the following feature into fine-grained features. 27//FIXME: split the following feature into fine-grained features.
@@ -70,12 +73,9 @@ pub use crate::{
70// } 73// }
71// ``` 74// ```
72// 75//
73// And experimental completions, enabled with the `rust-analyzer.completion.enableExperimental` setting. 76// And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities.
74// This flag enables or disables: 77// Those are the additional completion options with automatic `use` import and options from all project importable items,
75// 78// fuzzy matched agains the completion imput.
76// - Auto import: additional completion options with automatic `use` import and options from all project importable items, matched for the input
77//
78// Experimental completions might cause issues with performance and completion list look.
79 79
80/// Main entry point for completion. We run completion as a two-phase process. 80/// Main entry point for completion. We run completion as a two-phase process.
81/// 81///
@@ -131,6 +131,33 @@ pub fn completions(
131 Some(acc) 131 Some(acc)
132} 132}
133 133
134/// Resolves additional completion data at the position given.
135pub fn resolve_completion_edits(
136 db: &RootDatabase,
137 config: &CompletionConfig,
138 position: FilePosition,
139 full_import_path: &str,
140 imported_name: &str,
141) -> Option<Vec<TextEdit>> {
142 let ctx = CompletionContext::new(db, position, config)?;
143 let anchor = ctx.name_ref_syntax.as_ref()?;
144 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
145
146 let current_module = ctx.sema.scope(anchor.syntax()).module()?;
147 let current_crate = current_module.krate();
148
149 let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name)
150 .filter_map(|candidate| {
151 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
152 current_module.find_use_path(db, item)
153 })
154 .find(|mod_path| mod_path.to_string() == full_import_path)?;
155
156 ImportEdit { import_path, import_scope, merge_behaviour: config.merge }
157 .to_text_edit()
158 .map(|edit| vec![edit])
159}
160
134#[cfg(test)] 161#[cfg(test)]
135mod tests { 162mod tests {
136 use crate::config::CompletionConfig; 163 use crate::config::CompletionConfig;
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 504757a6a..b940388df 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -9,14 +9,13 @@ pub(crate) mod type_alias;
9 9
10mod builder_ext; 10mod builder_ext;
11 11
12use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; 12use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type};
13use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour};
14use ide_db::RootDatabase; 13use ide_db::RootDatabase;
15use syntax::TextRange; 14use syntax::TextRange;
16use test_utils::mark; 15use test_utils::mark;
17 16
18use crate::{ 17use crate::{
19 config::SnippetCap, item::ImportToAdd, CompletionContext, CompletionItem, CompletionItemKind, 18 config::SnippetCap, item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind,
20 CompletionKind, CompletionScore, 19 CompletionKind, CompletionScore,
21}; 20};
22 21
@@ -48,15 +47,12 @@ pub(crate) fn render_resolution<'a>(
48 47
49pub(crate) fn render_resolution_with_import<'a>( 48pub(crate) fn render_resolution_with_import<'a>(
50 ctx: RenderContext<'a>, 49 ctx: RenderContext<'a>,
51 import_path: ModPath, 50 import_edit: ImportEdit,
52 import_scope: ImportScope,
53 merge_behaviour: Option<MergeBehaviour>,
54 resolution: &ScopeDef, 51 resolution: &ScopeDef,
55) -> Option<CompletionItem> { 52) -> Option<CompletionItem> {
56 let local_name = import_path.segments.last()?.to_string();
57 Render::new(ctx).render_resolution( 53 Render::new(ctx).render_resolution(
58 local_name, 54 import_edit.import_path.segments.last()?.to_string(),
59 Some(ImportToAdd { import_path, import_scope, merge_behaviour }), 55 Some(import_edit),
60 resolution, 56 resolution,
61 ) 57 )
62} 58}
@@ -147,7 +143,7 @@ impl<'a> Render<'a> {
147 fn render_resolution( 143 fn render_resolution(
148 self, 144 self,
149 local_name: String, 145 local_name: String,
150 import_to_add: Option<ImportToAdd>, 146 import_to_add: Option<ImportEdit>,
151 resolution: &ScopeDef, 147 resolution: &ScopeDef,
152 ) -> Option<CompletionItem> { 148 ) -> Option<CompletionItem> {
153 let _p = profile::span("render_resolution"); 149 let _p = profile::span("render_resolution");
@@ -451,28 +447,6 @@ fn main() { let _: m::Spam = S<|> }
451 kind: Module, 447 kind: Module,
452 }, 448 },
453 CompletionItem { 449 CompletionItem {
454 label: "m::Spam",
455 source_range: 75..76,
456 text_edit: TextEdit {
457 indels: [
458 Indel {
459 insert: "use m::Spam;",
460 delete: 0..0,
461 },
462 Indel {
463 insert: "\n\n",
464 delete: 0..0,
465 },
466 Indel {
467 insert: "Spam",
468 delete: 75..76,
469 },
470 ],
471 },
472 kind: Enum,
473 lookup: "Spam",
474 },
475 CompletionItem {
476 label: "m::Spam::Foo", 450 label: "m::Spam::Foo",
477 source_range: 75..76, 451 source_range: 75..76,
478 delete: 75..76, 452 delete: 75..76,
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs
index f4bd02f25..8e0fea6c0 100644
--- a/crates/completion/src/render/enum_variant.rs
+++ b/crates/completion/src/render/enum_variant.rs
@@ -5,13 +5,13 @@ use itertools::Itertools;
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::{ 7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
9 render::{builder_ext::Params, RenderContext}, 9 render::{builder_ext::Params, RenderContext},
10}; 10};
11 11
12pub(crate) fn render_enum_variant<'a>( 12pub(crate) fn render_enum_variant<'a>(
13 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
14 import_to_add: Option<ImportToAdd>, 14 import_to_add: Option<ImportEdit>,
15 local_name: Option<String>, 15 local_name: Option<String>,
16 variant: hir::EnumVariant, 16 variant: hir::EnumVariant,
17 path: Option<ModPath>, 17 path: Option<ModPath>,
@@ -62,7 +62,7 @@ impl<'a> EnumVariantRender<'a> {
62 } 62 }
63 } 63 }
64 64
65 fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem { 65 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
66 let mut builder = CompletionItem::new( 66 let mut builder = CompletionItem::new(
67 CompletionKind::Reference, 67 CompletionKind::Reference,
68 self.ctx.source_range(), 68 self.ctx.source_range(),
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index 00e3eb203..d16005249 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -5,13 +5,13 @@ use syntax::{ast::Fn, display::function_declaration};
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::{ 7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
9 render::{builder_ext::Params, RenderContext}, 9 render::{builder_ext::Params, RenderContext},
10}; 10};
11 11
12pub(crate) fn render_fn<'a>( 12pub(crate) fn render_fn<'a>(
13 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
14 import_to_add: Option<ImportToAdd>, 14 import_to_add: Option<ImportEdit>,
15 local_name: Option<String>, 15 local_name: Option<String>,
16 fn_: hir::Function, 16 fn_: hir::Function,
17) -> CompletionItem { 17) -> CompletionItem {
@@ -39,7 +39,7 @@ impl<'a> FunctionRender<'a> {
39 FunctionRender { ctx, name, func: fn_, ast_node } 39 FunctionRender { ctx, name, func: fn_, ast_node }
40 } 40 }
41 41
42 fn render(self, import_to_add: Option<ImportToAdd>) -> CompletionItem { 42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
43 let params = self.params(); 43 let params = self.params();
44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
45 .kind(self.kind()) 45 .kind(self.kind())
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
index b4ab32c6e..eb3209bee 100644
--- a/crates/completion/src/render/macro_.rs
+++ b/crates/completion/src/render/macro_.rs
@@ -5,13 +5,13 @@ use syntax::display::macro_label;
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::{ 7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit},
9 render::RenderContext, 9 render::RenderContext,
10}; 10};
11 11
12pub(crate) fn render_macro<'a>( 12pub(crate) fn render_macro<'a>(
13 ctx: RenderContext<'a>, 13 ctx: RenderContext<'a>,
14 import_to_add: Option<ImportToAdd>, 14 import_to_add: Option<ImportEdit>,
15 name: String, 15 name: String,
16 macro_: hir::MacroDef, 16 macro_: hir::MacroDef,
17) -> Option<CompletionItem> { 17) -> Option<CompletionItem> {
@@ -38,7 +38,7 @@ impl<'a> MacroRender<'a> {
38 MacroRender { ctx, name, macro_, docs, bra, ket } 38 MacroRender { ctx, name, macro_, docs, bra, ket }
39 } 39 }
40 40
41 fn render(&self, import_to_add: Option<ImportToAdd>) -> Option<CompletionItem> { 41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
42 // FIXME: Currently proc-macro do not have ast-node, 42 // FIXME: Currently proc-macro do not have ast-node,
43 // such that it does not have source 43 // such that it does not have source
44 if self.macro_.is_proc_macro() { 44 if self.macro_.is_proc_macro() {
diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs
index 4c1b1a839..25f5f4924 100644
--- a/crates/completion/src/test_utils.rs
+++ b/crates/completion/src/test_utils.rs
@@ -96,7 +96,16 @@ pub(crate) fn check_edit_with_config(
96 .collect_tuple() 96 .collect_tuple()
97 .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions)); 97 .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions));
98 let mut actual = db.file_text(position.file_id).to_string(); 98 let mut actual = db.file_text(position.file_id).to_string();
99 completion.text_edit().apply(&mut actual); 99
100 let mut combined_edit = completion.text_edit().to_owned();
101 if let Some(import_text_edit) = completion.import_to_add().and_then(|edit| edit.to_text_edit())
102 {
103 combined_edit.union(import_text_edit).expect(
104 "Failed to apply completion resolve changes: change ranges overlap, but should not",
105 )
106 }
107
108 combined_edit.apply(&mut actual);
100 assert_eq_text!(&ra_fixture_after, &actual) 109 assert_eq_text!(&ra_fixture_after, &actual)
101} 110}
102 111