internal: better factoring for to_proto::completion

One source completion can produce up to two lsp completions.
Additionally, `preselct` and `sort_text` are global properties of the
whole set of completions, so the right granularity here is to convert
many completions.

As a side-benefit, we no loger allocate intermediate vec.
This commit is contained in:
Aleksey Kladov 2021-07-04 14:08:33 +03:00
parent 108b56f354
commit f34762abb7
4 changed files with 98 additions and 85 deletions

View file

@ -22,12 +22,10 @@ use lsp_types::{
FoldingRangeParams, HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, FoldingRangeParams, HoverContents, Location, NumberOrString, Position, PrepareRenameResponse,
Range, RenameParams, SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, Range, RenameParams, SemanticTokensDeltaParams, SemanticTokensFullDeltaResult,
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
TextDocumentPositionParams, Url, WorkspaceEdit,
}; };
use project_model::TargetKind; use project_model::TargetKind;
use serde::{Deserialize, Serialize}; use serde_json::json;
use serde_json::{json, to_value};
use stdx::format_to; use stdx::format_to;
use syntax::{algo, ast, AstNode, TextRange, TextSize}; use syntax::{algo, ast, AstNode, TextRange, TextSize};
@ -764,23 +762,13 @@ pub(crate) fn handle_completion(
}; };
let line_index = snap.file_line_index(position.file_id)?; let line_index = snap.file_line_index(position.file_id)?;
let insert_replace_support = let items = to_proto::completion_items(
snap.config.insert_replace_support().then(|| text_document_position.position); snap.config.insert_replace_support(),
let items: Vec<CompletionItem> = items completion_config.enable_imports_on_the_fly,
.into_iter() &line_index,
.flat_map(|item| { text_document_position.clone(),
let mut new_completion_items = items.clone(),
to_proto::completion_item(insert_replace_support, &line_index, item.clone()); );
if completion_config.enable_imports_on_the_fly {
for new_item in &mut new_completion_items {
fill_resolve_data(&mut new_item.data, &item, &text_document_position);
}
}
new_completion_items
})
.collect();
let completion_list = lsp_types::CompletionList { is_incomplete: true, items }; let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
Ok(Some(completion_list.into())) Ok(Some(completion_list.into()))
@ -800,16 +788,13 @@ pub(crate) fn handle_completion_resolve(
.into()); .into());
} }
let resolve_data = match original_completion let data = match original_completion.data.take() {
.data Some(it) => it,
.take()
.map(serde_json::from_value::<CompletionResolveData>)
.transpose()?
{
Some(data) => data,
None => return Ok(original_completion), None => return Ok(original_completion),
}; };
let resolve_data: lsp_ext::CompletionResolveData = serde_json::from_value(data)?;
let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?; let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
let line_index = snap.file_line_index(file_id)?; let line_index = snap.file_line_index(file_id)?;
let offset = from_proto::offset(&line_index, resolve_data.position.position); let offset = from_proto::offset(&line_index, resolve_data.position.position);
@ -1760,29 +1745,3 @@ fn run_rustfmt(
Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text)))) Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text))))
} }
} }
#[derive(Debug, Serialize, Deserialize)]
struct CompletionResolveData {
position: lsp_types::TextDocumentPositionParams,
full_import_path: String,
imported_name: String,
}
fn fill_resolve_data(
resolve_data: &mut Option<serde_json::Value>,
item: &ide::CompletionItem,
position: &TextDocumentPositionParams,
) -> Option<()> {
let import_edit = item.import_to_add()?;
let import_path = &import_edit.import.import_path;
*resolve_data = Some(
to_value(CompletionResolveData {
position: position.to_owned(),
full_import_path: import_path.to_string(),
imported_name: import_path.segments().last()?.to_string(),
})
.unwrap(),
);
Some(())
}

View file

@ -499,3 +499,10 @@ pub enum WorkspaceSymbolSearchKind {
OnlyTypes, OnlyTypes,
AllSymbols, AllSymbols,
} }
#[derive(Debug, Serialize, Deserialize)]
pub struct CompletionResolveData {
pub position: lsp_types::TextDocumentPositionParams,
pub full_import_path: String,
pub imported_name: String,
}

View file

