mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 14:13:58 +00:00
Auto merge of #14165 - Veykril:completion-item, r=Veykril
internal: Make CompletionItem more POD-like
This commit is contained in:
commit
b881deb66a
4 changed files with 70 additions and 106 deletions
|
@ -18,9 +18,10 @@ use crate::{
|
||||||
/// editor pop-up. It is basically a POD with various properties. To construct a
|
/// editor pop-up. It is basically a POD with various properties. To construct a
|
||||||
/// [`CompletionItem`], use [`Builder::new`] method and the [`Builder`] struct.
|
/// [`CompletionItem`], use [`Builder::new`] method and the [`Builder`] struct.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub struct CompletionItem {
|
pub struct CompletionItem {
|
||||||
/// Label in the completion pop up which identifies completion.
|
/// Label in the completion pop up which identifies completion.
|
||||||
label: SmolStr,
|
pub label: SmolStr,
|
||||||
/// Range of identifier that is being completed.
|
/// Range of identifier that is being completed.
|
||||||
///
|
///
|
||||||
/// It should be used primarily for UI, but we also use this to convert
|
/// It should be used primarily for UI, but we also use this to convert
|
||||||
|
@ -29,33 +30,33 @@ pub struct CompletionItem {
|
||||||
/// `source_range` must contain the completion offset. `text_edit` should
|
/// `source_range` must contain the completion offset. `text_edit` should
|
||||||
/// start with what `source_range` points to, or VSCode will filter out the
|
/// start with what `source_range` points to, or VSCode will filter out the
|
||||||
/// completion silently.
|
/// completion silently.
|
||||||
source_range: TextRange,
|
pub source_range: TextRange,
|
||||||
/// What happens when user selects this item.
|
/// What happens when user selects this item.
|
||||||
///
|
///
|
||||||
/// Typically, replaces `source_range` with new identifier.
|
/// Typically, replaces `source_range` with new identifier.
|
||||||
text_edit: TextEdit,
|
pub text_edit: TextEdit,
|
||||||
is_snippet: bool,
|
pub is_snippet: bool,
|
||||||
|
|
||||||
/// What item (struct, function, etc) are we completing.
|
/// What item (struct, function, etc) are we completing.
|
||||||
kind: CompletionItemKind,
|
pub kind: CompletionItemKind,
|
||||||
|
|
||||||
/// Lookup is used to check if completion item indeed can complete current
|
/// Lookup is used to check if completion item indeed can complete current
|
||||||
/// ident.
|
/// ident.
|
||||||
///
|
///
|
||||||
/// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it
|
/// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it
|
||||||
/// contains `bar` sub sequence), and `quux` will rejected.
|
/// contains `bar` sub sequence), and `quux` will rejected.
|
||||||
lookup: Option<SmolStr>,
|
pub lookup: Option<SmolStr>,
|
||||||
|
|
||||||
/// Additional info to show in the UI pop up.
|
/// Additional info to show in the UI pop up.
|
||||||
detail: Option<String>,
|
pub detail: Option<String>,
|
||||||
documentation: Option<Documentation>,
|
pub documentation: Option<Documentation>,
|
||||||
|
|
||||||
/// Whether this item is marked as deprecated
|
/// Whether this item is marked as deprecated
|
||||||
deprecated: bool,
|
pub deprecated: bool,
|
||||||
|
|
||||||
/// If completing a function call, ask the editor to show parameter popup
|
/// If completing a function call, ask the editor to show parameter popup
|
||||||
/// after completion.
|
/// after completion.
|
||||||
trigger_call_info: bool,
|
pub trigger_call_info: bool,
|
||||||
|
|
||||||
/// We use this to sort completion. Relevance records facts like "do the
|
/// We use this to sort completion. Relevance records facts like "do the
|
||||||
/// types align precisely?". We can't sort by relevances directly, they are
|
/// types align precisely?". We can't sort by relevances directly, they are
|
||||||
|
@ -64,36 +65,39 @@ pub struct CompletionItem {
|
||||||
/// Note that Relevance ignores fuzzy match score. We compute Relevance for
|
/// Note that Relevance ignores fuzzy match score. We compute Relevance for
|
||||||
/// all possible items, and then separately build an ordered completion list
|
/// all possible items, and then separately build an ordered completion list
|
||||||
/// based on relevance and fuzzy matching with the already typed identifier.
|
/// based on relevance and fuzzy matching with the already typed identifier.
|
||||||
relevance: CompletionRelevance,
|
pub relevance: CompletionRelevance,
|
||||||
|
|
||||||
/// Indicates that a reference or mutable reference to this variable is a
|
/// Indicates that a reference or mutable reference to this variable is a
|
||||||
/// possible match.
|
/// possible match.
|
||||||
ref_match: Option<(Mutability, TextSize)>,
|
// FIXME: We shouldn't expose Mutability here (that is HIR types at all), its fine for now though
|
||||||
|
// until we have more splitting completions in which case we should think about
|
||||||
|
// generalizing this. See https://github.com/rust-lang/rust-analyzer/issues/12571
|
||||||
|
pub ref_match: Option<(Mutability, TextSize)>,
|
||||||
|
|
||||||
/// The import data to add to completion's edits.
|
/// The import data to add to completion's edits.
|
||||||
import_to_add: SmallVec<[LocatedImport; 1]>,
|
pub import_to_add: SmallVec<[LocatedImport; 1]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use custom debug for CompletionItem to make snapshot tests more readable.
|
// We use custom debug for CompletionItem to make snapshot tests more readable.
|
||||||
impl fmt::Debug for CompletionItem {
|
impl fmt::Debug for CompletionItem {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut s = f.debug_struct("CompletionItem");
|
let mut s = f.debug_struct("CompletionItem");
|
||||||
s.field("label", &self.label()).field("source_range", &self.source_range());
|
s.field("label", &self.label).field("source_range", &self.source_range);
|
||||||
if self.text_edit().len() == 1 {
|
if self.text_edit.len() == 1 {
|
||||||
let atom = &self.text_edit().iter().next().unwrap();
|
let atom = &self.text_edit.iter().next().unwrap();
|
||||||
s.field("delete", &atom.delete);
|
s.field("delete", &atom.delete);
|
||||||
s.field("insert", &atom.insert);
|
s.field("insert", &atom.insert);
|
||||||
} else {
|
} else {
|
||||||
s.field("text_edit", &self.text_edit);
|
s.field("text_edit", &self.text_edit);
|
||||||
}
|
}
|
||||||
s.field("kind", &self.kind());
|
s.field("kind", &self.kind);
|
||||||
if self.lookup() != self.label() {
|
if self.lookup() != self.label {
|
||||||
s.field("lookup", &self.lookup());
|
s.field("lookup", &self.lookup());
|
||||||
}
|
}
|
||||||
if let Some(detail) = self.detail() {
|
if let Some(detail) = &self.detail {
|
||||||
s.field("detail", &detail);
|
s.field("detail", &detail);
|
||||||
}
|
}
|
||||||
if let Some(documentation) = self.documentation() {
|
if let Some(documentation) = &self.documentation {
|
||||||
s.field("documentation", &documentation);
|
s.field("documentation", &documentation);
|
||||||
}
|
}
|
||||||
if self.deprecated {
|
if self.deprecated {
|
||||||
|
@ -351,51 +355,11 @@ impl CompletionItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What user sees in pop-up in the UI.
|
|
||||||
pub fn label(&self) -> &str {
|
|
||||||
&self.label
|
|
||||||
}
|
|
||||||
pub fn source_range(&self) -> TextRange {
|
|
||||||
self.source_range
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn text_edit(&self) -> &TextEdit {
|
|
||||||
&self.text_edit
|
|
||||||
}
|
|
||||||
/// Whether `text_edit` is a snippet (contains `$0` markers).
|
|
||||||
pub fn is_snippet(&self) -> bool {
|
|
||||||
self.is_snippet
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Short one-line additional information, like a type
|
|
||||||
pub fn detail(&self) -> Option<&str> {
|
|
||||||
self.detail.as_deref()
|
|
||||||
}
|
|
||||||
/// A doc-comment
|
|
||||||
pub fn documentation(&self) -> Option<Documentation> {
|
|
||||||
self.documentation.clone()
|
|
||||||
}
|
|
||||||
/// What string is used for filtering.
|
/// What string is used for filtering.
|
||||||
pub fn lookup(&self) -> &str {
|
pub fn lookup(&self) -> &str {
|
||||||
self.lookup.as_deref().unwrap_or(&self.label)
|
self.lookup.as_deref().unwrap_or(&self.label)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> CompletionItemKind {
|
|
||||||
self.kind
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deprecated(&self) -> bool {
|
|
||||||
self.deprecated
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn relevance(&self) -> CompletionRelevance {
|
|
||||||
self.relevance
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trigger_call_info(&self) -> bool {
|
|
||||||
self.trigger_call_info
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ref_match(&self) -> Option<(String, text_edit::Indel, CompletionRelevance)> {
|
pub fn ref_match(&self) -> Option<(String, text_edit::Indel, CompletionRelevance)> {
|
||||||
// Relevance of the ref match should be the same as the original
|
// Relevance of the ref match should be the same as the original
|
||||||
// match, but with exact type match set because self.ref_match
|
// match, but with exact type match set because self.ref_match
|
||||||
|
@ -405,16 +369,12 @@ impl CompletionItem {
|
||||||
|
|
||||||
self.ref_match.map(|(mutability, offset)| {
|
self.ref_match.map(|(mutability, offset)| {
|
||||||
(
|
(
|
||||||
format!("&{}{}", mutability.as_keyword_for_ref(), self.label()),
|
format!("&{}{}", mutability.as_keyword_for_ref(), self.label),
|
||||||
text_edit::Indel::insert(offset, format!("&{}", mutability.as_keyword_for_ref())),
|
text_edit::Indel::insert(offset, format!("&{}", mutability.as_keyword_for_ref())),
|
||||||
relevance,
|
relevance,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn imports_to_add(&self) -> &[LocatedImport] {
|
|
||||||
&self.import_to_add
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper to make `CompletionItem`s.
|
/// A helper to make `CompletionItem`s.
|
||||||
|
|
|
@ -503,18 +503,18 @@ mod tests {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check_relevance_for_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) {
|
fn check_relevance_for_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) {
|
||||||
let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
|
let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
|
||||||
actual.retain(|it| kinds.contains(&it.kind()));
|
actual.retain(|it| kinds.contains(&it.kind));
|
||||||
actual.sort_by_key(|it| cmp::Reverse(it.relevance().score()));
|
actual.sort_by_key(|it| cmp::Reverse(it.relevance.score()));
|
||||||
check_relevance_(actual, expect);
|
check_relevance_(actual, expect);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check_relevance(ra_fixture: &str, expect: Expect) {
|
fn check_relevance(ra_fixture: &str, expect: Expect) {
|
||||||
let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
|
let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
|
||||||
actual.retain(|it| it.kind() != CompletionItemKind::Snippet);
|
actual.retain(|it| it.kind != CompletionItemKind::Snippet);
|
||||||
actual.retain(|it| it.kind() != CompletionItemKind::Keyword);
|
actual.retain(|it| it.kind != CompletionItemKind::Keyword);
|
||||||
actual.retain(|it| it.kind() != CompletionItemKind::BuiltinType);
|
actual.retain(|it| it.kind != CompletionItemKind::BuiltinType);
|
||||||
actual.sort_by_key(|it| cmp::Reverse(it.relevance().score()));
|
actual.sort_by_key(|it| cmp::Reverse(it.relevance.score()));
|
||||||
check_relevance_(actual, expect);
|
check_relevance_(actual, expect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,9 +525,9 @@ mod tests {
|
||||||
.flat_map(|it| {
|
.flat_map(|it| {
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
|
|
||||||
let tag = it.kind().tag();
|
let tag = it.kind.tag();
|
||||||
let relevance = display_relevance(it.relevance());
|
let relevance = display_relevance(it.relevance);
|
||||||
items.push(format!("{tag} {} {relevance}\n", it.label()));
|
items.push(format!("{tag} {} {relevance}\n", it.label));
|
||||||
|
|
||||||
if let Some((label, _indel, relevance)) = it.ref_match() {
|
if let Some((label, _indel, relevance)) = it.ref_match() {
|
||||||
let relevance = display_relevance(relevance);
|
let relevance = display_relevance(relevance);
|
||||||
|
|
|
@ -108,10 +108,10 @@ fn completion_list_with_config(
|
||||||
let items = get_all_items(config, ra_fixture, trigger_character);
|
let items = get_all_items(config, ra_fixture, trigger_character);
|
||||||
let items = items
|
let items = items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|it| it.kind() != CompletionItemKind::BuiltinType || it.label() == "u32")
|
.filter(|it| it.kind != CompletionItemKind::BuiltinType || it.label == "u32")
|
||||||
.filter(|it| include_keywords || it.kind() != CompletionItemKind::Keyword)
|
.filter(|it| include_keywords || it.kind != CompletionItemKind::Keyword)
|
||||||
.filter(|it| include_keywords || it.kind() != CompletionItemKind::Snippet)
|
.filter(|it| include_keywords || it.kind != CompletionItemKind::Snippet)
|
||||||
.sorted_by_key(|it| (it.kind(), it.label().to_owned(), it.detail().map(ToOwned::to_owned)))
|
.sorted_by_key(|it| (it.kind, it.label.clone(), it.detail.as_ref().map(ToOwned::to_owned)))
|
||||||
.collect();
|
.collect();
|
||||||
render_completion_list(items)
|
render_completion_list(items)
|
||||||
}
|
}
|
||||||
|
@ -138,8 +138,8 @@ pub(crate) fn do_completion_with_config(
|
||||||
) -> Vec<CompletionItem> {
|
) -> Vec<CompletionItem> {
|
||||||
get_all_items(config, code, None)
|
get_all_items(config, code, None)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|c| c.kind() == kind)
|
.filter(|c| c.kind == kind)
|
||||||
.sorted_by(|l, r| l.label().cmp(r.label()))
|
.sorted_by(|l, r| l.label.cmp(&r.label))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,18 +148,18 @@ fn render_completion_list(completions: Vec<CompletionItem>) -> String {
|
||||||
s.chars().count()
|
s.chars().count()
|
||||||
}
|
}
|
||||||
let label_width =
|
let label_width =
|
||||||
completions.iter().map(|it| monospace_width(it.label())).max().unwrap_or_default().min(22);
|
completions.iter().map(|it| monospace_width(&it.label)).max().unwrap_or_default().min(22);
|
||||||
completions
|
completions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|it| {
|
.map(|it| {
|
||||||
let tag = it.kind().tag();
|
let tag = it.kind.tag();
|
||||||
let var_name = format!("{tag} {}", it.label());
|
let var_name = format!("{tag} {}", it.label);
|
||||||
let mut buf = var_name;
|
let mut buf = var_name;
|
||||||
if let Some(detail) = it.detail() {
|
if let Some(detail) = it.detail {
|
||||||
let width = label_width.saturating_sub(monospace_width(it.label()));
|
let width = label_width.saturating_sub(monospace_width(&it.label));
|
||||||
format_to!(buf, "{:width$} {}", "", detail, width = width);
|
format_to!(buf, "{:width$} {}", "", detail, width = width);
|
||||||
}
|
}
|
||||||
if it.deprecated() {
|
if it.deprecated {
|
||||||
format_to!(buf, " DEPRECATED");
|
format_to!(buf, " DEPRECATED");
|
||||||
}
|
}
|
||||||
format_to!(buf, "\n");
|
format_to!(buf, "\n");
|
||||||
|
@ -191,13 +191,13 @@ pub(crate) fn check_edit_with_config(
|
||||||
.unwrap_or_else(|| panic!("can't find {what:?} completion in {completions:#?}"));
|
.unwrap_or_else(|| panic!("can't find {what:?} completion in {completions:#?}"));
|
||||||
let mut actual = db.file_text(position.file_id).to_string();
|
let mut actual = db.file_text(position.file_id).to_string();
|
||||||
|
|
||||||
let mut combined_edit = completion.text_edit().to_owned();
|
let mut combined_edit = completion.text_edit.clone();
|
||||||
|
|
||||||
resolve_completion_edits(
|
resolve_completion_edits(
|
||||||
&db,
|
&db,
|
||||||
&config,
|
&config,
|
||||||
position,
|
position,
|
||||||
completion.imports_to_add().iter().filter_map(|import_edit| {
|
completion.import_to_add.iter().filter_map(|import_edit| {
|
||||||
let import_path = &import_edit.import_path;
|
let import_path = &import_edit.import_path;
|
||||||
let import_name = import_path.segments().last()?;
|
let import_name = import_path.segments().last()?;
|
||||||
Some((import_path.to_string(), import_name.to_string()))
|
Some((import_path.to_string(), import_name.to_string()))
|
||||||
|
@ -225,7 +225,7 @@ pub(crate) fn get_all_items(
|
||||||
.map_or_else(Vec::default, Into::into);
|
.map_or_else(Vec::default, Into::into);
|
||||||
// validate
|
// validate
|
||||||
res.iter().for_each(|it| {
|
res.iter().for_each(|it| {
|
||||||
let sr = it.source_range();
|
let sr = it.source_range;
|
||||||
assert!(
|
assert!(
|
||||||
sr.contains_inclusive(position.offset),
|
sr.contains_inclusive(position.offset),
|
||||||
"source range {sr:?} does not contain the offset {:?} of the completion request: {it:?}",
|
"source range {sr:?} does not contain the offset {:?} of the completion request: {it:?}",
|
||||||
|
|
|
@ -212,7 +212,7 @@ pub(crate) fn completion_items(
|
||||||
tdpp: lsp_types::TextDocumentPositionParams,
|
tdpp: lsp_types::TextDocumentPositionParams,
|
||||||
items: Vec<CompletionItem>,
|
items: Vec<CompletionItem>,
|
||||||
) -> Vec<lsp_types::CompletionItem> {
|
) -> Vec<lsp_types::CompletionItem> {
|
||||||
let max_relevance = items.iter().map(|it| it.relevance().score()).max().unwrap_or_default();
|
let max_relevance = items.iter().map(|it| it.relevance.score()).max().unwrap_or_default();
|
||||||
let mut res = Vec::with_capacity(items.len());
|
let mut res = Vec::with_capacity(items.len());
|
||||||
for item in items {
|
for item in items {
|
||||||
completion_item(&mut res, config, line_index, &tdpp, max_relevance, item);
|
completion_item(&mut res, config, line_index, &tdpp, max_relevance, item);
|
||||||
|
@ -235,14 +235,17 @@ fn completion_item(
|
||||||
item: CompletionItem,
|
item: CompletionItem,
|
||||||
) {
|
) {
|
||||||
let insert_replace_support = config.insert_replace_support().then_some(tdpp.position);
|
let insert_replace_support = config.insert_replace_support().then_some(tdpp.position);
|
||||||
|
let ref_match = item.ref_match();
|
||||||
|
let lookup = item.lookup().to_string();
|
||||||
|
|
||||||
let mut additional_text_edits = Vec::new();
|
let mut additional_text_edits = Vec::new();
|
||||||
|
|
||||||
// LSP does not allow arbitrary edits in completion, so we have to do a
|
// LSP does not allow arbitrary edits in completion, so we have to do a
|
||||||
// non-trivial mapping here.
|
// non-trivial mapping here.
|
||||||
let text_edit = {
|
let text_edit = {
|
||||||
let mut text_edit = None;
|
let mut text_edit = None;
|
||||||
let source_range = item.source_range();
|
let source_range = item.source_range;
|
||||||
for indel in item.text_edit() {
|
for indel in item.text_edit {
|
||||||
if indel.delete.contains_range(source_range) {
|
if indel.delete.contains_range(source_range) {
|
||||||
// Extract this indel as the main edit
|
// Extract this indel as the main edit
|
||||||
text_edit = Some(if indel.delete == source_range {
|
text_edit = Some(if indel.delete == source_range {
|
||||||
|
@ -265,23 +268,23 @@ fn completion_item(
|
||||||
text_edit.unwrap()
|
text_edit.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let insert_text_format = item.is_snippet().then_some(lsp_types::InsertTextFormat::SNIPPET);
|
let insert_text_format = item.is_snippet.then_some(lsp_types::InsertTextFormat::SNIPPET);
|
||||||
let tags = item.deprecated().then(|| vec![lsp_types::CompletionItemTag::DEPRECATED]);
|
let tags = item.deprecated.then(|| vec![lsp_types::CompletionItemTag::DEPRECATED]);
|
||||||
let command = if item.trigger_call_info() && config.client_commands().trigger_parameter_hints {
|
let command = if item.trigger_call_info && config.client_commands().trigger_parameter_hints {
|
||||||
Some(command::trigger_parameter_hints())
|
Some(command::trigger_parameter_hints())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut lsp_item = lsp_types::CompletionItem {
|
let mut lsp_item = lsp_types::CompletionItem {
|
||||||
label: item.label().to_string(),
|
label: item.label.to_string(),
|
||||||
detail: item.detail().map(|it| it.to_string()),
|
detail: item.detail.map(|it| it.to_string()),
|
||||||
filter_text: Some(item.lookup().to_string()),
|
filter_text: Some(lookup),
|
||||||
kind: Some(completion_item_kind(item.kind())),
|
kind: Some(completion_item_kind(item.kind)),
|
||||||
text_edit: Some(text_edit),
|
text_edit: Some(text_edit),
|
||||||
additional_text_edits: Some(additional_text_edits),
|
additional_text_edits: Some(additional_text_edits),
|
||||||
documentation: item.documentation().map(documentation),
|
documentation: item.documentation.map(documentation),
|
||||||
deprecated: Some(item.deprecated()),
|
deprecated: Some(item.deprecated),
|
||||||
tags,
|
tags,
|
||||||
command,
|
command,
|
||||||
insert_text_format,
|
insert_text_format,
|
||||||
|
@ -295,12 +298,13 @@ fn completion_item(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
set_score(&mut lsp_item, max_relevance, item.relevance());
|
set_score(&mut lsp_item, max_relevance, item.relevance);
|
||||||
|
|
||||||
if config.completion().enable_imports_on_the_fly {
|
if config.completion().enable_imports_on_the_fly {
|
||||||
if let imports @ [_, ..] = item.imports_to_add() {
|
if !item.import_to_add.is_empty() {
|
||||||
let imports: Vec<_> = imports
|
let imports: Vec<_> = item
|
||||||
.iter()
|
.import_to_add
|
||||||
|
.into_iter()
|
||||||
.filter_map(|import_edit| {
|
.filter_map(|import_edit| {
|
||||||
let import_path = &import_edit.import_path;
|
let import_path = &import_edit.import_path;
|
||||||
let import_name = import_path.segments().last()?;
|
let import_name = import_path.segments().last()?;
|
||||||
|
@ -317,7 +321,7 @@ fn completion_item(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((label, indel, relevance)) = item.ref_match() {
|
if let Some((label, indel, relevance)) = ref_match {
|
||||||
let mut lsp_item_with_ref = lsp_types::CompletionItem { label, ..lsp_item.clone() };
|
let mut lsp_item_with_ref = lsp_types::CompletionItem { label, ..lsp_item.clone() };
|
||||||
lsp_item_with_ref
|
lsp_item_with_ref
|
||||||
.additional_text_edits
|
.additional_text_edits
|
||||||
|
|
Loading…
Reference in a new issue