Auto merge of #18167 - SomeoneToIgnore:fat-completions, r=Veykril

internal: Send less data during `textDocument/completion` if possible

Similar to https://github.com/rust-lang/rust-analyzer/pull/15522, stops sending extra data during `textDocument/completion` if that data was set in the client completions resolve capabilities, and sends those only during `completionItem/resolve` requests.
Currently, rust-analyzer sends back all fields (including potentially huge docs) for every completion item which might get large.

Same as the other one, this PR aims to keep the changes minimal and does not remove extra computations for such fields — instead, it just filters them out before sending to the client.

The PR omits primitive, boolean and integer, types such as `deprecated`, `preselect`, `insertTextFormat`, `insertTextMode`, etc.  AND `additionalTextEdits` — this one looks very dangerous to compute for each completion item (as the spec says we ought to if there's no corresponding resolve capabilities provided) due to the diff computations and the fact that this code had been in the resolution for some time.
It would be good to resolve this lazily too, please let me know if it's ok to do.

When tested with Zed which only defines `documentation` and `additionalTextEdits` in its client completion resolve capabilities, rust-analyzer starts to send almost 3 times less characters:

Request:
```json
{"jsonrpc":"2.0","id":104,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///Users/someonetoignore/work/rust-analyzer/crates/ide/src/inlay_hints.rs"},"position":{"line":90,"character":14},"context":{"triggerKind":1}}}
```

<img width="1338" alt="image" src="https://github.com/user-attachments/assets/104f19b5-7095-4fc1-b008-5d829623b2e2">

Before: 381944 characters
[before.json](https://github.com/user-attachments/files/17092385/before.json)

After: 140503 characters
[after.json](https://github.com/user-attachments/files/17092386/after.json)

After Zed's [patch](https://github.com/zed-industries/zed/pull/18212) to enable all resolving possible: 84452 characters
[after-after.json](https://github.com/user-attachments/files/17092755/after-after.json)
This commit is contained in:
bors 2024-09-30 08:36:54 +00:00
commit dfe6d503b8
11 changed files with 227 additions and 76 deletions

View file

@ -7,7 +7,7 @@
use hir::ImportPathConfig; use hir::ImportPathConfig;
use ide_db::{imports::insert_use::InsertUseConfig, SnippetCap}; use ide_db::{imports::insert_use::InsertUseConfig, SnippetCap};
use crate::snippet::Snippet; use crate::{snippet::Snippet, CompletionFieldsToResolve};
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct CompletionConfig { pub struct CompletionConfig {
@ -27,6 +27,7 @@ pub struct CompletionConfig {
pub prefer_absolute: bool, pub prefer_absolute: bool,
pub snippets: Vec<Snippet>, pub snippets: Vec<Snippet>,
pub limit: Option<usize>, pub limit: Option<usize>,
pub fields_to_resolve: CompletionFieldsToResolve,
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]

View file

@ -37,6 +37,31 @@ pub use crate::{
snippet::{Snippet, SnippetScope}, snippet::{Snippet, SnippetScope},
}; };
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct CompletionFieldsToResolve {
pub resolve_label_details: bool,
pub resolve_tags: bool,
pub resolve_detail: bool,
pub resolve_documentation: bool,
pub resolve_filter_text: bool,
pub resolve_text_edit: bool,
pub resolve_command: bool,
}
impl CompletionFieldsToResolve {
pub const fn empty() -> Self {
Self {
resolve_label_details: false,
resolve_tags: false,
resolve_detail: false,
resolve_documentation: false,
resolve_filter_text: false,
resolve_text_edit: false,
resolve_command: false,
}
}
}
//FIXME: split the following feature into fine-grained features. //FIXME: split the following feature into fine-grained features.
// Feature: Magic Completions // Feature: Magic Completions

View file

@ -37,8 +37,8 @@ use test_fixture::ChangeFixture;
use test_utils::assert_eq_text; use test_utils::assert_eq_text;
use crate::{ use crate::{
resolve_completion_edits, CallableSnippets, CompletionConfig, CompletionItem, resolve_completion_edits, CallableSnippets, CompletionConfig, CompletionFieldsToResolve,
CompletionItemKind, CompletionItem, CompletionItemKind,
}; };
/// Lots of basic item definitions /// Lots of basic item definitions
@ -84,6 +84,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
prefer_absolute: false, prefer_absolute: false,
snippets: Vec::new(), snippets: Vec::new(),
limit: None, limit: None,
fields_to_resolve: CompletionFieldsToResolve::empty(),
}; };
pub(crate) fn completion_list(ra_fixture: &str) -> String { pub(crate) fn completion_list(ra_fixture: &str) -> String {

View file

@ -119,8 +119,8 @@ pub use ide_assists::{
Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve, Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve,
}; };
pub use ide_completion::{ pub use ide_completion::{
CallableSnippets, CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, CallableSnippets, CompletionConfig, CompletionFieldsToResolve, CompletionItem,
Snippet, SnippetScope, CompletionItemKind, CompletionRelevance, Snippet, SnippetScope,
}; };
pub use ide_db::{ pub use ide_db::{
base_db::{Cancelled, CrateGraph, CrateId, FileChange, SourceRoot, SourceRootId}, base_db::{Cancelled, CrateGraph, CrateId, FileChange, SourceRoot, SourceRootId},

View file

@ -12,10 +12,10 @@ use std::{
use cfg::{CfgAtom, CfgDiff}; use cfg::{CfgAtom, CfgDiff};
use hir::Symbol; use hir::Symbol;
use ide::{ use ide::{
AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, AssistConfig, CallableSnippets, CompletionConfig, CompletionFieldsToResolve, DiagnosticsConfig,
GenericParameterHints, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, ExprFillDefaultMode, GenericParameterHints, HighlightConfig, HighlightRelatedConfig,
InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig,
MemoryLayoutHoverRenderKind, Snippet, SnippetScope, SourceRootId, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, SourceRootId,
}; };
use ide_db::{ use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
@ -1393,6 +1393,7 @@ impl Config {
} }
pub fn completion(&self, source_root: Option<SourceRootId>) -> CompletionConfig { pub fn completion(&self, source_root: Option<SourceRootId>) -> CompletionConfig {
let client_capability_fields = self.completion_resolve_support_properties();
CompletionConfig { CompletionConfig {
enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(), enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(),
enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned() enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned()
@ -1417,6 +1418,15 @@ impl Config {
limit: self.completion_limit(source_root).to_owned(), limit: self.completion_limit(source_root).to_owned(),
enable_term_search: self.completion_termSearch_enable(source_root).to_owned(), enable_term_search: self.completion_termSearch_enable(source_root).to_owned(),
term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64, term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64,
fields_to_resolve: CompletionFieldsToResolve {
resolve_label_details: client_capability_fields.contains("labelDetails"),
resolve_tags: client_capability_fields.contains("tags"),
resolve_detail: client_capability_fields.contains("detail"),
resolve_documentation: client_capability_fields.contains("documentation"),
resolve_filter_text: client_capability_fields.contains("filterText"),
resolve_text_edit: client_capability_fields.contains("textEdit"),
resolve_command: client_capability_fields.contains("command"),
},
} }
} }

View file

@ -10,9 +10,9 @@ use std::{
use anyhow::Context; use anyhow::Context;
use ide::{ use ide::{
AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange, AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve,
HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, FilePosition, FileRange, HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query,
Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, RangeInfo, ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
}; };
use ide_db::SymbolKind; use ide_db::SymbolKind;
use itertools::Itertools; use itertools::Itertools;
@ -1019,9 +1019,11 @@ pub(crate) fn handle_completion(
let items = to_proto::completion_items( let items = to_proto::completion_items(
&snap.config, &snap.config,
&completion_config.fields_to_resolve,
&line_index, &line_index,
snap.file_version(position.file_id), snap.file_version(position.file_id),
text_document_position, text_document_position,
completion_trigger_character,
items, items,
); );
@ -1054,36 +1056,70 @@ pub(crate) fn handle_completion_resolve(
}; };
let source_root = snap.analysis.source_root_id(file_id)?; let source_root = snap.analysis.source_root_id(file_id)?;
let additional_edits = snap let mut forced_resolve_completions_config = snap.config.completion(Some(source_root));
.analysis forced_resolve_completions_config.fields_to_resolve = CompletionFieldsToResolve::empty();
.resolve_completion_edits(
&snap.config.completion(Some(source_root)),
FilePosition { file_id, offset },
resolve_data
.imports
.into_iter()
.map(|import| (import.full_import_path, import.imported_name)),
)?
.into_iter()
.flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
.collect::<Vec<_>>();
if !all_edits_are_disjoint(&original_completion, &additional_edits) { let position = FilePosition { file_id, offset };
return Err(LspError::new( let Some(resolved_completions) = snap.analysis.completions(
ErrorCode::InternalError as i32, &forced_resolve_completions_config,
"Import edit overlaps with the original completion edits, this is not LSP-compliant" position,
.into(), resolve_data.trigger_character,
) )?
.into()); else {
return Ok(original_completion);
};
let resolved_completions = to_proto::completion_items(
&snap.config,
&forced_resolve_completions_config.fields_to_resolve,
&line_index,
snap.file_version(position.file_id),
resolve_data.position,
resolve_data.trigger_character,
resolved_completions,
);
let Some(mut resolved_completion) = resolved_completions.into_iter().find(|completion| {
completion.label == original_completion.label
&& completion.kind == original_completion.kind
&& completion.deprecated == original_completion.deprecated
&& completion.preselect == original_completion.preselect
&& completion.sort_text == original_completion.sort_text
}) else {
return Ok(original_completion);
};
if !resolve_data.imports.is_empty() {
let additional_edits = snap
.analysis
.resolve_completion_edits(
&forced_resolve_completions_config,
position,
resolve_data
.imports
.into_iter()
.map(|import| (import.full_import_path, import.imported_name)),
)?
.into_iter()
.flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
.collect::<Vec<_>>();
if !all_edits_are_disjoint(&resolved_completion, &additional_edits) {
return Err(LspError::new(
ErrorCode::InternalError as i32,
"Import edit overlaps with the original completion edits, this is not LSP-compliant"
.into(),
)
.into());
}
if let Some(original_additional_edits) = resolved_completion.additional_text_edits.as_mut()
{
original_additional_edits.extend(additional_edits)
} else {
resolved_completion.additional_text_edits = Some(additional_edits);
}
} }
if let Some(original_additional_edits) = original_completion.additional_text_edits.as_mut() { Ok(resolved_completion)
original_additional_edits.extend(additional_edits)
} else {
original_completion.additional_text_edits = Some(additional_edits);
}
Ok(original_completion)
} }
pub(crate) fn handle_folding_range( pub(crate) fn handle_folding_range(

View file

@ -12,7 +12,8 @@
use hir::ChangeWithProcMacros; use hir::ChangeWithProcMacros;
use ide::{ use ide::{
AnalysisHost, CallableSnippets, CompletionConfig, DiagnosticsConfig, FilePosition, TextSize, AnalysisHost, CallableSnippets, CompletionConfig, CompletionFieldsToResolve, DiagnosticsConfig,
FilePosition, TextSize,
}; };
use ide_db::{ use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig}, imports::insert_use::{ImportGranularity, InsertUseConfig},
@ -172,6 +173,7 @@ fn integrated_completion_benchmark() {
snippets: Vec::new(), snippets: Vec::new(),
limit: None, limit: None,
add_semicolon_to_unit: true, add_semicolon_to_unit: true,
fields_to_resolve: CompletionFieldsToResolve::empty(),
}; };
let position = let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@ -219,6 +221,7 @@ fn integrated_completion_benchmark() {
snippets: Vec::new(), snippets: Vec::new(),
limit: None, limit: None,
add_semicolon_to_unit: true, add_semicolon_to_unit: true,
fields_to_resolve: CompletionFieldsToResolve::empty(),
}; };
let position = let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@ -264,6 +267,7 @@ fn integrated_completion_benchmark() {
snippets: Vec::new(), snippets: Vec::new(),
limit: None, limit: None,
add_semicolon_to_unit: true, add_semicolon_to_unit: true,
fields_to_resolve: CompletionFieldsToResolve::empty(),
}; };
let position = let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };

View file

@ -448,7 +448,7 @@ impl ClientCapabilities {
.unwrap_or_default() .unwrap_or_default()
} }
pub fn inlay_hint_resolve_support_properties(&self) -> FxHashSet<String> { pub fn inlay_hint_resolve_support_properties(&self) -> FxHashSet<&str> {
self.0 self.0
.text_document .text_document
.as_ref() .as_ref()
@ -457,8 +457,22 @@ impl ClientCapabilities {
.map(|inlay_resolve| inlay_resolve.properties.iter()) .map(|inlay_resolve| inlay_resolve.properties.iter())
.into_iter() .into_iter()
.flatten() .flatten()
.cloned() .map(|s| s.as_str())
.collect::<FxHashSet<_>>() .collect()
}
pub fn completion_resolve_support_properties(&self) -> FxHashSet<&str> {
self.0
.text_document
.as_ref()
.and_then(|text| text.completion.as_ref())
.and_then(|completion_caps| completion_caps.completion_item.as_ref())
.and_then(|completion_item_caps| completion_item_caps.resolve_support.as_ref())
.map(|resolve_support| resolve_support.properties.iter())
.into_iter()
.flatten()
.map(|s| s.as_str())
.collect()
} }
pub fn hover_markdown_support(&self) -> bool { pub fn hover_markdown_support(&self) -> bool {

View file

@ -825,6 +825,7 @@ pub struct CompletionResolveData {
pub position: lsp_types::TextDocumentPositionParams, pub position: lsp_types::TextDocumentPositionParams,
pub imports: Vec<CompletionImport>, pub imports: Vec<CompletionImport>,
pub version: Option<i32>, pub version: Option<i32>,
pub trigger_character: Option<char>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]

View file

@ -6,9 +6,9 @@ use std::{
}; };
use ide::{ use ide::{
Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem, Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionFieldsToResolve,
CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange,
Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel,
InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup,
NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp,
SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
@ -227,9 +227,11 @@ pub(crate) fn snippet_text_edit_vec(
pub(crate) fn completion_items( pub(crate) fn completion_items(
config: &Config, config: &Config,
fields_to_resolve: &CompletionFieldsToResolve,
line_index: &LineIndex, line_index: &LineIndex,
version: Option<i32>, version: Option<i32>,
tdpp: lsp_types::TextDocumentPositionParams, tdpp: lsp_types::TextDocumentPositionParams,
completion_trigger_character: Option<char>,
mut items: Vec<CompletionItem>, mut items: Vec<CompletionItem>,
) -> Vec<lsp_types::CompletionItem> { ) -> Vec<lsp_types::CompletionItem> {
if config.completion_hide_deprecated() { if config.completion_hide_deprecated() {
@ -239,7 +241,17 @@ pub(crate) fn completion_items(
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, version, &tdpp, max_relevance, item); completion_item(
&mut res,
config,
fields_to_resolve,
line_index,
version,
&tdpp,
max_relevance,
completion_trigger_character,
item,
);
} }
if let Some(limit) = config.completion(None).limit { if let Some(limit) = config.completion(None).limit {
@ -253,21 +265,33 @@ pub(crate) fn completion_items(
fn completion_item( fn completion_item(
acc: &mut Vec<lsp_types::CompletionItem>, acc: &mut Vec<lsp_types::CompletionItem>,
config: &Config, config: &Config,
fields_to_resolve: &CompletionFieldsToResolve,
line_index: &LineIndex, line_index: &LineIndex,
version: Option<i32>, version: Option<i32>,
tdpp: &lsp_types::TextDocumentPositionParams, tdpp: &lsp_types::TextDocumentPositionParams,
max_relevance: u32, max_relevance: u32,
completion_trigger_character: Option<char>,
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 ref_match = item.ref_match();
let lookup = item.lookup().to_owned();
let mut additional_text_edits = Vec::new(); let mut additional_text_edits = Vec::new();
let mut something_to_resolve = false;
// LSP does not allow arbitrary edits in completion, so we have to do a let filter_text = if fields_to_resolve.resolve_filter_text {
// non-trivial mapping here. something_to_resolve = !item.lookup().is_empty();
let text_edit = { None
} else {
Some(item.lookup().to_owned())
};
let text_edit = if fields_to_resolve.resolve_text_edit {
something_to_resolve = true;
None
} else {
// LSP does not allow arbitrary edits in completion, so we have to do a
// non-trivial mapping here.
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 {
@ -290,25 +314,49 @@ fn completion_item(
additional_text_edits.push(text_edit); additional_text_edits.push(text_edit);
} }
} }
text_edit.unwrap() Some(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 = if fields_to_resolve.resolve_tags {
something_to_resolve = item.deprecated;
None
} else {
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()) if fields_to_resolve.resolve_command {
something_to_resolve = true;
None
} else {
Some(command::trigger_parameter_hints())
}
} else { } else {
None None
}; };
let detail = if fields_to_resolve.resolve_detail {
something_to_resolve = item.detail.is_some();
None
} else {
item.detail
};
let documentation = if fields_to_resolve.resolve_documentation {
something_to_resolve = item.documentation.is_some();
None
} else {
item.documentation.map(documentation)
};
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, detail,
filter_text: Some(lookup), filter_text,
kind: Some(completion_item_kind(item.kind)), kind: Some(completion_item_kind(item.kind)),
text_edit: Some(text_edit), text_edit,
additional_text_edits: Some(additional_text_edits), additional_text_edits: Some(additional_text_edits),
documentation: item.documentation.map(documentation), documentation,
deprecated: Some(item.deprecated), deprecated: Some(item.deprecated),
tags, tags,
command, command,
@ -317,29 +365,40 @@ fn completion_item(
}; };
if config.completion_label_details_support() { if config.completion_label_details_support() {
lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails { if fields_to_resolve.resolve_label_details {
detail: item.label_detail.as_ref().map(ToString::to_string), something_to_resolve = true;
description: lsp_item.detail.clone(), } else {
}); lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails {
detail: item.label_detail.as_ref().map(ToString::to_string),
description: lsp_item.detail.clone(),
});
}
} else if let Some(label_detail) = item.label_detail { } else if let Some(label_detail) = item.label_detail {
lsp_item.label.push_str(label_detail.as_str()); lsp_item.label.push_str(label_detail.as_str());
} }
set_score(&mut lsp_item, max_relevance, item.relevance); set_score(&mut lsp_item, max_relevance, item.relevance);
if config.completion(None).enable_imports_on_the_fly && !item.import_to_add.is_empty() { let imports =
let imports = item if config.completion(None).enable_imports_on_the_fly && !item.import_to_add.is_empty() {
.import_to_add item.import_to_add
.into_iter() .into_iter()
.map(|(import_path, import_name)| lsp_ext::CompletionImport { .map(|(import_path, import_name)| lsp_ext::CompletionImport {
full_import_path: import_path, full_import_path: import_path,
imported_name: import_name, imported_name: import_name,
}) })
.collect::<Vec<_>>(); .collect()
if !imports.is_empty() { } else {
let data = lsp_ext::CompletionResolveData { position: tdpp.clone(), imports, version }; Vec::new()
lsp_item.data = Some(to_value(data).unwrap()); };
} if something_to_resolve || !imports.is_empty() {
let data = lsp_ext::CompletionResolveData {
position: tdpp.clone(),
imports,
version,
trigger_character: completion_trigger_character,
};
lsp_item.data = Some(to_value(data).unwrap());
} }
if let Some((label, indel, relevance)) = ref_match { if let Some((label, indel, relevance)) = ref_match {

View file

@ -1,5 +1,5 @@
<!--- <!---
lsp/ext.rs hash: 6292ee8d88d4c9ec lsp/ext.rs hash: 90cf7718d54fe3c2
If you need to change the above hash to make the test pass, please check if you If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue: need to adjust this doc as well and ping this issue: