aboutsummaryrefslogtreecommitdiff
path: root/crates/completion
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-12-08 13:10:28 +0000
committerGitHub <[email protected]>2020-12-08 13:10:28 +0000
commit4d4f11925f793c45560c45c088d4b3139c2c171c (patch)
treef8c5e3c14a0bb55d4b435b8389bccf305975d39a /crates/completion
parent021e97ea03cf67ad7785ab39580e04bc69506b8c (diff)
parentbf24cb3e8db94a84fb4a24c407797ab6ff5ee109 (diff)
Merge #6706
6706: Move import text edit calculation into a completion resolve request r=matklad a=SomeoneToIgnore Part of https://github.com/rust-analyzer/rust-analyzer/issues/6612 (presumably fixing it) Part of https://github.com/rust-analyzer/rust-analyzer/issues/6366 (does not cover all possible resolve capabilities we can do) Closes https://github.com/rust-analyzer/rust-analyzer/issues/6594 Further improves imports on completion performance by deferring the computations for import inserts. To use the new mode, you have to have the experimental completions enabled and use the LSP 3.16-compliant client that reports `additionalTextEdits` in its `CompletionItemCapabilityResolveSupport` field in the client capabilities. rust-analyzer VSCode extension does this already hence picks up the changes completely. Performance implications are descrbed in: https://github.com/rust-analyzer/rust-analyzer/issues/6633#issuecomment-737295182 Co-authored-by: Kirill Bulatov <[email protected]>
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