aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/completion/src/item.rs29
-rw-r--r--crates/completion/src/lib.rs5
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs32
-rw-r--r--crates/rust-analyzer/src/global_state.rs13
-rw-r--r--crates/rust-analyzer/src/handlers.rs84
-rw-r--r--crates/rust-analyzer/src/main_loop.rs4
7 files changed, 116 insertions, 53 deletions
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index 4e56f28f3..dd25ca75c 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -4,10 +4,10 @@ use std::fmt;
4 4
5use hir::{Documentation, ModPath, Mutability}; 5use hir::{Documentation, ModPath, Mutability};
6use ide_db::helpers::{ 6use ide_db::helpers::{
7 insert_use::{self, ImportScope, MergeBehaviour}, 7 insert_use::{self, ImportScope, ImportScopePtr, MergeBehaviour},
8 mod_path_to_ast, 8 mod_path_to_ast,
9}; 9};
10use syntax::{algo, TextRange}; 10use syntax::{algo, SyntaxNode, TextRange};
11use text_edit::TextEdit; 11use text_edit::TextEdit;
12 12
13use crate::config::SnippetCap; 13use crate::config::SnippetCap;
@@ -275,7 +275,32 @@ pub struct ImportEdit {
275 pub merge_behaviour: Option<MergeBehaviour>, 275 pub merge_behaviour: Option<MergeBehaviour>,
276} 276}
277 277
278#[derive(Debug, Clone)]
279pub struct ImportEditPtr {
280 pub import_path: ModPath,
281 pub import_scope: ImportScopePtr,
282 pub merge_behaviour: Option<MergeBehaviour>,
283}
284
285impl 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
278impl ImportEdit { 295impl 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
279 /// Attempts to insert the import to the given scope, producing a text edit. 304 /// Attempts to insert the import to the given scope, producing a text edit.
280 /// May return no edit in edge cases, such as scope already containing the import. 305 /// May return no edit in edge cases, such as scope already containing the import.
281 pub fn to_text_edit(&self) -> Option<TextEdit> { 306 pub fn to_text_edit(&self) -> Option<TextEdit> {
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index c57203c80..c277cd466 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -18,7 +18,10 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi
18 18
19pub use crate::{ 19pub use crate::{
20 config::{CompletionConfig, CompletionResolveCapability}, 20 config::{CompletionConfig, CompletionResolveCapability},
21 item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, 21 item::{
22 CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, ImportEditPtr,
23 InsertTextFormat,
24 },
22}; 25};
23 26
24//FIXME: split the following feature into fine-grained features. 27//FIXME: split the following feature into fine-grained features.
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 9e38d6506..c52e53d27 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -81,7 +81,7 @@ pub use crate::{
81}; 81};
82pub use completion::{ 82pub use completion::{
83 CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability, 83 CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability,
84 CompletionScore, ImportEdit, InsertTextFormat, 84 CompletionScore, ImportEdit, ImportEditPtr, InsertTextFormat,
85}; 85};
86pub use ide_db::{ 86pub use ide_db::{
87 call_info::CallInfo, 87 call_info::CallInfo,
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 040843990..0dae9a541 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -11,7 +11,7 @@ use syntax::{
11 edit::{AstNodeEdit, IndentLevel}, 11 edit::{AstNodeEdit, IndentLevel},
12 make, AstNode, PathSegmentKind, VisibilityOwner, 12 make, AstNode, PathSegmentKind, VisibilityOwner,
13 }, 13 },
14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, 14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
15}; 15};
16use test_utils::mark; 16use test_utils::mark;
17 17
@@ -22,6 +22,36 @@ pub enum ImportScope {
22} 22}
23 23
24impl ImportScope { 24impl 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)]
36pub enum ImportScopePtr {
37 File(SyntaxNodePtr),
38 Module(SyntaxNodePtr),
39}
40
41impl 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
54impl ImportScope {
25 pub fn from(syntax: SyntaxNode) -> Option<Self> { 55 pub fn from(syntax: SyntaxNode) -> Option<Self> {
26 if let Some(module) = ast::Module::cast(syntax.clone()) { 56 if let Some(module) = ast::Module::cast(syntax.clone()) {
27 module.item_list().map(ImportScope::Module) 57 module.item_list().map(ImportScope::Module)
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index a35abe86d..0fe69b996 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -7,7 +7,7 @@ use std::{sync::Arc, time::Instant};
7 7
8use crossbeam_channel::{unbounded, Receiver, Sender}; 8use crossbeam_channel::{unbounded, Receiver, Sender};
9use flycheck::FlycheckHandle; 9use flycheck::FlycheckHandle;
10use ide::{Analysis, AnalysisHost, Change, FileId, ImportEdit}; 10use ide::{Analysis, AnalysisHost, Change, FileId, ImportEditPtr};
11use ide_db::base_db::{CrateId, VfsPath}; 11use ide_db::base_db::{CrateId, VfsPath};
12use lsp_types::{SemanticTokens, Url}; 12use lsp_types::{SemanticTokens, Url};
13use parking_lot::{Mutex, RwLock}; 13use parking_lot::{Mutex, RwLock};
@@ -51,11 +51,6 @@ pub(crate) struct Handle<H, C> {
51pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response); 51pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
52pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; 52pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
53 53
54pub(crate) struct CompletionResolveData {
55 pub(crate) file_id: FileId,
56 pub(crate) import_edit: ImportEdit,
57}
58
59/// `GlobalState` is the primary mutable state of the language server 54/// `GlobalState` is the primary mutable state of the language server
60/// 55///
61/// The most interesting components are `vfs`, which stores a consistent 56/// The most interesting components are `vfs`, which stores a consistent
@@ -74,7 +69,7 @@ pub(crate) struct GlobalState {
74 pub(crate) config: Config, 69 pub(crate) config: Config,
75 pub(crate) analysis_host: AnalysisHost, 70 pub(crate) analysis_host: AnalysisHost,
76 pub(crate) diagnostics: DiagnosticCollection, 71 pub(crate) diagnostics: DiagnosticCollection,
77 pub(crate) completion_resolve_data: FxHashMap<usize, CompletionResolveData>, 72 pub(crate) completion_resolve_data: Arc<FxHashMap<usize, ImportEditPtr>>,
78 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, 73 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
79 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>, 74 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
80 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, 75 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
@@ -96,6 +91,7 @@ pub(crate) struct GlobalStateSnapshot {
96 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>, 91 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
97 vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, 92 vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
98 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, 93 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
94 pub(crate) completion_resolve_data: Arc<FxHashMap<usize, ImportEditPtr>>,
99} 95}
100 96
101impl GlobalState { 97impl GlobalState {
@@ -127,7 +123,7 @@ impl GlobalState {
127 config, 123 config,
128 analysis_host, 124 analysis_host,
129 diagnostics: Default::default(), 125 diagnostics: Default::default(),
130 completion_resolve_data: FxHashMap::default(), 126 completion_resolve_data: Arc::new(FxHashMap::default()),
131 mem_docs: FxHashMap::default(), 127 mem_docs: FxHashMap::default(),
132 semantic_tokens_cache: Arc::new(Default::default()), 128 semantic_tokens_cache: Arc::new(Default::default()),
133 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), 129 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
@@ -198,6 +194,7 @@ impl GlobalState {
198 check_fixes: Arc::clone(&self.diagnostics.check_fixes), 194 check_fixes: Arc::clone(&self.diagnostics.check_fixes),
199 mem_docs: self.mem_docs.clone(), 195 mem_docs: self.mem_docs.clone(),
200 semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache), 196 semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
197 completion_resolve_data: Arc::clone(&self.completion_resolve_data),
201 } 198 }
202 } 199 }
203 200
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 3eb5f26bc..1ea1e1f43 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -5,11 +5,13 @@
5use std::{ 5use std::{
6 io::Write as _, 6 io::Write as _,
7 process::{self, Stdio}, 7 process::{self, Stdio},
8 sync::Arc,
8}; 9};
9 10
10use ide::{ 11use ide::{
11 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, ImportEdit, LineIndex, 12 CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData,
12 NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit, 13 ImportEdit, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
14 TextEdit,
13}; 15};
14use itertools::Itertools; 16use itertools::Itertools;
15use lsp_server::ErrorCode; 17use lsp_server::ErrorCode;
@@ -34,7 +36,7 @@ use crate::{
34 cargo_target_spec::CargoTargetSpec, 36 cargo_target_spec::CargoTargetSpec,
35 config::RustfmtConfig, 37 config::RustfmtConfig,
36 from_json, from_proto, 38 from_json, from_proto,
37 global_state::{CompletionResolveData, GlobalState, GlobalStateSnapshot}, 39 global_state::{GlobalState, GlobalStateSnapshot},
38 line_endings::LineEndings, 40 line_endings::LineEndings,
39 lsp_ext::{self, InlayHint, InlayHintsParams}, 41 lsp_ext::{self, InlayHint, InlayHintsParams},
40 to_proto, LspError, Result, 42 to_proto, LspError, Result,
@@ -542,6 +544,7 @@ pub(crate) fn handle_completion(
542) -> Result<Option<lsp_types::CompletionResponse>> { 544) -> Result<Option<lsp_types::CompletionResponse>> {
543 let _p = profile::span("handle_completion"); 545 let _p = profile::span("handle_completion");
544 let snap = global_state.snapshot(); 546 let snap = global_state.snapshot();
547 let text_document_url = params.text_document_position.text_document.uri.clone();
545 let position = from_proto::file_position(&snap, params.text_document_position)?; 548 let position = from_proto::file_position(&snap, params.text_document_position)?;
546 let completion_triggered_after_single_colon = { 549 let completion_triggered_after_single_colon = {
547 let mut res = false; 550 let mut res = false;
@@ -582,18 +585,15 @@ pub(crate) fn handle_completion(
582 585
583 if snap.config.completion.resolve_additional_edits_lazily() { 586 if snap.config.completion.resolve_additional_edits_lazily() {
584 if let Some(import_edit) = item.import_to_add() { 587 if let Some(import_edit) = item.import_to_add() {
585 completion_resolve_data.insert( 588 completion_resolve_data.insert(item_index, import_edit.get_edit_ptr());
586 item_index, 589
587 CompletionResolveData { 590 let data = serde_json::to_value(&CompletionData {
588 file_id: position.file_id, 591 document_url: text_document_url.clone(),
589 import_edit: import_edit.clone(), 592 import_id: item_index,
590 }, 593 })
591 ); 594 .expect(&format!("Should be able to serialize usize value {}", item_index));
592
593 let item_id = serde_json::to_value(&item_index)
594 .expect(&format!("Should be able to serialize usize value {}", item_index));
595 for new_item in &mut new_completion_items { 595 for new_item in &mut new_completion_items {
596 new_item.data = Some(item_id.clone()); 596 new_item.data = Some(data.clone());
597 } 597 }
598 } 598 }
599 } 599 }
@@ -602,50 +602,54 @@ pub(crate) fn handle_completion(
602 }) 602 })
603 .collect(); 603 .collect();
604 604
605 global_state.completion_resolve_data = completion_resolve_data; 605 global_state.completion_resolve_data = Arc::new(completion_resolve_data);
606 606
607 let completion_list = lsp_types::CompletionList { is_incomplete: true, items }; 607 let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
608 Ok(Some(completion_list.into())) 608 Ok(Some(completion_list.into()))
609} 609}
610 610
611pub(crate) fn handle_completion_resolve( 611pub(crate) fn handle_completion_resolve(
612 global_state: &mut GlobalState, 612 snap: GlobalStateSnapshot,
613 mut original_completion: lsp_types::CompletionItem, 613 mut original_completion: lsp_types::CompletionItem,
614) -> Result<lsp_types::CompletionItem> { 614) -> Result<lsp_types::CompletionItem> {
615 let _p = profile::span("handle_resolve_completion"); 615 let _p = profile::span("handle_resolve_completion");
616 616
617 let active_resolve_caps = &global_state.config.completion.active_resolve_capabilities; 617 // FIXME resolve the other capabilities also?
618 if active_resolve_caps.is_empty() { 618 if !snap
619 .config
620 .completion
621 .active_resolve_capabilities
622 .contains(&CompletionResolveCapability::AdditionalTextEdits)
623 {
619 return Ok(original_completion); 624 return Ok(original_completion);
620 } 625 }
621 626
622 let server_completion_data = match original_completion 627 let (import_edit_ptr, document_url) = match original_completion
623 .data 628 .data
624 .as_ref() 629 .as_ref()
625 .map(|data| serde_json::from_value::<usize>(data.clone())) 630 .map(|data| serde_json::from_value::<CompletionData>(data.clone()))
626 .transpose()? 631 .transpose()?
627 .and_then(|server_completion_id| { 632 .and_then(|data| {
628 global_state.completion_resolve_data.get(&server_completion_id) 633 let import_edit_ptr = snap.completion_resolve_data.get(&data.import_id).cloned();
634 Some((import_edit_ptr, data.document_url))
629 }) { 635 }) {
630 Some(data) => data, 636 Some(data) => data,
631 None => return Ok(original_completion), 637 None => return Ok(original_completion),
632 }; 638 };
633 639
634 let snap = &global_state.snapshot(); 640 let file_id = from_proto::file_id(&snap, &document_url)?;
635 for supported_completion_resolve_cap in active_resolve_caps { 641 let root = snap.analysis.parse(file_id)?;
636 match supported_completion_resolve_cap { 642
637 // FIXME actually add all additional edits here? see `to_proto::completion_item` for more 643 if let Some(import_to_add) =
638 ide::CompletionResolveCapability::AdditionalTextEdits => { 644 import_edit_ptr.and_then(|import_edit| import_edit.into_import_edit(root.syntax()))
639 append_import_edits( 645 {
640 &mut original_completion, 646 // FIXME actually add all additional edits here? see `to_proto::completion_item` for more
641 &server_completion_data.import_edit, 647 append_import_edits(
642 snap.analysis.file_line_index(server_completion_data.file_id)?.as_ref(), 648 &mut original_completion,
643 snap.file_line_endings(server_completion_data.file_id), 649 &import_to_add,
644 ); 650 snap.analysis.file_line_index(file_id)?.as_ref(),
645 } 651 snap.file_line_endings(file_id),
646 // FIXME resolve the other capabilities also? 652 );
647 _ => {}
648 }
649 } 653 }
650 654
651 Ok(original_completion) 655 Ok(original_completion)
@@ -1609,6 +1613,12 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>)
1609 } 1613 }
1610} 1614}
1611 1615
1616#[derive(Debug, Serialize, Deserialize)]
1617struct CompletionData {
1618 document_url: Url,
1619 import_id: usize,
1620}
1621
1612fn append_import_edits( 1622fn append_import_edits(
1613 completion: &mut lsp_types::CompletionItem, 1623 completion: &mut lsp_types::CompletionItem,
1614 import_to_add: &ImportEdit, 1624 import_to_add: &ImportEdit,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index db30b3dce..aad37fde1 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -437,9 +437,7 @@ impl GlobalState {
437 })? 437 })?
438 .on_sync::<lsp_ext::MemoryUsage>(|s, p| handlers::handle_memory_usage(s, p))? 438 .on_sync::<lsp_ext::MemoryUsage>(|s, p| handlers::handle_memory_usage(s, p))?
439 .on_sync::<lsp_types::request::Completion>(handlers::handle_completion)? 439 .on_sync::<lsp_types::request::Completion>(handlers::handle_completion)?
440 .on_sync::<lsp_types::request::ResolveCompletionItem>( 440 .on::<lsp_types::request::ResolveCompletionItem>(handlers::handle_completion_resolve)
441 handlers::handle_completion_resolve,
442 )?
443 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status) 441 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
444 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) 442 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
445 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro) 443 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)