@ -197,11 +197,35 @@ pub(crate) fn snippet_text_edit_vec(
.collect() .collect()
} }
pub(crate) fn completion_item( pub(crate) fn completion_items(
insert_replace_support: Option<lsp_types::Position>, insert_replace_support: bool,
enable_imports_on_the_fly: bool,
line_index: &LineIndex, line_index: &LineIndex,
item: CompletionItem, tdpp: lsp_types::TextDocumentPositionParams,
items: Vec<CompletionItem>,
) -> Vec<lsp_types::CompletionItem> { ) -> Vec<lsp_types::CompletionItem> {
let mut res = Vec::with_capacity(items.len());
for item in items {
completion_item(
&mut res,
insert_replace_support,
enable_imports_on_the_fly,
line_index,
&tdpp,
item,
)
}
res
}
fn completion_item(
acc: &mut Vec<lsp_types::CompletionItem>,
insert_replace_support: bool,
enable_imports_on_the_fly: bool,
line_index: &LineIndex,
tdpp: &lsp_types::TextDocumentPositionParams,
item: CompletionItem,
) {
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
@ -211,6 +235,7 @@ pub(crate) fn completion_item(
let source_range = item.source_range(); let source_range = item.source_range();
for indel in item.text_edit().iter() { for indel in item.text_edit().iter() {
if indel.delete.contains_range(source_range) { if indel.delete.contains_range(source_range) {
let insert_replace_support = insert_replace_support.then(|| tdpp.position);
text_edit = Some(if indel.delete == source_range { text_edit = Some(if indel.delete == source_range {
self::completion_text_edit(line_index, insert_replace_support, indel.clone()) self::completion_text_edit(line_index, insert_replace_support, indel.clone())
} else { } else {
@ -253,8 +278,22 @@ pub(crate) fn completion_item(
lsp_item.command = Some(command::trigger_parameter_hints()); lsp_item.command = Some(command::trigger_parameter_hints());
} }
let mut res = match item.ref_match() { lsp_item.insert_text_format = Some(insert_text_format(item.insert_text_format()));
Some((mutability, relevance)) => { if enable_imports_on_the_fly {
if let Some(import_edit) = item.import_to_add() {
let import_path = &import_edit.import.import_path;
if let Some(import_name) = import_path.segments().last() {
let data = lsp_ext::CompletionResolveData {
position: tdpp.clone(),
full_import_path: import_path.to_string(),
imported_name: import_name.to_string(),
};
lsp_item.data = Some(to_value(data).unwrap());
}
}
}
if let Some((mutability, relevance)) = item.ref_match() {
let mut lsp_item_with_ref = lsp_item.clone(); let mut lsp_item_with_ref = lsp_item.clone();
set_score(&mut lsp_item_with_ref, relevance); set_score(&mut lsp_item_with_ref, relevance);
lsp_item_with_ref.label = lsp_item_with_ref.label =
@ -266,16 +305,11 @@ pub(crate) fn completion_item(
}; };
*new_text = format!("&{}{}", mutability.as_keyword_for_ref(), new_text); *new_text = format!("&{}{}", mutability.as_keyword_for_ref(), new_text);
} }
vec![lsp_item_with_ref, lsp_item]
} acc.push(lsp_item_with_ref);
None => vec![lsp_item],
}; };
for lsp_item in res.iter_mut() { acc.push(lsp_item);
lsp_item.insert_text_format = Some(insert_text_format(item.insert_text_format()));
}
return res;
fn set_score(res: &mut lsp_types::CompletionItem, relevance: CompletionRelevance) { fn set_score(res: &mut lsp_types::CompletionItem, relevance: CompletionRelevance) {
if relevance.is_relevant() { if relevance.is_relevant() {
@ -1179,7 +1213,9 @@ mod tests {
encoding: OffsetEncoding::Utf16, encoding: OffsetEncoding::Utf16,
}; };
let (analysis, file_id) = Analysis::from_single_file(text); let (analysis, file_id) = Analysis::from_single_file(text);
let completions: Vec<(String, Option<String>)> = analysis
let file_position = ide_db::base_db::FilePosition { file_id, offset };
let mut items = analysis
.completions( .completions(
&ide::CompletionConfig { &ide::CompletionConfig {
enable_postfix_completions: true, enable_postfix_completions: true,
@ -1196,15 +1232,26 @@ mod tests {
skip_glob_imports: true, skip_glob_imports: true,
}, },
}, },
ide_db::base_db::FilePosition { file_id, offset }, file_position,
) )
.unwrap() .unwrap()
.unwrap() .unwrap();
.into_iter() items.retain(|c| c.label().ends_with("arg"));
.filter(|c| c.label().ends_with("arg")) let items = completion_items(
.map(|c| completion_item(None, &line_index, c)) false,
.flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text))) false,
.collect(); &line_index,
lsp_types::TextDocumentPositionParams {
text_document: lsp_types::TextDocumentIdentifier {
uri: "file://main.rs".parse().unwrap(),
},
position: position(&line_index, file_position.offset),
},
items,
);
let items: Vec<(String, Option<String>)> =
items.into_iter().map(|c| (c.label, c.sort_text)).collect();
expect_test::expect![[r#" expect_test::expect![[r#"
[ [
( (
@ -1221,7 +1268,7 @@ mod tests {
), ),
] ]
"#]] "#]]
.assert_debug_eq(&completions); .assert_debug_eq(&items);
} }
#[test] #[test]

View file

@ -1,5 +1,5 @@
<!--- <!---
lsp_ext.rs hash: 3f2879db0013a72 lsp_ext.rs hash: 3b2931972b33198b
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: