use hir::{
- Adt, AsAssocItem, AssocItemContainer, FieldSource, HasAttrs, HasSource, HirDisplay, Module,
- ModuleDef, ModuleSource, Semantics,
+ Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource,
+ HirDisplay, Module, ModuleDef, ModuleSource, Semantics,
};
-use ide_db::base_db::SourceDatabase;
use ide_db::{
+ base_db::SourceDatabase,
defs::{Definition, NameClass, NameRefClass},
+ helpers::FamousDefs,
RootDatabase,
};
use itertools::Itertools;
doc_links::{remove_links, rewrite_links},
markdown_remove::remove_markdown,
markup::Markup,
- runnables::runnable,
+ runnables::{runnable_fn, runnable_mod},
FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
};
pub markdown: bool,
}
-impl Default for HoverConfig {
- fn default() -> Self {
- Self {
- implementations: true,
- run: true,
- debug: true,
- goto_type_def: true,
- links_in_hover: true,
- markdown: true,
- }
- }
-}
-
impl HoverConfig {
pub const NO_ACTIONS: Self = Self {
implementations: false,
#[derive(Debug, Clone)]
pub enum HoverAction {
Runnable(Runnable),
- Implementaion(FilePosition),
+ Implementation(FilePosition),
GoToType(Vec<HoverGotoTypeData>),
}
let node = token.parent();
let definition = match_ast! {
match node {
- ast::Name(name) => NameClass::classify(&sema, &name).and_then(|d| d.defined(sema.db)),
+ // we don't use NameClass::referenced_or_defined here as we do not want to resolve
+ // field pattern shorthands to their definition
+ ast::Name(name) => NameClass::classify(&sema, &name).and_then(|class| match class {
+ NameClass::ConstReference(def) => Some(def),
+ def => def.defined(sema.db),
+ }),
ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime)
.map_or_else(|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced(sema.db)), |d| d.defined(sema.db)),
}
};
if let Some(definition) = definition {
- if let Some(markup) = hover_for_definition(db, definition) {
- let markup = if !markdown {
- remove_markdown(&markup.as_str())
- } else if links_in_hover {
- rewrite_links(db, &markup.as_str(), &definition)
- } else {
- remove_links(&markup.as_str())
- };
- res.markup = Markup::from(markup);
+ let famous_defs = match &definition {
+ Definition::ModuleDef(ModuleDef::BuiltinType(_)) => {
+ Some(FamousDefs(&sema, sema.scope(&node).krate()))
+ }
+ _ => None,
+ };
+ if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref()) {
+ res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown);
if let Some(action) = show_implementations_action(db, definition) {
res.actions.push(action);
}
// don't highlight the entire parent node on comment hover
return None;
}
+ if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
+ return res;
+ }
- let node = token.ancestors().find(|n| {
- ast::Expr::can_cast(n.kind())
- || ast::Pat::can_cast(n.kind())
- || ast::SelfParam::can_cast(n.kind())
- })?;
+ let node = token
+ .ancestors()
+ .find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?;
let ty = match_ast! {
match node {
ast::Expr(it) => sema.type_of_expr(&it)?,
ast::Pat(it) => sema.type_of_pat(&it)?,
- ast::SelfParam(self_param) => sema.type_of_self(&self_param)?,
// If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
// (e.g expanding a builtin macro). So we give up here.
ast::MacroCall(_it) => return None,
fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
fn to_action(nav_target: NavigationTarget) -> HoverAction {
- HoverAction::Implementaion(FilePosition {
+ HoverAction::Implementation(FilePosition {
file_id: nav_target.file_id,
offset: nav_target.focus_or_full_range().start(),
})
}
- match def {
- Definition::ModuleDef(it) => match it {
- ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.try_to_nav(db)?)),
- ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.try_to_nav(db)?)),
- ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.try_to_nav(db)?)),
- ModuleDef::Trait(it) => Some(to_action(it.try_to_nav(db)?)),
- _ => None,
- },
+ let adt = match def {
+ Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action),
+ Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it),
+ Definition::SelfType(it) => it.target_ty(db).as_adt(),
_ => None,
- }
+ }?;
+ adt.try_to_nav(db).map(to_action)
}
fn runnable_action(
) -> Option<HoverAction> {
match def {
Definition::ModuleDef(it) => match it {
- ModuleDef::Module(it) => match it.definition_source(sema.db).value {
- ModuleSource::Module(it) => runnable(&sema, it.syntax().clone(), file_id)
- .map(|it| HoverAction::Runnable(it)),
- _ => None,
- },
- ModuleDef::Function(it) => {
- #[allow(deprecated)]
- let src = it.source(sema.db)?;
+ ModuleDef::Module(it) => runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it)),
+ ModuleDef::Function(func) => {
+ let src = func.source(sema.db)?;
if src.file_id != file_id.into() {
mark::hit!(hover_macro_generated_struct_fn_doc_comment);
mark::hit!(hover_macro_generated_struct_fn_doc_attr);
-
return None;
}
- runnable(&sema, src.value.syntax().clone(), file_id)
- .map(|it| HoverAction::Runnable(it))
+ runnable_fn(&sema, func).map(HoverAction::Runnable)
}
_ => None,
},
}
fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
- match def {
- Definition::Local(it) => {
- let mut targets: Vec<ModuleDef> = Vec::new();
- let mut push_new_def = |item: ModuleDef| {
- if !targets.contains(&item) {
- targets.push(item);
- }
- };
-
- it.ty(db).walk(db, |t| {
- if let Some(adt) = t.as_adt() {
- push_new_def(adt.into());
- } else if let Some(trait_) = t.as_dyn_trait() {
- push_new_def(trait_.into());
- } else if let Some(traits) = t.as_impl_traits(db) {
- traits.into_iter().for_each(|it| push_new_def(it.into()));
- } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
- push_new_def(trait_.into());
- }
- });
-
- let targets = targets
- .into_iter()
- .filter_map(|it| {
- Some(HoverGotoTypeData {
- mod_path: render_path(
- db,
- it.module(db)?,
- it.name(db).map(|name| name.to_string()),
- ),
- nav: it.try_to_nav(db)?,
- })
- })
- .collect();
-
- Some(HoverAction::GoToType(targets))
+ let mut targets: Vec<ModuleDef> = Vec::new();
+ let mut push_new_def = |item: ModuleDef| {
+ if !targets.contains(&item) {
+ targets.push(item);
}
- _ => None,
+ };
+
+ if let Definition::GenericParam(GenericParam::TypeParam(it)) = def {
+ it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
+ } else {
+ let ty = match def {
+ Definition::Local(it) => it.ty(db),
+ Definition::GenericParam(GenericParam::ConstParam(it)) => it.ty(db),
+ _ => return None,
+ };
+
+ ty.walk(db, |t| {
+ if let Some(adt) = t.as_adt() {
+ push_new_def(adt.into());
+ } else if let Some(trait_) = t.as_dyn_trait() {
+ push_new_def(trait_.into());
+ } else if let Some(traits) = t.as_impl_traits(db) {
+ traits.into_iter().for_each(|it| push_new_def(it.into()));
+ } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
+ push_new_def(trait_.into());
+ }
+ });
}
+
+ let targets = targets
+ .into_iter()
+ .filter_map(|it| {
+ Some(HoverGotoTypeData {
+ mod_path: render_path(db, it.module(db)?, it.name(db).map(|name| name.to_string())),
+ nav: it.try_to_nav(db)?,
+ })
+ })
+ .collect();
+
+ Some(HoverAction::GoToType(targets))
}
fn hover_markup(
}
}
+fn process_markup(
+ db: &RootDatabase,
+ def: Definition,
+ markup: &Markup,
+ links_in_hover: bool,
+ markdown: bool,
+) -> Markup {
+ let markup = markup.as_str();
+ let markup = if !markdown {
+ remove_markdown(markup)
+ } else if links_in_hover {
+ rewrite_links(db, markup, &def)
+ } else {
+ remove_links(markup)
+ };
+ Markup::from(markup)
+}
+
fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
match def {
Definition::Field(f) => Some(f.parent_def(db).name(db)),
def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
}
-fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
+fn hover_for_definition(
+ db: &RootDatabase,
+ def: Definition,
+ famous_defs: Option<&FamousDefs>,
+) -> Option<Markup> {
let mod_path = definition_mod_path(db, &def);
return match def {
Definition::Macro(it) => {
from_def_source_labeled(db, it, Some(label), mod_path)
}
Definition::Field(def) => {
- #[allow(deprecated)]
let src = def.source(db)?.value;
if let FieldSource::Named(it) = src {
from_def_source_labeled(db, def, it.short_label(), mod_path)
match it.definition_source(db).value {
ModuleSource::Module(it) => it.short_label(),
ModuleSource::SourceFile(it) => it.short_label(),
+ ModuleSource::BlockExpr(it) => it.short_label(),
},
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(Markup::fenced_block(&it)),
+ ModuleDef::BuiltinType(it) => famous_defs
+ .and_then(|fd| hover_for_builtin(fd, it))
+ .or_else(|| Some(Markup::fenced_block(&it.name()))),
},
Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))),
Definition::SelfType(impl_def) => {
})
}
Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
- Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))),
- Definition::TypeParam(_) | Definition::ConstParam(_) => {
- // FIXME: Hover for generic param
- None
- }
+ Definition::GenericParam(it) => match it {
+ GenericParam::TypeParam(it) => Some(Markup::fenced_block(&it.display(db))),
+ GenericParam::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))),
+ GenericParam::ConstParam(it) => from_def_source(db, it, None),
+ },
};
fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
D: HasSource<Ast = A> + HasAttrs + Copy,
A: ShortLabel,
{
- #[allow(deprecated)]
let short_label = def.source(db)?.value.short_label();
from_def_source_labeled(db, def, short_label, mod_path)
}
}
}
+fn hover_for_keyword(
+ sema: &Semantics<RootDatabase>,
+ links_in_hover: bool,
+ markdown: bool,
+ token: &SyntaxToken,
+) -> Option<RangeInfo<HoverResult>> {
+ if !token.kind().is_keyword() {
+ return None;
+ }
+ let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()).krate());
+ // std exposes {}_keyword modules with docstrings on the root to document keywords
+ let keyword_mod = format!("{}_keyword", token.text());
+ let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
+ let docs = doc_owner.attrs(sema.db).docs()?;
+ let markup = process_markup(
+ sema.db,
+ Definition::ModuleDef(doc_owner.into()),
+ &hover_markup(Some(docs.into()), Some(token.text().into()), None)?,
+ links_in_hover,
+ markdown,
+ );
+ Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() }))
+}
+
+fn hover_for_builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Option<Markup> {
+ // std exposes prim_{} modules with docstrings on the root to document the builtins
+ let primitive_mod = format!("prim_{}", builtin.name());
+ let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
+ let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
+ hover_markup(Some(docs.into()), Some(builtin.name().to_string()), None)
+}
+
+fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> {
+ let db = famous_defs.0.db;
+ let std_crate = famous_defs.std()?;
+ let std_root_module = std_crate.root_module(db);
+ std_root_module
+ .children(db)
+ .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
+}
+
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
return tokens.max_by_key(priority);
fn priority(n: &SyntaxToken) -> usize {
match n.kind() {
- IDENT | INT_NUMBER | LIFETIME_IDENT => 3,
+ IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
pub fn foo() -> u32 { 1 }
fn main() {
- let foo_test = foo()<|>;
+ let foo_test = foo()$0;
}
"#,
expect![[r#"
pub fn foo() -> u32 { 1 }
fn main() {
- let foo_test = foo()<|>;
+ let foo_test = foo()$0;
}
"#,
expect![[r#"
Option::Some(*memo + value)
};
let number = 5u32;
- let mut iter<|> = scan(OtherStruct { i: num }, closure, number);
+ let mut iter$0 = scan(OtherStruct { i: num }, closure, number);
}
"#,
expect![[r#"
r#"
pub fn foo() -> u32 { 1 }
-fn main() { let foo_test = fo<|>o(); }
+fn main() { let foo_test = fo$0o(); }
"#,
expect![[r#"
*foo*
mod b;
mod c;
-fn main() { let foo_test = fo<|>o(); }
+fn main() { let foo_test = fo$0o(); }
"#,
expect![[r#"
*foo*
r#"
pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
-fn main() { let foo_test = fo<|>o(); }
+fn main() { let foo_test = fo$0o(); }
"#,
expect![[r#"
*foo*
fn hover_shows_fn_signature_on_fn_name() {
check(
r#"
-pub fn foo<|>(a: u32, b: u32) -> u32 {}
+pub fn foo$0(a: u32, b: u32) -> u32 {}
fn main() { }
"#,
/// #
/// foo(Path::new("hello, world!"))
/// ```
-pub fn foo<|>(_: &Path) {}
+pub fn foo$0(_: &Path) {}
fn main() { }
"#,
check(
r##"
#[doc = r#"Raw string doc attr"#]
-pub fn foo<|>(_: &Path) {}
+pub fn foo$0(_: &Path) {}
fn main() { }
"##,
struct Foo { field_a: u32 }
fn main() {
- let foo = Foo { field_a<|>: 0, };
+ let foo = Foo { field_a$0: 0, };
}
"#,
expect![[r#"
// Hovering over the field in the definition
check(
r#"
-struct Foo { field_a<|>: u32 }
+struct Foo { field_a$0: u32 }
fn main() {
let foo = Foo { field_a: 0 };
#[test]
fn hover_const_static() {
check(
- r#"const foo<|>: u32 = 123;"#,
+ r#"const foo$0: u32 = 123;"#,
expect![[r#"
*foo*
"#]],
);
check(
- r#"static foo<|>: u32 = 456;"#,
+ r#"static foo$0: u32 = 456;"#,
expect![[r#"
*foo*
struct Test<K, T = u8> { k: K, t: T }
fn main() {
- let zz<|> = Test { t: 23u8, k: 33 };
+ let zz$0 = Test { t: 23u8, k: 33 };
}"#,
expect![[r#"
*zz*
enum Option<T> { Some(T) }
use Option::Some;
-fn main() { So<|>me(12); }
+fn main() { So$0me(12); }
"#,
expect![[r#"
*Some*
enum Option<T> { Some(T) }
use Option::Some;
-fn main() { let b<|>ar = Some(12); }
+fn main() { let b$0ar = Some(12); }
"#,
expect![[r#"
*bar*
r#"
enum Option<T> {
/// The None variant
- Non<|>e
+ Non$0e
}
"#,
expect![[r#"
Some(T)
}
fn main() {
- let s = Option::Som<|>e(12);
+ let s = Option::Som$0e(12);
}
"#,
expect![[r#"
#[test]
fn hover_for_local_variable() {
check(
- r#"fn func(foo: i32) { fo<|>o; }"#,
+ r#"fn func(foo: i32) { fo$0o; }"#,
expect![[r#"
*foo*
#[test]
fn hover_for_local_variable_pat() {
check(
- r#"fn func(fo<|>o: i32) {}"#,
+ r#"fn func(fo$0o: i32) {}"#,
expect![[r#"
*foo*
#[test]
fn hover_local_var_edge() {
check(
- r#"fn func(foo: i32) { if true { <|>foo; }; }"#,
+ r#"fn func(foo: i32) { if true { $0foo; }; }"#,
expect![[r#"
*foo*
#[test]
fn hover_for_param_edge() {
check(
- r#"fn func(<|>foo: i32) {}"#,
+ r#"fn func($0foo: i32) {}"#,
expect![[r#"
*foo*
trait DerefMut {
type Target: ?Sized;
}
- fn f(_x<|>: impl Deref<Target=u8> + DerefMut<Target=u8>) {}"#,
+ fn f(_x$0: impl Deref<Target=u8> + DerefMut<Target=u8>) {}"#,
expect![[r#"
*_x*
fn new() -> Thing { Thing { x: 0 } }
}
-fn main() { let foo_<|>test = Thing::new(); }
+fn main() { let foo_$0test = Thing::new(); }
"#,
expect![[r#"
*foo_test*
}
}
-fn main() { let foo_test = wrapper::Thing::new<|>(); }
+fn main() { let foo_test = wrapper::Thing::new$0(); }
"#,
expect![[r#"
*new*
fn main() {
match 1 {
- X::C<|> => {},
+ X::C$0 => {},
2 => {},
_ => {}
};
r#"
struct Thing { x: u32 }
impl Thing {
- fn new() -> Self { Self<|> { x: 0 } }
+ fn new() -> Self { Self$0 { x: 0 } }
}
"#,
expect![[r#"
r#"
struct Thing { x: u32 }
impl Thing {
- fn new() -> Self<|> { Self { x: 0 } }
+ fn new() -> Self$0 { Self { x: 0 } }
}
"#,
expect![[r#"
r#"
enum Thing { A }
impl Thing {
- pub fn new() -> Self<|> { Thing::A }
+ pub fn new() -> Self$0 { Thing::A }
}
"#,
expect![[r#"
r#"
enum Thing { A }
impl Thing {
- pub fn thing(a: Self<|>) {}
+ pub fn thing(a: Self$0) {}
}
"#,
expect![[r#"
fn y() {
let x = 0i32;
- x<|>;
+ x$0;
}
"#,
expect![[r#"
r#"
macro_rules! foo { () => {} }
-fn f() { fo<|>o!(); }
+fn f() { fo$0o!(); }
"#,
expect![[r#"
*foo*
#[test]
fn test_hover_tuple_field() {
check(
- r#"struct TS(String, i32<|>);"#,
+ r#"struct TS(String, i32$0);"#,
expect![[r#"
*i32*
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
fn foo() {}
id! {
- fn bar() { fo<|>o(); }
+ fn bar() { fo$0o(); }
}
"#,
expect![[r#"
check(
r#"
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
-fn foo(bar:u32) { let a = id!(ba<|>r); }
+fn foo(bar:u32) { let a = id!(ba$0r); }
"#,
expect![[r#"
*bar*
r#"
macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
-fn foo(bar:u32) { let a = id!(ba<|>r); }
+fn foo(bar:u32) { let a = id!(ba$0r); }
"#,
expect![[r#"
*bar*
macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
fn bar() -> u32 { 0 }
-fn foo() { let a = id!([0u32, bar(<|>)] ); }
+fn foo() { let a = id!([0u32, bar($0)] ); }
"#,
expect![[r#"
*bar()*
macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } }
fn foo() {
let mastered_for_itunes = "";
- let _ = arr!("Tr<|>acks", &mastered_for_itunes);
+ let _ = arr!("Tr$0acks", &mastered_for_itunes);
}
"#,
expect![[r#"
fn bar() -> bool { true }
fn foo() {
- assert!(ba<|>r());
+ assert!(ba$0r());
}
"#,
expect![[r#"
macro_rules! format {}
fn foo() {
- format!("hel<|>lo {}", 0);
+ format!("hel$0lo {}", 0);
}
"#,
);
/// <- `\u{3000}` here
fn foo() { }
-fn bar() { fo<|>o(); }
+fn bar() { fo$0o(); }
",
expect![[r#"
*foo*
#[test]
fn test_hover_function_show_qualifiers() {
check(
- r#"async fn foo<|>() {}"#,
+ r#"async fn foo$0() {}"#,
expect![[r#"
*foo*
"#]],
);
check(
- r#"pub const unsafe fn foo<|>() {}"#,
+ r#"pub const unsafe fn foo$0() {}"#,
expect![[r#"
*foo*
"#]],
);
check(
- r#"pub(crate) async unsafe extern "C" fn foo<|>() {}"#,
+ r#"pub(crate) async unsafe extern "C" fn foo$0() {}"#,
expect![[r#"
*foo*
#[test]
fn test_hover_trait_show_qualifiers() {
check_actions(
- r"unsafe trait foo<|>() {}",
+ r"unsafe trait foo$0() {}",
expect![[r#"
[
- Implementaion(
+ Implementation(
FilePosition {
file_id: FileId(
0,
check(
r#"
//- /main.rs crate:main deps:std
-extern crate st<|>d;
+extern crate st$0d;
//- /std/lib.rs crate:std
//! Standard library for this test
//!
check(
r#"
//- /main.rs crate:main deps:std
-extern crate std as ab<|>c;
+extern crate std as ab$0c;
//- /std/lib.rs crate:std
//! Standard library for this test
//!
fn test_hover_mod_with_same_name_as_function() {
check(
r#"
-use self::m<|>y::Bar;
+use self::m$0y::Bar;
mod my { pub struct Bar; }
fn my() {}
/// bar docs
struct Bar;
-fn foo() { let bar = Ba<|>r; }
+fn foo() { let bar = Ba$0r; }
"#,
expect![[r#"
*Bar*
#[doc = "bar docs"]
struct Bar;
-fn foo() { let bar = Ba<|>r; }
+fn foo() { let bar = Ba$0r; }
"#,
expect![[r#"
*Bar*
#[doc = "bar docs 2"]
struct Bar;
-fn foo() { let bar = Ba<|>r; }
+fn foo() { let bar = Ba$0r; }
"#,
expect![[r#"
*Bar*
r#"
pub struct Foo;
/// [Foo](struct.Foo.html)
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
r#"
pub struct Foo;
/// [struct Foo](struct.Foo.html)
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
pub struct Foo;
pub struct Bar {
/// [Foo](struct.Foo.html)
- fie<|>ld: ()
+ fie$0ld: ()
}
"#,
expect![[r#"
pub struct Foo;
}
/// [Foo](foo::Foo)
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
pub struct Foo;
}
/// [Foo](foo::Foo)
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
r#"
pub struct Foo;
/// [Foo]
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
r#"
pub struct Foo;
/// [`Foo`]
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
pub struct Foo;
fn Foo() {}
/// [Foo()]
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
r#"
pub struct Foo;
/// [`struct Foo`]
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
r#"
pub struct Foo;
/// [`struct@Foo`]
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
/// [my Foo][foo]
///
/// [foo]: Foo
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
"#]],
);
}
+ #[test]
+ fn test_hover_intra_link_reference_to_trait_method() {
+ check(
+ r#"
+pub trait Foo {
+ fn buzz() -> usize;
+}
+/// [Foo][buzz]
+///
+/// [buzz]: Foo::buzz
+pub struct B$0ar
+"#,
+ expect![[r#"
+ *Bar*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ pub struct Bar
+ ```
+
+ ---
+
+ [Foo](https://docs.rs/test/*/test/trait.Foo.html#tymethod.buzz)
+ "#]],
+ );
+ }
#[test]
fn test_hover_external_url() {
r#"
pub struct Foo;
/// [external](https://www.google.com)
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
r#"
pub struct Foo;
/// [baz](Baz)
-pub struct B<|>ar
+pub struct B$0ar
"#,
expect![[r#"
*Bar*
r#"
enum E {
/// [E]
- V<|> { field: i32 }
+ V$0 { field: i32 }
}
"#,
expect![[r#"
r#"
struct S {
/// [`S`]
- field<|>: i32
+ field$0: i32
}
"#,
expect![[r#"
/// Test cases:
/// case 1. bare URL: https://www.example.com/
/// case 2. inline URL with title: [example](https://www.example.com/)
-/// case 3. code refrence: [`Result`]
-/// case 4. code refrence but miss footnote: [`String`]
+/// case 3. code reference: [`Result`]
+/// case 4. code reference but miss footnote: [`String`]
/// case 5. autolink: <http://www.example.com/>
/// case 6. email address: <test@example.com>
-/// case 7. refrence: [example][example]
+/// case 7. reference: [example][example]
/// case 8. collapsed link: [example][]
/// case 9. shortcut link: [example]
/// case 10. inline without URL: [example]()
-/// case 11. refrence: [foo][foo]
-/// case 12. refrence: [foo][bar]
+/// case 11. reference: [foo][foo]
+/// case 12. reference: [foo][bar]
/// case 13. collapsed link: [foo][]
/// case 14. shortcut link: [foo]
/// case 15. inline without URL: [foo]()
///
/// [`Result`]: ../../std/result/enum.Result.html
/// [^example]: https://www.example.com/
-pub fn fo<|>o() {}
+pub fn fo$0o() {}
"#,
expect![[r#"
*foo*
Test cases:
case 1. bare URL: https://www.example.com/
case 2. inline URL with title: [example](https://www.example.com/)
- case 3. code refrence: `Result`
- case 4. code refrence but miss footnote: `String`
+ case 3. code reference: `Result`
+ case 4. code reference but miss footnote: `String`
case 5. autolink: http://www.example.com/
case 6. email address: test@example.com
- case 7. refrence: example
+ case 7. reference: example
case 8. collapsed link: example
case 9. shortcut link: example
case 10. inline without URL: example
- case 11. refrence: foo
- case 12. refrence: foo
+ case 11. reference: foo
+ case 12. reference: foo
case 13. collapsed link: foo
case 14. shortcut link: foo
case 15. inline without URL: foo
bar!();
-fn foo() { let bar = Bar; bar.fo<|>o(); }
+fn foo() { let bar = Bar; bar.fo$0o(); }
"#,
expect![[r#"
*foo*
bar!();
-fn foo() { let bar = Bar; bar.fo<|>o(); }
+fn foo() { let bar = Bar; bar.fo$0o(); }
"#,
expect![[r#"
*foo*
#[test]
fn test_hover_trait_has_impl_action() {
check_actions(
- r#"trait foo<|>() {}"#,
+ r#"trait foo$0() {}"#,
expect![[r#"
[
- Implementaion(
+ Implementation(
FilePosition {
file_id: FileId(
0,
#[test]
fn test_hover_struct_has_impl_action() {
check_actions(
- r"struct foo<|>() {}",
+ r"struct foo$0() {}",
expect![[r#"
[
- Implementaion(
+ Implementation(
FilePosition {
file_id: FileId(
0,
#[test]
fn test_hover_union_has_impl_action() {
check_actions(
- r#"union foo<|>() {}"#,
+ r#"union foo$0() {}"#,
expect![[r#"
[
- Implementaion(
+ Implementation(
FilePosition {
file_id: FileId(
0,
#[test]
fn test_hover_enum_has_impl_action() {
check_actions(
- r"enum foo<|>() { A, B }",
+ r"enum foo$0() { A, B }",
expect![[r#"
[
- Implementaion(
+ Implementation(
FilePosition {
file_id: FileId(
0,
);
}
+ #[test]
+ fn test_hover_self_has_impl_action() {
+ check_actions(
+ r#"struct foo where Self$0:;"#,
+ expect![[r#"
+ [
+ Implementation(
+ FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 7,
+ },
+ ),
+ ]
+ "#]],
+ );
+ }
+
#[test]
fn test_hover_test_has_action() {
check_actions(
r#"
#[test]
-fn foo_<|>test() {}
+fn foo_$0test() {}
"#,
expect![[r#"
[
fn test_hover_test_mod_has_action() {
check_actions(
r#"
-mod tests<|> {
+mod tests$0 {
#[test]
fn foo_test() {}
}
r#"
struct S{ f1: u32 }
-fn main() { let s<|>t = S{ f1:0 }; }
+fn main() { let s$0t = S{ f1:0 }; }
"#,
expect![[r#"
[
struct Arg(u32);
struct S<T>{ f1: T }
-fn main() { let s<|>t = S{ f1:Arg(0) }; }
+fn main() { let s$0t = S{ f1:Arg(0) }; }
"#,
expect![[r#"
[
struct Arg(u32);
struct S<T>{ f1: T }
-fn main() { let s<|>t = S{ f1: S{ f1: Arg(0) } }; }
+fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
"#,
expect![[r#"
[
pub struct C(u32);
}
-fn main() { let s<|>t = (A(1), B(2), M::C(3) ); }
+fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
"#,
expect![[r#"
[
trait Foo {}
fn foo() -> impl Foo {}
-fn main() { let s<|>t = foo(); }
+fn main() { let s$0t = foo(); }
"#,
expect![[r#"
[
struct S;
fn foo() -> impl Foo<S> {}
-fn main() { let s<|>t = foo(); }
+fn main() { let s$0t = foo(); }
"#,
expect![[r#"
[
trait Bar {}
fn foo() -> impl Foo + Bar {}
-fn main() { let s<|>t = foo(); }
+fn main() { let s$0t = foo(); }
"#,
expect![[r#"
[
fn foo() -> impl Foo<S1> + Bar<S2> {}
-fn main() { let s<|>t = foo(); }
+fn main() { let s$0t = foo(); }
"#,
expect![[r#"
[
check_actions(
r#"
trait Foo {}
-fn foo(ar<|>g: &impl Foo) {}
+fn foo(ar$0g: &impl Foo) {}
"#,
expect![[r#"
[
trait Bar<T> {}
struct S{}
-fn foo(ar<|>g: &impl Foo + Bar<S>) {}
+fn foo(ar$0g: &impl Foo + Bar<S>) {}
"#,
expect![[r#"
[
r#"
struct S;
fn foo() {
- let fo<|>o = async { S };
+ let fo$0o = async { S };
}
#[prelude_import] use future::*;
r#"
trait Foo<T> {}
struct S {}
-fn foo(ar<|>g: &impl Foo<S>) {}
+fn foo(ar$0g: &impl Foo<S>) {}
"#,
expect![[r#"
[
struct B<T>{}
fn foo() -> B<dyn Foo> {}
-fn main() { let s<|>t = foo(); }
+fn main() { let s$0t = foo(); }
"#,
expect![[r#"
[
check_actions(
r#"
trait Foo {}
-fn foo(ar<|>g: &dyn Foo) {}
+fn foo(ar$0g: &dyn Foo) {}
"#,
expect![[r#"
[
r#"
trait Foo<T> {}
struct S {}
-fn foo(ar<|>g: &dyn Foo<S>) {}
+fn foo(ar$0g: &dyn Foo<S>) {}
"#,
expect![[r#"
[
struct B<T> {}
struct S {}
-fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
+fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
"#,
expect![[r#"
[
fn test() -> impl Foo { S {} }
-fn main() { let s<|>t = test().get(); }
+fn main() { let s$0t = test().get(); }
"#,
expect![[r#"
[
);
}
+ #[test]
+ fn test_hover_const_param_has_goto_type_action() {
+ check_actions(
+ r#"
+struct Bar;
+struct Foo<const BAR: Bar>;
+
+impl<const BAR: Bar> Foo<BAR$0> {}
+"#,
+ expect![[r#"
+ [
+ GoToType(
+ [
+ HoverGotoTypeData {
+ mod_path: "test::Bar",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..11,
+ focus_range: 7..10,
+ name: "Bar",
+ kind: Struct,
+ description: "struct Bar",
+ },
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_hover_type_param_has_goto_type_action() {
+ check_actions(
+ r#"
+trait Foo {}
+
+fn foo<T: Foo>(t: T$0){}
+"#,
+ expect![[r#"
+ [
+ GoToType(
+ [
+ HoverGotoTypeData {
+ mod_path: "test::Foo",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..12,
+ focus_range: 6..9,
+ name: "Foo",
+ kind: Trait,
+ description: "trait Foo",
+ },
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_hover_self_has_go_to_type() {
+ check_actions(
+ r#"
+struct Foo;
+impl Foo {
+ fn foo(&self$0) {}
+}
+"#,
+ expect![[r#"
+ [
+ GoToType(
+ [
+ HoverGotoTypeData {
+ mod_path: "test::Foo",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..11,
+ focus_range: 7..10,
+ name: "Foo",
+ kind: Struct,
+ description: "struct Foo",
+ },
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+ }
+
#[test]
fn hover_displays_normalized_crate_names() {
check(
}
//- /main.rs crate:main deps:name-with-dashes
-fn main() { let foo_test = name_with_dashes::wrapper::Thing::new<|>(); }
+fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); }
"#,
expect![[r#"
*new*
fn main() {
let s = S { f: 0 };
- let S { f<|> } = &s;
+ let S { f$0 } = &s;
}
"#,
expect![[r#"
r#"
struct Foo {}
impl Foo {
- fn bar(&sel<|>f) {}
+ fn bar(&sel$0f) {}
}
"#,
expect![[r#"
- *&self*
+ *self*
+
```rust
&Foo
```
struct Arc<T>(T);
struct Foo {}
impl Foo {
- fn bar(sel<|>f: Arc<Foo>) {}
+ fn bar(sel$0f: Arc<Foo>) {}
}
"#,
expect![[r#"
- *self: Arc<Foo>*
+ *self*
+
```rust
Arc<Foo>
```
check(
r#"
/// Be quick;
-mod Foo<|> {
+mod Foo$0 {
//! time is mana
/// This comment belongs to the function
check(
r#"
#[doc = "Be quick;"]
-mod Foo<|> {
+mod Foo$0 {
#![doc = "time is mana"]
#[doc = "This comment belongs to the function"]
check_hover_no_result(
r#"
fn no_hover() {
- // no<|>hover
+ // no$0hover
}
"#,
);
check(
r#"
fn foo() {
- 'label<|>: loop {}
+ 'label$0: loop {}
}
"#,
expect![[r#"
#[test]
fn hover_lifetime() {
check(
- r#"fn foo<'lifetime>(_: &'lifetime<|> ()) {}"#,
+ r#"fn foo<'lifetime>(_: &'lifetime$0 ()) {}"#,
expect![[r#"
*'lifetime*
"#]],
);
}
+
+ #[test]
+ fn hover_type_param() {
+ check(
+ r#"
+struct Foo<T>(T);
+trait Copy {}
+trait Clone {}
+trait Sized {}
+impl<T: Copy + Clone> Foo<T$0> where T: Sized {}
+"#,
+ expect![[r#"
+ *T*
+
+ ```rust
+ T: Copy + Clone + Sized
+ ```
+ "#]],
+ );
+ check(
+ r#"
+struct Foo<T>(T);
+impl<T> Foo<T$0> {}
+"#,
+ expect![[r#"
+ *T*
+
+ ```rust
+ T
+ ```
+ "#]],
+ );
+ // lifetimes bounds arent being tracked yet
+ check(
+ r#"
+struct Foo<T>(T);
+impl<T: 'static> Foo<T$0> {}
+"#,
+ expect![[r#"
+ *T*
+
+ ```rust
+ T
+ ```
+ "#]],
+ );
+ }
+
+ #[test]
+ fn hover_const_param() {
+ check(
+ r#"
+struct Foo<const LEN: usize>;
+impl<const LEN: usize> Foo<LEN$0> {}
+"#,
+ expect![[r#"
+ *LEN*
+
+ ```rust
+ const LEN: usize
+ ```
+ "#]],
+ );
+ }
+
+ #[test]
+ fn hover_const_pat() {
+ check(
+ r#"
+/// This is a doc
+const FOO: usize = 3;
+fn foo() {
+ match 5 {
+ FOO$0 => (),
+ _ => ()
+ }
+}
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: usize = 3
+ ```
+
+ ---
+
+ This is a doc
+ "#]],
+ );
+ }
+
+ #[test]
+ fn hover_mod_def() {
+ check(
+ r#"
+//- /main.rs
+mod foo$0;
+//- /foo.rs
+//! For the horde!
+"#,
+ expect![[r#"
+ *foo*
+ For the horde!
+ "#]],
+ );
+ }
+
+ #[test]
+ fn hover_self_in_use() {
+ check(
+ r#"
+//! This should not appear
+mod foo {
+ /// But this should appear
+ pub mod bar {}
+}
+use foo::bar::{self$0};
+"#,
+ expect![[r#"
+ *self*
+
+ ```rust
+ test::foo
+ ```
+
+ ```rust
+ pub mod bar
+ ```
+
+ ---
+
+ But this should appear
+ "#]],
+ )
+ }
+
+ #[test]
+ fn hover_keyword() {
+ let ra_fixture = r#"//- /main.rs crate:main deps:std
+fn f() { retur$0n; }"#;
+ let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
+ check(
+ &fixture,
+ expect![[r#"
+ *return*
+
+ ```rust
+ return
+ ```
+
+ ---
+
+ Docs for return_keyword
+ "#]],
+ );
+ }
+
+ #[test]
+ fn hover_builtin() {
+ let ra_fixture = r#"//- /main.rs crate:main deps:std
+cosnt _: &str$0 = ""; }"#;
+ let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
+ check(
+ &fixture,
+ expect![[r#"
+ *str*
+
+ ```rust
+ str
+ ```
+
+ ---
+
+ Docs for prim_str
+ "#]],
+ );
+ }
}