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,
Range, RenameParams, SemanticTokensDeltaParams, SemanticTokensFullDeltaResult,
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier,
TextDocumentPositionParams, Url, WorkspaceEdit,
SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
};
use project_model::TargetKind;
use serde::{Deserialize, Serialize};
use serde_json::{json, to_value};
use serde_json::json;
use stdx::format_to;
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 insert_replace_support =
snap.config.insert_replace_support().then(|| text_document_position.position);
let items: Vec<CompletionItem> = items
.into_iter()
.flat_map(|item| {
let mut new_completion_items =
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 items = to_proto::completion_items(
snap.config.insert_replace_support(),
completion_config.enable_imports_on_the_fly,
&line_index,
text_document_position.clone(),
items.clone(),
);
let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
Ok(Some(completion_list.into()))
@ -800,16 +788,13 @@ pub(crate) fn handle_completion_resolve(
.into());
}
let resolve_data = match original_completion
.data
.take()
.map(serde_json::from_value::<CompletionResolveData>)
.transpose()?
{
Some(data) => data,
let data = match original_completion.data.take() {
Some(it) => it,
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 line_index = snap.file_line_index(file_id)?;
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))))
}
}
#[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,
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()
}
pub(crate) fn completion_item(
insert_replace_support: Option<lsp_types::Position>,
pub(crate) fn completion_items(
insert_replace_support: bool,
enable_imports_on_the_fly: bool,
line_index: &LineIndex,
item: CompletionItem,
tdpp: lsp_types::TextDocumentPositionParams,
items: Vec<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();
// 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();
for indel in item.text_edit().iter() {
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 {
self::completion_text_edit(line_index, insert_replace_support, indel.clone())
} else {
@ -253,29 +278,38 @@ pub(crate) fn completion_item(
lsp_item.command = Some(command::trigger_parameter_hints());
}
let mut res = match item.ref_match() {
Some((mutability, relevance)) => {
let mut lsp_item_with_ref = lsp_item.clone();
set_score(&mut lsp_item_with_ref, relevance);
lsp_item_with_ref.label =
format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
if let Some(it) = &mut lsp_item_with_ref.text_edit {
let new_text = match it {
lsp_types::CompletionTextEdit::Edit(it) => &mut it.new_text,
lsp_types::CompletionTextEdit::InsertAndReplace(it) => &mut it.new_text,
lsp_item.insert_text_format = Some(insert_text_format(item.insert_text_format()));
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(),
};
*new_text = format!("&{}{}", mutability.as_keyword_for_ref(), new_text);
lsp_item.data = Some(to_value(data).unwrap());
}
vec![lsp_item_with_ref, lsp_item]
}
None => vec![lsp_item],
};
for lsp_item in res.iter_mut() {
lsp_item.insert_text_format = Some(insert_text_format(item.insert_text_format()));
}
return res;
if let Some((mutability, relevance)) = item.ref_match() {
let mut lsp_item_with_ref = lsp_item.clone();
set_score(&mut lsp_item_with_ref, relevance);
lsp_item_with_ref.label =
format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
if let Some(it) = &mut lsp_item_with_ref.text_edit {
let new_text = match it {
lsp_types::CompletionTextEdit::Edit(it) => &mut it.new_text,
lsp_types::CompletionTextEdit::InsertAndReplace(it) => &mut it.new_text,
};
*new_text = format!("&{}{}", mutability.as_keyword_for_ref(), new_text);
}
acc.push(lsp_item_with_ref);
};
acc.push(lsp_item);
fn set_score(res: &mut lsp_types::CompletionItem, relevance: CompletionRelevance) {
if relevance.is_relevant() {
@ -1179,7 +1213,9 @@ mod tests {
encoding: OffsetEncoding::Utf16,
};
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(
&ide::CompletionConfig {
enable_postfix_completions: true,
@ -1196,15 +1232,26 @@ mod tests {
skip_glob_imports: true,
},
},
ide_db::base_db::FilePosition { file_id, offset },
file_position,
)
.unwrap()
.unwrap()
.into_iter()
.filter(|c| c.label().ends_with("arg"))
.map(|c| completion_item(None, &line_index, c))
.flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text)))
.collect();
.unwrap();
items.retain(|c| c.label().ends_with("arg"));
let items = completion_items(
false,
false,
&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#"
[
(
@ -1221,7 +1268,7 @@ mod tests {
),
]
"#]]
.assert_debug_eq(&completions);
.assert_debug_eq(&items);
}
#[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
need to adjust this doc as well and ping this issue: