Show mod path in hover tooltip

This commit is contained in:
Kirill Bulatov 2020-03-06 01:02:14 +02:00
parent aff82cf7ac
commit 32f5276465
4 changed files with 132 additions and 29 deletions

View file

@ -480,6 +480,14 @@ impl Adt {
pub fn krate(self, db: &impl HirDatabase) -> Option<Crate> {
Some(self.module(db).krate())
}
pub fn name(&self, db: &impl HirDatabase) -> Name {
match self {
Adt::Struct(s) => s.name(db),
Adt::Union(u) => u.name(db),
Adt::Enum(e) => e.name(db),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@ -507,6 +515,14 @@ impl VariantDef {
}
}
pub fn name(&self, db: &impl HirDatabase) -> Name {
match self {
VariantDef::Struct(s) => s.name(db),
VariantDef::Union(u) => u.name(db),
VariantDef::EnumVariant(e) => e.name(db),
}
}
pub(crate) fn variant_data(self, db: &impl DefDatabase) -> Arc<VariantData> {
match self {
VariantDef::Struct(it) => it.variant_data(db),
@ -534,6 +550,14 @@ impl DefWithBody {
DefWithBody::Static(s) => s.module(db),
}
}
pub fn name(self, db: &impl HirDatabase) -> Option<Name> {
match self {
DefWithBody::Function(f) => Some(f.name(db)),
DefWithBody::Static(s) => s.name(db),
DefWithBody::Const(c) => c.name(db),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

View file

@ -68,17 +68,23 @@ pub(crate) fn macro_label(node: &ast::MacroCall) -> String {
}
pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String {
rust_code_markup_with_doc::<_, &str>(val, None)
rust_code_markup_with_doc::<_, &str>(val, None, None)
}
pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String
pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(
val: CODE,
doc: Option<DOC>,
mod_path: Option<String>,
) -> String
where
CODE: AsRef<str>,
DOC: AsRef<str>,
{
let mod_path =
mod_path.filter(|path| !path.is_empty()).map(|path| path + "\n").unwrap_or_default();
if let Some(doc) = doc {
format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref())
format!("```rust\n{}{}\n```\n\n{}", mod_path, val.as_ref(), doc.as_ref())
} else {
format!("```rust\n{}\n```", val.as_ref())
format!("```rust\n{}{}\n```", mod_path, val.as_ref())
}
}

View file

@ -1,6 +1,8 @@
//! FIXME: write short doc here
use hir::{Adt, HasSource, HirDisplay, Semantics};
use hir::{
Adt, AsAssocItem, AssocItemContainer, HasSource, HirDisplay, ModuleDef, ModuleSource, Semantics,
};
use ra_ide_db::{
defs::{classify_name, classify_name_ref, Definition},
RootDatabase,
@ -16,6 +18,8 @@ use crate::{
display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
FilePosition, RangeInfo,
};
use itertools::Itertools;
use std::iter::once;
/// Contains the results when hovering over an item
#[derive(Debug, Clone)]
@ -83,44 +87,86 @@ impl HoverResult {
}
}
fn hover_text(docs: Option<String>, desc: Option<String>) -> Option<String> {
match (desc, docs) {
(Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)),
(None, Some(docs)) => Some(docs),
fn hover_text(
docs: Option<String>,
desc: Option<String>,
mod_path: Option<String>,
) -> Option<String> {
match (desc, docs, mod_path) {
(Some(desc), docs, mod_path) => Some(rust_code_markup_with_doc(desc, docs, mod_path)),
(None, Some(docs), _) => Some(docs),
_ => None,
}
}
fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
match def {
Definition::StructField(f) => Some(f.parent_def(db).name(db)),
Definition::Local(l) => l.parent(db).name(db),
Definition::ModuleDef(md) => match md {
ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) {
AssocItemContainer::Trait(t) => Some(t.name(db)),
AssocItemContainer::ImplDef(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)),
},
ModuleDef::EnumVariant(e) => Some(e.parent_enum(db).name(db)),
_ => None,
},
Definition::SelfType(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)),
_ => None,
}
.map(|name| name.to_string())
}
fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
let mod_path = def.module(db).map(|module| {
once(db.get_crate_original_name(&module.krate().into()))
.chain(
module
.path_to_root(db)
.into_iter()
.rev()
.map(|it| it.name(db).map(|name| name.to_string())),
)
.chain(once(definition_owner_name(db, def)))
.filter_map(std::convert::identity)
.join("::")
});
mod_path
}
fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> {
let mod_path = determine_mod_path(db, &def);
return match def {
Definition::Macro(it) => {
let src = it.source(db);
hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)))
hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path)
}
Definition::StructField(it) => {
let src = it.source(db);
match src.value {
hir::FieldSource::Named(it) => hover_text(it.doc_comment_text(), it.short_label()),
hir::FieldSource::Named(it) => {
hover_text(it.doc_comment_text(), it.short_label(), mod_path)
}
_ => None,
}
}
Definition::ModuleDef(it) => match it {
hir::ModuleDef::Module(it) => match it.definition_source(db).value {
hir::ModuleSource::Module(it) => {
hover_text(it.doc_comment_text(), it.short_label())
ModuleDef::Module(it) => match it.definition_source(db).value {
ModuleSource::Module(it) => {
hover_text(it.doc_comment_text(), it.short_label(), mod_path)
}
_ => None,
},
hir::ModuleDef::Function(it) => from_def_source(db, it),
hir::ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it),
hir::ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it),
hir::ModuleDef::Adt(Adt::Enum(it)) => from_def_source(db, it),
hir::ModuleDef::EnumVariant(it) => from_def_source(db, it),
hir::ModuleDef::Const(it) => from_def_source(db, it),
hir::ModuleDef::Static(it) => from_def_source(db, it),
hir::ModuleDef::Trait(it) => from_def_source(db, it),
hir::ModuleDef::TypeAlias(it) => from_def_source(db, it),
hir::ModuleDef::BuiltinType(it) => Some(it.to_string()),
ModuleDef::Function(it) => from_def_source(db, it, mod_path),
ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it, mod_path),
ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it, mod_path),
ModuleDef::Adt(Adt::Enum(it)) => from_def_source(db, it, mod_path),
ModuleDef::EnumVariant(it) => from_def_source(db, it, mod_path),
ModuleDef::Const(it) => from_def_source(db, it, mod_path),
ModuleDef::Static(it) => from_def_source(db, it, mod_path),
ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
ModuleDef::BuiltinType(it) => Some(it.to_string()),
},
Definition::Local(it) => {
Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string()))
@ -131,13 +177,13 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
}
};
fn from_def_source<A, D>(db: &RootDatabase, def: D) -> Option<String>
fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String>
where
D: HasSource<Ast = A>,
A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
{
let src = def.source(db);
hover_text(src.value.doc_comment_text(), src.value.short_label())
hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path)
}
}
@ -345,7 +391,7 @@ mod tests {
};
}
"#,
&["field_a: u32"],
&["Foo\nfield_a: u32"],
);
// Hovering over the field in the definition
@ -362,7 +408,7 @@ mod tests {
};
}
"#,
&["field_a: u32"],
&["Foo\nfield_a: u32"],
);
}
@ -415,7 +461,7 @@ fn main() {
",
);
let hover = analysis.hover(position).unwrap().unwrap();
assert_eq!(trim_markup_opt(hover.info.first()), Some("Some"));
assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\nSome"));
let (analysis, position) = single_file_with_position(
"
@ -442,6 +488,7 @@ fn main() {
}
"#,
&["
Option
None
```
@ -462,6 +509,7 @@ The None variant
}
"#,
&["
Option
Some
```
@ -815,4 +863,25 @@ fn func(foo: i32) { if true { <|>foo; }; }
&["fn foo()\n```\n\n<- `\u{3000}` here"],
);
}
#[test]
fn zzz() {
check_hover_result(
"
//- /main.rs
mod vvv {
pub struct Test;
impl Test {
pub fn whatever() {}
}
}
fn main() {
vvv::Test::what<|>ever();
}
",
&["vvv::Test\npub fn whatever()"],
);
}
}

View file

@ -104,6 +104,10 @@ impl RootDatabase {
db.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity);
db
}
pub fn get_crate_original_name(&self, crate_id: &CrateId) -> Option<String> {
self.debug_data.crate_names.get(crate_id).cloned()
}
}
impl salsa::ParallelDatabase for RootDatabase {