diff --git a/Cargo.lock b/Cargo.lock index 633b997580..e7d1782ccc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,6 +53,7 @@ dependencies = [ "either", "hir", "ide_db", + "ide_helpers", "itertools", "profile", "rustc-hash", @@ -253,12 +254,12 @@ dependencies = [ name = "completion" version = "0.0.0" dependencies = [ - "assists", "base_db", "either", "expect-test", "hir", "ide_db", + "ide_helpers", "itertools", "log", "profile", @@ -657,6 +658,7 @@ dependencies = [ "expect-test", "hir", "ide_db", + "ide_helpers", "indexmap", "itertools", "log", @@ -693,6 +695,19 @@ dependencies = [ "text_edit", ] +[[package]] +name = "ide_helpers" +version = "0.0.0" +dependencies = [ + "either", + "hir", + "ide_db", + "itertools", + "profile", + "syntax", + "test_utils", +] + [[package]] name = "idna" version = "0.2.0" @@ -1361,6 +1376,7 @@ dependencies = [ "hir_ty", "ide", "ide_db", + "ide_helpers", "itertools", "jod-thread", "log", diff --git a/crates/assists/Cargo.toml b/crates/assists/Cargo.toml index 108f656e92..91d9b6e3f7 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" } @@ -21,3 +21,4 @@ profile = { path = "../profile", version = "0.0.0" } ide_db = { path = "../ide_db", version = "0.0.0" } hir = { path = "../hir", version = "0.0.0" } test_utils = { path = "../test_utils", version = "0.0.0" } +ide_helpers = { path = "../ide_helpers", version = "0.0.0" } diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs index b24527ec4d..6d9934e931 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_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 ac72f3f02e..95b0605993 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_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 d665837a2f..0b2d508d59 100644 --- a/crates/assists/src/handlers/auto_import.rs +++ b/crates/assists/src/handlers/auto_import.rs @@ -1,8 +1,11 @@ +use ide_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 cac77c49bb..fddd5354aa 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs @@ -3,6 +3,10 @@ use std::iter; use either::Either; use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; use ide_db::{defs::Definition, search::Reference, RootDatabase}; +use ide_helpers::{ + insert_use::{insert_use, ImportScope}, + mod_path_to_ast, +}; use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ algo::{find_node_at_offset, SyntaxRewriter}, @@ -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_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 eda45f5b3f..bd42e0f166 100644 --- a/crates/assists/src/handlers/fill_match_arms.rs +++ b/crates/assists/src/handlers/fill_match_arms.rs @@ -2,12 +2,13 @@ use std::iter; use hir::{Adt, HasSource, ModuleDef, Semantics}; use ide_db::RootDatabase; +use ide_helpers::{mod_path_to_ast, FamousDefs}; 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_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 674e5a1757..01b14d94d5 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::RootDatabase; +use ide_helpers::FamousDefs; 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 fd9c9e03c7..8207f0e6eb 100644 --- a/crates/assists/src/handlers/merge_imports.rs +++ b/crates/assists/src/handlers/merge_imports.rs @@ -1,3 +1,4 @@ +use ide_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 d5bc4e574f..c0ee7ea0b8 100644 --- a/crates/assists/src/handlers/qualify_path.rs +++ b/crates/assists/src/handlers/qualify_path.rs @@ -2,6 +2,7 @@ use std::iter; use hir::AsName; use ide_db::RootDatabase; +use ide_helpers::mod_path_to_ast; use syntax::{ ast, ast::{make, ArgListOwner}, @@ -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 453a6cebfb..fe262377c9 100644 --- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs @@ -1,4 +1,5 @@ use ide_db::imports_locator; +use ide_helpers::mod_path_to_ast; use itertools::Itertools; use syntax::{ ast::{self, make, AstNode}, @@ -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 a66db9ae3a..9b8caacd99 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_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 // @@ -53,7 +51,7 @@ pub(crate) fn replace_qualified_name_with_use( ) } -/// Adds replacements to `re` that shorten `path` in all descendants of `node`. +/// Adds replacements to `re` that shorten `path` in all descendants of `node`.g fn shorten_paths(rewriter: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: &ast::Path) { for child in node.children() { match_ast! { diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 0487465874..01f5c291fb 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 e7df9d9556..102de33f8f 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" } @@ -23,6 +22,7 @@ base_db = { path = "../base_db", version = "0.0.0" } ide_db = { path = "../ide_db", version = "0.0.0" } profile = { path = "../profile", version = "0.0.0" } test_utils = { path = "../test_utils", version = "0.0.0" } +ide_helpers = { path = "../ide_helpers", version = "0.0.0" } # completions crate should depend only on the top-level `hir` package. if you need # something from some `hir_xxx` subpackage, reexport the API via `hir`. diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs index 2049b9d091..218a1923c5 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_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_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 3bd7769052..db5dbb7ddb 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -1,9 +1,9 @@ //! 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::imports_locator; +use ide_helpers::insert_use::ImportScope; use syntax::AstNode; use test_utils::mark; diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index f507353729..1995b07548 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_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 7b62c2c4ed..675cef8c45 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_helpers::{ + insert_use::{self, ImportScope, MergeBehaviour}, + mod_path_to_ast, +}; use syntax::{algo, TextRange}; use text_edit::TextEdit; @@ -300,7 +303,7 @@ 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_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); } diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index bce02f5773..e9704c27c9 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs @@ -9,9 +9,9 @@ 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::RootDatabase; +use ide_helpers::insert_use::{ImportScope, MergeBehaviour}; use syntax::TextRange; use test_utils::mark; diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs index 64e742b779..5d4fbb6411 100644 --- a/crates/completion/src/render/enum_variant.rs +++ b/crates/completion/src/render/enum_variant.rs @@ -1,7 +1,7 @@ //! Renderer for `enum` variants. -use assists::utils::{ImportScope, MergeBehaviour}; use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; +use ide_helpers::insert_use::{ImportScope, MergeBehaviour}; use itertools::Itertools; use test_utils::mark; diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs index e8b726ad61..07e99058af 100644 --- a/crates/completion/src/render/function.rs +++ b/crates/completion/src/render/function.rs @@ -1,7 +1,7 @@ //! Renderer for function calls. -use assists::utils::{ImportScope, MergeBehaviour}; use hir::{HasSource, ModPath, Type}; +use ide_helpers::insert_use::{ImportScope, MergeBehaviour}; use syntax::{ast::Fn, display::function_declaration}; use crate::{ diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index 91055a2969..b1284f2019 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs @@ -1,7 +1,7 @@ //! Renderer for macro invocations. -use assists::utils::{ImportScope, MergeBehaviour}; use hir::{Documentation, HasSource, ModPath}; +use ide_helpers::insert_use::{ImportScope, MergeBehaviour}; use syntax::display::macro_label; use test_utils::mark; @@ -12,6 +12,7 @@ use crate::{ pub(crate) fn render_macro<'a>( ctx: RenderContext<'a>, + // TODO kb add some object instead of a tuple? import_data: Option<(ModPath, ImportScope, Option)>, name: String, macro_: hir::MacroDef, diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index 4d483580df..92d4e5e9fa 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml @@ -24,6 +24,7 @@ stdx = { path = "../stdx", version = "0.0.0" } syntax = { path = "../syntax", version = "0.0.0" } text_edit = { path = "../text_edit", version = "0.0.0" } ide_db = { path = "../ide_db", version = "0.0.0" } +ide_helpers = { path = "../ide_helpers", version = "0.0.0" } cfg = { path = "../cfg", version = "0.0.0" } profile = { path = "../profile", version = "0.0.0" } test_utils = { path = "../test_utils", version = "0.0.0" } diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 6cfb22e13e..9c8bb7c45c 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -1,7 +1,7 @@ -use assists::utils::FamousDefs; use either::Either; use hir::{known, Callable, HirDisplay, Semantics}; use ide_db::RootDatabase; +use ide_helpers::FamousDefs; use stdx::to_lower_snake_case; use syntax::{ ast::{self, ArgListOwner, AstNode, NameOwner}, @@ -427,8 +427,8 @@ fn get_callable(sema: &Semantics, expr: &ast::Expr) -> Option 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_helpers/src/lib.rs b/crates/ide_helpers/src/lib.rs new file mode 100644 index 0000000000..069cb2bdd2 --- /dev/null +++ b/crates/ide_helpers/src/lib.rs @@ -0,0 +1,201 @@ +use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; +use ide_db::RootDatabase; +use syntax::ast::{self, make}; + +pub mod insert_use; + +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) +} + +/// 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/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 436f5041bc..3afcd2edaa 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_helpers = { path = "../ide_helpers", 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" } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index a334cdb116..d7b711f945 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_helpers::insert_use::MergeBehaviour; use lsp_types::{ClientCapabilities, MarkupKind}; use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; use rustc_hash::FxHashSet;