Skip to content

Commit 9a4daff

Browse files
Resolve import inserts better
1 parent a539267 commit 9a4daff

File tree

2 files changed

+91
-38
lines changed

2 files changed

+91
-38
lines changed

crates/rust-analyzer/src/global_state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ pub(crate) struct GlobalState {
6969
pub(crate) config: Config,
7070
pub(crate) analysis_host: AnalysisHost,
7171
pub(crate) diagnostics: DiagnosticCollection,
72-
pub(crate) additional_imports: FxHashMap<String, ImportToAdd>,
72+
pub(crate) additional_imports: FxHashMap<usize, ImportToAdd>,
7373
pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
7474
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
7575
pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,

crates/rust-analyzer/src/handlers.rs

Lines changed: 90 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
use std::{
66
io::Write as _,
77
process::{self, Stdio},
8+
sync::Arc,
89
};
910

1011
use ide::{
11-
FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query,
12-
RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit,
12+
FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, ImportToAdd, LineIndex,
13+
NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit,
1314
};
1415
use ide_db::helpers::{insert_use, mod_path_to_ast};
1516
use itertools::Itertools;
@@ -36,6 +37,7 @@ use crate::{
3637
config::RustfmtConfig,
3738
from_json, from_proto,
3839
global_state::{GlobalState, GlobalStateSnapshot},
40+
line_endings::LineEndings,
3941
lsp_ext::{self, InlayHint, InlayHintsParams},
4042
to_proto, LspError, Result,
4143
};
@@ -536,6 +538,12 @@ pub(crate) fn handle_runnables(
536538
Ok(res)
537539
}
538540

541+
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
542+
pub(crate) struct ResolveCompletionData {
543+
completion_id: usize,
544+
completion_file_id: u32,
545+
}
546+
539547
pub(crate) fn handle_completion(
540548
global_state: &mut GlobalState,
541549
params: lsp_types::CompletionParams,
@@ -575,20 +583,31 @@ pub(crate) fn handle_completion(
575583

576584
let items: Vec<CompletionItem> = items
577585
.into_iter()
578-
.flat_map(|item| {
586+
.enumerate()
587+
.flat_map(|(item_index, item)| {
588+
let resolve_completion_data = ResolveCompletionData {
589+
completion_id: item_index,
590+
completion_file_id: position.file_id.0,
591+
};
579592
let import_to_add = item.import_to_add().cloned();
580-
let new_completion_items = to_proto::completion_item(&line_index, line_endings, item);
593+
let mut new_completion_items =
594+
to_proto::completion_item(&line_index, line_endings, item);
595+
581596
if let Some(import_to_add) = import_to_add {
582-
for new_item in &new_completion_items {
583-
additional_imports.insert(new_item.label.clone(), import_to_add.clone());
597+
for new_item in &mut new_completion_items {
598+
match serde_json::to_value(&resolve_completion_data) {
599+
Ok(resolve_value) => {
600+
new_item.data = Some(resolve_value);
601+
additional_imports.insert(item_index, import_to_add.clone());
602+
}
603+
Err(e) => {
604+
log::error!("Failed to serialize completion resolve metadata: {}", e)
605+
}
606+
}
584607
}
585608
}
586609
new_completion_items
587610
})
588-
.map(|mut item| {
589-
item.data = Some(position.file_id.0.into());
590-
item
591-
})
592611
.collect();
593612

594613
global_state.additional_imports = additional_imports;
@@ -601,41 +620,75 @@ pub(crate) fn handle_resolve_completion(
601620
global_state: &mut GlobalState,
602621
mut original_completion: lsp_types::CompletionItem,
603622
) -> Result<lsp_types::CompletionItem> {
604-
// TODO kb slow, takes over 130ms
605623
let _p = profile::span("handle_resolve_completion");
606624

607-
if let Some(import_data) =
608-
global_state.additional_imports.get(dbg!(original_completion.label.as_str()))
609-
{
610-
let rewriter = insert_use::insert_use(
611-
&import_data.import_scope,
612-
mod_path_to_ast(&import_data.import_path),
613-
import_data.merge_behaviour,
614-
);
615-
if let Some((old_ast, file_id)) =
616-
// TODO kb for file_id, better use &str and then cast to u32?
617-
rewriter
618-
.rewrite_root()
619-
.zip(original_completion.data.as_ref().and_then(|value| Some(value.as_u64()? as u32)))
620-
{
621-
let snap = global_state.snapshot();
622-
let mut import_insert = TextEdit::builder();
623-
algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
624-
let line_index = snap.analysis.file_line_index(FileId(file_id))?;
625-
let line_endings = snap.file_line_endings(FileId(file_id));
626-
let text_edit = import_insert.finish();
627-
628-
let mut new_edits = original_completion.additional_text_edits.unwrap_or_default();
629-
for indel in text_edit {
630-
new_edits.push(to_proto::text_edit(&line_index, line_endings, indel));
625+
match original_completion.data.as_ref() {
626+
Some(completion_data) => {
627+
match serde_json::from_value::<ResolveCompletionData>(completion_data.clone()) {
628+
Ok(resolve_completion_data) => {
629+
if let Some(import_to_add) =
630+
global_state.additional_imports.get(&resolve_completion_data.completion_id)
631+
{
632+
let snap = global_state.snapshot();
633+
let file_id = FileId(resolve_completion_data.completion_file_id);
634+
let line_index = snap.analysis.file_line_index(file_id)?;
635+
let line_endings = snap.file_line_endings(file_id);
636+
637+
let resolved_edits =
638+
resolve_additional_edits(import_to_add, line_index, line_endings);
639+
640+
original_completion.additional_text_edits =
641+
match original_completion.additional_text_edits {
642+
Some(mut original_additional_edits) => {
643+
if let Some(mut new_edits) = resolved_edits {
644+
original_additional_edits.extend(new_edits.drain(..))
645+
}
646+
Some(original_additional_edits)
647+
}
648+
None => resolved_edits,
649+
};
650+
} else {
651+
log::error!(
652+
"Got no import data for completion with label {}, id {}",
653+
original_completion.label,
654+
resolve_completion_data.completion_id
655+
)
656+
}
657+
}
658+
Err(e) => log::error!("Failed to deserialize completion resolve metadata: {}", e),
631659
}
632-
original_completion.additional_text_edits = Some(new_edits);
633660
}
661+
None => (),
634662
}
635-
636663
Ok(original_completion)
637664
}
638665

666+
// TODO kb what to do when no resolve is available on the client?
667+
fn resolve_additional_edits(
668+
import_to_add: &ImportToAdd,
669+
line_index: Arc<LineIndex>,
670+
line_endings: LineEndings,
671+
) -> Option<Vec<lsp_types::TextEdit>> {
672+
let _p = profile::span("resolve_additional_edits");
673+
674+
let rewriter = insert_use::insert_use(
675+
&import_to_add.import_scope,
676+
mod_path_to_ast(&import_to_add.import_path),
677+
import_to_add.merge_behaviour,
678+
);
679+
let old_ast = rewriter.rewrite_root()?;
680+
let mut import_insert = TextEdit::builder();
681+
algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
682+
let text_edit = import_insert.finish();
683+
684+
Some(
685+
text_edit
686+
.into_iter()
687+
.map(|indel| to_proto::text_edit(&line_index, line_endings, indel))
688+
.collect_vec(),
689+
)
690+
}
691+
639692
pub(crate) fn handle_folding_range(
640693
snap: GlobalStateSnapshot,
641694
params: FoldingRangeParams,

0 commit comments

Comments
 (0)