From deda74edd89affb3f77d274776d2a672bc11db90 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 4 Dec 2020 16:03:22 +0200 Subject: Use stateless completion resolve --- .../completion/src/completions/unqualified_path.rs | 10 +- crates/completion/src/item.rs | 1 - crates/completion/src/lib.rs | 32 +++++- crates/completion/src/render.rs | 12 +-- crates/ide/src/lib.rs | 22 ++++ crates/rust-analyzer/src/handlers.rs | 118 +++++++++++---------- 6 files changed, 124 insertions(+), 71 deletions(-) (limited to 'crates') diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 81691cd7f..26a2b7a1b 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -9,7 +9,7 @@ use test_utils::mark; use crate::{ render::{render_resolution_with_import, RenderContext}, - CompletionContext, Completions, + CompletionContext, Completions, ImportEdit, }; pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { @@ -103,9 +103,11 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() .filter_map(|(import_path, definition)| { render_resolution_with_import( RenderContext::new(ctx), - import_path.clone(), - import_scope.clone(), - ctx.config.merge, + ImportEdit { + import_path: import_path.clone(), + import_scope: import_scope.clone(), + merge_behaviour: ctx.config.merge, + }, &definition, ) }); diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 2dadf7e5b..4e56f28f3 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -276,7 +276,6 @@ pub struct ImportEdit { } impl ImportEdit { - // TODO kb remove this at all now, since it's used only once? /// Attempts to insert the import to the given scope, producing a text edit. /// May return no edit in edge cases, such as scope already containing the import. pub fn to_text_edit(&self) -> Option { diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index c57203c80..938c92dbb 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs @@ -11,8 +11,11 @@ mod render; mod completions; -use ide_db::base_db::FilePosition; -use ide_db::RootDatabase; +use ide_db::{ + base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, +}; +use syntax::AstNode; +use text_edit::TextEdit; use crate::{completions::Completions, context::CompletionContext, item::CompletionKind}; @@ -131,6 +134,31 @@ pub fn completions( Some(acc) } +/// Resolves additional completion data at the position given. +pub fn resolve_completion_edits( + db: &RootDatabase, + config: &CompletionConfig, + position: FilePosition, + full_import_path: &str, + imported_name: &str, +) -> Option { + let ctx = CompletionContext::new(db, position, config)?; + let anchor = ctx.name_ref_syntax.as_ref()?; + let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; + + let current_module = ctx.sema.scope(anchor.syntax()).module()?; + let current_crate = current_module.krate(); + + let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) + .filter_map(|candidate| { + let item: hir::ItemInNs = candidate.either(Into::into, Into::into); + current_module.find_use_path(db, item) + }) + .find(|mod_path| mod_path.to_string() == full_import_path)?; + + ImportEdit { import_path, import_scope, merge_behaviour: config.merge }.to_text_edit() +} + #[cfg(test)] mod tests { use crate::config::CompletionConfig; diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index a6faedb18..9a43480e1 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs @@ -9,8 +9,7 @@ pub(crate) mod type_alias; mod builder_ext; -use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; -use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour}; +use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; use ide_db::RootDatabase; use syntax::TextRange; use test_utils::mark; @@ -48,15 +47,12 @@ pub(crate) fn render_resolution<'a>( pub(crate) fn render_resolution_with_import<'a>( ctx: RenderContext<'a>, - import_path: ModPath, - import_scope: ImportScope, - merge_behaviour: Option, + import_edit: ImportEdit, resolution: &ScopeDef, ) -> Option { - let local_name = import_path.segments.last()?.to_string(); Render::new(ctx).render_resolution( - local_name, - Some(ImportEdit { import_path, import_scope, merge_behaviour }), + import_edit.import_path.segments.last()?.to_string(), + Some(import_edit), resolution, ) } diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 9e38d6506..4a274f5ba 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -469,6 +469,28 @@ impl Analysis { self.with_db(|db| completion::completions(db, config, position).map(Into::into)) } + /// Resolves additional completion data at the position given. + pub fn resolve_completion_edits( + &self, + config: &CompletionConfig, + position: FilePosition, + full_import_path: &str, + imported_name: &str, + ) -> Cancelable> { + Ok(self + .with_db(|db| { + completion::resolve_completion_edits( + db, + config, + position, + full_import_path, + imported_name, + ) + })? + .map(|edit| vec![edit]) + .unwrap_or_default()) + } + /// Computes resolved assists with source changes for the given position. pub fn resolved_assists( &self, diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index dacd4ec50..f92280524 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -8,8 +8,8 @@ use std::{ }; use ide::{ - CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, - ImportEdit, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, + CompletionConfig, CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, + HoverGotoTypeData, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit, }; use itertools::Itertools; @@ -22,7 +22,7 @@ use lsp_types::{ HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, - SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, + SymbolTag, TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkspaceEdit, }; use project_model::TargetKind; use serde::{Deserialize, Serialize}; @@ -35,7 +35,6 @@ use crate::{ config::RustfmtConfig, from_json, from_proto, global_state::{GlobalState, GlobalStateSnapshot}, - line_endings::LineEndings, lsp_ext::{self, InlayHint, InlayHintsParams}, to_proto, LspError, Result, }; @@ -541,7 +540,7 @@ pub(crate) fn handle_completion( params: lsp_types::CompletionParams, ) -> Result> { let _p = profile::span("handle_completion"); - let text_document_url = params.text_document_position.text_document.uri.clone(); + let text_document_position = params.text_document_position.clone(); let position = from_proto::file_position(&snap, params.text_document_position)?; let completion_triggered_after_single_colon = { let mut res = false; @@ -574,23 +573,18 @@ pub(crate) fn handle_completion( let items: Vec = items .into_iter() - .enumerate() - .flat_map(|(item_index, item)| { + .flat_map(|item| { let mut new_completion_items = to_proto::completion_item(&line_index, line_endings, item.clone()); - if snap.config.completion.resolve_additional_edits_lazily() { - // TODO kb add resolve data somehow here - if let Some(import_edit) = item.import_to_add() { - // let data = serde_json::to_value(&CompletionData { - // document_url: text_document_url.clone(), - // import_id: item_index, - // }) - // .expect(&format!("Should be able to serialize usize value {}", item_index)); - for new_item in &mut new_completion_items { - // new_item.data = Some(data.clone()); - } - } + for new_item in &mut new_completion_items { + let _ = fill_resolve_data( + &mut new_item.data, + &item, + &snap.config.completion, + &text_document_position, + ) + .take(); } new_completion_items @@ -603,8 +597,8 @@ pub(crate) fn handle_completion( pub(crate) fn handle_completion_resolve( snap: GlobalStateSnapshot, - mut original_completion: lsp_types::CompletionItem, -) -> Result { + mut original_completion: CompletionItem, +) -> Result { let _p = profile::span("handle_resolve_completion"); // FIXME resolve the other capabilities also? @@ -627,21 +621,30 @@ pub(crate) fn handle_completion_resolve( None => return Ok(original_completion), }; - // TODO kb get the resolve data and somehow reparse the whole ast again? - // let file_id = from_proto::file_id(&snap, &document_url)?; - // let root = snap.analysis.parse(file_id)?; - - // if let Some(import_to_add) = - // import_edit_ptr.and_then(|import_edit| import_edit.into_import_edit(root.syntax())) - // { - // // FIXME actually add all additional edits here? see `to_proto::completion_item` for more - // append_import_edits( - // &mut original_completion, - // &import_to_add, - // snap.analysis.file_line_index(file_id)?.as_ref(), - // snap.file_line_endings(file_id), - // ); - // } + let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?; + let line_index = snap.analysis.file_line_index(file_id)?; + let line_endings = snap.file_line_endings(file_id); + let offset = from_proto::offset(&line_index, resolve_data.position.position); + + let mut additional_edits = snap + .analysis + .resolve_completion_edits( + &snap.config.completion, + FilePosition { file_id, offset }, + &resolve_data.full_import_path, + &resolve_data.imported_name, + )? + .into_iter() + .flat_map(|edit| { + edit.into_iter().map(|indel| to_proto::text_edit(&line_index, line_endings, indel)) + }) + .collect_vec(); + + if let Some(original_additional_edits) = original_completion.additional_text_edits.as_mut() { + original_additional_edits.extend(additional_edits.drain(..)) + } else { + original_completion.additional_text_edits = Some(additional_edits); + } Ok(original_completion) } @@ -1606,27 +1609,30 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) #[derive(Debug, Serialize, Deserialize)] struct CompletionResolveData { - document_url: Url, - import_id: usize, + position: lsp_types::TextDocumentPositionParams, + full_import_path: String, + imported_name: String, } -fn append_import_edits( - completion: &mut lsp_types::CompletionItem, - import_to_add: &ImportEdit, - line_index: &LineIndex, - line_endings: LineEndings, -) { - let import_edits = import_to_add.to_text_edit().map(|import_edit| { - import_edit - .into_iter() - .map(|indel| to_proto::text_edit(line_index, line_endings, indel)) - .collect_vec() - }); - if let Some(original_additional_edits) = completion.additional_text_edits.as_mut() { - if let Some(mut new_edits) = import_edits { - original_additional_edits.extend(new_edits.drain(..)) - } - } else { - completion.additional_text_edits = import_edits; +fn fill_resolve_data( + resolve_data: &mut Option, + item: &ide::CompletionItem, + completion_config: &CompletionConfig, + position: &TextDocumentPositionParams, +) -> Option<()> { + if completion_config.resolve_additional_edits_lazily() { + let import_edit = item.import_to_add()?; + let full_import_path = import_edit.import_path.to_string(); + let imported_name = import_edit.import_path.segments.clone().pop()?.to_string(); + + *resolve_data = Some( + serde_json::to_value(CompletionResolveData { + position: position.to_owned(), + full_import_path, + imported_name, + }) + .expect("Failed to serialize a regular struct with derives"), + ) } + Some(()) } -- cgit v1.2.3