From d08c63cb9e3574fa97374a8529136814530bf416 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 20 May 2020 23:51:20 +0200 Subject: Add an ImportMap --- crates/ra_db/src/lib.rs | 4 +- crates/ra_hir_def/src/db.rs | 4 + crates/ra_hir_def/src/find_path.rs | 19 +-- crates/ra_hir_def/src/import_map.rs | 323 ++++++++++++++++++++++++++++++++++++ crates/ra_hir_def/src/lib.rs | 1 + crates/ra_hir_def/src/path.rs | 13 ++ crates/ra_hir_def/src/per_ns.rs | 10 +- 7 files changed, 360 insertions(+), 14 deletions(-) create mode 100644 crates/ra_hir_def/src/import_map.rs (limited to 'crates') diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index fd4280de2..e7868268b 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -11,8 +11,8 @@ use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; pub use crate::{ cancellation::Canceled, input::{ - CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId, - FileId, ProcMacroId, SourceRoot, SourceRootId, + CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, + ExternSourceId, FileId, ProcMacroId, SourceRoot, SourceRootId, }, }; pub use relative_path::{RelativePath, RelativePathBuf}; diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index 945a0025e..a23d65371 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -14,6 +14,7 @@ use crate::{ docs::Documentation, find_path, generics::GenericParams, + import_map::ImportMap, item_scope::ItemInNs, lang_item::{LangItemTarget, LangItems}, nameres::{raw::RawItems, CrateDefMap}, @@ -122,6 +123,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast { #[salsa::invoke(find_path::find_path_inner_query)] fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option; + + #[salsa::invoke(ImportMap::import_map_query)] + fn import_map(&self, krate: CrateId) -> Arc; } fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc { diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 4db798473..088e8dd32 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -36,17 +36,6 @@ impl ModPath { let first_segment = self.segments.first(); first_segment == Some(&known::alloc) || first_segment == Some(&known::core) } - - fn len(&self) -> usize { - self.segments.len() - + match self.kind { - PathKind::Plain => 0, - PathKind::Super(i) => i as usize, - PathKind::Crate => 1, - PathKind::Abs => 0, - PathKind::DollarCrate(_) => 1, - } - } } pub(crate) fn find_path_inner_query( @@ -192,9 +181,17 @@ fn find_importable_locations( ) -> Vec<(ModuleId, Name)> { let crate_graph = db.crate_graph(); let mut result = Vec::new(); + // We only look in the crate from which we are importing, and the direct // dependencies. We cannot refer to names from transitive dependencies // directly (only through reexports in direct dependencies). + + // For the crate from which we're importing, we have to check whether any + // module visible to `from` exports the item we're looking for. + // For dependencies of the crate only `pub` items reachable through `pub` + // modules from the crate root are relevant. For that we precompute an + // import map that tells us the shortest path to any importable item with a + // single lookup. for krate in Some(from.krate) .into_iter() .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs new file mode 100644 index 000000000..7dae64efa --- /dev/null +++ b/crates/ra_hir_def/src/import_map.rs @@ -0,0 +1,323 @@ +//! A map of all publicly exported items in a crate. + +use crate::{ + db::DefDatabase, + item_scope::ItemInNs, + path::{ModPath, PathKind}, + visibility::Visibility, + ModuleDefId, ModuleId, +}; +use ra_db::CrateId; +use rustc_hash::FxHashMap; +use std::{collections::hash_map::Entry, sync::Arc}; + +/// A map from publicly exported items to the path needed to import/name them from a downstream +/// crate. +/// +/// Reexports of items are taken into account, ie. if something is exported under multiple +/// names, the one with the shortest import path will be used. +/// +/// Note that all paths are relative to the containing crate's root, so the crate name still needs +/// to be prepended to the `ModPath` before the path is valid. +#[derive(Debug, Eq, PartialEq)] +pub struct ImportMap { + map: FxHashMap, +} + +impl ImportMap { + pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { + let _p = ra_prof::profile("import_map_query"); + let def_map = db.crate_def_map(krate); + let mut import_map = FxHashMap::with_capacity_and_hasher(64, Default::default()); + + // We look only into modules that are public(ly reexported), starting with the crate root. + let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; + let root = ModuleId { krate, local_id: def_map.root }; + let mut worklist = vec![(root, empty)]; + while let Some((module, mod_path)) = worklist.pop() { + let ext_def_map; + let mod_data = if module.krate == krate { + &def_map[module.local_id] + } else { + // The crate might reexport a module defined in another crate. + ext_def_map = db.crate_def_map(module.krate); + &ext_def_map[module.local_id] + }; + + let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| { + let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public); + if per_ns.is_none() { + None + } else { + Some((name, per_ns)) + } + }); + + for (name, per_ns) in visible_items { + let mk_path = || { + let mut path = mod_path.clone(); + path.segments.push(name.clone()); + path + }; + + for item in per_ns.iter_items() { + let path = mk_path(); + match import_map.entry(item) { + Entry::Vacant(entry) => { + entry.insert(path); + } + Entry::Occupied(mut entry) => { + // If the new path is shorter, prefer that one. + if path.len() < entry.get().len() { + *entry.get_mut() = path; + } else { + continue; + } + } + } + + // If we've just added a path to a module, descend into it. + if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { + worklist.push((mod_id, mk_path())); + } + } + } + } + + Arc::new(Self { map: import_map }) + } + + /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. + pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { + self.map.get(&item) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_db::TestDB; + use insta::assert_snapshot; + use ra_db::fixture::WithFixture; + use ra_db::SourceDatabase; + + fn import_map(ra_fixture: &str) -> String { + let db = TestDB::with_files(ra_fixture); + let crate_graph = db.crate_graph(); + + let import_maps: Vec<_> = crate_graph + .iter() + .filter_map(|krate| { + let cdata = &crate_graph[krate]; + let name = cdata.display_name.as_ref()?; + + let map = db.import_map(krate); + + let mut importable_paths: Vec<_> = map + .map + .iter() + .map(|(item, modpath)| { + let ns = match item { + ItemInNs::Types(_) => "t", + ItemInNs::Values(_) => "v", + ItemInNs::Macros(_) => "m", + }; + format!("- {} ({})", modpath, ns) + }) + .collect(); + + importable_paths.sort(); + let importable_paths = importable_paths.join("\n"); + + Some(format!("{}:\n{}", name, importable_paths)) + }) + .collect(); + + import_maps.join("\n") + } + + #[test] + fn smoke() { + let map = import_map( + r" + //- /main.rs crate:main deps:lib + + mod private { + pub use lib::Pub; + pub struct InPrivateModule; + } + + pub mod publ1 { + use lib::Pub; + } + + pub mod real_pub { + pub use lib::Pub; + } + pub mod real_pu2 { // same path length as above + pub use lib::Pub; + } + + //- /lib.rs crate:lib + pub struct Pub {} + pub struct Pub2; // t + v + struct Priv; + ", + ); + + assert_snapshot!(map, @r###" + main: + - publ1 (t) + - real_pu2 (t) + - real_pub (t) + - real_pub::Pub (t) + lib: + - Pub (t) + - Pub2 (t) + - Pub2 (v) + "###); + } + + #[test] + fn prefers_shortest_path() { + let map = import_map( + r" + //- /main.rs crate:main + + pub mod sub { + pub mod subsub { + pub struct Def {} + } + + pub use super::sub::subsub::Def; + } + ", + ); + + assert_snapshot!(map, @r###" + main: + - sub (t) + - sub::Def (t) + - sub::subsub (t) + "###); + } + + #[test] + fn type_reexport_cross_crate() { + // Reexports need to be visible from a crate, even if the original crate exports the item + // at a shorter path. + let map = import_map( + r" + //- /main.rs crate:main deps:lib + pub mod m { + pub use lib::S; + } + //- /lib.rs crate:lib + pub struct S; + ", + ); + + assert_snapshot!(map, @r###" + main: + - m (t) + - m::S (t) + - m::S (v) + lib: + - S (t) + - S (v) + "###); + } + + #[test] + fn macro_reexport() { + let map = import_map( + r" + //- /main.rs crate:main deps:lib + pub mod m { + pub use lib::pub_macro; + } + //- /lib.rs crate:lib + #[macro_export] + macro_rules! pub_macro { + () => {}; + } + ", + ); + + assert_snapshot!(map, @r###" + main: + - m (t) + - m::pub_macro (m) + lib: + - pub_macro (m) + "###); + } + + #[test] + fn module_reexport() { + // Reexporting modules from a dependency adds all contents to the import map. + let map = import_map( + r" + //- /main.rs crate:main deps:lib + pub use lib::module as reexported_module; + //- /lib.rs crate:lib + pub mod module { + pub struct S; + } + ", + ); + + assert_snapshot!(map, @r###" + main: + - reexported_module (t) + - reexported_module::S (t) + - reexported_module::S (v) + lib: + - module (t) + - module::S (t) + - module::S (v) + "###); + } + + #[test] + fn cyclic_module_reexport() { + // Reexporting modules from a dependency adds all contents to the import map. + let map = import_map( + r" + //- /lib.rs crate:lib + pub mod module { + pub struct S; + pub use super::sub::*; + } + + pub mod sub { + pub use super::module; + } + ", + ); + + assert_snapshot!(map, @r###" + lib: + - module (t) + - module::S (t) + - module::S (v) + - sub (t) + "###); + } + + #[test] + fn private_macro() { + let map = import_map( + r" + //- /lib.rs crate:lib + macro_rules! private_macro { + () => {}; + } + ", + ); + + assert_snapshot!(map, @r###" + lib: + "###); + } +} diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 5325a2760..de490fcc5 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -43,6 +43,7 @@ pub mod child_by_source; pub mod visibility; pub mod find_path; +pub mod import_map; #[cfg(test)] mod test_db; diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index 4512448e0..bfa921de2 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs @@ -76,6 +76,19 @@ impl ModPath { } } + /// Returns the number of segments in the path (counting special segments like `$crate` and + /// `super`). + pub fn len(&self) -> usize { + self.segments.len() + + match self.kind { + PathKind::Plain => 0, + PathKind::Super(i) => i as usize, + PathKind::Crate => 1, + PathKind::Abs => 0, + PathKind::DollarCrate(_) => 1, + } + } + pub fn is_ident(&self) -> bool { self.kind == PathKind::Plain && self.segments.len() == 1 } diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs index 6e435c8c1..74665c588 100644 --- a/crates/ra_hir_def/src/per_ns.rs +++ b/crates/ra_hir_def/src/per_ns.rs @@ -5,7 +5,7 @@ use hir_expand::MacroDefId; -use crate::{visibility::Visibility, ModuleDefId}; +use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct PerNs { @@ -84,4 +84,12 @@ impl PerNs { macros: self.macros.or(other.macros), } } + + pub fn iter_items(self) -> impl Iterator { + self.types + .map(|it| ItemInNs::Types(it.0)) + .into_iter() + .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter()) + .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter()) + } } -- cgit v1.2.3 From 3c496f7fa7afe78102ea2c7ee5f7e006a66629d4 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 4 Jun 2020 19:30:29 +0200 Subject: Use `ImportMap` in `find_path`, remove old queries --- crates/ra_hir_def/src/db.rs | 16 +-- crates/ra_hir_def/src/find_path.rs | 199 +++++++++++++++++++++--------------- crates/ra_hir_def/src/item_scope.rs | 22 +++- 3 files changed, 140 insertions(+), 97 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index a23d65371..10cc26480 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -1,7 +1,7 @@ //! Defines database & queries for name resolution. use std::sync::Arc; -use hir_expand::{db::AstDatabase, name::Name, HirFileId}; +use hir_expand::{db::AstDatabase, HirFileId}; use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; use ra_prof::profile; use ra_syntax::SmolStr; @@ -12,14 +12,10 @@ use crate::{ body::{scope::ExprScopes, Body, BodySourceMap}, data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, docs::Documentation, - find_path, generics::GenericParams, import_map::ImportMap, - item_scope::ItemInNs, lang_item::{LangItemTarget, LangItems}, nameres::{raw::RawItems, CrateDefMap}, - path::ModPath, - visibility::Visibility, AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, @@ -114,16 +110,6 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast { #[salsa::invoke(Documentation::documentation_query)] fn documentation(&self, def: AttrDefId) -> Option; - #[salsa::invoke(find_path::importable_locations_of_query)] - fn importable_locations_of( - &self, - item: ItemInNs, - krate: CrateId, - ) -> Arc<[(ModuleId, Name, Visibility)]>; - - #[salsa::invoke(find_path::find_path_inner_query)] - fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option; - #[salsa::invoke(ImportMap::import_map_query)] fn import_map(&self, krate: CrateId) -> Arc; } diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 088e8dd32..79f4afab6 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -1,7 +1,5 @@ //! An algorithm to find a path to refer to a certain item. -use std::sync::Arc; - use hir_expand::name::{known, AsName, Name}; use ra_prof::profile; use test_utils::mark; @@ -11,8 +9,9 @@ use crate::{ item_scope::ItemInNs, path::{ModPath, PathKind}, visibility::Visibility, - CrateId, ModuleDefId, ModuleId, + ModuleDefId, ModuleId, }; +use rustc_hash::FxHashSet; // FIXME: handle local items @@ -20,7 +19,7 @@ use crate::{ /// *from where* you're referring to the item, hence the `from` parameter. pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option { let _p = profile("find_path"); - db.find_path_inner(item, from, MAX_PATH_LEN) + find_path_inner(db, item, from, MAX_PATH_LEN) } const MAX_PATH_LEN: usize = 15; @@ -38,7 +37,7 @@ impl ModPath { } } -pub(crate) fn find_path_inner_query( +pub(crate) fn find_path_inner( db: &dyn DefDatabase, item: ItemInNs, from: ModuleId, @@ -122,31 +121,61 @@ pub(crate) fn find_path_inner_query( } // - otherwise, look for modules containing (reexporting) it and import it from one of those + let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; let crate_attrs = db.attrs(crate_root.into()); let prefer_no_std = crate_attrs.by_key("no_std").exists(); - let importable_locations = find_importable_locations(db, item, from); let mut best_path = None; let mut best_path_len = max_len; - for (module_id, name) in importable_locations { - let mut path = match db.find_path_inner( - ItemInNs::Types(ModuleDefId::ModuleId(module_id)), - from, - best_path_len - 1, - ) { - None => continue, - Some(path) => path, - }; - path.segments.push(name); - let new_path = if let Some(best_path) = best_path { - select_best_path(best_path, path, prefer_no_std) - } else { - path - }; - best_path_len = new_path.len(); - best_path = Some(new_path); + if item.defining_crate(db) == Some(from.krate) { + // Item was defined in the same crate that wants to import it. It cannot be found in any + // dependency in this case. + + let local_imports = find_local_import_locations(db, item, from); + for (module_id, name) in local_imports { + if let Some(mut path) = find_path_inner( + db, + ItemInNs::Types(ModuleDefId::ModuleId(module_id)), + from, + best_path_len - 1, + ) { + path.segments.push(name); + + let new_path = if let Some(best_path) = best_path { + select_best_path(best_path, path, prefer_no_std) + } else { + path + }; + best_path_len = new_path.len(); + best_path = Some(new_path); + } + } + } else { + // Item was defined in some upstream crate. This means that it must be exported from one, + // too (unless we can't name it at all). It could *also* be (re)exported by the same crate + // that wants to import it here, but we always prefer to use the external path here. + + let crate_graph = db.crate_graph(); + let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { + let import_map = db.import_map(dep.crate_id); + import_map.path_of(item).map(|modpath| { + let mut modpath = modpath.clone(); + modpath.segments.insert(0, dep.as_name()); + modpath + }) + }); + + for path in extern_paths { + let new_path = if let Some(best_path) = best_path { + select_best_path(best_path, path, prefer_no_std) + } else { + path + }; + best_path = Some(new_path); + } } + best_path } @@ -174,77 +203,86 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) - } } -fn find_importable_locations( +/// Finds locations in `from.krate` from which `item` can be imported by `from`. +fn find_local_import_locations( db: &dyn DefDatabase, item: ItemInNs, from: ModuleId, ) -> Vec<(ModuleId, Name)> { - let crate_graph = db.crate_graph(); - let mut result = Vec::new(); - - // We only look in the crate from which we are importing, and the direct - // dependencies. We cannot refer to names from transitive dependencies - // directly (only through reexports in direct dependencies). - - // For the crate from which we're importing, we have to check whether any - // module visible to `from` exports the item we're looking for. - // For dependencies of the crate only `pub` items reachable through `pub` - // modules from the crate root are relevant. For that we precompute an - // import map that tells us the shortest path to any importable item with a - // single lookup. - for krate in Some(from.krate) - .into_iter() - .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) - { - result.extend( - db.importable_locations_of(item, krate) - .iter() - .filter(|(_, _, vis)| vis.is_visible_from(db, from)) - .map(|(m, n, _)| (*m, n.clone())), - ); - } - result -} + let _p = profile("find_local_import_locations"); + + // `from` can import anything below `from` with visibility of at least `from`, and anything + // above `from` with any visibility. That means we do not need to descend into private siblings + // of `from` (and similar). + + let def_map = db.crate_def_map(from.krate); + + // Compute the initial worklist. We start with all direct child modules of `from` as well as all + // of its (recursive) parent modules. + let data = &def_map.modules[from.local_id]; + let mut worklist = data + .children + .values() + .map(|child| ModuleId { krate: from.krate, local_id: *child }) + .collect::>(); + let mut parent = data.parent; + while let Some(p) = parent { + worklist.push(ModuleId { krate: from.krate, local_id: p }); + parent = def_map.modules[p].parent; + } + + let mut seen: FxHashSet<_> = FxHashSet::default(); + + let mut locations = Vec::new(); + while let Some(module) = worklist.pop() { + if !seen.insert(module) { + continue; // already processed this module + } + + let ext_def_map; + let data = if module.krate == from.krate { + &def_map[module.local_id] + } else { + // The crate might reexport a module defined in another crate. + ext_def_map = db.crate_def_map(module.krate); + &ext_def_map[module.local_id] + }; -/// Collects all locations from which we might import the item in a particular -/// crate. These include the original definition of the item, and any -/// non-private `use`s. -/// -/// Note that the crate doesn't need to be the one in which the item is defined; -/// it might be re-exported in other crates. -pub(crate) fn importable_locations_of_query( - db: &dyn DefDatabase, - item: ItemInNs, - krate: CrateId, -) -> Arc<[(ModuleId, Name, Visibility)]> { - let _p = profile("importable_locations_of_query"); - let def_map = db.crate_def_map(krate); - let mut result = Vec::new(); - for (local_id, data) in def_map.modules.iter() { if let Some((name, vis)) = data.scope.name_of(item) { - let is_private = if let Visibility::Module(private_to) = vis { - private_to.local_id == local_id - } else { - false - }; - let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { - data.scope.declarations().any(|it| it == module_def_id) - } else { - false - }; - if is_private && !is_original_def { + if vis.is_visible_from(db, from) { + let is_private = if let Visibility::Module(private_to) = vis { + private_to.local_id == module.local_id + } else { + false + }; + let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { + data.scope.declarations().any(|it| it == module_def_id) + } else { + false + }; + // Ignore private imports. these could be used if we are // in a submodule of this module, but that's usually not // what the user wants; and if this module can import // the item and we're a submodule of it, so can we. // Also this keeps the cached data smaller. - continue; + if !is_private || is_original_def { + locations.push((module, name.clone())); + } + } + } + + // Descend into all modules visible from `from`. + for (_, per_ns) in data.scope.entries() { + if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() { + if vis.is_visible_from(db, from) { + worklist.push(module); + } } - result.push((ModuleId { krate, local_id }, name.clone(), vis)); } } - Arc::from(result) + locations } #[cfg(test)] @@ -382,6 +420,7 @@ mod tests { #[test] fn different_crate_renamed() { + // Even if a local path exists, if the item is defined externally, prefer an external path. let code = r#" //- /main.rs crate:main deps:std extern crate std as std_renamed; @@ -389,7 +428,7 @@ mod tests { //- /std.rs crate:std pub struct S; "#; - check_found_path(code, "std_renamed::S"); + check_found_path(code, "std::S"); } #[test] diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index fc15948ad..ede1aa045 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs @@ -6,9 +6,10 @@ use once_cell::sync::Lazy; use rustc_hash::FxHashMap; use crate::{ - per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, - TraitId, + db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, + Lookup, MacroDefId, ModuleDefId, TraitId, }; +use ra_db::CrateId; #[derive(Debug, Default, PartialEq, Eq)] pub struct ItemScope { @@ -203,4 +204,21 @@ impl ItemInNs { ItemInNs::Macros(_) => None, } } + + pub fn defining_crate(&self, db: &dyn DefDatabase) -> Option { + Some(match self { + ItemInNs::Types(did) | ItemInNs::Values(did) => match did { + ModuleDefId::ModuleId(id) => id.krate, + ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate, + ModuleDefId::AdtId(id) => id.module(db).krate, + ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate, + ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate, + ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate, + ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate, + ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate, + ModuleDefId::BuiltinType(_) => return None, + }, + ItemInNs::Macros(id) => return id.krate, + }) + } } -- cgit v1.2.3 From e0e9c6d1a4cfdd4410b802ad8db1e29c9f1b4291 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:04:35 +0200 Subject: Fix wrong comment --- crates/ra_hir_def/src/import_map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 7dae64efa..7c8c4b6cb 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -281,7 +281,7 @@ mod tests { #[test] fn cyclic_module_reexport() { - // Reexporting modules from a dependency adds all contents to the import map. + // A cyclic reexport does not hang. let map = import_map( r" //- /lib.rs crate:lib -- cgit v1.2.3 From 86fbd8cc2b71f473b9e6d41f4fe3e1a114f1f992 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:05:19 +0200 Subject: defining_crate -> krate --- crates/ra_hir_def/src/find_path.rs | 2 +- crates/ra_hir_def/src/item_scope.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 79f4afab6..e6cd89478 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -128,7 +128,7 @@ pub(crate) fn find_path_inner( let mut best_path = None; let mut best_path_len = max_len; - if item.defining_crate(db) == Some(from.krate) { + if item.krate(db) == Some(from.krate) { // Item was defined in the same crate that wants to import it. It cannot be found in any // dependency in this case. diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index ede1aa045..d340e1f13 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs @@ -205,7 +205,8 @@ impl ItemInNs { } } - pub fn defining_crate(&self, db: &dyn DefDatabase) -> Option { + /// Returns the crate defining this item (or `None` if `self` is built-in). + pub fn krate(&self, db: &dyn DefDatabase) -> Option { Some(match self { ItemInNs::Types(did) | ItemInNs::Values(did) => match did { ModuleDefId::ModuleId(id) => id.krate, -- cgit v1.2.3 From f085e592fe4ddd47c20e96a944c5902ff8a2f439 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:10:43 +0200 Subject: Measure memory usage of ImportMap --- crates/ra_hir/src/db.rs | 10 +++++----- crates/ra_ide_db/src/change.rs | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index ec931b34f..098b66529 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -3,11 +3,11 @@ pub use hir_def::db::{ AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, - ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternConstQuery, - InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery, - InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery, - LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, StructDataQuery, - TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, + ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, + InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, + InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, + InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, + StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, }; pub use hir_expand::db::{ AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs index 8446ef88e..ea78e49e0 100644 --- a/crates/ra_ide_db/src/change.rs +++ b/crates/ra_ide_db/src/change.rs @@ -334,6 +334,7 @@ impl RootDatabase { hir::db::CrateLangItemsQuery hir::db::LangItemQuery hir::db::DocumentationQuery + hir::db::ImportMapQuery // InternDatabase hir::db::InternFunctionQuery -- cgit v1.2.3 From 5f23f8ca449e456087d0657f925837bfb1a3f5ec Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:11:53 +0200 Subject: Make `find_path_inner` private again --- crates/ra_hir_def/src/find_path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index e6cd89478..d6ae970dc 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -37,7 +37,7 @@ impl ModPath { } } -pub(crate) fn find_path_inner( +fn find_path_inner( db: &dyn DefDatabase, item: ItemInNs, from: ModuleId, -- cgit v1.2.3 From 8395396782e343c6fe6bd318c74e8c9884b22323 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:15:16 +0200 Subject: Reorder imports --- crates/ra_hir_def/src/find_path.rs | 2 +- crates/ra_hir_def/src/import_map.rs | 8 +++++--- crates/ra_hir_def/src/item_scope.rs | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index d6ae970dc..a7f59e028 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs @@ -2,6 +2,7 @@ use hir_expand::name::{known, AsName, Name}; use ra_prof::profile; +use rustc_hash::FxHashSet; use test_utils::mark; use crate::{ @@ -11,7 +12,6 @@ use crate::{ visibility::Visibility, ModuleDefId, ModuleId, }; -use rustc_hash::FxHashSet; // FIXME: handle local items diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 7c8c4b6cb..1c812a19a 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -1,5 +1,10 @@ //! A map of all publicly exported items in a crate. +use std::{collections::hash_map::Entry, sync::Arc}; + +use ra_db::CrateId; +use rustc_hash::FxHashMap; + use crate::{ db::DefDatabase, item_scope::ItemInNs, @@ -7,9 +12,6 @@ use crate::{ visibility::Visibility, ModuleDefId, ModuleId, }; -use ra_db::CrateId; -use rustc_hash::FxHashMap; -use std::{collections::hash_map::Entry, sync::Arc}; /// A map from publicly exported items to the path needed to import/name them from a downstream /// crate. diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index d340e1f13..b03ba939a 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs @@ -3,13 +3,13 @@ use hir_expand::name::Name; use once_cell::sync::Lazy; +use ra_db::CrateId; use rustc_hash::FxHashMap; use crate::{ db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, Lookup, MacroDefId, ModuleDefId, TraitId, }; -use ra_db::CrateId; #[derive(Debug, Default, PartialEq, Eq)] pub struct ItemScope { -- cgit v1.2.3 From 2fb3d87bf77826f213d2876c921308e9f168ca63 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 13:36:19 +0200 Subject: impl Debug for ImportMap --- crates/ra_hir_def/src/import_map.rs | 42 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 19 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 1c812a19a..70749f380 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -1,6 +1,6 @@ //! A map of all publicly exported items in a crate. -use std::{collections::hash_map::Entry, sync::Arc}; +use std::{collections::hash_map::Entry, fmt, sync::Arc}; use ra_db::CrateId; use rustc_hash::FxHashMap; @@ -21,7 +21,7 @@ use crate::{ /// /// Note that all paths are relative to the containing crate's root, so the crate name still needs /// to be prepended to the `ModPath` before the path is valid. -#[derive(Debug, Eq, PartialEq)] +#[derive(Eq, PartialEq)] pub struct ImportMap { map: FxHashMap, } @@ -95,6 +95,26 @@ impl ImportMap { } } +impl fmt::Debug for ImportMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut importable_paths: Vec<_> = self + .map + .iter() + .map(|(item, modpath)| { + let ns = match item { + ItemInNs::Types(_) => "t", + ItemInNs::Values(_) => "v", + ItemInNs::Macros(_) => "m", + }; + format!("- {} ({})", modpath, ns) + }) + .collect(); + + importable_paths.sort(); + f.write_str(&importable_paths.join("\n")) + } +} + #[cfg(test)] mod tests { use super::*; @@ -115,23 +135,7 @@ mod tests { let map = db.import_map(krate); - let mut importable_paths: Vec<_> = map - .map - .iter() - .map(|(item, modpath)| { - let ns = match item { - ItemInNs::Types(_) => "t", - ItemInNs::Values(_) => "v", - ItemInNs::Macros(_) => "m", - }; - format!("- {} ({})", modpath, ns) - }) - .collect(); - - importable_paths.sort(); - let importable_paths = importable_paths.join("\n"); - - Some(format!("{}:\n{}", name, importable_paths)) + Some(format!("{}:\n{:?}", name, map)) }) .collect(); -- cgit v1.2.3 From 7d0dd17b09240385333805637ea17992a8089cf2 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 14:15:54 +0300 Subject: Add hover actions as LSP extension --- crates/ra_ide/src/hover.rs | 134 ++++++++++++++++++++++--- crates/ra_ide/src/lib.rs | 2 +- crates/ra_ide_db/src/defs.rs | 2 +- crates/rust-analyzer/src/config.rs | 28 ++++-- crates/rust-analyzer/src/lsp_ext.rs | 34 +++++++ crates/rust-analyzer/src/main_loop.rs | 2 +- crates/rust-analyzer/src/main_loop/handlers.rs | 119 +++++++++++++++++----- 7 files changed, 272 insertions(+), 49 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 9636cd0d6..baa9fc8a8 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -13,14 +13,43 @@ use ra_ide_db::{ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; use crate::{ - display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, - FilePosition, RangeInfo, + display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, + FilePosition, RangeInfo, NavigationTarget, }; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct HoverConfig { + pub implementations: bool, +} + +impl Default for HoverConfig { + fn default() -> Self { + Self { implementations: true } + } +} + +impl HoverConfig { + pub const NO_ACTIONS: Self = Self { implementations: false }; + + pub fn any(&self) -> bool { + self.implementations + } + + pub fn none(&self) -> bool { + !self.any() + } +} + +#[derive(Debug, Clone)] +pub enum HoverAction { + Implementaion(FilePosition), +} + /// Contains the results when hovering over an item #[derive(Debug, Default)] pub struct HoverResult { results: Vec, + actions: Vec, } impl HoverResult { @@ -48,10 +77,20 @@ impl HoverResult { &self.results } + pub fn actions(&self) -> &[HoverAction] { + &self.actions + } + + pub fn push_action(&mut self, action: HoverAction) { + self.actions.push(action); + } + /// Returns the results converted into markup /// for displaying in a UI + /// + /// Does not process actions! pub fn to_markup(&self) -> String { - self.results.join("\n\n---\n") + self.results.join("\n\n___\n") } } @@ -82,6 +121,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option Option Option { + fn to_action(nav_target: NavigationTarget) -> HoverAction { + HoverAction::Implementaion(FilePosition { + file_id: nav_target.file_id(), + offset: nav_target.range().start(), + }) + } + + match def { + Definition::ModuleDef(it) => match it { + ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))), + ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))), + ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))), + ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))), + _ => None, + }, + _ => None, + } +} + fn hover_text( docs: Option, desc: Option, @@ -228,6 +291,8 @@ fn pick_best(tokens: TokenAtOffset) -> Option { #[cfg(test)] mod tests { + use super::*; + use ra_db::FileLoader; use ra_syntax::TextRange; @@ -241,7 +306,14 @@ mod tests { s.map(trim_markup) } - fn check_hover_result(fixture: &str, expected: &[&str]) -> String { + fn assert_impl_action(action: &HoverAction, position: u32) { + let offset = match action { + HoverAction::Implementaion(pos) => pos.offset + }; + assert_eq!(offset, position.into()); + } + + fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec) { let (analysis, position) = analysis_and_position(fixture); let hover = analysis.hover(position).unwrap().unwrap(); let mut results = Vec::from(hover.info.results()); @@ -256,7 +328,7 @@ mod tests { assert_eq!(hover.info.len(), expected.len()); let content = analysis.db.file_text(position.file_id); - content[hover.range].to_string() + (content[hover.range].to_string(), hover.info.actions().to_vec()) } fn check_hover_no_result(fixture: &str) { @@ -746,7 +818,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_macro() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( " //- /lib.rs macro_rules! id { @@ -767,7 +839,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_expr_in_macro() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( " //- /lib.rs macro_rules! id { @@ -785,7 +857,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_expr_in_macro_recursive() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( " //- /lib.rs macro_rules! id_deep { @@ -806,7 +878,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_func_in_macro_recursive() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( " //- /lib.rs macro_rules! id_deep { @@ -830,7 +902,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_literal_string_in_macro() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( r#" //- /lib.rs macro_rules! arr { @@ -849,7 +921,7 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_through_assert_macro() { - let hover_on = check_hover_result( + let (hover_on, _) = check_hover_result( r#" //- /lib.rs #[rustc_builtin_macro] @@ -925,13 +997,14 @@ fn func(foo: i32) { if true { <|>foo; }; } #[test] fn test_hover_trait_show_qualifiers() { - check_hover_result( + let (_, actions) = check_hover_result( " //- /lib.rs unsafe trait foo<|>() {} ", &["unsafe trait foo"], ); + assert_impl_action(&actions[0], 13); } #[test] @@ -1052,4 +1125,41 @@ fn func(foo: i32) { if true { <|>foo; }; } &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], ); } + + #[test] + fn test_hover_trait_hash_impl_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + trait foo<|>() {} + ", + &["trait foo"], + ); + assert_impl_action(&actions[0], 6); + } + + #[test] + fn test_hover_struct_hash_impl_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + struct foo<|>() {} + ", + &["struct foo"], + ); + assert_impl_action(&actions[0], 7); + } + + #[test] + fn test_hover_union_hash_impl_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + union foo<|>() {} + ", + &["union foo"], + ); + assert_impl_action(&actions[0], 6); + } + } diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 34c2d75fe..a9601400f 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -66,7 +66,7 @@ pub use crate::{ display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, expand_macro::ExpandedMacro, folding_ranges::{Fold, FoldKind}, - hover::HoverResult, + hover::{HoverResult, HoverAction, HoverConfig}, inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, runnables::{Runnable, RunnableKind, TestId}, diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 8b06cbfc5..1db60b87f 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs @@ -18,7 +18,7 @@ use ra_syntax::{ use crate::RootDatabase; // FIXME: a more precise name would probably be `Symbol`? -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Definition { Macro(MacroDef), Field(Field), diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 23168c3ae..e7c859577 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -11,7 +11,7 @@ use std::{ffi::OsString, path::PathBuf}; use lsp_types::ClientCapabilities; use ra_flycheck::FlycheckConfig; -use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; +use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig, HoverConfig}; use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; use serde::Deserialize; @@ -34,6 +34,7 @@ pub struct Config { pub assist: AssistConfig, pub call_info_full: bool, pub lens: LensConfig, + pub hover: HoverConfig, pub with_sysroot: bool, pub linked_projects: Vec, @@ -124,6 +125,7 @@ pub struct ClientCapsConfig { pub work_done_progress: bool, pub code_action_group: bool, pub resolve_code_action: bool, + pub hover_actions: bool, } impl Default for Config { @@ -162,6 +164,7 @@ impl Default for Config { assist: AssistConfig::default(), call_info_full: true, lens: LensConfig::default(), + hover: HoverConfig::default(), linked_projects: Vec::new(), } } @@ -278,6 +281,14 @@ impl Config { } } + let mut use_hover_actions = false; + set(value, "/hoverActions/enable", &mut use_hover_actions); + if use_hover_actions { + set(value, "/hoverActions/implementations", &mut self.hover.implementations); + } else { + self.hover = HoverConfig::NO_ACTIONS; + } + log::info!("Config::update() = {:#?}", self); fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option { @@ -331,17 +342,14 @@ impl Config { self.assist.allow_snippets(false); if let Some(experimental) = &caps.experimental { - let snippet_text_edit = - experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); - self.assist.allow_snippets(snippet_text_edit); + let get_bool = |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true); - let code_action_group = - experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); - self.client_caps.code_action_group = code_action_group; + let snippet_text_edit = get_bool("snippetTextEdit"); + self.assist.allow_snippets(snippet_text_edit); - let resolve_code_action = - experimental.get("resolveCodeAction").and_then(|it| it.as_bool()) == Some(true); - self.client_caps.resolve_code_action = resolve_code_action; + self.client_caps.code_action_group = get_bool("codeActionGroup"); + self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); + self.client_caps.hover_actions = get_bool("hoverActions"); } } } diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 3b957534d..145a389ce 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -260,3 +260,37 @@ pub struct SnippetTextEdit { #[serde(skip_serializing_if = "Option::is_none")] pub insert_text_format: Option, } + +pub enum HoverRequest {} + +impl Request for HoverRequest { + type Params = lsp_types::HoverParams; + type Result = Option; + const METHOD: &'static str = "textDocument/hover"; +} + +#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] +pub struct Hover { + pub contents: lsp_types::HoverContents, + #[serde(skip_serializing_if = "Option::is_none")] + pub range: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub actions: Option>, +} + +#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)] +pub struct CommandLinkGroup { + pub title: Option, + pub commands: Vec, +} + +// LSP v3.15 Command does not have a `tooltip` field, vscode supports one. +#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)] +pub struct CommandLink { + pub title: String, + pub command: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub tooltip: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub arguments: Option>, +} diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index e60337b8e..752dbf145 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -510,6 +510,7 @@ fn on_request( .on::(handlers::handle_inlay_hints)? .on::(handlers::handle_code_action)? .on::(handlers::handle_resolve_code_action)? + .on::(handlers::handle_hover)? .on::(handlers::handle_on_type_formatting)? .on::(handlers::handle_document_symbol)? .on::(handlers::handle_workspace_symbol)? @@ -521,7 +522,6 @@ fn on_request( .on::(handlers::handle_code_lens_resolve)? .on::(handlers::handle_folding_range)? .on::(handlers::handle_signature_help)? - .on::(handlers::handle_hover)? .on::(handlers::handle_prepare_rename)? .on::(handlers::handle_rename)? .on::(handlers::handle_references)? diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 6acf80c58..0958a231f 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -12,13 +12,14 @@ use lsp_types::{ CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, - DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, - MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, - SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, - SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, + DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, MarkupContent, + MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams, + SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, + TextDocumentIdentifier, Url, WorkspaceEdit, }; use ra_ide::{ - FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit, + FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, RunnableKind, SearchScope, + TextEdit, }; use ra_prof::profile; use ra_project_model::TargetKind; @@ -537,7 +538,7 @@ pub fn handle_signature_help( pub fn handle_hover( snap: GlobalStateSnapshot, params: lsp_types::HoverParams, -) -> Result> { +) -> Result> { let _p = profile("handle_hover"); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let info = match snap.analysis().hover(position)? { @@ -546,12 +547,13 @@ pub fn handle_hover( }; let line_index = snap.analysis.file_line_index(position.file_id)?; let range = to_proto::range(&line_index, info.range); - let res = Hover { + let res = lsp_ext::Hover { contents: HoverContents::Markup(MarkupContent { kind: MarkupKind::Markdown, value: crate::markdown::format_docs(&info.info.to_markup()), }), range: Some(range), + actions: Some(prepare_hover_actions(&world, info.info.actions())), }; Ok(Some(res)) } @@ -924,24 +926,13 @@ pub fn handle_code_lens_resolve( _ => vec![], }; - let title = if locations.len() == 1 { - "1 implementation".into() - } else { - format!("{} implementations", locations.len()) - }; - - // We cannot use the 'editor.action.showReferences' command directly - // because that command requires vscode types which we convert in the handler - // on the client side. - let cmd = Command { + let title = implementation_title(locations.len()); + let cmd = show_references_command( title, - command: "rust-analyzer.showReferences".into(), - arguments: Some(vec![ - to_value(&lens_params.text_document_position_params.text_document.uri).unwrap(), - to_value(code_lens.range.start).unwrap(), - to_value(locations).unwrap(), - ]), - }; + &lens_params.text_document_position_params.text_document.uri, + code_lens.range.start, + locations, + ); Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) } None => Ok(CodeLens { @@ -1145,3 +1136,83 @@ pub fn handle_semantic_tokens_range( let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); Ok(Some(semantic_tokens.into())) } + +fn implementation_title(count: usize) -> String { + if count == 1 { + "1 implementation".into() + } else { + format!("{} implementations", count) + } +} + +fn show_references_command( + title: String, + uri: &lsp_types::Url, + position: lsp_types::Position, + locations: Vec, +) -> Command { + // We cannot use the 'editor.action.showReferences' command directly + // because that command requires vscode types which we convert in the handler + // on the client side. + + Command { + title, + command: "rust-analyzer.showReferences".into(), + arguments: Some(vec![ + to_value(uri).unwrap(), + to_value(position).unwrap(), + to_value(locations).unwrap(), + ]), + } +} + +fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { + lsp_ext::CommandLink { + tooltip: Some(tooltip), + title: command.title, + command: command.command, + arguments: command.arguments, + } +} + +fn show_impl_command_link( + world: &WorldSnapshot, + position: &FilePosition, +) -> Option { + if world.config.hover.implementations { + if let Some(nav_data) = world.analysis().goto_implementation(*position).unwrap_or(None) { + let uri = to_proto::url(world, position.file_id).ok()?; + let line_index = world.analysis().file_line_index(position.file_id).ok()?; + let position = to_proto::position(&line_index, position.offset); + let locations: Vec<_> = nav_data + .info + .iter() + .filter_map(|it| to_proto::location(world, it.file_range()).ok()) + .collect(); + let title = implementation_title(locations.len()); + let command = show_references_command(title, &uri, position, locations); + + return Some(lsp_ext::CommandLinkGroup { + commands: vec![to_command_link(command, "Go to implementations".into())], + ..Default::default() + }); + } + } + None +} + +fn prepare_hover_actions( + world: &WorldSnapshot, + actions: &[HoverAction], +) -> Vec { + if world.config.hover.none() || !world.config.client_caps.hover_actions { + return Vec::new(); + } + + actions + .iter() + .filter_map(|it| match it { + HoverAction::Implementaion(position) => show_impl_command_link(world, position), + }) + .collect() +} -- cgit v1.2.3 From b147e6eb95b8bbc8cd9a56f9a9a629b8671bdc0e Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 14:44:40 +0300 Subject: Code formatting --- crates/ra_ide/src/hover.rs | 5 ++--- crates/ra_ide/src/lib.rs | 2 +- crates/rust-analyzer/src/config.rs | 5 +++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index baa9fc8a8..2b9095a82 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -14,7 +14,7 @@ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffs use crate::{ display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, - FilePosition, RangeInfo, NavigationTarget, + FilePosition, NavigationTarget, RangeInfo, }; #[derive(Clone, Debug, PartialEq, Eq)] @@ -308,7 +308,7 @@ mod tests { fn assert_impl_action(action: &HoverAction, position: u32) { let offset = match action { - HoverAction::Implementaion(pos) => pos.offset + HoverAction::Implementaion(pos) => pos.offset, }; assert_eq!(offset, position.into()); } @@ -1161,5 +1161,4 @@ fn func(foo: i32) { if true { <|>foo; }; } ); assert_impl_action(&actions[0], 6); } - } diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index a9601400f..a56718d3f 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -66,7 +66,7 @@ pub use crate::{ display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, expand_macro::ExpandedMacro, folding_ranges::{Fold, FoldKind}, - hover::{HoverResult, HoverAction, HoverConfig}, + hover::{HoverAction, HoverConfig, HoverResult}, inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, runnables::{Runnable, RunnableKind, TestId}, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index e7c859577..14c4fe9ad 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -11,7 +11,7 @@ use std::{ffi::OsString, path::PathBuf}; use lsp_types::ClientCapabilities; use ra_flycheck::FlycheckConfig; -use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig, HoverConfig}; +use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig}; use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; use serde::Deserialize; @@ -342,7 +342,8 @@ impl Config { self.assist.allow_snippets(false); if let Some(experimental) = &caps.experimental { - let get_bool = |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true); + let get_bool = + |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true); let snippet_text_edit = get_bool("snippetTextEdit"); self.assist.allow_snippets(snippet_text_edit); -- cgit v1.2.3 From 5d0c1aa1625a17723209e537590dc7fc7f181df1 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 15:13:26 +0300 Subject: Rebase on the latest master. --- crates/ra_ide/src/hover.rs | 6 +++--- crates/rust-analyzer/src/main_loop/handlers.rs | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 2b9095a82..2fbe0ba1f 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -1127,7 +1127,7 @@ fn func(foo: i32) { if true { <|>foo; }; } } #[test] - fn test_hover_trait_hash_impl_action() { + fn test_hover_trait_has_impl_action() { let (_, actions) = check_hover_result( " //- /lib.rs @@ -1139,7 +1139,7 @@ fn func(foo: i32) { if true { <|>foo; }; } } #[test] - fn test_hover_struct_hash_impl_action() { + fn test_hover_struct_has_impl_action() { let (_, actions) = check_hover_result( " //- /lib.rs @@ -1151,7 +1151,7 @@ fn func(foo: i32) { if true { <|>foo; }; } } #[test] - fn test_hover_union_hash_impl_action() { + fn test_hover_union_has_impl_action() { let (_, actions) = check_hover_result( " //- /lib.rs diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 0958a231f..d998d9ddd 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -553,7 +553,7 @@ pub fn handle_hover( value: crate::markdown::format_docs(&info.info.to_markup()), }), range: Some(range), - actions: Some(prepare_hover_actions(&world, info.info.actions())), + actions: Some(prepare_hover_actions(&snap, info.info.actions())), }; Ok(Some(res)) } @@ -1176,18 +1176,18 @@ fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { } fn show_impl_command_link( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, position: &FilePosition, ) -> Option { - if world.config.hover.implementations { - if let Some(nav_data) = world.analysis().goto_implementation(*position).unwrap_or(None) { - let uri = to_proto::url(world, position.file_id).ok()?; - let line_index = world.analysis().file_line_index(position.file_id).ok()?; + if snap.config.hover.implementations { + if let Some(nav_data) = snap.analysis().goto_implementation(*position).unwrap_or(None) { + let uri = to_proto::url(snap, position.file_id).ok()?; + let line_index = snap.analysis().file_line_index(position.file_id).ok()?; let position = to_proto::position(&line_index, position.offset); let locations: Vec<_> = nav_data .info .iter() - .filter_map(|it| to_proto::location(world, it.file_range()).ok()) + .filter_map(|it| to_proto::location(snap, it.file_range()).ok()) .collect(); let title = implementation_title(locations.len()); let command = show_references_command(title, &uri, position, locations); @@ -1202,17 +1202,17 @@ fn show_impl_command_link( } fn prepare_hover_actions( - world: &WorldSnapshot, + snap: &GlobalStateSnapshot, actions: &[HoverAction], ) -> Vec { - if world.config.hover.none() || !world.config.client_caps.hover_actions { + if snap.config.hover.none() || !snap.config.client_caps.hover_actions { return Vec::new(); } actions .iter() .filter_map(|it| match it { - HoverAction::Implementaion(position) => show_impl_command_link(world, position), + HoverAction::Implementaion(position) => show_impl_command_link(snap, position), }) .collect() } -- cgit v1.2.3 From 92cfc0f2a1edd4e825d4dea96d4f96dcea513629 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 15:29:03 +0300 Subject: Add enum hover action test. --- crates/ra_ide/src/hover.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'crates') diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 2fbe0ba1f..62df07459 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -1161,4 +1161,19 @@ fn func(foo: i32) { if true { <|>foo; }; } ); assert_impl_action(&actions[0], 6); } + + #[test] + fn test_hover_enum_has_impl_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + enum foo<|>() { + A, + B + } + ", + &["enum foo"], + ); + assert_impl_action(&actions[0], 5); + } } -- cgit v1.2.3 From e35418ceb9b07bd596cc09144f4f4df13432a712 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 16:39:32 +0300 Subject: Apply suggestions from @kjeremy review --- crates/rust-analyzer/src/lsp_ext.rs | 16 +++++++--------- crates/rust-analyzer/src/main_loop/handlers.rs | 21 +++++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 145a389ce..75ea48892 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -271,26 +271,24 @@ impl Request for HoverRequest { #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] pub struct Hover { - pub contents: lsp_types::HoverContents, - #[serde(skip_serializing_if = "Option::is_none")] - pub range: Option, + #[serde(flatten)] + pub hover: lsp_types::Hover, #[serde(skip_serializing_if = "Option::is_none")] pub actions: Option>, } -#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)] +#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] pub struct CommandLinkGroup { + #[serde(skip_serializing_if = "Option::is_none")] pub title: Option, pub commands: Vec, } // LSP v3.15 Command does not have a `tooltip` field, vscode supports one. -#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)] +#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] pub struct CommandLink { - pub title: String, - pub command: String, + #[serde(flatten)] + pub command: lsp_types::Command, #[serde(skip_serializing_if = "Option::is_none")] pub tooltip: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub arguments: Option>, } diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index d998d9ddd..894df5837 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -547,15 +547,18 @@ pub fn handle_hover( }; let line_index = snap.analysis.file_line_index(position.file_id)?; let range = to_proto::range(&line_index, info.range); - let res = lsp_ext::Hover { - contents: HoverContents::Markup(MarkupContent { - kind: MarkupKind::Markdown, - value: crate::markdown::format_docs(&info.info.to_markup()), - }), - range: Some(range), + let hover = lsp_ext::Hover { + hover: lsp_types::Hover { + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: crate::markdown::format_docs(&info.info.to_markup()), + }), + range: Some(range), + }, actions: Some(prepare_hover_actions(&snap, info.info.actions())), }; - Ok(Some(res)) + + Ok(Some(hover)) } pub fn handle_prepare_rename( @@ -1169,9 +1172,7 @@ fn show_references_command( fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { lsp_ext::CommandLink { tooltip: Some(tooltip), - title: command.title, - command: command.command, - arguments: command.arguments, + command, } } -- cgit v1.2.3 From 0fe43a124bb2b135cfd1268fda2941c3ac170c96 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 3 Jun 2020 17:35:26 +0300 Subject: Add capabilities tests. --- crates/rust-analyzer/src/lsp_ext.rs | 4 +- crates/rust-analyzer/src/main_loop/handlers.rs | 7 +- crates/rust-analyzer/tests/heavy_tests/main.rs | 180 +++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 7 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 75ea48892..1371f6cb4 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -273,8 +273,8 @@ impl Request for HoverRequest { pub struct Hover { #[serde(flatten)] pub hover: lsp_types::Hover, - #[serde(skip_serializing_if = "Option::is_none")] - pub actions: Option>, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub actions: Vec, } #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 894df5837..3ff779702 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -555,7 +555,7 @@ pub fn handle_hover( }), range: Some(range), }, - actions: Some(prepare_hover_actions(&snap, info.info.actions())), + actions: prepare_hover_actions(&snap, info.info.actions()), }; Ok(Some(hover)) @@ -1170,10 +1170,7 @@ fn show_references_command( } fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { - lsp_ext::CommandLink { - tooltip: Some(tooltip), - command, - } + lsp_ext::CommandLink { tooltip: Some(tooltip), command } } fn show_impl_command_link( diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index ad3476310..78c6195d7 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -715,3 +715,183 @@ pub fn foo(_input: TokenStream) -> TokenStream { let value = res.get("contents").unwrap().get("value").unwrap().to_string(); assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) } + +#[test] +fn test_client_support_hover_actions() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- src/lib.rs +struct Foo(u32); + +struct NoImpl(u32); + +impl Foo { + fn new() -> Self { + Self(1) + } +} +"#, + ) + .with_config(|config| { + config.client_caps.hover_actions = true; + }) + .server(); + + server.wait_until_workspace_is_loaded(); + + // has 1 implementation + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(0, 9), + ), + work_done_progress_params: Default::default(), + }, + json!({ + "actions": [{ + "commands": [{ + "arguments": [ + "file:///[..]src/lib.rs", + { + "character": 7, + "line": 0 + }, + [{ + "range": { "end": { "character": 1, "line": 8 }, "start": { "character": 0, "line": 4 } }, + "uri": "file:///[..]src/lib.rs" + }] + ], + "command": "rust-analyzer.showReferences", + "title": "1 implementation", + "tooltip": "Go to implementations" + }] + }], + "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" }, + "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } } + }) + ); + + // no hover + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(1, 0), + ), + work_done_progress_params: Default::default(), + }, + json!(null), + ); + + // no implementations + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(2, 12), + ), + work_done_progress_params: Default::default(), + }, + json!({ + "actions": [{ + "commands": [{ + "arguments": [ + "file:///[..]src/lib.rs", + { "character": 7, "line": 2 }, + [] + ], + "command": "rust-analyzer.showReferences", + "title": "0 implementations", + "tooltip": "Go to implementations" + }] + }], + "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" }, + "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } } + }) + ); +} + +#[test] +fn test_client_does_not_support_hover_actions() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- src/lib.rs +struct Foo(u32); + +struct NoImpl(u32); + +impl Foo { + fn new() -> Self { + Self(1) + } +} +"#, + ) + .with_config(|config| { + config.client_caps.hover_actions = false; + }) + .server(); + + server.wait_until_workspace_is_loaded(); + + // has 1 implementation + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(0, 9), + ), + work_done_progress_params: Default::default(), + }, + json!({ + "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" }, + "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } } + }) + ); + + // no hover + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(1, 0), + ), + work_done_progress_params: Default::default(), + }, + json!(null), + ); + + // no implementations + server.request::( + HoverParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(2, 12), + ), + work_done_progress_params: Default::default(), + }, + json!({ + "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" }, + "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } } + }) + ); +} -- cgit v1.2.3 From bc2d1729957a25bf5ee8e2213d07460e22c76def Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 5 Jun 2020 14:24:51 +0200 Subject: Clarify when we visit modules multiple times --- crates/ra_hir_def/src/import_map.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 70749f380..4284a0a91 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs @@ -78,7 +78,9 @@ impl ImportMap { } } - // If we've just added a path to a module, descend into it. + // If we've just added a path to a module, descend into it. We might traverse + // modules multiple times, but only if the new path to it is shorter than the + // first (else we `continue` above). if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { worklist.push((mod_id, mk_path())); } -- cgit v1.2.3 From 78c9223b7bd4bffe64b7ec69e5fee08604dc0057 Mon Sep 17 00:00:00 2001 From: vsrs Date: Fri, 5 Jun 2020 15:25:01 +0300 Subject: Remove hover contents marking as trusted. Hover contents might be extracted from raw doc comments and need some validation. --- crates/rust-analyzer/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 14c4fe9ad..8d6efdbe8 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -349,7 +349,7 @@ impl Config { self.assist.allow_snippets(snippet_text_edit); self.client_caps.code_action_group = get_bool("codeActionGroup"); - self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); + self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); self.client_caps.hover_actions = get_bool("hoverActions"); } } -- cgit v1.2.3 From bd9d7b6ad885f775df91ff3dfebd8927c8e272b2 Mon Sep 17 00:00:00 2001 From: vsrs Date: Fri, 5 Jun 2020 15:26:46 +0300 Subject: Remove hover actions heavy tests. --- crates/rust-analyzer/tests/heavy_tests/main.rs | 180 ------------------------- 1 file changed, 180 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 78c6195d7..ad3476310 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -715,183 +715,3 @@ pub fn foo(_input: TokenStream) -> TokenStream { let value = res.get("contents").unwrap().get("value").unwrap().to_string(); assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) } - -#[test] -fn test_client_support_hover_actions() { - if skip_slow_tests() { - return; - } - - let server = Project::with_fixture( - r#" -//- Cargo.toml -[package] -name = "foo" -version = "0.0.0" - -//- src/lib.rs -struct Foo(u32); - -struct NoImpl(u32); - -impl Foo { - fn new() -> Self { - Self(1) - } -} -"#, - ) - .with_config(|config| { - config.client_caps.hover_actions = true; - }) - .server(); - - server.wait_until_workspace_is_loaded(); - - // has 1 implementation - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(0, 9), - ), - work_done_progress_params: Default::default(), - }, - json!({ - "actions": [{ - "commands": [{ - "arguments": [ - "file:///[..]src/lib.rs", - { - "character": 7, - "line": 0 - }, - [{ - "range": { "end": { "character": 1, "line": 8 }, "start": { "character": 0, "line": 4 } }, - "uri": "file:///[..]src/lib.rs" - }] - ], - "command": "rust-analyzer.showReferences", - "title": "1 implementation", - "tooltip": "Go to implementations" - }] - }], - "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" }, - "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } } - }) - ); - - // no hover - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(1, 0), - ), - work_done_progress_params: Default::default(), - }, - json!(null), - ); - - // no implementations - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(2, 12), - ), - work_done_progress_params: Default::default(), - }, - json!({ - "actions": [{ - "commands": [{ - "arguments": [ - "file:///[..]src/lib.rs", - { "character": 7, "line": 2 }, - [] - ], - "command": "rust-analyzer.showReferences", - "title": "0 implementations", - "tooltip": "Go to implementations" - }] - }], - "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" }, - "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } } - }) - ); -} - -#[test] -fn test_client_does_not_support_hover_actions() { - if skip_slow_tests() { - return; - } - - let server = Project::with_fixture( - r#" -//- Cargo.toml -[package] -name = "foo" -version = "0.0.0" - -//- src/lib.rs -struct Foo(u32); - -struct NoImpl(u32); - -impl Foo { - fn new() -> Self { - Self(1) - } -} -"#, - ) - .with_config(|config| { - config.client_caps.hover_actions = false; - }) - .server(); - - server.wait_until_workspace_is_loaded(); - - // has 1 implementation - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(0, 9), - ), - work_done_progress_params: Default::default(), - }, - json!({ - "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" }, - "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } } - }) - ); - - // no hover - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(1, 0), - ), - work_done_progress_params: Default::default(), - }, - json!(null), - ); - - // no implementations - server.request::( - HoverParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/lib.rs"), - Position::new(2, 12), - ), - work_done_progress_params: Default::default(), - }, - json!({ - "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" }, - "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } } - }) - ); -} -- cgit v1.2.3