From 48acd7d455be43960d67632adc9eb176a10a8afe Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 28 Nov 2020 16:26:30 +0200 Subject: Draft the new lsp handler --- crates/completion/src/item.rs | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index e85549fef..ce6a44e57 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -3,11 +3,8 @@ use std::fmt; use hir::{Documentation, ModPath, Mutability}; -use ide_db::helpers::{ - insert_use::{self, ImportScope, MergeBehaviour}, - mod_path_to_ast, -}; -use syntax::{algo, TextRange}; +use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour}; +use syntax::TextRange; use text_edit::TextEdit; use crate::config::SnippetCap; @@ -65,6 +62,10 @@ pub struct CompletionItem { /// Indicates that a reference or mutable reference to this variable is a /// possible match. ref_match: Option<(Mutability, CompletionScore)>, + + /// The data later to be used in the `completionItem/resolve` response + /// to add the insert import edit. + import_to_add: Option, } // We use custom debug for CompletionItem to make snapshot tests more readable. @@ -294,11 +295,9 @@ impl Builder { let mut label = self.label; let mut lookup = self.lookup; let mut insert_text = self.insert_text; - let mut text_edits = TextEdit::builder(); - if let Some(import_data) = self.import_to_add { - let import = mod_path_to_ast(&import_data.import_path); - let mut import_path_without_last_segment = import_data.import_path; + if let Some(import_to_add) = self.import_to_add.as_ref() { + let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); let _ = import_path_without_last_segment.segments.pop(); if !import_path_without_last_segment.segments.is_empty() { @@ -310,32 +309,20 @@ impl Builder { } label = format!("{}::{}", import_path_without_last_segment, label); } - - let rewriter = insert_use::insert_use( - &import_data.import_scope, - import, - import_data.merge_behaviour, - ); - if let Some(old_ast) = rewriter.rewrite_root() { - algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); - } } - let original_edit = match self.text_edit { + let text_edit = match self.text_edit { Some(it) => it, None => { TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone())) } }; - let mut resulting_edit = text_edits.finish(); - resulting_edit.union(original_edit).expect("Failed to unite text edits"); - CompletionItem { source_range: self.source_range, label, insert_text_format: self.insert_text_format, - text_edit: resulting_edit, + text_edit, detail: self.detail, documentation: self.documentation, lookup, @@ -345,6 +332,7 @@ impl Builder { trigger_call_info: self.trigger_call_info.unwrap_or(false), score: self.score, ref_match: self.ref_match, + import_to_add: self.import_to_add, } } pub(crate) fn lookup_by(mut self, lookup: impl Into) -> Builder { -- cgit v1.2.3 From 6d2d27938985e210d5b5e561df8a48be20343be7 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 30 Nov 2020 22:28:19 +0200 Subject: Working resolve completion imports prototype --- crates/completion/src/item.rs | 14 +++++++++----- crates/completion/src/lib.rs | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index ce6a44e57..0e59f73cb 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -257,14 +257,18 @@ impl CompletionItem { pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> { self.ref_match } + + pub fn import_to_add(&self) -> Option<&ImportToAdd> { + self.import_to_add.as_ref() + } } /// An extra import to add after the completion is applied. -#[derive(Clone)] -pub(crate) struct ImportToAdd { - pub(crate) import_path: ModPath, - pub(crate) import_scope: ImportScope, - pub(crate) merge_behaviour: Option, +#[derive(Debug, Clone)] +pub struct ImportToAdd { + pub import_path: ModPath, + pub import_scope: ImportScope, + pub merge_behaviour: Option, } /// A helper to make `CompletionItem`s. diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index 1ec2e9be7..28209d4e0 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs @@ -18,7 +18,7 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi pub use crate::{ config::CompletionConfig, - item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat}, + item::{CompletionItem, CompletionItemKind, CompletionScore, ImportToAdd, InsertTextFormat}, }; //FIXME: split the following feature into fine-grained features. -- cgit v1.2.3 From 47464e556c160ce705c2e3c84f501ad4e8dbb123 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 1 Dec 2020 22:46:06 +0200 Subject: Properly fill client completion resolve capabilities data --- crates/completion/src/config.rs | 10 ++++++++++ crates/completion/src/lib.rs | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index 654a76f7b..736af455e 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs @@ -5,6 +5,7 @@ //! completions if we are allowed to. use ide_db::helpers::insert_use::MergeBehaviour; +use rustc_hash::FxHashSet; #[derive(Clone, Debug, PartialEq, Eq)] pub struct CompletionConfig { @@ -14,6 +15,14 @@ pub struct CompletionConfig { pub add_call_argument_snippets: bool, pub snippet_cap: Option, pub merge: Option, + pub resolve_capabilities: FxHashSet, +} + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] +pub enum CompletionResolveCapability { + Documentation, + Detail, + AdditionalTextEdits, } impl CompletionConfig { @@ -36,6 +45,7 @@ impl Default for CompletionConfig { add_call_argument_snippets: true, snippet_cap: Some(SnippetCap { _private: () }), merge: Some(MergeBehaviour::Full), + resolve_capabilities: FxHashSet::default(), } } } diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index 28209d4e0..c689b0dde 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs @@ -17,7 +17,7 @@ use ide_db::RootDatabase; use crate::{completions::Completions, context::CompletionContext, item::CompletionKind}; pub use crate::{ - config::CompletionConfig, + config::{CompletionConfig, CompletionResolveCapability}, item::{CompletionItem, CompletionItemKind, CompletionScore, ImportToAdd, InsertTextFormat}, }; -- cgit v1.2.3 From 2a7be4afb000d97948bb7f11bcd074fc1e11716e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 2 Dec 2020 01:02:15 +0200 Subject: Better support client completion resolve caps --- crates/completion/src/item.rs | 1 + crates/completion/src/test_utils.rs | 1 + 2 files changed, 2 insertions(+) (limited to 'crates/completion/src') diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 0e59f73cb..dc67df075 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -12,6 +12,7 @@ use crate::config::SnippetCap; /// `CompletionItem` describes a single completion variant in the editor pop-up. /// It is basically a POD with various properties. To construct a /// `CompletionItem`, use `new` method and the `Builder` struct. +#[derive(Clone)] pub struct CompletionItem { /// Used only internally in tests, to check only specific kind of /// completion (postfix, keyword, reference, etc). diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs index 4c1b1a839..516a63b4d 100644 --- a/crates/completion/src/test_utils.rs +++ b/crates/completion/src/test_utils.rs @@ -97,6 +97,7 @@ pub(crate) fn check_edit_with_config( .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions)); let mut actual = db.file_text(position.file_id).to_string(); completion.text_edit().apply(&mut actual); + // TODO kb how to apply imports now? assert_eq_text!(&ra_fixture_after, &actual) } -- cgit v1.2.3 From d9bd1f171dde11ff04f0619b14d8f25e5e4fc56e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 2 Dec 2020 23:55:35 +0200 Subject: Add eager resolve capability --- crates/completion/src/config.rs | 4 ++++ crates/completion/src/item.rs | 36 ++++++++++++++++++++++++---- crates/completion/src/render.rs | 4 ++-- crates/completion/src/render/enum_variant.rs | 2 +- crates/completion/src/render/function.rs | 2 +- crates/completion/src/render/macro_.rs | 2 +- crates/completion/src/test_utils.rs | 2 +- 7 files changed, 41 insertions(+), 11 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index 736af455e..e9a02aeb8 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs @@ -29,6 +29,10 @@ impl CompletionConfig { pub fn allow_snippets(&mut self, yes: bool) { self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None } } + + pub fn should_resolve_immediately(&self) -> bool { + !self.resolve_capabilities.contains(&CompletionResolveCapability::AdditionalTextEdits) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index dc67df075..5906637a6 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -3,8 +3,11 @@ use std::fmt; use hir::{Documentation, ModPath, Mutability}; -use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour}; -use syntax::TextRange; +use ide_db::helpers::{ + insert_use::{self, ImportScope, MergeBehaviour}, + mod_path_to_ast, +}; +use syntax::{algo, TextRange}; use text_edit::TextEdit; use crate::config::SnippetCap; @@ -207,6 +210,7 @@ impl CompletionItem { score: None, ref_match: None, import_to_add: None, + resolve_import_immediately: true, } } @@ -279,6 +283,7 @@ pub(crate) struct Builder { source_range: TextRange, completion_kind: CompletionKind, import_to_add: Option, + resolve_import_immediately: bool, label: String, insert_text: Option, insert_text_format: InsertTextFormat, @@ -300,6 +305,7 @@ impl Builder { let mut label = self.label; let mut lookup = self.lookup; let mut insert_text = self.insert_text; + let mut text_edits = TextEdit::builder(); if let Some(import_to_add) = self.import_to_add.as_ref() { let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); @@ -314,20 +320,35 @@ impl Builder { } label = format!("{}::{}", import_path_without_last_segment, label); } + + if self.resolve_import_immediately { + let rewriter = insert_use::insert_use( + &import_to_add.import_scope, + mod_path_to_ast(&import_to_add.import_path), + import_to_add.merge_behaviour, + ); + if let Some(old_ast) = rewriter.rewrite_root() { + algo::diff(&old_ast, &rewriter.rewrite(&old_ast)) + .into_text_edit(&mut text_edits); + } + } } - let text_edit = match self.text_edit { + let original_edit = match self.text_edit { Some(it) => it, None => { TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone())) } }; + let mut resulting_edit = text_edits.finish(); + resulting_edit.union(original_edit).expect("Failed to unite text edits"); + CompletionItem { source_range: self.source_range, label, insert_text_format: self.insert_text_format, - text_edit, + text_edit: resulting_edit, detail: self.detail, documentation: self.documentation, lookup, @@ -400,8 +421,13 @@ impl Builder { self.trigger_call_info = Some(true); self } - pub(crate) fn add_import(mut self, import_to_add: Option) -> Builder { + pub(crate) fn add_import( + mut self, + import_to_add: Option, + resolve_import_immediately: bool, + ) -> Builder { self.import_to_add = import_to_add; + self.resolve_import_immediately = resolve_import_immediately; self } pub(crate) fn set_ref_match( diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 504757a6a..b7a3a3935 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs @@ -194,7 +194,7 @@ impl<'a> Render<'a> { local_name, ) .kind(CompletionItemKind::UnresolvedReference) - .add_import(import_to_add) + .add_import(import_to_add, self.ctx.completion.config.should_resolve_immediately()) .build(); return Some(item); } @@ -249,7 +249,7 @@ impl<'a> Render<'a> { let item = item .kind(kind) - .add_import(import_to_add) + .add_import(import_to_add, self.ctx.completion.config.should_resolve_immediately()) .set_documentation(docs) .set_ref_match(ref_match) .build(); diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index f4bd02f25..c08824c6a 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> { .kind(CompletionItemKind::EnumVariant) .set_documentation(self.variant.docs(self.ctx.db())) .set_deprecated(self.ctx.is_deprecated(self.variant)) - .add_import(import_to_add) + .add_import(import_to_add, self.ctx.completion.config.should_resolve_immediately()) .detail(self.detail()); if self.variant_kind == StructKind::Tuple { diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs index 00e3eb203..3492384c6 100644 --- a/crates/completion/src/render/function.rs +++ b/crates/completion/src/render/function.rs @@ -47,7 +47,7 @@ impl<'a> FunctionRender<'a> { .set_deprecated(self.ctx.is_deprecated(self.func)) .detail(self.detail()) .add_call_parens(self.ctx.completion, self.name, params) - .add_import(import_to_add) + .add_import(import_to_add, self.ctx.completion.config.should_resolve_immediately()) .build() } diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index b4ab32c6e..15648b5b7 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs @@ -50,7 +50,7 @@ impl<'a> MacroRender<'a> { .kind(CompletionItemKind::Macro) .set_documentation(self.docs.clone()) .set_deprecated(self.ctx.is_deprecated(self.macro_)) - .add_import(import_to_add) + .add_import(import_to_add, self.ctx.completion.config.should_resolve_immediately()) .detail(self.detail()); let needs_bang = self.needs_bang(); diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs index 516a63b4d..88351ee15 100644 --- a/crates/completion/src/test_utils.rs +++ b/crates/completion/src/test_utils.rs @@ -97,7 +97,7 @@ pub(crate) fn check_edit_with_config( .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions)); let mut actual = db.file_text(position.file_id).to_string(); completion.text_edit().apply(&mut actual); - // TODO kb how to apply imports now? + // git how to apply imports now? assert_eq_text!(&ra_fixture_after, &actual) } -- cgit v1.2.3 From 50e06ee95ab12bc204fdce557ab0fb7aa5e5ab2f Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 3 Dec 2020 00:13:32 +0200 Subject: Refactor the code --- crates/completion/src/config.rs | 9 ++++++++- crates/completion/src/item.rs | 3 +-- crates/completion/src/render.rs | 10 ++++++++-- crates/completion/src/render/enum_variant.rs | 5 ++++- crates/completion/src/render/function.rs | 5 ++++- crates/completion/src/render/macro_.rs | 5 ++++- crates/completion/src/test_utils.rs | 1 - 7 files changed, 29 insertions(+), 9 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index e9a02aeb8..f2fa5c27b 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs @@ -15,9 +15,15 @@ pub struct CompletionConfig { pub add_call_argument_snippets: bool, pub snippet_cap: Option, pub merge: Option, + /// A set of capabilities, enabled on the cliend and supported on the server. pub resolve_capabilities: FxHashSet, } +/// A resolve capability, supported on a server. +/// If the client registers any of those in its completion resolve capabilities, +/// the server is able to render completion items' corresponding fields later, +/// not during an initial completion item request. +/// See https://github.com/rust-analyzer/rust-analyzer/issues/6366 for more details. #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] pub enum CompletionResolveCapability { Documentation, @@ -30,7 +36,8 @@ impl CompletionConfig { self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None } } - pub fn should_resolve_immediately(&self) -> bool { + /// Whether the completions' additional edits are calculated later, during a resolve request or not. + pub fn should_resolve_additional_edits_immediately(&self) -> bool { !self.resolve_capabilities.contains(&CompletionResolveCapability::AdditionalTextEdits) } } diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 5906637a6..775245b3b 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -67,8 +67,7 @@ pub struct CompletionItem { /// possible match. ref_match: Option<(Mutability, CompletionScore)>, - /// The data later to be used in the `completionItem/resolve` response - /// to add the insert import edit. + /// The import data to add to completion's edits. import_to_add: Option, } diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index b7a3a3935..3a793000b 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs @@ -194,7 +194,10 @@ impl<'a> Render<'a> { local_name, ) .kind(CompletionItemKind::UnresolvedReference) - .add_import(import_to_add, self.ctx.completion.config.should_resolve_immediately()) + .add_import( + import_to_add, + self.ctx.completion.config.should_resolve_additional_edits_immediately(), + ) .build(); return Some(item); } @@ -249,7 +252,10 @@ impl<'a> Render<'a> { let item = item .kind(kind) - .add_import(import_to_add, self.ctx.completion.config.should_resolve_immediately()) + .add_import( + import_to_add, + self.ctx.completion.config.should_resolve_additional_edits_immediately(), + ) .set_documentation(docs) .set_ref_match(ref_match) .build(); diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index c08824c6a..6548b4676 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs @@ -71,7 +71,10 @@ impl<'a> EnumVariantRender<'a> { .kind(CompletionItemKind::EnumVariant) .set_documentation(self.variant.docs(self.ctx.db())) .set_deprecated(self.ctx.is_deprecated(self.variant)) - .add_import(import_to_add, self.ctx.completion.config.should_resolve_immediately()) + .add_import( + import_to_add, + self.ctx.completion.config.should_resolve_additional_edits_immediately(), + ) .detail(self.detail()); if self.variant_kind == StructKind::Tuple { diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs index 3492384c6..b13e0dafc 100644 --- a/crates/completion/src/render/function.rs +++ b/crates/completion/src/render/function.rs @@ -47,7 +47,10 @@ impl<'a> FunctionRender<'a> { .set_deprecated(self.ctx.is_deprecated(self.func)) .detail(self.detail()) .add_call_parens(self.ctx.completion, self.name, params) - .add_import(import_to_add, self.ctx.completion.config.should_resolve_immediately()) + .add_import( + import_to_add, + self.ctx.completion.config.should_resolve_additional_edits_immediately(), + ) .build() } diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index 15648b5b7..7a8eeb7b9 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs @@ -50,7 +50,10 @@ impl<'a> MacroRender<'a> { .kind(CompletionItemKind::Macro) .set_documentation(self.docs.clone()) .set_deprecated(self.ctx.is_deprecated(self.macro_)) - .add_import(import_to_add, self.ctx.completion.config.should_resolve_immediately()) + .add_import( + import_to_add, + self.ctx.completion.config.should_resolve_additional_edits_immediately(), + ) .detail(self.detail()); let needs_bang = self.needs_bang(); diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs index 88351ee15..4c1b1a839 100644 --- a/crates/completion/src/test_utils.rs +++ b/crates/completion/src/test_utils.rs @@ -97,7 +97,6 @@ pub(crate) fn check_edit_with_config( .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions)); let mut actual = db.file_text(position.file_id).to_string(); completion.text_edit().apply(&mut actual); - // git how to apply imports now? assert_eq_text!(&ra_fixture_after, &actual) } -- cgit v1.2.3 From 68a747efe048e8e92eedafaa27b0c0d2f317f04d Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 3 Dec 2020 00:27:26 +0200 Subject: Remove redundant code --- crates/completion/src/config.rs | 17 ++++++++++------- crates/completion/src/render.rs | 10 ++-------- crates/completion/src/render/enum_variant.rs | 5 +---- crates/completion/src/render/function.rs | 5 +---- crates/completion/src/render/macro_.rs | 5 +---- 5 files changed, 15 insertions(+), 27 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index f2fa5c27b..eacdd3449 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs @@ -15,12 +15,12 @@ pub struct CompletionConfig { pub add_call_argument_snippets: bool, pub snippet_cap: Option, pub merge: Option, - /// A set of capabilities, enabled on the cliend and supported on the server. - pub resolve_capabilities: FxHashSet, + /// A set of capabilities, enabled on the client and supported on the server. + pub active_resolve_capabilities: FxHashSet, } -/// A resolve capability, supported on a server. -/// If the client registers any of those in its completion resolve capabilities, +/// A resolve capability, supported on the server. +/// If the client registers any completion resolve capabilities, /// the server is able to render completion items' corresponding fields later, /// not during an initial completion item request. /// See https://github.com/rust-analyzer/rust-analyzer/issues/6366 for more details. @@ -37,8 +37,11 @@ impl CompletionConfig { } /// Whether the completions' additional edits are calculated later, during a resolve request or not. - pub fn should_resolve_additional_edits_immediately(&self) -> bool { - !self.resolve_capabilities.contains(&CompletionResolveCapability::AdditionalTextEdits) + /// See `CompletionResolveCapability` for the details. + pub fn resolve_edits_immediately(&self) -> bool { + !self + .active_resolve_capabilities + .contains(&CompletionResolveCapability::AdditionalTextEdits) } } @@ -56,7 +59,7 @@ impl Default for CompletionConfig { add_call_argument_snippets: true, snippet_cap: Some(SnippetCap { _private: () }), merge: Some(MergeBehaviour::Full), - resolve_capabilities: FxHashSet::default(), + active_resolve_capabilities: FxHashSet::default(), } } } diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 3a793000b..2b4f1ea14 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs @@ -194,10 +194,7 @@ impl<'a> Render<'a> { local_name, ) .kind(CompletionItemKind::UnresolvedReference) - .add_import( - import_to_add, - self.ctx.completion.config.should_resolve_additional_edits_immediately(), - ) + .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) .build(); return Some(item); } @@ -252,10 +249,7 @@ impl<'a> Render<'a> { let item = item .kind(kind) - .add_import( - import_to_add, - self.ctx.completion.config.should_resolve_additional_edits_immediately(), - ) + .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) .set_documentation(docs) .set_ref_match(ref_match) .build(); diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index 6548b4676..4a91fe3c7 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs @@ -71,10 +71,7 @@ impl<'a> EnumVariantRender<'a> { .kind(CompletionItemKind::EnumVariant) .set_documentation(self.variant.docs(self.ctx.db())) .set_deprecated(self.ctx.is_deprecated(self.variant)) - .add_import( - import_to_add, - self.ctx.completion.config.should_resolve_additional_edits_immediately(), - ) + .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) .detail(self.detail()); if self.variant_kind == StructKind::Tuple { diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs index b13e0dafc..20f2b9b7e 100644 --- a/crates/completion/src/render/function.rs +++ b/crates/completion/src/render/function.rs @@ -47,10 +47,7 @@ impl<'a> FunctionRender<'a> { .set_deprecated(self.ctx.is_deprecated(self.func)) .detail(self.detail()) .add_call_parens(self.ctx.completion, self.name, params) - .add_import( - import_to_add, - self.ctx.completion.config.should_resolve_additional_edits_immediately(), - ) + .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) .build() } diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index 7a8eeb7b9..be7c53659 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs @@ -50,10 +50,7 @@ impl<'a> MacroRender<'a> { .kind(CompletionItemKind::Macro) .set_documentation(self.docs.clone()) .set_deprecated(self.ctx.is_deprecated(self.macro_)) - .add_import( - import_to_add, - self.ctx.completion.config.should_resolve_additional_edits_immediately(), - ) + .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) .detail(self.detail()); let needs_bang = self.needs_bang(); -- cgit v1.2.3 From f6d2540df09bc0dcd8a748ec0ed7cb33ac76d9f2 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 3 Dec 2020 11:13:28 +0200 Subject: Simplify import edit calculation --- crates/completion/src/config.rs | 10 ++--- crates/completion/src/item.rs | 63 ++++++++++++++++------------ crates/completion/src/lib.rs | 2 +- crates/completion/src/render.rs | 13 +++--- crates/completion/src/render/enum_variant.rs | 8 ++-- crates/completion/src/render/function.rs | 8 ++-- crates/completion/src/render/macro_.rs | 11 +++-- 7 files changed, 65 insertions(+), 50 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index eacdd3449..487c1d0f1 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs @@ -36,12 +36,10 @@ impl CompletionConfig { self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None } } - /// Whether the completions' additional edits are calculated later, during a resolve request or not. - /// See `CompletionResolveCapability` for the details. - pub fn resolve_edits_immediately(&self) -> bool { - !self - .active_resolve_capabilities - .contains(&CompletionResolveCapability::AdditionalTextEdits) + /// Whether the completions' additional edits are calculated when sending an initional completions list + /// or later, in a separate resolve request. + pub fn resolve_additional_edits_lazily(&self) -> bool { + self.active_resolve_capabilities.contains(&CompletionResolveCapability::AdditionalTextEdits) } } diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 775245b3b..4e56f28f3 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -68,7 +68,7 @@ pub struct CompletionItem { ref_match: Option<(Mutability, CompletionScore)>, /// The import data to add to completion's edits. - import_to_add: Option, + import_to_add: Option, } // We use custom debug for CompletionItem to make snapshot tests more readable. @@ -209,7 +209,7 @@ impl CompletionItem { score: None, ref_match: None, import_to_add: None, - resolve_import_immediately: true, + resolve_import_lazily: false, } } @@ -262,27 +262,46 @@ impl CompletionItem { self.ref_match } - pub fn import_to_add(&self) -> Option<&ImportToAdd> { + pub fn import_to_add(&self) -> Option<&ImportEdit> { self.import_to_add.as_ref() } } /// An extra import to add after the completion is applied. #[derive(Debug, Clone)] -pub struct ImportToAdd { +pub struct ImportEdit { pub import_path: ModPath, pub import_scope: ImportScope, pub merge_behaviour: Option, } +impl ImportEdit { + /// Attempts to insert the import to the given scope, producing a text edit. + /// May return no edit in edge cases, such as scope already containing the import. + pub fn to_text_edit(&self) -> Option { + let _p = profile::span("ImportEdit::to_edit"); + + let rewriter = insert_use::insert_use( + &self.import_scope, + mod_path_to_ast(&self.import_path), + self.merge_behaviour, + ); + let old_ast = rewriter.rewrite_root()?; + let mut import_insert = TextEdit::builder(); + algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); + + Some(import_insert.finish()) + } +} + /// A helper to make `CompletionItem`s. #[must_use] #[derive(Clone)] pub(crate) struct Builder { source_range: TextRange, completion_kind: CompletionKind, - import_to_add: Option, - resolve_import_immediately: bool, + import_to_add: Option, + resolve_import_lazily: bool, label: String, insert_text: Option, insert_text_format: InsertTextFormat, @@ -304,7 +323,6 @@ impl Builder { let mut label = self.label; let mut lookup = self.lookup; let mut insert_text = self.insert_text; - let mut text_edits = TextEdit::builder(); if let Some(import_to_add) = self.import_to_add.as_ref() { let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); @@ -319,35 +337,28 @@ impl Builder { } label = format!("{}::{}", import_path_without_last_segment, label); } - - if self.resolve_import_immediately { - let rewriter = insert_use::insert_use( - &import_to_add.import_scope, - mod_path_to_ast(&import_to_add.import_path), - import_to_add.merge_behaviour, - ); - if let Some(old_ast) = rewriter.rewrite_root() { - algo::diff(&old_ast, &rewriter.rewrite(&old_ast)) - .into_text_edit(&mut text_edits); - } - } } - let original_edit = match self.text_edit { + let mut text_edit = match self.text_edit { Some(it) => it, None => { TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone())) } }; - let mut resulting_edit = text_edits.finish(); - resulting_edit.union(original_edit).expect("Failed to unite text edits"); + if !self.resolve_import_lazily { + if let Some(import_edit) = + self.import_to_add.as_ref().and_then(|import_edit| import_edit.to_text_edit()) + { + text_edit.union(import_edit).expect("Failed to unite import and completion edits"); + } + } CompletionItem { source_range: self.source_range, label, insert_text_format: self.insert_text_format, - text_edit: resulting_edit, + text_edit, detail: self.detail, documentation: self.documentation, lookup, @@ -422,11 +433,11 @@ impl Builder { } pub(crate) fn add_import( mut self, - import_to_add: Option, - resolve_import_immediately: bool, + import_to_add: Option, + resolve_import_lazily: bool, ) -> Builder { self.import_to_add = import_to_add; - self.resolve_import_immediately = resolve_import_immediately; + self.resolve_import_lazily = resolve_import_lazily; self } pub(crate) fn set_ref_match( diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index c689b0dde..c57203c80 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs @@ -18,7 +18,7 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi pub use crate::{ config::{CompletionConfig, CompletionResolveCapability}, - item::{CompletionItem, CompletionItemKind, CompletionScore, ImportToAdd, InsertTextFormat}, + item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, }; //FIXME: split the following feature into fine-grained features. diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 2b4f1ea14..a6faedb18 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs @@ -16,7 +16,7 @@ use syntax::TextRange; use test_utils::mark; use crate::{ - config::SnippetCap, item::ImportToAdd, CompletionContext, CompletionItem, CompletionItemKind, + config::SnippetCap, item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionScore, }; @@ -56,7 +56,7 @@ pub(crate) fn render_resolution_with_import<'a>( let local_name = import_path.segments.last()?.to_string(); Render::new(ctx).render_resolution( local_name, - Some(ImportToAdd { import_path, import_scope, merge_behaviour }), + Some(ImportEdit { import_path, import_scope, merge_behaviour }), resolution, ) } @@ -147,7 +147,7 @@ impl<'a> Render<'a> { fn render_resolution( self, local_name: String, - import_to_add: Option, + import_to_add: Option, resolution: &ScopeDef, ) -> Option { let _p = profile::span("render_resolution"); @@ -194,7 +194,10 @@ impl<'a> Render<'a> { local_name, ) .kind(CompletionItemKind::UnresolvedReference) - .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) + .add_import( + import_to_add, + self.ctx.completion.config.resolve_additional_edits_lazily(), + ) .build(); return Some(item); } @@ -249,7 +252,7 @@ impl<'a> Render<'a> { let item = item .kind(kind) - .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) + .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily()) .set_documentation(docs) .set_ref_match(ref_match) .build(); diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index 4a91fe3c7..e979a090b 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs @@ -5,13 +5,13 @@ use itertools::Itertools; use test_utils::mark; use crate::{ - item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, + item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, render::{builder_ext::Params, RenderContext}, }; pub(crate) fn render_enum_variant<'a>( ctx: RenderContext<'a>, - import_to_add: Option, + import_to_add: Option, local_name: Option, variant: hir::EnumVariant, path: Option, @@ -62,7 +62,7 @@ impl<'a> EnumVariantRender<'a> { } } - fn render(self, import_to_add: Option) -> CompletionItem { + fn render(self, import_to_add: Option) -> CompletionItem { let mut builder = CompletionItem::new( CompletionKind::Reference, self.ctx.source_range(), @@ -71,7 +71,7 @@ impl<'a> EnumVariantRender<'a> { .kind(CompletionItemKind::EnumVariant) .set_documentation(self.variant.docs(self.ctx.db())) .set_deprecated(self.ctx.is_deprecated(self.variant)) - .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) + .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily()) .detail(self.detail()); if self.variant_kind == StructKind::Tuple { diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs index 20f2b9b7e..dd2c999ef 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}; use test_utils::mark; use crate::{ - item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, + item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, render::{builder_ext::Params, RenderContext}, }; pub(crate) fn render_fn<'a>( ctx: RenderContext<'a>, - import_to_add: Option, + import_to_add: Option, local_name: Option, fn_: hir::Function, ) -> CompletionItem { @@ -39,7 +39,7 @@ impl<'a> FunctionRender<'a> { FunctionRender { ctx, name, func: fn_, ast_node } } - fn render(self, import_to_add: Option) -> CompletionItem { + fn render(self, import_to_add: Option) -> CompletionItem { let params = self.params(); CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) .kind(self.kind()) @@ -47,7 +47,7 @@ impl<'a> FunctionRender<'a> { .set_deprecated(self.ctx.is_deprecated(self.func)) .detail(self.detail()) .add_call_parens(self.ctx.completion, self.name, params) - .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) + .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily()) .build() } diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index be7c53659..bdbc642ca 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs @@ -5,13 +5,13 @@ use syntax::display::macro_label; use test_utils::mark; use crate::{ - item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, + item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, render::RenderContext, }; pub(crate) fn render_macro<'a>( ctx: RenderContext<'a>, - import_to_add: Option, + import_to_add: Option, name: String, macro_: hir::MacroDef, ) -> Option { @@ -38,7 +38,7 @@ impl<'a> MacroRender<'a> { MacroRender { ctx, name, macro_, docs, bra, ket } } - fn render(&self, import_to_add: Option) -> Option { + fn render(&self, import_to_add: Option) -> Option { // FIXME: Currently proc-macro do not have ast-node, // such that it does not have source if self.macro_.is_proc_macro() { @@ -50,7 +50,10 @@ impl<'a> MacroRender<'a> { .kind(CompletionItemKind::Macro) .set_documentation(self.docs.clone()) .set_deprecated(self.ctx.is_deprecated(self.macro_)) - .add_import(import_to_add, self.ctx.completion.config.resolve_edits_immediately()) + .add_import( + import_to_add, + self.ctx.completion.config.resolve_additional_edits_lazily(), + ) .detail(self.detail()); let needs_bang = self.needs_bang(); -- cgit v1.2.3 From 74c3bbacc9b352057f2fc7ab69bd13e53022beb0 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 3 Dec 2020 15:58:18 +0200 Subject: Make completion resolve async --- crates/completion/src/item.rs | 29 +++++++++++++++++++++++++++-- crates/completion/src/lib.rs | 5 ++++- 2 files changed, 31 insertions(+), 3 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 4e56f28f3..dd25ca75c 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -4,10 +4,10 @@ use std::fmt; use hir::{Documentation, ModPath, Mutability}; use ide_db::helpers::{ - insert_use::{self, ImportScope, MergeBehaviour}, + insert_use::{self, ImportScope, ImportScopePtr, MergeBehaviour}, mod_path_to_ast, }; -use syntax::{algo, TextRange}; +use syntax::{algo, SyntaxNode, TextRange}; use text_edit::TextEdit; use crate::config::SnippetCap; @@ -275,7 +275,32 @@ pub struct ImportEdit { pub merge_behaviour: Option, } +#[derive(Debug, Clone)] +pub struct ImportEditPtr { + pub import_path: ModPath, + pub import_scope: ImportScopePtr, + pub merge_behaviour: Option, +} + +impl ImportEditPtr { + pub fn into_import_edit(self, root: &SyntaxNode) -> Option { + Some(ImportEdit { + import_path: self.import_path, + import_scope: self.import_scope.into_scope(root)?, + merge_behaviour: self.merge_behaviour, + }) + } +} + impl ImportEdit { + pub fn get_edit_ptr(&self) -> ImportEditPtr { + ImportEditPtr { + import_path: self.import_path.clone(), + import_scope: self.import_scope.get_ptr(), + merge_behaviour: self.merge_behaviour, + } + } + /// Attempts to insert the import to the given scope, producing a text edit. /// May return no edit in edge cases, such as scope already containing the import. pub fn to_text_edit(&self) -> Option { diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index c57203c80..c277cd466 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs @@ -18,7 +18,10 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi pub use crate::{ config::{CompletionConfig, CompletionResolveCapability}, - item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, + item::{ + CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, ImportEditPtr, + InsertTextFormat, + }, }; //FIXME: split the following feature into fine-grained features. -- cgit v1.2.3 From 93bc009a5968c964693299263689b50b2efe9abc Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 4 Dec 2020 10:02:22 +0200 Subject: Remove the state --- crates/completion/src/item.rs | 30 +++--------------------------- crates/completion/src/lib.rs | 5 +---- 2 files changed, 4 insertions(+), 31 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index dd25ca75c..2dadf7e5b 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -4,10 +4,10 @@ use std::fmt; use hir::{Documentation, ModPath, Mutability}; use ide_db::helpers::{ - insert_use::{self, ImportScope, ImportScopePtr, MergeBehaviour}, + insert_use::{self, ImportScope, MergeBehaviour}, mod_path_to_ast, }; -use syntax::{algo, SyntaxNode, TextRange}; +use syntax::{algo, TextRange}; use text_edit::TextEdit; use crate::config::SnippetCap; @@ -275,32 +275,8 @@ pub struct ImportEdit { pub merge_behaviour: Option, } -#[derive(Debug, Clone)] -pub struct ImportEditPtr { - pub import_path: ModPath, - pub import_scope: ImportScopePtr, - pub merge_behaviour: Option, -} - -impl ImportEditPtr { - pub fn into_import_edit(self, root: &SyntaxNode) -> Option { - Some(ImportEdit { - import_path: self.import_path, - import_scope: self.import_scope.into_scope(root)?, - merge_behaviour: self.merge_behaviour, - }) - } -} - impl ImportEdit { - pub fn get_edit_ptr(&self) -> ImportEditPtr { - ImportEditPtr { - import_path: self.import_path.clone(), - import_scope: self.import_scope.get_ptr(), - merge_behaviour: self.merge_behaviour, - } - } - + // TODO kb remove this at all now, since it's used only once? /// Attempts to insert the import to the given scope, producing a text edit. /// May return no edit in edge cases, such as scope already containing the import. pub fn to_text_edit(&self) -> Option { diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index c277cd466..c57203c80 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs @@ -18,10 +18,7 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi pub use crate::{ config::{CompletionConfig, CompletionResolveCapability}, - item::{ - CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, ImportEditPtr, - InsertTextFormat, - }, + item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, }; //FIXME: split the following feature into fine-grained features. -- cgit v1.2.3 From deda74edd89affb3f77d274776d2a672bc11db90 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 4 Dec 2020 16:03:22 +0200 Subject: Use stateless completion resolve --- .../completion/src/completions/unqualified_path.rs | 10 ++++--- crates/completion/src/item.rs | 1 - crates/completion/src/lib.rs | 32 ++++++++++++++++++++-- crates/completion/src/render.rs | 12 +++----- 4 files changed, 40 insertions(+), 15 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 81691cd7f..26a2b7a1b 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; use crate::{ render::{render_resolution_with_import, RenderContext}, - CompletionContext, Completions, + CompletionContext, Completions, ImportEdit, }; pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { @@ -103,9 +103,11 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() .filter_map(|(import_path, definition)| { render_resolution_with_import( RenderContext::new(ctx), - import_path.clone(), - import_scope.clone(), - ctx.config.merge, + ImportEdit { + import_path: import_path.clone(), + import_scope: import_scope.clone(), + merge_behaviour: ctx.config.merge, + }, &definition, ) }); diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 2dadf7e5b..4e56f28f3 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -276,7 +276,6 @@ pub struct ImportEdit { } impl ImportEdit { - // TODO kb remove this at all now, since it's used only once? /// Attempts to insert the import to the given scope, producing a text edit. /// May return no edit in edge cases, such as scope already containing the import. pub fn to_text_edit(&self) -> Option { diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index c57203c80..938c92dbb 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs @@ -11,8 +11,11 @@ mod render; mod completions; -use ide_db::base_db::FilePosition; -use ide_db::RootDatabase; +use ide_db::{ + base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, +}; +use syntax::AstNode; +use text_edit::TextEdit; use crate::{completions::Completions, context::CompletionContext, item::CompletionKind}; @@ -131,6 +134,31 @@ pub fn completions( Some(acc) } +/// Resolves additional completion data at the position given. +pub fn resolve_completion_edits( + db: &RootDatabase, + config: &CompletionConfig, + position: FilePosition, + full_import_path: &str, + imported_name: &str, +) -> Option { + let ctx = CompletionContext::new(db, position, config)?; + let anchor = ctx.name_ref_syntax.as_ref()?; + let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; + + let current_module = ctx.sema.scope(anchor.syntax()).module()?; + let current_crate = current_module.krate(); + + let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) + .filter_map(|candidate| { + let item: hir::ItemInNs = candidate.either(Into::into, Into::into); + current_module.find_use_path(db, item) + }) + .find(|mod_path| mod_path.to_string() == full_import_path)?; + + ImportEdit { import_path, import_scope, merge_behaviour: config.merge }.to_text_edit() +} + #[cfg(test)] mod tests { use crate::config::CompletionConfig; diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index a6faedb18..9a43480e1 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs @@ -9,8 +9,7 @@ pub(crate) mod type_alias; mod builder_ext; -use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; -use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour}; +use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; use ide_db::RootDatabase; use syntax::TextRange; use test_utils::mark; @@ -48,15 +47,12 @@ pub(crate) fn render_resolution<'a>( pub(crate) fn render_resolution_with_import<'a>( ctx: RenderContext<'a>, - import_path: ModPath, - import_scope: ImportScope, - merge_behaviour: Option, + import_edit: ImportEdit, resolution: &ScopeDef, ) -> Option { - let local_name = import_path.segments.last()?.to_string(); Render::new(ctx).render_resolution( - local_name, - Some(ImportEdit { import_path, import_scope, merge_behaviour }), + import_edit.import_path.segments.last()?.to_string(), + Some(import_edit), resolution, ) } -- cgit v1.2.3 From 045d7f096fc83cb14472a7ded9b4438a42f116a5 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 4 Dec 2020 20:02:42 +0200 Subject: Fix the profiling label --- crates/completion/src/item.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 4e56f28f3..519204bfa 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -279,7 +279,7 @@ impl ImportEdit { /// Attempts to insert the import to the given scope, producing a text edit. /// May return no edit in edge cases, such as scope already containing the import. pub fn to_text_edit(&self) -> Option { - let _p = profile::span("ImportEdit::to_edit"); + let _p = profile::span("ImportEdit::to_text_edit"); let rewriter = insert_use::insert_use( &self.import_scope, -- cgit v1.2.3 From 077c1c3c1f16b5387c9e20cfa087c517dac3f4c8 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 4 Dec 2020 21:23:30 +0200 Subject: Less panic, more tests --- .../completion/src/completions/unqualified_path.rs | 41 ++++++++++++++++++++-- crates/completion/src/item.rs | 28 +++++++++++---- 2 files changed, 60 insertions(+), 9 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 26a2b7a1b..fe6507c55 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -122,8 +122,8 @@ mod tests { use test_utils::mark; use crate::{ - test_utils::{check_edit, completion_list}, - CompletionKind, + test_utils::{check_edit, check_edit_with_config, completion_list}, + CompletionConfig, CompletionKind, }; fn check(ra_fixture: &str, expect: Expect) { @@ -807,6 +807,43 @@ use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}}; fn main() { ThirdStruct } +"#, + ); + } + + /// LSP protocol supports separate completion resolve requests to do the heavy computations there. + /// This test checks that for a certain resolve capatilities no such operations (autoimport) are done. + #[test] + fn no_fuzzy_completions_applied_for_certain_resolve_capability() { + let mut completion_config = CompletionConfig::default(); + completion_config + .active_resolve_capabilities + .insert(crate::CompletionResolveCapability::AdditionalTextEdits); + + check_edit_with_config( + completion_config, + "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}; + +fn main() { +ThirdStruct +} "#, ); } diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 519204bfa..978ea76f9 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -346,13 +346,17 @@ impl Builder { } }; - if !self.resolve_import_lazily { - if let Some(import_edit) = - self.import_to_add.as_ref().and_then(|import_edit| import_edit.to_text_edit()) - { - text_edit.union(import_edit).expect("Failed to unite import and completion edits"); + let import_to_add = if self.resolve_import_lazily { + self.import_to_add + } else { + match apply_import_eagerly(self.import_to_add.as_ref(), &mut text_edit) { + Ok(()) => self.import_to_add, + Err(()) => { + log::error!("Failed to apply eager import edit: original edit and import edit intersect"); + None + } } - } + }; CompletionItem { source_range: self.source_range, @@ -368,7 +372,7 @@ impl Builder { trigger_call_info: self.trigger_call_info.unwrap_or(false), score: self.score, ref_match: self.ref_match, - import_to_add: self.import_to_add, + import_to_add, } } pub(crate) fn lookup_by(mut self, lookup: impl Into) -> Builder { @@ -449,6 +453,16 @@ impl Builder { } } +fn apply_import_eagerly( + import_to_add: Option<&ImportEdit>, + original_edit: &mut TextEdit, +) -> Result<(), ()> { + match import_to_add.and_then(|import_edit| import_edit.to_text_edit()) { + Some(import_edit) => original_edit.union(import_edit).map_err(|_| ()), + None => Ok(()), + } +} + impl<'a> Into for Builder { fn into(self) -> CompletionItem { self.build() -- cgit v1.2.3 From 19cfa5802eabddd5747bbdb04c81b50fc9f6e623 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sun, 6 Dec 2020 23:58:15 +0200 Subject: Simplify --- crates/completion/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index 938c92dbb..066d589af 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs @@ -141,7 +141,7 @@ pub fn resolve_completion_edits( position: FilePosition, full_import_path: &str, imported_name: &str, -) -> Option { +) -> Option> { let ctx = CompletionContext::new(db, position, config)?; let anchor = ctx.name_ref_syntax.as_ref()?; let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; @@ -156,7 +156,9 @@ pub fn resolve_completion_edits( }) .find(|mod_path| mod_path.to_string() == full_import_path)?; - ImportEdit { import_path, import_scope, merge_behaviour: config.merge }.to_text_edit() + ImportEdit { import_path, import_scope, merge_behaviour: config.merge } + .to_text_edit() + .map(|edit| vec![edit]) } #[cfg(test)] -- cgit v1.2.3 From 9656ceb8963273047dab4ee9f3d0b19976890599 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 7 Dec 2020 00:39:03 +0200 Subject: Document the feature --- .../completion/src/completions/unqualified_path.rs | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'crates/completion/src') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index fe6507c55..1482df8fb 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -73,6 +73,49 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T } } +// Feature: Fuzzy Completion and Autoimports +// +// When completing names in the current scope, proposes additional imports from other modules or crates, +// if they can be qualified in the scope and their name contains all symbols from the completion input +// (case-insensitive, in any order or places). +// +// ``` +// fn main() { +// pda<|> +// } +// # pub mod std { pub mod marker { pub struct PhantomData { } } } +// ``` +// -> +// ``` +// use std::marker::PhantomData; +// +// fn main() { +// PhantomData +// } +// # pub mod std { pub mod marker { pub struct PhantomData { } } } +// ``` +// +// .Fuzzy search details +// +// To avoid an excessive amount of the results returned, completion input is checked for inclusion in the identifiers only +// (i.e. in `HashMap` in the `std::collections::HashMap` path), also not in the module indentifiers. +// +// .Merge Behaviour +// +// It is possible to configure how use-trees are merged with the `importMergeBehaviour` setting. +// Mimics the corresponding behaviour of the `Auto Import` feature. +// +// .LSP and performance implications +// +// LSP 3.16 provides the way to defer the computation of some completion data, including the import edits for this feature. +// If the LSP client supports the `additionalTextEdits` (case sensitive) resolve client capability, rust-analyzer computes +// the completion edits only when a corresponding completion item is selected. +// For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones, +// which might be slow. +// +// .Feature toggle +// +// The feature can be turned off in the settings with the `rust-analyzer.completion.enableExperimental` flag. fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { let _p = profile::span("fuzzy_completion"); let current_module = ctx.scope.module()?; -- cgit v1.2.3 From 3183ff3a7b1fbcf3cb5379cf162a3d769a21be7a Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 8 Dec 2020 00:46:56 +0200 Subject: Disable the completion for no corresponding client resolve capabilities --- .../completion/src/completions/unqualified_path.rs | 134 +++++++++------------ crates/completion/src/config.rs | 4 +- crates/completion/src/item.rs | 35 +----- crates/completion/src/lib.rs | 2 +- crates/completion/src/render.rs | 29 +---- crates/completion/src/render/enum_variant.rs | 2 +- crates/completion/src/render/function.rs | 2 +- crates/completion/src/render/macro_.rs | 5 +- crates/completion/src/test_utils.rs | 11 +- 9 files changed, 79 insertions(+), 145 deletions(-) (limited to 'crates/completion/src') 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 acc.add_resolution(ctx, name.to_string(), &res) }); - if ctx.config.enable_experimental_completions { + if !ctx.config.disable_fuzzy_autoimports && ctx.config.resolve_additional_edits_lazily() { fuzzy_completion(acc, ctx).unwrap_or_default() } } @@ -99,6 +99,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T // // To avoid an excessive amount of the results returned, completion input is checked for inclusion in the identifiers only // (i.e. in `HashMap` in the `std::collections::HashMap` path), also not in the module indentifiers. +// It also avoids searching for any imports for inputs with their length less that 3 symbols. // // .Merge Behaviour // @@ -107,53 +108,53 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T // // .LSP and performance implications // -// LSP 3.16 provides the way to defer the computation of some completion data, including the import edits for this feature. -// If the LSP client supports the `additionalTextEdits` (case sensitive) resolve client capability, rust-analyzer computes -// the completion edits only when a corresponding completion item is selected. +// The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits` +// (case sensitive) resolve client capability in its client capabilities. +// This way the server is able to defer the costly computations, doing them for a selected completion item only. // For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones, -// which might be slow. +// which might be slow ergo the feature is automatically disabled. // // .Feature toggle // -// The feature can be turned off in the settings with the `rust-analyzer.completion.enableExperimental` flag. +// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.disableFuzzyAutoimports` flag. fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { let _p = profile::span("fuzzy_completion"); + let potential_import_name = ctx.token.to_string(); + + if potential_import_name.len() < 3 { + 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)?; - let potential_import_name = ctx.token.to_string(); - - let possible_imports = imports_locator::find_similar_imports( - &ctx.sema, - ctx.krate?, - &potential_import_name, - 50, - true, - ) - .filter_map(|import_candidate| { - Some(match import_candidate { - Either::Left(module_def) => { - (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def)) - } - Either::Right(macro_def) => { - (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def)) - } - }) - }) - .filter(|(mod_path, _)| mod_path.len() > 1) - .take(20) - .filter_map(|(import_path, definition)| { - render_resolution_with_import( - RenderContext::new(ctx), - ImportEdit { - import_path: import_path.clone(), - import_scope: import_scope.clone(), - merge_behaviour: ctx.config.merge, - }, - &definition, - ) - }); + let possible_imports = + imports_locator::find_similar_imports(&ctx.sema, ctx.krate?, &potential_import_name, true) + .filter_map(|import_candidate| { + Some(match import_candidate { + Either::Left(module_def) => ( + current_module.find_use_path(ctx.db, module_def)?, + ScopeDef::ModuleDef(module_def), + ), + Either::Right(macro_def) => ( + current_module.find_use_path(ctx.db, macro_def)?, + ScopeDef::MacroDef(macro_def), + ), + }) + }) + .filter(|(mod_path, _)| mod_path.len() > 1) + .filter_map(|(import_path, definition)| { + render_resolution_with_import( + RenderContext::new(ctx), + ImportEdit { + import_path: import_path.clone(), + import_scope: import_scope.clone(), + merge_behaviour: ctx.config.merge, + }, + &definition, + ) + }); acc.add_all(possible_imports); Some(()) @@ -775,7 +776,13 @@ impl My<|> #[test] fn function_fuzzy_completion() { - check_edit( + let mut completion_config = CompletionConfig::default(); + completion_config + .active_resolve_capabilities + .insert(crate::CompletionResolveCapability::AdditionalTextEdits); + + check_edit_with_config( + completion_config, "stdin", r#" //- /lib.rs crate:dep @@ -800,7 +807,13 @@ fn main() { #[test] fn macro_fuzzy_completion() { - check_edit( + let mut completion_config = CompletionConfig::default(); + completion_config + .active_resolve_capabilities + .insert(crate::CompletionResolveCapability::AdditionalTextEdits); + + check_edit_with_config( + completion_config, "macro_with_curlies!", r#" //- /lib.rs crate:dep @@ -827,37 +840,6 @@ fn main() { #[test] fn struct_fuzzy_completion() { - check_edit( - "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 -} -"#, - ); - } - - /// LSP protocol supports separate completion resolve requests to do the heavy computations there. - /// This test checks that for a certain resolve capatilities no such operations (autoimport) are done. - #[test] - fn no_fuzzy_completions_applied_for_certain_resolve_capability() { let mut completion_config = CompletionConfig::default(); completion_config .active_resolve_capabilities @@ -870,22 +852,22 @@ fn main() { //- /lib.rs crate:dep pub struct FirstStruct; pub mod some_module { -pub struct SecondStruct; -pub struct ThirdStruct; + pub struct SecondStruct; + pub struct ThirdStruct; } //- /main.rs crate:main deps:dep use dep::{FirstStruct, some_module::SecondStruct}; fn main() { -this<|> + this<|> } "#, r#" -use dep::{FirstStruct, some_module::SecondStruct}; +use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}}; fn main() { -ThirdStruct + ThirdStruct } "#, ); 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; #[derive(Clone, Debug, PartialEq, Eq)] pub struct CompletionConfig { pub enable_postfix_completions: bool, - pub enable_experimental_completions: bool, + pub disable_fuzzy_autoimports: bool, pub add_call_parenthesis: bool, pub add_call_argument_snippets: bool, pub snippet_cap: Option, @@ -52,7 +52,7 @@ impl Default for CompletionConfig { fn default() -> Self { CompletionConfig { enable_postfix_completions: true, - enable_experimental_completions: true, + disable_fuzzy_autoimports: false, add_call_parenthesis: true, add_call_argument_snippets: true, 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 { score: None, ref_match: None, import_to_add: None, - resolve_import_lazily: false, } } @@ -301,7 +300,6 @@ pub(crate) struct Builder { source_range: TextRange, completion_kind: CompletionKind, import_to_add: Option, - resolve_import_lazily: bool, label: String, insert_text: Option, insert_text_format: InsertTextFormat, @@ -339,25 +337,13 @@ impl Builder { } } - let mut text_edit = match self.text_edit { + let text_edit = match self.text_edit { Some(it) => it, None => { TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone())) } }; - let import_to_add = if self.resolve_import_lazily { - self.import_to_add - } else { - match apply_import_eagerly(self.import_to_add.as_ref(), &mut text_edit) { - Ok(()) => self.import_to_add, - Err(()) => { - log::error!("Failed to apply eager import edit: original edit and import edit intersect"); - None - } - } - }; - CompletionItem { source_range: self.source_range, label, @@ -372,7 +358,7 @@ impl Builder { trigger_call_info: self.trigger_call_info.unwrap_or(false), score: self.score, ref_match: self.ref_match, - import_to_add, + import_to_add: self.import_to_add, } } pub(crate) fn lookup_by(mut self, lookup: impl Into) -> Builder { @@ -435,13 +421,8 @@ impl Builder { self.trigger_call_info = Some(true); self } - pub(crate) fn add_import( - mut self, - import_to_add: Option, - resolve_import_lazily: bool, - ) -> Builder { + pub(crate) fn add_import(mut self, import_to_add: Option) -> Builder { self.import_to_add = import_to_add; - self.resolve_import_lazily = resolve_import_lazily; self } pub(crate) fn set_ref_match( @@ -453,16 +434,6 @@ impl Builder { } } -fn apply_import_eagerly( - import_to_add: Option<&ImportEdit>, - original_edit: &mut TextEdit, -) -> Result<(), ()> { - match import_to_add.and_then(|import_edit| import_edit.to_text_edit()) { - Some(import_edit) => original_edit.union(import_edit).map_err(|_| ()), - None => Ok(()), - } -} - impl<'a> Into for Builder { fn into(self) -> CompletionItem { 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::{ // } // ``` // -// And experimental completions, enabled with the `rust-analyzer.completion.enableExperimental` setting. +// And experimental completions, enabled with the `rust-analyzer.completion.disableFuzzyAutoimports` setting. // This flag enables or disables: // // - 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> { local_name, ) .kind(CompletionItemKind::UnresolvedReference) - .add_import( - import_to_add, - self.ctx.completion.config.resolve_additional_edits_lazily(), - ) + .add_import(import_to_add) .build(); return Some(item); } @@ -248,7 +245,7 @@ impl<'a> Render<'a> { let item = item .kind(kind) - .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily()) + .add_import(import_to_add) .set_documentation(docs) .set_ref_match(ref_match) .build(); @@ -449,28 +446,6 @@ fn main() { let _: m::Spam = S<|> } insert: "m", kind: Module, }, - CompletionItem { - label: "m::Spam", - source_range: 75..76, - text_edit: TextEdit { - indels: [ - Indel { - insert: "use m::Spam;", - delete: 0..0, - }, - Indel { - insert: "\n\n", - delete: 0..0, - }, - Indel { - insert: "Spam", - delete: 75..76, - }, - ], - }, - kind: Enum, - lookup: "Spam", - }, CompletionItem { label: "m::Spam::Foo", source_range: 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> { .kind(CompletionItemKind::EnumVariant) .set_documentation(self.variant.docs(self.ctx.db())) .set_deprecated(self.ctx.is_deprecated(self.variant)) - .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily()) + .add_import(import_to_add) .detail(self.detail()); 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> { .set_deprecated(self.ctx.is_deprecated(self.func)) .detail(self.detail()) .add_call_parens(self.ctx.completion, self.name, params) - .add_import(import_to_add, self.ctx.completion.config.resolve_additional_edits_lazily()) + .add_import(import_to_add) .build() } 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> { .kind(CompletionItemKind::Macro) .set_documentation(self.docs.clone()) .set_deprecated(self.ctx.is_deprecated(self.macro_)) - .add_import( - import_to_add, - self.ctx.completion.config.resolve_additional_edits_lazily(), - ) + .add_import(import_to_add) .detail(self.detail()); 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( .collect_tuple() .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions)); let mut actual = db.file_text(position.file_id).to_string(); - completion.text_edit().apply(&mut actual); + + let mut combined_edit = completion.text_edit().to_owned(); + if let Some(import_text_edit) = completion.import_to_add().and_then(|edit| edit.to_text_edit()) + { + combined_edit.union(import_text_edit).expect( + "Failed to apply completion resolve changes: change ranges overlap, but should not", + ) + } + + combined_edit.apply(&mut actual); assert_eq_text!(&ra_fixture_after, &actual) } -- cgit v1.2.3 From cbd3717f2c52b17aa9b15c2df4a364c62d17e4e1 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 8 Dec 2020 14:27:18 +0200 Subject: Better config name --- crates/completion/src/completions/unqualified_path.rs | 6 ++++-- crates/completion/src/config.rs | 4 ++-- crates/completion/src/lib.rs | 9 +++------ 3 files changed, 9 insertions(+), 10 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 2a315cb86..f65709adf 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 acc.add_resolution(ctx, name.to_string(), &res) }); - if !ctx.config.disable_fuzzy_autoimports && ctx.config.resolve_additional_edits_lazily() { + if ctx.config.enable_autoimport_completions && ctx.config.resolve_additional_edits_lazily() { fuzzy_completion(acc, ctx).unwrap_or_default() } } @@ -116,7 +116,9 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T // // .Feature toggle // -// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.disableFuzzyAutoimports` flag. +// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag. +// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding +// capability enabled. fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { let _p = profile::span("fuzzy_completion"); let potential_import_name = ctx.token.to_string(); diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index 8082ec9cb..5175b9d69 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs @@ -10,7 +10,7 @@ use rustc_hash::FxHashSet; #[derive(Clone, Debug, PartialEq, Eq)] pub struct CompletionConfig { pub enable_postfix_completions: bool, - pub disable_fuzzy_autoimports: bool, + pub enable_autoimport_completions: bool, pub add_call_parenthesis: bool, pub add_call_argument_snippets: bool, pub snippet_cap: Option, @@ -52,7 +52,7 @@ impl Default for CompletionConfig { fn default() -> Self { CompletionConfig { enable_postfix_completions: true, - disable_fuzzy_autoimports: false, + enable_autoimport_completions: true, add_call_parenthesis: true, add_call_argument_snippets: true, snippet_cap: Some(SnippetCap { _private: () }), diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index 8df9f00fe..f60f87243 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs @@ -73,12 +73,9 @@ pub use crate::{ // } // ``` // -// And experimental completions, enabled with the `rust-analyzer.completion.disableFuzzyAutoimports` setting. -// This flag enables or disables: -// -// - Auto import: additional completion options with automatic `use` import and options from all project importable items, matched for the input -// -// Experimental completions might cause issues with performance and completion list look. +// And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities. +// Those are the additional completion options with automatic `use` import and options from all project importable items, +// fuzzy matched agains the completion imput. /// Main entry point for completion. We run completion as a two-phase process. /// -- cgit v1.2.3 From bf24cb3e8db94a84fb4a24c407797ab6ff5ee109 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 8 Dec 2020 14:38:43 +0200 Subject: Tweak the search query params for better lookup speed --- .../completion/src/completions/unqualified_path.rs | 60 +++++++++++----------- 1 file changed, 29 insertions(+), 31 deletions(-) (limited to 'crates/completion/src') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index f65709adf..4e4e2b36f 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -99,7 +99,6 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T // // To avoid an excessive amount of the results returned, completion input is checked for inclusion in the identifiers only // (i.e. in `HashMap` in the `std::collections::HashMap` path), also not in the module indentifiers. -// It also avoids searching for any imports for inputs with their length less that 3 symbols. // // .Merge Behaviour // @@ -123,40 +122,39 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() let _p = profile::span("fuzzy_completion"); let potential_import_name = ctx.token.to_string(); - if potential_import_name.len() < 3 { - 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)?; - let possible_imports = - imports_locator::find_similar_imports(&ctx.sema, ctx.krate?, &potential_import_name, true) - .filter_map(|import_candidate| { - Some(match import_candidate { - Either::Left(module_def) => ( - current_module.find_use_path(ctx.db, module_def)?, - ScopeDef::ModuleDef(module_def), - ), - Either::Right(macro_def) => ( - current_module.find_use_path(ctx.db, macro_def)?, - ScopeDef::MacroDef(macro_def), - ), - }) - }) - .filter(|(mod_path, _)| mod_path.len() > 1) - .filter_map(|(import_path, definition)| { - render_resolution_with_import( - RenderContext::new(ctx), - ImportEdit { - import_path: import_path.clone(), - import_scope: import_scope.clone(), - merge_behaviour: ctx.config.merge, - }, - &definition, - ) - }); + let possible_imports = imports_locator::find_similar_imports( + &ctx.sema, + ctx.krate?, + Some(100), + &potential_import_name, + true, + ) + .filter_map(|import_candidate| { + Some(match import_candidate { + Either::Left(module_def) => { + (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def)) + } + Either::Right(macro_def) => { + (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def)) + } + }) + }) + .filter(|(mod_path, _)| mod_path.len() > 1) + .filter_map(|(import_path, definition)| { + render_resolution_with_import( + RenderContext::new(ctx), + ImportEdit { + import_path: import_path.clone(), + import_scope: import_scope.clone(), + merge_behaviour: ctx.config.merge, + }, + &definition, + ) + }); acc.add_all(possible_imports); Some(()) -- cgit v1.2.3