aboutsummaryrefslogtreecommitdiff
path: root/crates/completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion')
-rw-r--r--crates/completion/src/completions/unqualified_path.rs134
-rw-r--r--crates/completion/src/config.rs4
-rw-r--r--crates/completion/src/item.rs35
-rw-r--r--crates/completion/src/lib.rs2
-rw-r--r--crates/completion/src/render.rs29
-rw-r--r--crates/completion/src/render/enum_variant.rs2
-rw-r--r--crates/completion/src/render/function.rs2
-rw-r--r--crates/completion/src/render/macro_.rs5
-rw-r--r--crates/completion/src/test_utils.rs11
9 files changed, 79 insertions, 145 deletions
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 1482df8fb..2a315cb86 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -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.disable_fuzzy_autoimports && 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}
@@ -99,6 +99,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
99// 99//
100// To avoid an excessive amount of the results returned, completion input is checked for inclusion in the identifiers only 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. 101// (i.e. in `HashMap` in the `std::collections::HashMap` path), also not in the module indentifiers.
102// It also avoids searching for any imports for inputs with their length less that 3 symbols.
102// 103//
103// .Merge Behaviour 104// .Merge Behaviour
104// 105//
@@ -107,53 +108,53 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
107// 108//
108// .LSP and performance implications 109// .LSP and performance implications
109// 110//
110// LSP 3.16 provides the way to defer the computation of some completion data, including the import edits for this feature. 111// The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
111// If the LSP client supports the `additionalTextEdits` (case sensitive) resolve client capability, rust-analyzer computes 112// (case sensitive) resolve client capability in its client capabilities.
112// the completion edits only when a corresponding completion item is selected. 113// 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// 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. 115// which might be slow ergo the feature is automatically disabled.
115// 116//
116// .Feature toggle 117// .Feature toggle
117// 118//
118// The feature can be turned off in the settings with the `rust-analyzer.completion.enableExperimental` flag. 119// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.disableFuzzyAutoimports` flag.
119fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 120fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
120 let _p = profile::span("fuzzy_completion"); 121 let _p = profile::span("fuzzy_completion");
122 let potential_import_name = ctx.token.to_string();
123
124 if potential_import_name.len() < 3 {
125 return None;
126 }
127
121 let current_module = ctx.scope.module()?; 128 let current_module = ctx.scope.module()?;
122 let anchor = ctx.name_ref_syntax.as_ref()?; 129 let anchor = ctx.name_ref_syntax.as_ref()?;
123 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; 130 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
124 131
125 let potential_import_name = ctx.token.to_string(); 132 let possible_imports =
126 133 imports_locator::find_similar_imports(&ctx.sema, ctx.krate?, &potential_import_name, true)
127 let possible_imports = imports_locator::find_similar_imports( 134 .filter_map(|import_candidate| {
128 &ctx.sema, 135 Some(match import_candidate {
129 ctx.krate?, 136 Either::Left(module_def) => (
130 &potential_import_name, 137 current_module.find_use_path(ctx.db, module_def)?,
131 50, 138 ScopeDef::ModuleDef(module_def),
132 true, 139 ),
133 ) 140 Either::Right(macro_def) => (
134 .filter_map(|import_candidate| { 141 current_module.find_use_path(ctx.db, macro_def)?,
135 Some(match import_candidate { 142 ScopeDef::MacroDef(macro_def),
136 Either::Left(module_def) => { 143 ),
137 (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def)) 144 })
138 } 145 })
139 Either::Right(macro_def) => { 146 .filter(|(mod_path, _)| mod_path.len() > 1)
140 (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def)) 147 .filter_map(|(import_path, definition)| {
141 } 148 render_resolution_with_import(
142 }) 149 RenderContext::new(ctx),
143 }) 150 ImportEdit {
144 .filter(|(mod_path, _)| mod_path.len() > 1) 151 import_path: import_path.clone(),
145 .take(20) 152 import_scope: import_scope.clone(),
146 .filter_map(|(import_path, definition)| { 153 merge_behaviour: ctx.config.merge,
147 render_resolution_with_import( 154 },
148 RenderContext::new(ctx), 155 &definition,
149 ImportEdit { 156 )
150 import_path: import_path.clone(), 157 });
151 import_scope: import_scope.clone(),
152 merge_behaviour: ctx.config.merge,
153 },
154 &definition,
155 )
156 });
157 158
158 acc.add_all(possible_imports); 159 acc.add_all(possible_imports);
159 Some(()) 160 Some(())
@@ -775,7 +776,13 @@ impl My<|>
775 776
776 #[test] 777 #[test]
777 fn function_fuzzy_completion() { 778 fn function_fuzzy_completion() {
778 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,
779 "stdin", 786 "stdin",
780 r#" 787 r#"
781//- /lib.rs crate:dep 788//- /lib.rs crate:dep
@@ -800,7 +807,13 @@ fn main() {
800 807
801 #[test] 808 #[test]
802 fn macro_fuzzy_completion() { 809 fn macro_fuzzy_completion() {
803 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,
804 "macro_with_curlies!", 817 "macro_with_curlies!",
805 r#" 818 r#"
806//- /lib.rs crate:dep 819//- /lib.rs crate:dep
@@ -827,37 +840,6 @@ fn main() {
827 840
828 #[test] 841 #[test]
829 fn struct_fuzzy_completion() { 842 fn struct_fuzzy_completion() {
830 check_edit(
831 "ThirdStruct",
832 r#"
833//- /lib.rs crate:dep
834pub struct FirstStruct;
835pub mod some_module {
836 pub struct SecondStruct;
837 pub struct ThirdStruct;
838}
839
840//- /main.rs crate:main deps:dep
841use dep::{FirstStruct, some_module::SecondStruct};
842
843fn main() {
844 this<|>
845}
846"#,
847 r#"
848use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
849
850fn main() {
851 ThirdStruct
852}
853"#,
854 );
855 }
856
857 /// LSP protocol supports separate completion resolve requests to do the heavy computations there.
858 /// This test checks that for a certain resolve capatilities no such operations (autoimport) are done.
859 #[test]
860 fn no_fuzzy_completions_applied_for_certain_resolve_capability() {
861 let mut completion_config = CompletionConfig::default(); 843 let mut completion_config = CompletionConfig::default();
862 completion_config 844 completion_config
863 .active_resolve_capabilities 845 .active_resolve_capabilities
@@ -870,22 +852,22 @@ fn main() {
870//- /lib.rs crate:dep 852//- /lib.rs crate:dep
871pub struct FirstStruct; 853pub struct FirstStruct;
872pub mod some_module { 854pub mod some_module {
873pub struct SecondStruct; 855 pub struct SecondStruct;
874pub struct ThirdStruct; 856 pub struct ThirdStruct;
875} 857}
876 858
877//- /main.rs crate:main deps:dep 859//- /main.rs crate:main deps:dep
878use dep::{FirstStruct, some_module::SecondStruct}; 860use dep::{FirstStruct, some_module::SecondStruct};
879 861
880fn main() { 862fn main() {
881this<|> 863 this<|>
882} 864}
883"#, 865"#,
884 r#" 866 r#"
885use dep::{FirstStruct, some_module::SecondStruct}; 867use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
886 868
887fn main() { 869fn main() {
888ThirdStruct 870 ThirdStruct
889} 871}
890"#, 872"#,
891 ); 873 );
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index 487c1d0f1..8082ec9cb 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -10,7 +10,7 @@ use rustc_hash::FxHashSet;
10#[derive(Clone, Debug, PartialEq, Eq)] 10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct CompletionConfig { 11pub struct CompletionConfig {
12 pub enable_postfix_completions: bool, 12 pub enable_postfix_completions: bool,
13 pub enable_experimental_completions: bool, 13 pub disable_fuzzy_autoimports: bool,
14 pub add_call_parenthesis: bool, 14 pub add_call_parenthesis: bool,
15 pub add_call_argument_snippets: bool, 15 pub add_call_argument_snippets: bool,
16 pub snippet_cap: Option<SnippetCap>, 16 pub snippet_cap: Option<SnippetCap>,
@@ -52,7 +52,7 @@ impl Default for CompletionConfig {
52 fn default() -> Self { 52 fn default() -> Self {
53 CompletionConfig { 53 CompletionConfig {
54 enable_postfix_completions: true, 54 enable_postfix_completions: true,
55 enable_experimental_completions: true, 55 disable_fuzzy_autoimports: false,
56 add_call_parenthesis: true, 56 add_call_parenthesis: true,
57 add_call_argument_snippets: true, 57 add_call_argument_snippets: true,
58 snippet_cap: Some(SnippetCap { _private: () }), 58 snippet_cap: Some(SnippetCap { _private: () }),
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index 978ea76f9..bd94402d7 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -209,7 +209,6 @@ impl CompletionItem {
209 score: None, 209 score: None,
210 ref_match: None, 210 ref_match: None,
211 import_to_add: None, 211 import_to_add: None,
212 resolve_import_lazily: false,
213 } 212 }
214 } 213 }
215 214
@@ -301,7 +300,6 @@ pub(crate) struct Builder {
301 source_range: TextRange, 300 source_range: TextRange,
302 completion_kind: CompletionKind, 301 completion_kind: CompletionKind,
303 import_to_add: Option<ImportEdit>, 302 import_to_add: Option<ImportEdit>,
304 resolve_import_lazily: bool,
305 label: String, 303 label: String,
306 insert_text: Option<String>, 304 insert_text: Option<String>,
307 insert_text_format: InsertTextFormat, 305 insert_text_format: InsertTextFormat,
@@ -339,25 +337,13 @@ impl Builder {
339 } 337 }
340 } 338 }
341 339
342 let mut text_edit = match self.text_edit { 340 let text_edit = match self.text_edit {
343 Some(it) => it, 341 Some(it) => it,
344 None => { 342 None => {
345 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()))
346 } 344 }
347 }; 345 };
348 346
349 let import_to_add = if self.resolve_import_lazily {
350 self.import_to_add
351 } else {
352 match apply_import_eagerly(self.import_to_add.as_ref(), &mut text_edit) {
353 Ok(()) => self.import_to_add,
354 Err(()) => {
355 log::error!("Failed to apply eager import edit: original edit and import edit intersect");
356 None
357 }
358 }
359 };
360
361 CompletionItem { 347 CompletionItem {
362 source_range: self.source_range, 348 source_range: self.source_range,
363 label, 349 label,
@@ -372,7 +358,7 @@ impl Builder {
372 trigger_call_info: self.trigger_call_info.unwrap_or(false), 358 trigger_call_info: self.trigger_call_info.unwrap_or(false),
373 score: self.score, 359 score: self.score,
374 ref_match: self.ref_match, 360 ref_match: self.ref_match,
375 import_to_add, 361 import_to_add: self.import_to_add,
376 } 362 }
377 } 363 }
378 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 {
@@ -435,13 +421,8 @@ impl Builder {
435 self.trigger_call_info = Some(true); 421 self.trigger_call_info = Some(true);
436 self 422 self
437 } 423 }
438 pub(crate) fn add_import( 424 pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder {
439 mut self,
440 import_to_add: Option<ImportEdit>,
441 resolve_import_lazily: bool,
442 ) -> Builder {
443 self.import_to_add = import_to_add; 425 self.import_to_add = import_to_add;
444 self.resolve_import_lazily = resolve_import_lazily;
445 self 426 self
446 } 427 }
447 pub(crate) fn set_ref_match( 428 pub(crate) fn set_ref_match(
@@ -453,16 +434,6 @@ impl Builder {
453 } 434 }
454} 435}
455 436
456fn apply_import_eagerly(
457 import_to_add: Option<&ImportEdit>,
458 original_edit: &mut TextEdit,
459) -> Result<(), ()> {
460 match import_to_add.and_then(|import_edit| import_edit.to_text_edit()) {
461 Some(import_edit) => original_edit.union(import_edit).map_err(|_| ()),
462 None => Ok(()),
463 }
464}
465
466impl<'a> Into<CompletionItem> for Builder { 437impl<'a> Into<CompletionItem> for Builder {
467 fn into(self) -> CompletionItem { 438 fn into(self) -> CompletionItem {
468 self.build() 439 self.build()
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index 066d589af..8df9f00fe 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -73,7 +73,7 @@ pub use crate::{
73// } 73// }
74// ``` 74// ```
75// 75//
76// And experimental completions, enabled with the `rust-analyzer.completion.enableExperimental` setting. 76// And experimental completions, enabled with the `rust-analyzer.completion.disableFuzzyAutoimports` setting.
77// This flag enables or disables: 77// This flag enables or disables:
78// 78//
79// - Auto import: additional completion options with automatic `use` import and options from all project importable items, matched for the input 79// - Auto import: additional completion options with automatic `use` import and options from all project importable items, matched for the input
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 9a43480e1..b940388df 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -190,10 +190,7 @@ impl<'a> Render<'a> {
190 local_name, 190 local_name,
191 ) 191 )
192 .kind(CompletionItemKind::UnresolvedReference) 192 .kind(CompletionItemKind::UnresolvedReference)
193 .add_import( 193 .add_import(import_to_add)
194 import_to_add,
195 self.ctx.completion.config.resolve_additional_edits_lazily(),
196 )
197 .build(); 194 .build();
198 return Some(item); 195 return Some(item);
199 } 196 }
@@ -248,7 +245,7 @@ impl<'a> Render<'a> {
248 245
249 let item = item 246 let item = item
250 .kind(kind) 247 .kind(kind)
251 .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily()) 248 .add_import(import_to_add)
252 .set_documentation(docs) 249 .set_documentation(docs)
253 .set_ref_match(ref_match) 250 .set_ref_match(ref_match)
254 .build(); 251 .build();
@@ -450,28 +447,6 @@ fn main() { let _: m::Spam = S<|> }
450 kind: Module, 447 kind: Module,
451 }, 448 },
452 CompletionItem { 449 CompletionItem {
453 label: "m::Spam",
454 source_range: 75..76,
455 text_edit: TextEdit {
456 indels: [
457 Indel {
458 insert: "use m::Spam;",
459 delete: 0..0,
460 },
461 Indel {
462 insert: "\n\n",
463 delete: 0..0,
464 },
465 Indel {
466 insert: "Spam",
467 delete: 75..76,
468 },
469 ],
470 },
471 kind: Enum,
472 lookup: "Spam",
473 },
474 CompletionItem {
475 label: "m::Spam::Foo", 450 label: "m::Spam::Foo",
476 source_range: 75..76, 451 source_range: 75..76,
477 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 e979a090b..8e0fea6c0 100644
--- a/crates/completion/src/render/enum_variant.rs
+++ b/crates/completion/src/render/enum_variant.rs
@@ -71,7 +71,7 @@ impl<'a> EnumVariantRender<'a> {
71 .kind(CompletionItemKind::EnumVariant) 71 .kind(CompletionItemKind::EnumVariant)
72 .set_documentation(self.variant.docs(self.ctx.db())) 72 .set_documentation(self.variant.docs(self.ctx.db()))
73 .set_deprecated(self.ctx.is_deprecated(self.variant)) 73 .set_deprecated(self.ctx.is_deprecated(self.variant))
74 .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily()) 74 .add_import(import_to_add)
75 .detail(self.detail()); 75 .detail(self.detail());
76 76
77 if self.variant_kind == StructKind::Tuple { 77 if self.variant_kind == StructKind::Tuple {
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index dd2c999ef..d16005249 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -47,7 +47,7 @@ impl<'a> FunctionRender<'a> {
47 .set_deprecated(self.ctx.is_deprecated(self.func)) 47 .set_deprecated(self.ctx.is_deprecated(self.func))
48 .detail(self.detail()) 48 .detail(self.detail())
49 .add_call_parens(self.ctx.completion, self.name, params) 49 .add_call_parens(self.ctx.completion, self.name, params)
50 .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily()) 50 .add_import(import_to_add)
51 .build() 51 .build()
52 } 52 }
53 53
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
index bdbc642ca..eb3209bee 100644
--- a/crates/completion/src/render/macro_.rs
+++ b/crates/completion/src/render/macro_.rs
@@ -50,10 +50,7 @@ impl<'a> MacroRender<'a> {
50 .kind(CompletionItemKind::Macro) 50 .kind(CompletionItemKind::Macro)
51 .set_documentation(self.docs.clone()) 51 .set_documentation(self.docs.clone())
52 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 52 .set_deprecated(self.ctx.is_deprecated(self.macro_))
53 .add_import( 53 .add_import(import_to_add)
54 import_to_add,
55 self.ctx.completion.config.resolve_additional_edits_lazily(),
56 )
57 .detail(self.detail()); 54 .detail(self.detail());
58 55
59 let needs_bang = self.needs_bang(); 56 let needs_bang = self.needs_bang();
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