diff options
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 127 |
2 files changed, 91 insertions, 38 deletions
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index dc9e7113b..ba4bf2eeb 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -69,7 +69,7 @@ pub(crate) struct GlobalState { | |||
69 | pub(crate) config: Config, | 69 | pub(crate) config: Config, |
70 | pub(crate) analysis_host: AnalysisHost, | 70 | pub(crate) analysis_host: AnalysisHost, |
71 | pub(crate) diagnostics: DiagnosticCollection, | 71 | pub(crate) diagnostics: DiagnosticCollection, |
72 | pub(crate) additional_imports: FxHashMap<String, ImportToAdd>, | 72 | pub(crate) additional_imports: FxHashMap<usize, ImportToAdd>, |
73 | pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, | 73 | pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, |
74 | pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>, | 74 | pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>, |
75 | pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, | 75 | pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 853f7fa84..d9fdf0434 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -5,11 +5,12 @@ | |||
5 | use std::{ | 5 | use std::{ |
6 | io::Write as _, | 6 | io::Write as _, |
7 | process::{self, Stdio}, | 7 | process::{self, Stdio}, |
8 | sync::Arc, | ||
8 | }; | 9 | }; |
9 | 10 | ||
10 | use ide::{ | 11 | use ide::{ |
11 | FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query, | 12 | FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, ImportToAdd, LineIndex, |
12 | RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit, | 13 | NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit, |
13 | }; | 14 | }; |
14 | use ide_db::helpers::{insert_use, mod_path_to_ast}; | 15 | use ide_db::helpers::{insert_use, mod_path_to_ast}; |
15 | use itertools::Itertools; | 16 | use itertools::Itertools; |
@@ -36,6 +37,7 @@ use crate::{ | |||
36 | config::RustfmtConfig, | 37 | config::RustfmtConfig, |
37 | from_json, from_proto, | 38 | from_json, from_proto, |
38 | global_state::{GlobalState, GlobalStateSnapshot}, | 39 | global_state::{GlobalState, GlobalStateSnapshot}, |
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, |
41 | }; | 43 | }; |
@@ -536,6 +538,12 @@ pub(crate) fn handle_runnables( | |||
536 | Ok(res) | 538 | Ok(res) |
537 | } | 539 | } |
538 | 540 | ||
541 | #[derive(Debug, Copy, Clone, Serialize, Deserialize)] | ||
542 | pub(crate) struct ResolveCompletionData { | ||
543 | completion_id: usize, | ||
544 | completion_file_id: u32, | ||
545 | } | ||
546 | |||
539 | pub(crate) fn handle_completion( | 547 | pub(crate) fn handle_completion( |
540 | global_state: &mut GlobalState, | 548 | global_state: &mut GlobalState, |
541 | params: lsp_types::CompletionParams, | 549 | params: lsp_types::CompletionParams, |
@@ -575,20 +583,31 @@ pub(crate) fn handle_completion( | |||
575 | 583 | ||
576 | let items: Vec<CompletionItem> = items | 584 | let items: Vec<CompletionItem> = items |
577 | .into_iter() | 585 | .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 | }; | ||
579 | let import_to_add = item.import_to_add().cloned(); | 592 | 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 | |||
581 | if let Some(import_to_add) = import_to_add { | 596 | if let Some(import_to_add) = import_to_add { |
582 | for new_item in &new_completion_items { | 597 | for new_item in &mut new_completion_items { |
583 | additional_imports.insert(new_item.label.clone(), import_to_add.clone()); | 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 | } | ||
584 | } | 607 | } |
585 | } | 608 | } |
586 | new_completion_items | 609 | new_completion_items |
587 | }) | 610 | }) |
588 | .map(|mut item| { | ||
589 | item.data = Some(position.file_id.0.into()); | ||
590 | item | ||
591 | }) | ||
592 | .collect(); | 611 | .collect(); |
593 | 612 | ||
594 | global_state.additional_imports = additional_imports; | 613 | global_state.additional_imports = additional_imports; |
@@ -601,41 +620,75 @@ pub(crate) fn handle_resolve_completion( | |||
601 | global_state: &mut GlobalState, | 620 | global_state: &mut GlobalState, |
602 | mut original_completion: lsp_types::CompletionItem, | 621 | mut original_completion: lsp_types::CompletionItem, |
603 | ) -> Result<lsp_types::CompletionItem> { | 622 | ) -> Result<lsp_types::CompletionItem> { |
604 | // TODO kb slow, takes over 130ms | ||
605 | let _p = profile::span("handle_resolve_completion"); | 623 | let _p = profile::span("handle_resolve_completion"); |
606 | 624 | ||
607 | if let Some(import_data) = | 625 | match original_completion.data.as_ref() { |
608 | global_state.additional_imports.get(dbg!(original_completion.label.as_str())) | 626 | Some(completion_data) => { |
609 | { | 627 | match serde_json::from_value::<ResolveCompletionData>(completion_data.clone()) { |
610 | let rewriter = insert_use::insert_use( | 628 | Ok(resolve_completion_data) => { |
611 | &import_data.import_scope, | 629 | if let Some(import_to_add) = |
612 | mod_path_to_ast(&import_data.import_path), | 630 | global_state.additional_imports.get(&resolve_completion_data.completion_id) |
613 | import_data.merge_behaviour, | 631 | { |
614 | ); | 632 | let snap = global_state.snapshot(); |
615 | if let Some((old_ast, file_id)) = | 633 | let file_id = FileId(resolve_completion_data.completion_file_id); |
616 | // TODO kb for file_id, better use &str and then cast to u32? | 634 | let line_index = snap.analysis.file_line_index(file_id)?; |
617 | rewriter | 635 | let line_endings = snap.file_line_endings(file_id); |
618 | .rewrite_root() | 636 | |
619 | .zip(original_completion.data.as_ref().and_then(|value| Some(value.as_u64()? as u32))) | 637 | let resolved_edits = |
620 | { | 638 | resolve_additional_edits(import_to_add, line_index, line_endings); |
621 | let snap = global_state.snapshot(); | 639 | |
622 | let mut import_insert = TextEdit::builder(); | 640 | original_completion.additional_text_edits = |
623 | algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); | 641 | match original_completion.additional_text_edits { |
624 | let line_index = snap.analysis.file_line_index(FileId(file_id))?; | 642 | Some(mut original_additional_edits) => { |
625 | let line_endings = snap.file_line_endings(FileId(file_id)); | 643 | if let Some(mut new_edits) = resolved_edits { |
626 | let text_edit = import_insert.finish(); | 644 | original_additional_edits.extend(new_edits.drain(..)) |
627 | 645 | } | |
628 | let mut new_edits = original_completion.additional_text_edits.unwrap_or_default(); | 646 | Some(original_additional_edits) |
629 | for indel in text_edit { | 647 | } |
630 | new_edits.push(to_proto::text_edit(&line_index, line_endings, indel)); | 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), | ||
631 | } | 659 | } |
632 | original_completion.additional_text_edits = Some(new_edits); | ||
633 | } | 660 | } |
661 | None => (), | ||
634 | } | 662 | } |
635 | |||
636 | Ok(original_completion) | 663 | Ok(original_completion) |
637 | } | 664 | } |
638 | 665 | ||
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 | |||
639 | pub(crate) fn handle_folding_range( | 692 | pub(crate) fn handle_folding_range( |
640 | snap: GlobalStateSnapshot, | 693 | snap: GlobalStateSnapshot, |
641 | params: FoldingRangeParams, | 694 | params: FoldingRangeParams, |