aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2020-12-01 13:25:26 +0000
committerKirill Bulatov <[email protected]>2020-12-07 21:41:08 +0000
commit9a4daffe16f81e154b542a195fdf81463e5212bb (patch)
tree12ae871613ee93e2032c8f4d44bca706e1c97a70 /crates
parenta539267c3b18ff3b58d9e33105fcae27a35316c5 (diff)
Resolve import inserts better
Diffstat (limited to 'crates')
-rw-r--r--crates/rust-analyzer/src/global_state.rs2
-rw-r--r--crates/rust-analyzer/src/handlers.rs127
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 @@
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, 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};
14use ide_db::helpers::{insert_use, mod_path_to_ast}; 15use ide_db::helpers::{insert_use, mod_path_to_ast};
15use itertools::Itertools; 16use 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)]
542pub(crate) struct ResolveCompletionData {
543 completion_id: usize,
544 completion_file_id: u32,
545}
546
539pub(crate) fn handle_completion( 547pub(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?
667fn 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
639pub(crate) fn handle_folding_range( 692pub(crate) fn handle_folding_range(
640 snap: GlobalStateSnapshot, 693 snap: GlobalStateSnapshot,
641 params: FoldingRangeParams, 694 params: FoldingRangeParams,