Skip to content

Commit 74c3bba

Browse files
Make completion resolve async
1 parent f6d2540 commit 74c3bba

File tree

7 files changed

+116
-53
lines changed

7 files changed

+116
-53
lines changed

crates/completion/src/item.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ use std::fmt;
44

55
use hir::{Documentation, ModPath, Mutability};
66
use ide_db::helpers::{
7-
insert_use::{self, ImportScope, MergeBehaviour},
7+
insert_use::{self, ImportScope, ImportScopePtr, MergeBehaviour},
88
mod_path_to_ast,
99
};
10-
use syntax::{algo, TextRange};
10+
use syntax::{algo, SyntaxNode, TextRange};
1111
use text_edit::TextEdit;
1212

1313
use crate::config::SnippetCap;
@@ -275,7 +275,32 @@ pub struct ImportEdit {
275275
pub merge_behaviour: Option<MergeBehaviour>,
276276
}
277277

278+
#[derive(Debug, Clone)]
279+
pub struct ImportEditPtr {
280+
pub import_path: ModPath,
281+
pub import_scope: ImportScopePtr,
282+
pub merge_behaviour: Option<MergeBehaviour>,
283+
}
284+
285+
impl ImportEditPtr {
286+
pub fn into_import_edit(self, root: &SyntaxNode) -> Option<ImportEdit> {
287+
Some(ImportEdit {
288+
import_path: self.import_path,
289+
import_scope: self.import_scope.into_scope(root)?,
290+
merge_behaviour: self.merge_behaviour,
291+
})
292+
}
293+
}
294+
278295
impl ImportEdit {
296+
pub fn get_edit_ptr(&self) -> ImportEditPtr {
297+
ImportEditPtr {
298+
import_path: self.import_path.clone(),
299+
import_scope: self.import_scope.get_ptr(),
300+
merge_behaviour: self.merge_behaviour,
301+
}
302+
}
303+
279304
/// Attempts to insert the import to the given scope, producing a text edit.
280305
/// May return no edit in edge cases, such as scope already containing the import.
281306
pub fn to_text_edit(&self) -> Option<TextEdit> {

crates/completion/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi
1818

1919
pub use crate::{
2020
config::{CompletionConfig, CompletionResolveCapability},
21-
item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat},
21+
item::{
22+
CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, ImportEditPtr,
23+
InsertTextFormat,
24+
},
2225
};
2326

2427
//FIXME: split the following feature into fine-grained features.

crates/ide/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ pub use crate::{
8181
};
8282
pub use completion::{
8383
CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability,
84-
CompletionScore, ImportEdit, InsertTextFormat,
84+
CompletionScore, ImportEdit, ImportEditPtr, InsertTextFormat,
8585
};
8686
pub use ide_db::{
8787
call_info::CallInfo,

crates/ide_db/src/helpers/insert_use.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use syntax::{
1111
edit::{AstNodeEdit, IndentLevel},
1212
make, AstNode, PathSegmentKind, VisibilityOwner,
1313
},
14-
AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
14+
AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
1515
};
1616
use test_utils::mark;
1717

@@ -21,6 +21,36 @@ pub enum ImportScope {
2121
Module(ast::ItemList),
2222
}
2323

24+
impl ImportScope {
25+
pub fn get_ptr(&self) -> ImportScopePtr {
26+
match self {
27+
ImportScope::File(file) => ImportScopePtr::File(SyntaxNodePtr::new(file.syntax())),
28+
ImportScope::Module(module) => {
29+
ImportScopePtr::Module(SyntaxNodePtr::new(module.syntax()))
30+
}
31+
}
32+
}
33+
}
34+
35+
#[derive(Debug, Clone)]
36+
pub enum ImportScopePtr {
37+
File(SyntaxNodePtr),
38+
Module(SyntaxNodePtr),
39+
}
40+
41+
impl ImportScopePtr {
42+
pub fn into_scope(self, root: &SyntaxNode) -> Option<ImportScope> {
43+
Some(match self {
44+
ImportScopePtr::File(file_ptr) => {
45+
ImportScope::File(ast::SourceFile::cast(file_ptr.to_node(root))?)
46+
}
47+
ImportScopePtr::Module(module_ptr) => {
48+
ImportScope::File(ast::SourceFile::cast(module_ptr.to_node(root))?)
49+
}
50+
})
51+
}
52+
}
53+
2454
impl ImportScope {
2555
pub fn from(syntax: SyntaxNode) -> Option<Self> {
2656
if let Some(module) = ast::Module::cast(syntax.clone()) {

crates/rust-analyzer/src/global_state.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{sync::Arc, time::Instant};
77

88
use crossbeam_channel::{unbounded, Receiver, Sender};
99
use flycheck::FlycheckHandle;
10-
use ide::{Analysis, AnalysisHost, Change, FileId, ImportEdit};
10+
use ide::{Analysis, AnalysisHost, Change, FileId, ImportEditPtr};
1111
use ide_db::base_db::{CrateId, VfsPath};
1212
use lsp_types::{SemanticTokens, Url};
1313
use parking_lot::{Mutex, RwLock};
@@ -51,11 +51,6 @@ pub(crate) struct Handle<H, C> {
5151
pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
5252
pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
5353

54-
pub(crate) struct CompletionResolveData {
55-
pub(crate) file_id: FileId,
56-
pub(crate) import_edit: ImportEdit,
57-
}
58-
5954
/// `GlobalState` is the primary mutable state of the language server
6055
///
6156
/// The most interesting components are `vfs`, which stores a consistent
@@ -74,7 +69,7 @@ pub(crate) struct GlobalState {
7469
pub(crate) config: Config,
7570
pub(crate) analysis_host: AnalysisHost,
7671
pub(crate) diagnostics: DiagnosticCollection,
77-
pub(crate) completion_resolve_data: FxHashMap<usize, CompletionResolveData>,
72+
pub(crate) completion_resolve_data: Arc<FxHashMap<usize, ImportEditPtr>>,
7873
pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
7974
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
8075
pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
@@ -96,6 +91,7 @@ pub(crate) struct GlobalStateSnapshot {
9691
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
9792
vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
9893
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
94+
pub(crate) completion_resolve_data: Arc<FxHashMap<usize, ImportEditPtr>>,
9995
}
10096

10197
impl GlobalState {
@@ -127,7 +123,7 @@ impl GlobalState {
127123
config,
128124
analysis_host,
129125
diagnostics: Default::default(),
130-
completion_resolve_data: FxHashMap::default(),
126+
completion_resolve_data: Arc::new(FxHashMap::default()),
131127
mem_docs: FxHashMap::default(),
132128
semantic_tokens_cache: Arc::new(Default::default()),
133129
vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
@@ -198,6 +194,7 @@ impl GlobalState {
198194
check_fixes: Arc::clone(&self.diagnostics.check_fixes),
199195
mem_docs: self.mem_docs.clone(),
200196
semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
197+
completion_resolve_data: Arc::clone(&self.completion_resolve_data),
201198
}
202199
}
203200

crates/rust-analyzer/src/handlers.rs

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
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, ImportEdit, LineIndex,
12-
NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit,
12+
CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData,
13+
ImportEdit, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
14+
TextEdit,
1315
};
1416
use itertools::Itertools;
1517
use lsp_server::ErrorCode;
@@ -34,7 +36,7 @@ use crate::{
3436
cargo_target_spec::CargoTargetSpec,
3537
config::RustfmtConfig,
3638
from_json, from_proto,
37-
global_state::{CompletionResolveData, GlobalState, GlobalStateSnapshot},
39+
global_state::{GlobalState, GlobalStateSnapshot},
3840
line_endings::LineEndings,
3941
lsp_ext::{self, InlayHint, InlayHintsParams},
4042
to_proto, LspError, Result,
@@ -542,6 +544,7 @@ pub(crate) fn handle_completion(
542544
) -> Result<Option<lsp_types::CompletionResponse>> {
543545
let _p = profile::span("handle_completion");
544546
let snap = global_state.snapshot();
547+
let text_document_url = params.text_document_position.text_document.uri.clone();
545548
let position = from_proto::file_position(&snap, params.text_document_position)?;
546549
let completion_triggered_after_single_colon = {
547550
let mut res = false;
@@ -582,18 +585,15 @@ pub(crate) fn handle_completion(
582585

583586
if snap.config.completion.resolve_additional_edits_lazily() {
584587
if let Some(import_edit) = item.import_to_add() {
585-
completion_resolve_data.insert(
586-
item_index,
587-
CompletionResolveData {
588-
file_id: position.file_id,
589-
import_edit: import_edit.clone(),
590-
},
591-
);
592-
593-
let item_id = serde_json::to_value(&item_index)
594-
.expect(&format!("Should be able to serialize usize value {}", item_index));
588+
completion_resolve_data.insert(item_index, import_edit.get_edit_ptr());
589+
590+
let data = serde_json::to_value(&CompletionData {
591+
document_url: text_document_url.clone(),
592+
import_id: item_index,
593+
})
594+
.expect(&format!("Should be able to serialize usize value {}", item_index));
595595
for new_item in &mut new_completion_items {
596-
new_item.data = Some(item_id.clone());
596+
new_item.data = Some(data.clone());
597597
}
598598
}
599599
}
@@ -602,50 +602,54 @@ pub(crate) fn handle_completion(
602602
})
603603
.collect();
604604

605-
global_state.completion_resolve_data = completion_resolve_data;
605+
global_state.completion_resolve_data = Arc::new(completion_resolve_data);
606606

607607
let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
608608
Ok(Some(completion_list.into()))
609609
}
610610

611611
pub(crate) fn handle_completion_resolve(
612-
global_state: &mut GlobalState,
612+
snap: GlobalStateSnapshot,
613613
mut original_completion: lsp_types::CompletionItem,
614614
) -> Result<lsp_types::CompletionItem> {
615615
let _p = profile::span("handle_resolve_completion");
616616

617-
let active_resolve_caps = &global_state.config.completion.active_resolve_capabilities;
618-
if active_resolve_caps.is_empty() {
617+
// FIXME resolve the other capabilities also?
618+
if !snap
619+
.config
620+
.completion
621+
.active_resolve_capabilities
622+
.contains(&CompletionResolveCapability::AdditionalTextEdits)
623+
{
619624
return Ok(original_completion);
620625
}
621626

622-
let server_completion_data = match original_completion
627+
let (import_edit_ptr, document_url) = match original_completion
623628
.data
624629
.as_ref()
625-
.map(|data| serde_json::from_value::<usize>(data.clone()))
630+
.map(|data| serde_json::from_value::<CompletionData>(data.clone()))
626631
.transpose()?
627-
.and_then(|server_completion_id| {
628-
global_state.completion_resolve_data.get(&server_completion_id)
632+
.and_then(|data| {
633+
let import_edit_ptr = snap.completion_resolve_data.get(&data.import_id).cloned();
634+
Some((import_edit_ptr, data.document_url))
629635
}) {
630636
Some(data) => data,
631637
None => return Ok(original_completion),
632638
};
633639

634-
let snap = &global_state.snapshot();
635-
for supported_completion_resolve_cap in active_resolve_caps {
636-
match supported_completion_resolve_cap {
637-
// FIXME actually add all additional edits here? see `to_proto::completion_item` for more
638-
ide::CompletionResolveCapability::AdditionalTextEdits => {
639-
append_import_edits(
640-
&mut original_completion,
641-
&server_completion_data.import_edit,
642-
snap.analysis.file_line_index(server_completion_data.file_id)?.as_ref(),
643-
snap.file_line_endings(server_completion_data.file_id),
644-
);
645-
}
646-
// FIXME resolve the other capabilities also?
647-
_ => {}
648-
}
640+
let file_id = from_proto::file_id(&snap, &document_url)?;
641+
let root = snap.analysis.parse(file_id)?;
642+
643+
if let Some(import_to_add) =
644+
import_edit_ptr.and_then(|import_edit| import_edit.into_import_edit(root.syntax()))
645+
{
646+
// FIXME actually add all additional edits here? see `to_proto::completion_item` for more
647+
append_import_edits(
648+
&mut original_completion,
649+
&import_to_add,
650+
snap.analysis.file_line_index(file_id)?.as_ref(),
651+
snap.file_line_endings(file_id),
652+
);
649653
}
650654

651655
Ok(original_completion)
@@ -1609,6 +1613,12 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>)
16091613
}
16101614
}
16111615

1616+
#[derive(Debug, Serialize, Deserialize)]
1617+
struct CompletionData {
1618+
document_url: Url,
1619+
import_id: usize,
1620+
}
1621+
16121622
fn append_import_edits(
16131623
completion: &mut lsp_types::CompletionItem,
16141624
import_to_add: &ImportEdit,

crates/rust-analyzer/src/main_loop.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,7 @@ impl GlobalState {
437437
})?
438438
.on_sync::<lsp_ext::MemoryUsage>(|s, p| handlers::handle_memory_usage(s, p))?
439439
.on_sync::<lsp_types::request::Completion>(handlers::handle_completion)?
440-
.on_sync::<lsp_types::request::ResolveCompletionItem>(
441-
handlers::handle_completion_resolve,
442-
)?
440+
.on::<lsp_types::request::ResolveCompletionItem>(handlers::handle_completion_resolve)
443441
.on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
444442
.on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
445443
.on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)

0 commit comments

Comments
 (0)