diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index 1aa727e11f..423782a0e0 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs @@ -15,7 +15,7 @@ use syntax::{ }; use test_utils::mark; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ImportScope { File(ast::SourceFile), Module(ast::ItemList), diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs index 75dbb1a23b..9b7d6c5809 100644 --- a/crates/completion/src/completions.rs +++ b/crates/completion/src/completions.rs @@ -90,7 +90,7 @@ impl Completions { Some(it) => it, None => return, }; - if let Some(item) = render_macro(RenderContext::new(ctx), name, macro_) { + if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) { self.add(item); } } @@ -101,7 +101,7 @@ impl Completions { func: hir::Function, local_name: Option, ) { - let item = render_fn(RenderContext::new(ctx), local_name, func); + let item = render_fn(RenderContext::new(ctx), None, local_name, func); self.add(item) } @@ -123,7 +123,7 @@ impl Completions { variant: hir::EnumVariant, path: ModPath, ) { - let item = render_enum_variant(RenderContext::new(ctx), None, variant, Some(path)); + let item = render_enum_variant(RenderContext::new(ctx), None, None, variant, Some(path)); self.add(item); } @@ -133,7 +133,7 @@ impl Completions { variant: hir::EnumVariant, local_name: Option, ) { - let item = render_enum_variant(RenderContext::new(ctx), local_name, variant, None); + let item = render_enum_variant(RenderContext::new(ctx), None, local_name, variant, None); self.add(item); } } diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 4f8ec1e675..86c143b637 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -1,14 +1,14 @@ //! Completion of names from the current scope, e.g. locals and imported items. -use assists::utils::{insert_use, mod_path_to_ast, ImportScope}; +use assists::utils::ImportScope; use either::Either; use hir::{Adt, ModuleDef, ScopeDef, Type}; use ide_db::imports_locator; -use syntax::{algo, AstNode}; +use syntax::AstNode; use test_utils::mark; use crate::{ - render::{render_resolution, RenderContext}, + render::{render_resolution_with_import, RenderContext}, CompletionContext, Completions, }; @@ -95,35 +95,13 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() )), }) .filter(|(mod_path, _)| mod_path.len() > 1) - .filter_map(|(mod_path, definition)| { - let use_to_insert = mod_path_to_ast(&mod_path); - let mut mod_path_without_last_segment = mod_path; - let name_after_import = mod_path_without_last_segment.segments.pop()?.to_string(); - - let resolution_with_missing_import = - render_resolution(RenderContext::new(ctx), name_after_import, &definition)?; - let lookup_string = resolution_with_missing_import.lookup().to_owned(); - - let mut text_edits = - resolution_with_missing_import.text_edit().to_owned().into_builder(); - let rewriter = insert_use(&import_scope, use_to_insert, ctx.config.merge); - let old_ast = rewriter.rewrite_root()?; - algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); - - let qualifier_string = mod_path_without_last_segment.to_string(); - let qualified_label = if qualifier_string.is_empty() { - resolution_with_missing_import.label().to_owned() - } else { - format!("{}::{}", qualifier_string, resolution_with_missing_import.label()) - }; - - Some( - resolution_with_missing_import - .into_builder() - .text_edit(text_edits.finish()) - .label(qualified_label) - .lookup_by(lookup_string) - .build(), + .filter_map(|(import_path, definition)| { + render_resolution_with_import( + RenderContext::new(ctx), + import_path.clone(), + import_scope.clone(), + ctx.config.merge, + &definition, ) }) .take(20); diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 24b9d036a3..b13c3f3762 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -2,8 +2,9 @@ use std::fmt; -use hir::{Documentation, Mutability}; -use syntax::TextRange; +use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour}; +use hir::{Documentation, ModPath, Mutability}; +use syntax::{algo, TextRange}; use text_edit::TextEdit; use crate::config::SnippetCap; @@ -200,25 +201,7 @@ impl CompletionItem { trigger_call_info: None, score: None, ref_match: None, - } - } - - pub(crate) fn into_builder(self) -> Builder { - Builder { - source_range: self.source_range, - completion_kind: self.completion_kind, - label: self.label, - insert_text: None, - insert_text_format: self.insert_text_format, - detail: self.detail, - documentation: self.documentation, - lookup: self.lookup, - kind: self.kind, - text_edit: Some(self.text_edit), - deprecated: Some(self.deprecated), - trigger_call_info: Some(self.trigger_call_info), - score: self.score, - ref_match: self.ref_match, + import_data: None, } } @@ -278,6 +261,7 @@ impl CompletionItem { pub(crate) struct Builder { source_range: TextRange, completion_kind: CompletionKind, + import_data: Option<(ModPath, ImportScope, Option)>, label: String, insert_text: Option, insert_text_format: InsertTextFormat, @@ -294,23 +278,50 @@ pub(crate) struct Builder { impl Builder { pub(crate) fn build(self) -> CompletionItem { - let label = self.label; - let text_edit = match self.text_edit { + let mut label = self.label; + let mut lookup = self.lookup; + let mut insert_text = self.insert_text; + let mut text_edits = TextEdit::builder(); + + if let Some((import_path, import_scope, merge_behaviour)) = self.import_data { + let import = mod_path_to_ast(&import_path); + let mut import_path_without_last_segment = import_path; + let _ = import_path_without_last_segment.segments.pop(); + + if !import_path_without_last_segment.segments.is_empty() { + if lookup.is_none() { + lookup = Some(label.clone()); + } + if insert_text.is_none() { + insert_text = Some(label.clone()); + } + label = format!("{}::{}", import_path_without_last_segment, label); + } + + let rewriter = insert_use(&import_scope, import, merge_behaviour); + if let Some(old_ast) = rewriter.rewrite_root() { + algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); + } + } + + let original_edit = match self.text_edit { Some(it) => it, - None => TextEdit::replace( - self.source_range, - self.insert_text.unwrap_or_else(|| label.clone()), - ), + None => { + TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone())) + } }; + let mut resulting_edit = text_edits.finish(); + resulting_edit.union(original_edit).expect("Failed to unite text edits"); + CompletionItem { source_range: self.source_range, label, insert_text_format: self.insert_text_format, - text_edit, + text_edit: resulting_edit, detail: self.detail, documentation: self.documentation, - lookup: self.lookup, + lookup, kind: self.kind, completion_kind: self.completion_kind, deprecated: self.deprecated.unwrap_or(false), @@ -379,6 +390,13 @@ impl Builder { self.trigger_call_info = Some(true); self } + pub(crate) fn import_data( + mut self, + import_data: Option<(ModPath, ImportScope, Option)>, + ) -> Builder { + self.import_data = import_data; + self + } pub(crate) fn set_ref_match( mut self, ref_match: Option<(Mutability, CompletionScore)>, diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 5a43538461..e892d4de85 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs @@ -9,7 +9,8 @@ pub(crate) mod type_alias; mod builder_ext; -use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; +use assists::utils::{ImportScope, MergeBehaviour}; +use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; use ide_db::RootDatabase; use syntax::TextRange; use test_utils::mark; @@ -42,7 +43,22 @@ pub(crate) fn render_resolution<'a>( local_name: String, resolution: &ScopeDef, ) -> Option { - Render::new(ctx).render_resolution(local_name, resolution) + Render::new(ctx).render_resolution(local_name, None, resolution) +} + +pub(crate) fn render_resolution_with_import<'a>( + ctx: RenderContext<'a>, + import: ModPath, + import_scope: ImportScope, + merge_behaviour: Option, + resolution: &ScopeDef, +) -> Option { + let local_name = import.segments.last()?.to_string(); + Render::new(ctx).render_resolution( + local_name, + Some((import, import_scope, merge_behaviour)), + resolution, + ) } /// Interface for data and methods required for items rendering. @@ -131,6 +147,7 @@ impl<'a> Render<'a> { fn render_resolution( self, local_name: String, + import_data: Option<(ModPath, ImportScope, Option)>, resolution: &ScopeDef, ) -> Option { use hir::ModuleDef::*; @@ -142,15 +159,15 @@ impl<'a> Render<'a> { let kind = match resolution { ScopeDef::ModuleDef(Function(func)) => { - let item = render_fn(self.ctx, Some(local_name), *func); + let item = render_fn(self.ctx, import_data, Some(local_name), *func); return Some(item); } ScopeDef::ModuleDef(EnumVariant(var)) => { - let item = render_enum_variant(self.ctx, Some(local_name), *var, None); + let item = render_enum_variant(self.ctx, import_data, Some(local_name), *var, None); return Some(item); } ScopeDef::MacroDef(mac) => { - let item = render_macro(self.ctx, local_name, *mac); + let item = render_macro(self.ctx, import_data, local_name, *mac); return item; } @@ -175,6 +192,7 @@ impl<'a> Render<'a> { local_name, ) .kind(CompletionItemKind::UnresolvedReference) + .import_data(import_data) .build(); return Some(item); } @@ -227,7 +245,12 @@ impl<'a> Render<'a> { } } - let item = item.kind(kind).set_documentation(docs).set_ref_match(ref_match).build(); + let item = item + .kind(kind) + .import_data(import_data) + .set_documentation(docs) + .set_ref_match(ref_match) + .build(); Some(item) } diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index fd412ed0ee..6070e9b1d7 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs @@ -1,5 +1,6 @@ //! Renderer for `enum` variants. +use assists::utils::{ImportScope, MergeBehaviour}; use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; use itertools::Itertools; use test_utils::mark; @@ -11,11 +12,12 @@ use crate::{ pub(crate) fn render_enum_variant<'a>( ctx: RenderContext<'a>, + import_data: Option<(ModPath, ImportScope, Option)>, local_name: Option, variant: hir::EnumVariant, path: Option, ) -> CompletionItem { - EnumVariantRender::new(ctx, local_name, variant, path).render() + EnumVariantRender::new(ctx, local_name, variant, path).render(import_data) } #[derive(Debug)] @@ -60,7 +62,10 @@ impl<'a> EnumVariantRender<'a> { } } - fn render(self) -> CompletionItem { + fn render( + self, + import_data: Option<(ModPath, ImportScope, Option)>, + ) -> CompletionItem { let mut builder = CompletionItem::new( CompletionKind::Reference, self.ctx.source_range(), @@ -69,6 +74,7 @@ impl<'a> EnumVariantRender<'a> { .kind(CompletionItemKind::EnumVariant) .set_documentation(self.variant.docs(self.ctx.db())) .set_deprecated(self.ctx.is_deprecated(self.variant)) + .import_data(import_data) .detail(self.detail()); if self.variant_kind == StructKind::Tuple { diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs index 4fa6eafd72..9dd5cd18c5 100644 --- a/crates/completion/src/render/function.rs +++ b/crates/completion/src/render/function.rs @@ -1,6 +1,7 @@ //! Renderer for function calls. -use hir::{HasSource, Type}; +use assists::utils::{ImportScope, MergeBehaviour}; +use hir::{HasSource, ModPath, Type}; use syntax::{ast::Fn, display::function_declaration}; use crate::{ @@ -10,10 +11,11 @@ use crate::{ pub(crate) fn render_fn<'a>( ctx: RenderContext<'a>, + import_data: Option<(ModPath, ImportScope, Option)>, local_name: Option, fn_: hir::Function, ) -> CompletionItem { - FunctionRender::new(ctx, local_name, fn_).render() + FunctionRender::new(ctx, local_name, fn_).render(import_data) } #[derive(Debug)] @@ -36,7 +38,10 @@ impl<'a> FunctionRender<'a> { FunctionRender { ctx, name, fn_, ast_node } } - fn render(self) -> CompletionItem { + fn render( + self, + import_data: Option<(ModPath, ImportScope, Option)>, + ) -> CompletionItem { let params = self.params(); CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) .kind(self.kind()) @@ -44,6 +49,7 @@ impl<'a> FunctionRender<'a> { .set_deprecated(self.ctx.is_deprecated(self.fn_)) .detail(self.detail()) .add_call_parens(self.ctx.completion, self.name, params) + .import_data(import_data) .build() } diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index 96be59cc33..fead59e41c 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs @@ -1,6 +1,7 @@ //! Renderer for macro invocations. -use hir::{Documentation, HasSource}; +use assists::utils::{ImportScope, MergeBehaviour}; +use hir::{Documentation, HasSource, ModPath}; use syntax::display::macro_label; use test_utils::mark; @@ -11,10 +12,11 @@ use crate::{ pub(crate) fn render_macro<'a>( ctx: RenderContext<'a>, + import_data: Option<(ModPath, ImportScope, Option)>, name: String, macro_: hir::MacroDef, ) -> Option { - MacroRender::new(ctx, name, macro_).render() + MacroRender::new(ctx, name, macro_).render(import_data) } #[derive(Debug)] @@ -36,7 +38,10 @@ impl<'a> MacroRender<'a> { MacroRender { ctx, name, macro_, docs, bra, ket } } - fn render(&self) -> Option { + fn render( + &self, + import_data: Option<(ModPath, ImportScope, Option)>, + ) -> Option { // FIXME: Currently proc-macro do not have ast-node, // such that it does not have source if self.macro_.is_proc_macro() { @@ -48,6 +53,7 @@ impl<'a> MacroRender<'a> { .kind(CompletionItemKind::Macro) .set_documentation(self.docs.clone()) .set_deprecated(self.ctx.is_deprecated(self.macro_)) + .import_data(import_data) .detail(self.detail()); let needs_bang = self.needs_bang(); diff --git a/crates/text_edit/src/lib.rs b/crates/text_edit/src/lib.rs index 9eef7a8906..eb3c8caa2a 100644 --- a/crates/text_edit/src/lib.rs +++ b/crates/text_edit/src/lib.rs @@ -48,10 +48,6 @@ impl TextEdit { TextEditBuilder::default() } - pub fn into_builder(self) -> TextEditBuilder { - TextEditBuilder { indels: self.indels } - } - pub fn insert(offset: TextSize, text: String) -> TextEdit { let mut builder = TextEdit::builder(); builder.insert(offset, text);