Better support client completion resolve caps

This commit is contained in:
Kirill Bulatov
2020-12-02 01:02:15 +02:00
parent 47464e556c
commit 2a7be4afb0
5 changed files with 118 additions and 103 deletions

View File

@@ -5,14 +5,12 @@
use std::{
io::Write as _,
process::{self, Stdio},
sync::Arc,
};
use ide::{
FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, ImportToAdd, LineIndex,
NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit,
FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query,
RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit,
};
use ide_db::helpers::{insert_use, mod_path_to_ast};
use itertools::Itertools;
use lsp_server::ErrorCode;
use lsp_types::{
@@ -36,10 +34,10 @@ use crate::{
cargo_target_spec::CargoTargetSpec,
config::RustfmtConfig,
from_json, from_proto,
global_state::{GlobalState, GlobalStateSnapshot},
line_endings::LineEndings,
global_state::{CompletionResolveData, GlobalState, GlobalStateSnapshot},
lsp_ext::{self, InlayHint, InlayHintsParams},
to_proto, LspError, Result,
to_proto::{self, append_import_edits},
LspError, Result,
};
pub(crate) fn handle_analyzer_status(
@@ -538,12 +536,6 @@ pub(crate) fn handle_runnables(
Ok(res)
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub(crate) struct ResolveCompletionData {
completion_id: usize,
completion_file_id: u32,
}
pub(crate) fn handle_completion(
global_state: &mut GlobalState,
params: lsp_types::CompletionParams,
@@ -579,38 +571,31 @@ pub(crate) fn handle_completion(
};
let line_index = snap.analysis.file_line_index(position.file_id)?;
let line_endings = snap.file_line_endings(position.file_id);
let mut additional_imports = FxHashMap::default();
let mut completion_resolve_data = FxHashMap::default();
let items: Vec<CompletionItem> = items
.into_iter()
.enumerate()
.flat_map(|(item_index, item)| {
let resolve_completion_data = ResolveCompletionData {
completion_id: item_index,
completion_file_id: position.file_id.0,
};
let import_to_add = item.import_to_add().cloned();
let mut new_completion_items =
to_proto::completion_item(&line_index, line_endings, item);
let mut new_completion_items = to_proto::completion_item(
&line_index,
line_endings,
item.clone(),
&snap.config.completion.resolve_capabilities,
);
if let Some(import_to_add) = import_to_add {
for new_item in &mut new_completion_items {
match serde_json::to_value(&resolve_completion_data) {
Ok(resolve_value) => {
new_item.data = Some(resolve_value);
additional_imports.insert(item_index, import_to_add.clone());
}
Err(e) => {
log::error!("Failed to serialize completion resolve metadata: {}", e)
}
}
}
let item_id = serde_json::to_value(&item_index)
.expect(&format!("Should be able to serialize usize value {}", item_index));
completion_resolve_data
.insert(item_index, CompletionResolveData { file_id: position.file_id, item });
for new_item in &mut new_completion_items {
new_item.data = Some(item_id.clone());
}
new_completion_items
})
.collect();
global_state.additional_imports = additional_imports;
global_state.completion_resolve_data = completion_resolve_data;
let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
Ok(Some(completion_list.into()))
@@ -622,73 +607,40 @@ pub(crate) fn handle_resolve_completion(
) -> Result<lsp_types::CompletionItem> {
let _p = profile::span("handle_resolve_completion");
match original_completion.data.as_ref() {
Some(completion_data) => {
match serde_json::from_value::<ResolveCompletionData>(completion_data.clone()) {
Ok(resolve_completion_data) => {
if let Some(import_to_add) =
global_state.additional_imports.get(&resolve_completion_data.completion_id)
{
let snap = global_state.snapshot();
let file_id = FileId(resolve_completion_data.completion_file_id);
let line_index = snap.analysis.file_line_index(file_id)?;
let line_endings = snap.file_line_endings(file_id);
let server_completion_data = match original_completion
.data
.as_ref()
.map(|data| serde_json::from_value::<usize>(data.clone()))
.transpose()?
.and_then(|server_completion_id| {
global_state.completion_resolve_data.get(&server_completion_id)
}) {
Some(data) => data,
None => return Ok(original_completion),
};
let resolved_edits =
resolve_additional_edits(import_to_add, line_index, line_endings);
original_completion.additional_text_edits =
match original_completion.additional_text_edits {
Some(mut original_additional_edits) => {
if let Some(mut new_edits) = resolved_edits {
original_additional_edits.extend(new_edits.drain(..))
}
Some(original_additional_edits)
}
None => resolved_edits,
};
} else {
log::error!(
"Got no import data for completion with label {}, id {}",
original_completion.label,
resolve_completion_data.completion_id
)
}
let snap = &global_state.snapshot();
for supported_completion_resolve_cap in &snap.config.completion.resolve_capabilities {
match supported_completion_resolve_cap {
ide::CompletionResolveCapability::AdditionalTextEdits => {
// TODO kb actually add all additional edits here?
if let Some(import_to_add) = server_completion_data.item.import_to_add() {
append_import_edits(
&mut original_completion,
import_to_add,
snap.analysis.file_line_index(server_completion_data.file_id)?.as_ref(),
snap.file_line_endings(server_completion_data.file_id),
);
}
Err(e) => log::error!("Failed to deserialize completion resolve metadata: {}", e),
}
// TODO kb calculate the rest also?
_ => {}
}
None => (),
}
Ok(original_completion)
}
// TODO kb what to do when no resolve is available on the client?
fn resolve_additional_edits(
import_to_add: &ImportToAdd,
line_index: Arc<LineIndex>,
line_endings: LineEndings,
) -> Option<Vec<lsp_types::TextEdit>> {
let _p = profile::span("resolve_additional_edits");
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,
);
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);
let text_edit = import_insert.finish();
Some(
text_edit
.into_iter()
.map(|indel| to_proto::text_edit(&line_index, line_endings, indel))
.collect_vec(),
)
}
pub(crate) fn handle_folding_range(
snap: GlobalStateSnapshot,
params: FoldingRangeParams,