diff --git a/Cargo.lock b/Cargo.lock index 633b997580a3..f50e9edd66ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,7 +253,6 @@ dependencies = [ name = "completion" version = "0.0.0" dependencies = [ - "assists", "base_db", "either", "expect-test", @@ -682,6 +681,7 @@ dependencies = [ "expect-test", "fst", "hir", + "itertools", "log", "once_cell", "profile", diff --git a/crates/assists/Cargo.toml b/crates/assists/Cargo.toml index 108f656e920e..3fd8327d6a32 100644 --- a/crates/assists/Cargo.toml +++ b/crates/assists/Cargo.toml @@ -12,7 +12,7 @@ doctest = false [dependencies] rustc-hash = "1.1.0" itertools = "0.9.0" -either = "1.5.3" +either = "1.6.1" stdx = { path = "../stdx", version = "0.0.0" } syntax = { path = "../syntax", version = "0.0.0" } diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs index b24527ec4d43..786224cfa1ca 100644 --- a/crates/assists/src/assist_config.rs +++ b/crates/assists/src/assist_config.rs @@ -5,8 +5,9 @@ //! assists if we are allowed to. use hir::PrefixKind; +use ide_db::helpers::insert_use::MergeBehaviour; -use crate::{utils::MergeBehaviour, AssistKind}; +use crate::AssistKind; #[derive(Clone, Debug, PartialEq, Eq)] pub struct AssistConfig { diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs index ac72f3f02e24..66e4634b1d30 100644 --- a/crates/assists/src/ast_transform.rs +++ b/crates/assists/src/ast_transform.rs @@ -1,5 +1,6 @@ //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. use hir::{HirDisplay, PathResolution, SemanticsScope}; +use ide_db::helpers::mod_path_to_ast; use rustc_hash::FxHashMap; use syntax::{ algo::SyntaxRewriter, @@ -7,8 +8,6 @@ use syntax::{ SyntaxNode, }; -use crate::utils::mod_path_to_ast; - pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { SyntaxRewriter::from_fn(|element| match element { syntax::SyntaxElement::Node(n) => { diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs index d665837a2f3b..bd5bba64601e 100644 --- a/crates/assists/src/handlers/auto_import.rs +++ b/crates/assists/src/handlers/auto_import.rs @@ -1,8 +1,11 @@ +use ide_db::helpers::{ + insert_use::{insert_use, ImportScope}, + mod_path_to_ast, +}; use syntax::ast; use crate::{ utils::import_assets::{ImportAssets, ImportCandidate}, - utils::{insert_use, mod_path_to_ast, ImportScope}, AssistContext, AssistId, AssistKind, Assists, GroupLabel, }; diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index cac77c49bbc4..d85767b4ecdc 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs @@ -2,6 +2,10 @@ use std::iter; use either::Either; use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; +use ide_db::helpers::{ + insert_use::{insert_use, ImportScope}, + mod_path_to_ast, +}; use ide_db::{defs::Definition, search::Reference, RootDatabase}; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ @@ -10,10 +14,7 @@ use syntax::{ SourceFile, SyntaxElement, SyntaxNode, T, }; -use crate::{ - utils::{insert_use, mod_path_to_ast, ImportScope}, - AssistContext, AssistId, AssistKind, Assists, -}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: extract_struct_from_enum_variant // @@ -236,10 +237,9 @@ fn update_reference( #[cfg(test)] mod tests { - use crate::{ - tests::{check_assist, check_assist_not_applicable}, - utils::FamousDefs, - }; + use ide_db::helpers::FamousDefs; + + use crate::tests::{check_assist, check_assist_not_applicable}; use super::*; diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs index eda45f5b3fe2..ef12ef0cf1ad 100644 --- a/crates/assists/src/handlers/fill_match_arms.rs +++ b/crates/assists/src/handlers/fill_match_arms.rs @@ -1,13 +1,14 @@ use std::iter; use hir::{Adt, HasSource, ModuleDef, Semantics}; +use ide_db::helpers::{mod_path_to_ast, FamousDefs}; use ide_db::RootDatabase; use itertools::Itertools; use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; use test_utils::mark; use crate::{ - utils::{mod_path_to_ast, render_snippet, Cursor, FamousDefs}, + utils::{render_snippet, Cursor}, AssistContext, AssistId, AssistKind, Assists, }; @@ -212,12 +213,10 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O #[cfg(test)] mod tests { + use ide_db::helpers::FamousDefs; use test_utils::mark; - use crate::{ - tests::{check_assist, check_assist_not_applicable, check_assist_target}, - utils::FamousDefs, - }; + use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; use super::fill_match_arms; diff --git a/crates/assists/src/handlers/generate_from_impl_for_enum.rs b/crates/assists/src/handlers/generate_from_impl_for_enum.rs index 674e5a175715..3c374e5d95bc 100644 --- a/crates/assists/src/handlers/generate_from_impl_for_enum.rs +++ b/crates/assists/src/handlers/generate_from_impl_for_enum.rs @@ -1,8 +1,9 @@ +use ide_db::helpers::FamousDefs; use ide_db::RootDatabase; use syntax::ast::{self, AstNode, NameOwner}; use test_utils::mark; -use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: generate_from_impl_for_enum // diff --git a/crates/assists/src/handlers/merge_imports.rs b/crates/assists/src/handlers/merge_imports.rs index fd9c9e03c725..b7e853994b3f 100644 --- a/crates/assists/src/handlers/merge_imports.rs +++ b/crates/assists/src/handlers/merge_imports.rs @@ -1,3 +1,4 @@ +use ide_db::helpers::insert_use::{try_merge_imports, try_merge_trees, MergeBehaviour}; use syntax::{ algo::{neighbor, SyntaxRewriter}, ast, AstNode, @@ -5,10 +6,7 @@ use syntax::{ use crate::{ assist_context::{AssistContext, Assists}, - utils::{ - insert_use::{try_merge_imports, try_merge_trees}, - next_prev, MergeBehaviour, - }, + utils::next_prev, AssistId, AssistKind, }; diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs index d5bc4e574f18..6f9810fe88dc 100644 --- a/crates/assists/src/handlers/qualify_path.rs +++ b/crates/assists/src/handlers/qualify_path.rs @@ -1,6 +1,7 @@ use std::iter; use hir::AsName; +use ide_db::helpers::mod_path_to_ast; use ide_db::RootDatabase; use syntax::{ ast, @@ -12,7 +13,6 @@ use test_utils::mark; use crate::{ assist_context::{AssistContext, Assists}, utils::import_assets::{ImportAssets, ImportCandidate}, - utils::mod_path_to_ast, AssistId, AssistKind, GroupLabel, }; diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs index 453a6cebfb8c..4d6a1956bb19 100644 --- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs @@ -1,3 +1,4 @@ +use ide_db::helpers::mod_path_to_ast; use ide_db::imports_locator; use itertools::Itertools; use syntax::{ @@ -10,8 +11,7 @@ use syntax::{ use crate::{ assist_context::{AssistBuilder, AssistContext, Assists}, utils::{ - add_trait_assoc_items_to_impl, filter_assoc_items, mod_path_to_ast, render_snippet, Cursor, - DefaultMethods, + add_trait_assoc_items_to_impl, filter_assoc_items, render_snippet, Cursor, DefaultMethods, }, AssistId, AssistKind, }; diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs index a66db9ae3a15..8bdf9eea5e47 100644 --- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs @@ -1,10 +1,8 @@ +use ide_db::helpers::insert_use::{insert_use, ImportScope}; use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; use test_utils::mark; -use crate::{ - utils::{insert_use, ImportScope}, - AssistContext, AssistId, AssistKind, Assists, -}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: replace_qualified_name_with_use // diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 0487465874a8..01f5c291fb35 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs @@ -1,10 +1,9 @@ //! Assorted functions shared by several assists. -pub(crate) mod insert_use; pub(crate) mod import_assets; use std::ops; -use hir::{Crate, Enum, HasSource, Module, ScopeDef, Semantics, Trait}; +use hir::HasSource; use ide_db::RootDatabase; use itertools::Itertools; use syntax::{ @@ -22,30 +21,6 @@ use crate::{ ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, }; -pub use insert_use::{insert_use, ImportScope, MergeBehaviour}; - -pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { - let _p = profile::span("mod_path_to_ast"); - let mut segments = Vec::new(); - let mut is_abs = false; - match path.kind { - hir::PathKind::Plain => {} - hir::PathKind::Super(0) => segments.push(make::path_segment_self()), - hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())), - hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => { - segments.push(make::path_segment_crate()) - } - hir::PathKind::Abs => is_abs = true, - } - - segments.extend( - path.segments - .iter() - .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))), - ); - make::path_from_segments(segments, is_abs) -} - pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { extract_trivial_expression(&block) .filter(|expr| !expr.syntax().text().contains_char('\n')) @@ -260,179 +235,6 @@ fn invert_special_case(expr: &ast::Expr) -> Option { } } -/// Helps with finding well-know things inside the standard library. This is -/// somewhat similar to the known paths infra inside hir, but it different; We -/// want to make sure that IDE specific paths don't become interesting inside -/// the compiler itself as well. -pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option); - -#[allow(non_snake_case)] -impl FamousDefs<'_, '_> { - pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core -pub mod convert { - pub trait From { - fn from(t: T) -> Self; - } -} - -pub mod default { - pub trait Default { - fn default() -> Self; - } -} - -pub mod iter { - pub use self::traits::{collect::IntoIterator, iterator::Iterator}; - mod traits { - pub(crate) mod iterator { - use crate::option::Option; - pub trait Iterator { - type Item; - fn next(&mut self) -> Option; - fn by_ref(&mut self) -> &mut Self { - self - } - fn take(self, n: usize) -> crate::iter::Take { - crate::iter::Take { inner: self } - } - } - - impl Iterator for &mut I { - type Item = I::Item; - fn next(&mut self) -> Option { - (**self).next() - } - } - } - pub(crate) mod collect { - pub trait IntoIterator { - type Item; - } - } - } - - pub use self::sources::*; - pub(crate) mod sources { - use super::Iterator; - use crate::option::Option::{self, *}; - pub struct Repeat { - element: A, - } - - pub fn repeat(elt: T) -> Repeat { - Repeat { element: elt } - } - - impl Iterator for Repeat { - type Item = A; - - fn next(&mut self) -> Option { - None - } - } - } - - pub use self::adapters::*; - pub(crate) mod adapters { - use super::Iterator; - use crate::option::Option::{self, *}; - pub struct Take { pub(crate) inner: I } - impl Iterator for Take where I: Iterator { - type Item = ::Item; - fn next(&mut self) -> Option<::Item> { - None - } - } - } -} - -pub mod option { - pub enum Option { None, Some(T)} -} - -pub mod prelude { - pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default}; -} -#[prelude_import] -pub use prelude::*; -"#; - - pub fn core(&self) -> Option { - self.find_crate("core") - } - - pub(crate) fn core_convert_From(&self) -> Option { - self.find_trait("core:convert:From") - } - - pub(crate) fn core_option_Option(&self) -> Option { - self.find_enum("core:option:Option") - } - - pub fn core_default_Default(&self) -> Option { - self.find_trait("core:default:Default") - } - - pub fn core_iter_Iterator(&self) -> Option { - self.find_trait("core:iter:traits:iterator:Iterator") - } - - pub fn core_iter(&self) -> Option { - self.find_module("core:iter") - } - - fn find_trait(&self, path: &str) -> Option { - match self.find_def(path)? { - hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), - _ => None, - } - } - - fn find_enum(&self, path: &str) -> Option { - match self.find_def(path)? { - hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it), - _ => None, - } - } - - fn find_module(&self, path: &str) -> Option { - match self.find_def(path)? { - hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(it)) => Some(it), - _ => None, - } - } - - fn find_crate(&self, name: &str) -> Option { - let krate = self.1?; - let db = self.0.db; - let res = - krate.dependencies(db).into_iter().find(|dep| dep.name.to_string() == name)?.krate; - Some(res) - } - - fn find_def(&self, path: &str) -> Option { - let db = self.0.db; - let mut path = path.split(':'); - let trait_ = path.next_back()?; - let std_crate = path.next()?; - let std_crate = self.find_crate(std_crate)?; - let mut module = std_crate.root_module(db); - for segment in path { - module = module.children(db).find_map(|child| { - let name = child.name(db)?; - if name.to_string() == segment { - Some(child) - } else { - None - } - })?; - } - let def = - module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1; - Some(def) - } -} - pub(crate) fn next_prev() -> impl Iterator { [Direction::Next, Direction::Prev].iter().copied() } diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml index e7df9d955656..35e169a28c11 100644 --- a/crates/completion/Cargo.toml +++ b/crates/completion/Cargo.toml @@ -15,7 +15,6 @@ log = "0.4.8" rustc-hash = "1.1.0" either = "1.6.1" -assists = { path = "../assists", version = "0.0.0" } stdx = { path = "../stdx", version = "0.0.0" } syntax = { path = "../syntax", version = "0.0.0" } text_edit = { path = "../text_edit", version = "0.0.0" } diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs index 2049b9d09116..eaa44c97d1f1 100644 --- a/crates/completion/src/completions/record.rs +++ b/crates/completion/src/completions/record.rs @@ -1,5 +1,5 @@ //! Complete fields in record literals and patterns. -use assists::utils::FamousDefs; +use ide_db::helpers::FamousDefs; use syntax::ast::Expr; use crate::{ @@ -45,8 +45,8 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> #[cfg(test)] mod tests { - use assists::utils::FamousDefs; use expect_test::{expect, Expect}; + use ide_db::helpers::FamousDefs; use crate::{test_utils::completion_list, CompletionKind}; diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 3bd776905226..81691cd7f23b 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -1,8 +1,8 @@ //! Completion of names from the current scope, e.g. locals and imported items. -use assists::utils::ImportScope; use either::Either; use hir::{Adt, ModuleDef, ScopeDef, Type}; +use ide_db::helpers::insert_use::ImportScope; use ide_db::imports_locator; use syntax::AstNode; use test_utils::mark; diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index f5073537295b..654a76f7b33e 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs @@ -4,7 +4,7 @@ //! module, and we use to statically check that we only produce snippet //! completions if we are allowed to. -use assists::utils::MergeBehaviour; +use ide_db::helpers::insert_use::MergeBehaviour; #[derive(Clone, Debug, PartialEq, Eq)] pub struct CompletionConfig { diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 7b62c2c4ed13..e85549fef4d4 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs @@ -2,8 +2,11 @@ use std::fmt; -use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour}; use hir::{Documentation, ModPath, Mutability}; +use ide_db::helpers::{ + insert_use::{self, ImportScope, MergeBehaviour}, + mod_path_to_ast, +}; use syntax::{algo, TextRange}; use text_edit::TextEdit; @@ -201,7 +204,7 @@ impl CompletionItem { trigger_call_info: None, score: None, ref_match: None, - import_data: None, + import_to_add: None, } } @@ -255,13 +258,21 @@ impl CompletionItem { } } +/// An extra import to add after the completion is applied. +#[derive(Clone)] +pub(crate) struct ImportToAdd { + pub(crate) import_path: ModPath, + pub(crate) import_scope: ImportScope, + pub(crate) merge_behaviour: Option, +} + /// A helper to make `CompletionItem`s. #[must_use] #[derive(Clone)] pub(crate) struct Builder { source_range: TextRange, completion_kind: CompletionKind, - import_data: Option<(ModPath, ImportScope, Option)>, + import_to_add: Option, label: String, insert_text: Option, insert_text_format: InsertTextFormat, @@ -285,9 +296,9 @@ impl Builder { 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; + if let Some(import_data) = self.import_to_add { + let import = mod_path_to_ast(&import_data.import_path); + let mut import_path_without_last_segment = import_data.import_path; let _ = import_path_without_last_segment.segments.pop(); if !import_path_without_last_segment.segments.is_empty() { @@ -300,7 +311,11 @@ impl Builder { label = format!("{}::{}", import_path_without_last_segment, label); } - let rewriter = insert_use(&import_scope, import, merge_behaviour); + let rewriter = insert_use::insert_use( + &import_data.import_scope, + import, + import_data.merge_behaviour, + ); if let Some(old_ast) = rewriter.rewrite_root() { algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits); } @@ -392,11 +407,8 @@ 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; + pub(crate) fn add_import(mut self, import_to_add: Option) -> Builder { + self.import_to_add = import_to_add; self } pub(crate) fn set_ref_match( diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index bce02f577388..504757a6ae2f 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs @@ -9,15 +9,15 @@ pub(crate) mod type_alias; mod builder_ext; -use assists::utils::{ImportScope, MergeBehaviour}; use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type}; +use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour}; use ide_db::RootDatabase; use syntax::TextRange; use test_utils::mark; use crate::{ - config::SnippetCap, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, - CompletionScore, + config::SnippetCap, item::ImportToAdd, CompletionContext, CompletionItem, CompletionItemKind, + CompletionKind, CompletionScore, }; use crate::render::{enum_variant::render_enum_variant, function::render_fn, macro_::render_macro}; @@ -48,15 +48,15 @@ pub(crate) fn render_resolution<'a>( pub(crate) fn render_resolution_with_import<'a>( ctx: RenderContext<'a>, - import: ModPath, + import_path: ModPath, import_scope: ImportScope, merge_behaviour: Option, resolution: &ScopeDef, ) -> Option { - let local_name = import.segments.last()?.to_string(); + let local_name = import_path.segments.last()?.to_string(); Render::new(ctx).render_resolution( local_name, - Some((import, import_scope, merge_behaviour)), + Some(ImportToAdd { import_path, import_scope, merge_behaviour }), resolution, ) } @@ -147,7 +147,7 @@ impl<'a> Render<'a> { fn render_resolution( self, local_name: String, - import_data: Option<(ModPath, ImportScope, Option)>, + import_to_add: Option, resolution: &ScopeDef, ) -> Option { let _p = profile::span("render_resolution"); @@ -160,15 +160,16 @@ impl<'a> Render<'a> { let kind = match resolution { ScopeDef::ModuleDef(Function(func)) => { - let item = render_fn(self.ctx, import_data, Some(local_name), *func); + let item = render_fn(self.ctx, import_to_add, Some(local_name), *func); return Some(item); } ScopeDef::ModuleDef(EnumVariant(var)) => { - let item = render_enum_variant(self.ctx, import_data, Some(local_name), *var, None); + let item = + render_enum_variant(self.ctx, import_to_add, Some(local_name), *var, None); return Some(item); } ScopeDef::MacroDef(mac) => { - let item = render_macro(self.ctx, import_data, local_name, *mac); + let item = render_macro(self.ctx, import_to_add, local_name, *mac); return item; } @@ -193,7 +194,7 @@ impl<'a> Render<'a> { local_name, ) .kind(CompletionItemKind::UnresolvedReference) - .import_data(import_data) + .add_import(import_to_add) .build(); return Some(item); } @@ -248,7 +249,7 @@ impl<'a> Render<'a> { let item = item .kind(kind) - .import_data(import_data) + .add_import(import_to_add) .set_documentation(docs) .set_ref_match(ref_match) .build(); diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index 64e742b77900..f4bd02f25815 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs @@ -1,24 +1,23 @@ //! Renderer for `enum` variants. -use assists::utils::{ImportScope, MergeBehaviour}; use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; use itertools::Itertools; use test_utils::mark; use crate::{ - item::{CompletionItem, CompletionItemKind, CompletionKind}, + item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, render::{builder_ext::Params, RenderContext}, }; pub(crate) fn render_enum_variant<'a>( ctx: RenderContext<'a>, - import_data: Option<(ModPath, ImportScope, Option)>, + import_to_add: Option, local_name: Option, variant: hir::EnumVariant, path: Option, ) -> CompletionItem { let _p = profile::span("render_enum_variant"); - EnumVariantRender::new(ctx, local_name, variant, path).render(import_data) + EnumVariantRender::new(ctx, local_name, variant, path).render(import_to_add) } #[derive(Debug)] @@ -63,10 +62,7 @@ impl<'a> EnumVariantRender<'a> { } } - fn render( - self, - import_data: Option<(ModPath, ImportScope, Option)>, - ) -> CompletionItem { + fn render(self, import_to_add: Option) -> CompletionItem { let mut builder = CompletionItem::new( CompletionKind::Reference, self.ctx.source_range(), @@ -75,7 +71,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) + .add_import(import_to_add) .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 e8b726ad61b3..542383d7e770 100644 --- a/crates/completion/src/render/function.rs +++ b/crates/completion/src/render/function.rs @@ -1,22 +1,21 @@ //! Renderer for function calls. -use assists::utils::{ImportScope, MergeBehaviour}; -use hir::{HasSource, ModPath, Type}; +use hir::{HasSource, Type}; use syntax::{ast::Fn, display::function_declaration}; use crate::{ - item::{CompletionItem, CompletionItemKind, CompletionKind}, + item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, render::{builder_ext::Params, RenderContext}, }; pub(crate) fn render_fn<'a>( ctx: RenderContext<'a>, - import_data: Option<(ModPath, ImportScope, Option)>, + import_to_add: Option, local_name: Option, fn_: hir::Function, ) -> CompletionItem { let _p = profile::span("render_fn"); - FunctionRender::new(ctx, local_name, fn_).render(import_data) + FunctionRender::new(ctx, local_name, fn_).render(import_to_add) } #[derive(Debug)] @@ -39,10 +38,7 @@ impl<'a> FunctionRender<'a> { FunctionRender { ctx, name, fn_, ast_node } } - fn render( - self, - import_data: Option<(ModPath, ImportScope, Option)>, - ) -> CompletionItem { + fn render(self, import_to_add: Option) -> CompletionItem { let params = self.params(); CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) .kind(self.kind()) @@ -50,7 +46,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) + .add_import(import_to_add) .build() } diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index 91055a2969a7..b4ab32c6e43c 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs @@ -1,23 +1,22 @@ //! Renderer for macro invocations. -use assists::utils::{ImportScope, MergeBehaviour}; -use hir::{Documentation, HasSource, ModPath}; +use hir::{Documentation, HasSource}; use syntax::display::macro_label; use test_utils::mark; use crate::{ - item::{CompletionItem, CompletionItemKind, CompletionKind}, + item::{CompletionItem, CompletionItemKind, CompletionKind, ImportToAdd}, render::RenderContext, }; pub(crate) fn render_macro<'a>( ctx: RenderContext<'a>, - import_data: Option<(ModPath, ImportScope, Option)>, + import_to_add: Option, name: String, macro_: hir::MacroDef, ) -> Option { let _p = profile::span("render_macro"); - MacroRender::new(ctx, name, macro_).render(import_data) + MacroRender::new(ctx, name, macro_).render(import_to_add) } #[derive(Debug)] @@ -39,10 +38,7 @@ impl<'a> MacroRender<'a> { MacroRender { ctx, name, macro_, docs, bra, ket } } - fn render( - &self, - import_data: Option<(ModPath, ImportScope, Option)>, - ) -> Option { + fn render(&self, import_to_add: Option) -> Option { // FIXME: Currently proc-macro do not have ast-node, // such that it does not have source if self.macro_.is_proc_macro() { @@ -54,7 +50,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) + .add_import(import_to_add) .detail(self.detail()); let needs_bang = self.needs_bang(); diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 6cfb22e13ee3..65df7979cfa2 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -1,6 +1,6 @@ -use assists::utils::FamousDefs; use either::Either; use hir::{known, Callable, HirDisplay, Semantics}; +use ide_db::helpers::FamousDefs; use ide_db::RootDatabase; use stdx::to_lower_snake_case; use syntax::{ @@ -427,8 +427,8 @@ fn get_callable(sema: &Semantics, expr: &ast::Expr) -> Option ast::Path { + let _p = profile::span("mod_path_to_ast"); + + let mut segments = Vec::new(); + let mut is_abs = false; + match path.kind { + hir::PathKind::Plain => {} + hir::PathKind::Super(0) => segments.push(make::path_segment_self()), + hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())), + hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => { + segments.push(make::path_segment_crate()) + } + hir::PathKind::Abs => is_abs = true, + } + + segments.extend( + path.segments + .iter() + .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))), + ); + make::path_from_segments(segments, is_abs) +} + +/// Helps with finding well-know things inside the standard library. This is +/// somewhat similar to the known paths infra inside hir, but it different; We +/// want to make sure that IDE specific paths don't become interesting inside +/// the compiler itself as well. +pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option); + +#[allow(non_snake_case)] +impl FamousDefs<'_, '_> { + pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core +pub mod convert { + pub trait From { + fn from(t: T) -> Self; + } +} + +pub mod default { + pub trait Default { + fn default() -> Self; + } +} + +pub mod iter { + pub use self::traits::{collect::IntoIterator, iterator::Iterator}; + mod traits { + pub(crate) mod iterator { + use crate::option::Option; + pub trait Iterator { + type Item; + fn next(&mut self) -> Option; + fn by_ref(&mut self) -> &mut Self { + self + } + fn take(self, n: usize) -> crate::iter::Take { + crate::iter::Take { inner: self } + } + } + + impl Iterator for &mut I { + type Item = I::Item; + fn next(&mut self) -> Option { + (**self).next() + } + } + } + pub(crate) mod collect { + pub trait IntoIterator { + type Item; + } + } + } + + pub use self::sources::*; + pub(crate) mod sources { + use super::Iterator; + use crate::option::Option::{self, *}; + pub struct Repeat { + element: A, + } + + pub fn repeat(elt: T) -> Repeat { + Repeat { element: elt } + } + + impl Iterator for Repeat { + type Item = A; + + fn next(&mut self) -> Option { + None + } + } + } + + pub use self::adapters::*; + pub(crate) mod adapters { + use super::Iterator; + use crate::option::Option::{self, *}; + pub struct Take { pub(crate) inner: I } + impl Iterator for Take where I: Iterator { + type Item = ::Item; + fn next(&mut self) -> Option<::Item> { + None + } + } + } +} + +pub mod option { + pub enum Option { None, Some(T)} +} + +pub mod prelude { + pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default}; +} +#[prelude_import] +pub use prelude::*; +"#; + + pub fn core(&self) -> Option { + self.find_crate("core") + } + + pub fn core_convert_From(&self) -> Option { + self.find_trait("core:convert:From") + } + + pub fn core_option_Option(&self) -> Option { + self.find_enum("core:option:Option") + } + + pub fn core_default_Default(&self) -> Option { + self.find_trait("core:default:Default") + } + + pub fn core_iter_Iterator(&self) -> Option { + self.find_trait("core:iter:traits:iterator:Iterator") + } + + pub fn core_iter(&self) -> Option { + self.find_module("core:iter") + } + + fn find_trait(&self, path: &str) -> Option { + match self.find_def(path)? { + hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), + _ => None, + } + } + + fn find_enum(&self, path: &str) -> Option { + match self.find_def(path)? { + hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it), + _ => None, + } + } + + fn find_module(&self, path: &str) -> Option { + match self.find_def(path)? { + hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(it)) => Some(it), + _ => None, + } + } + + fn find_crate(&self, name: &str) -> Option { + let krate = self.1?; + let db = self.0.db; + let res = + krate.dependencies(db).into_iter().find(|dep| dep.name.to_string() == name)?.krate; + Some(res) + } + + fn find_def(&self, path: &str) -> Option { + let db = self.0.db; + let mut path = path.split(':'); + let trait_ = path.next_back()?; + let std_crate = path.next()?; + let std_crate = self.find_crate(std_crate)?; + let mut module = std_crate.root_module(db); + for segment in path { + module = module.children(db).find_map(|child| { + let name = child.name(db)?; + if name.to_string() == segment { + Some(child) + } else { + None + } + })?; + } + let def = + module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1; + Some(def) + } +} diff --git a/crates/assists/src/utils/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs similarity index 99% rename from crates/assists/src/utils/insert_use.rs rename to crates/ide_db/src/helpers/insert_use.rs index 304adb93d4e2..67e800fad88f 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs @@ -1,8 +1,8 @@ //! Handle syntactic aspects of inserting a new `use`. use std::{cmp::Ordering, iter::successors}; +use crate::RootDatabase; use hir::Semantics; -use ide_db::RootDatabase; use itertools::{EitherOrBoth, Itertools}; use syntax::{ algo::SyntaxRewriter, @@ -22,7 +22,7 @@ pub enum ImportScope { } impl ImportScope { - pub(crate) fn from(syntax: SyntaxNode) -> Option { + pub fn from(syntax: SyntaxNode) -> Option { if let Some(module) = ast::Module::cast(syntax.clone()) { module.item_list().map(ImportScope::Module) } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) { @@ -180,7 +180,7 @@ fn eq_visibility(vis0: Option, vis1: Option) - } } -pub(crate) fn try_merge_imports( +pub fn try_merge_imports( lhs: &ast::Use, rhs: &ast::Use, merge_behaviour: MergeBehaviour, @@ -195,7 +195,7 @@ pub(crate) fn try_merge_imports( Some(lhs.with_use_tree(merged)) } -pub(crate) fn try_merge_trees( +pub fn try_merge_trees( lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehaviour, diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 05139a651584..fceaa089ade5 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs @@ -13,6 +13,7 @@ pub mod source_change; pub mod ty_filter; pub mod traits; pub mod call_info; +pub mod helpers; use std::{fmt, sync::Arc}; diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 436f5041bc45..08559b53adbd 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -39,6 +39,7 @@ tracing-tree = { version = "0.1.4" } stdx = { path = "../stdx", version = "0.0.0" } flycheck = { path = "../flycheck", version = "0.0.0" } ide = { path = "../ide", version = "0.0.0" } +ide_db = { path = "../ide_db", version = "0.0.0" } profile = { path = "../profile", version = "0.0.0" } project_model = { path = "../project_model", version = "0.0.0" } syntax = { path = "../syntax", version = "0.0.0" } @@ -49,7 +50,6 @@ cfg = { path = "../cfg", version = "0.0.0" } toolchain = { path = "../toolchain", version = "0.0.0" } # This should only be used in CLI -ide_db = { path = "../ide_db", version = "0.0.0" } ssr = { path = "../ssr", version = "0.0.0" } hir = { path = "../hir", version = "0.0.0" } hir_def = { path = "../hir_def", version = "0.0.0" } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index a334cdb116a2..30299a4659e8 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -11,10 +11,8 @@ use std::{convert::TryFrom, ffi::OsString, path::PathBuf}; use flycheck::FlycheckConfig; use hir::PrefixKind; -use ide::{ - AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, - MergeBehaviour, -}; +use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig}; +use ide_db::helpers::insert_use::MergeBehaviour; use lsp_types::{ClientCapabilities, MarkupKind}; use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; use rustc_hash::FxHashSet;