Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit ffeaee8

Browse files
committed
Goto type actions for notable trait hovers
1 parent 82e8355 commit ffeaee8

File tree

3 files changed

+212
-79
lines changed

3 files changed

+212
-79
lines changed

crates/ide/src/hover.rs

Lines changed: 70 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ mod render;
33
#[cfg(test)]
44
mod tests;
55

6-
use std::iter;
6+
use std::{iter, ops::Not};
77

88
use either::Either;
9-
use hir::{db::DefDatabase, DescendPreference, HasSource, LangItem, Semantics};
9+
use hir::{db::DefDatabase, DescendPreference, HasCrate, HasSource, LangItem, Semantics};
1010
use ide_db::{
1111
base_db::FileRange,
1212
defs::{Definition, IdentClass, NameRefClass, OperatorClass},
@@ -64,7 +64,7 @@ pub enum HoverAction {
6464
}
6565

6666
impl HoverAction {
67-
fn goto_type_from_targets(db: &RootDatabase, targets: Vec<hir::ModuleDef>) -> Self {
67+
fn goto_type_from_targets(db: &RootDatabase, targets: Vec<hir::ModuleDef>) -> Option<Self> {
6868
let targets = targets
6969
.into_iter()
7070
.filter_map(|it| {
@@ -77,8 +77,8 @@ impl HoverAction {
7777
nav: it.try_to_nav(db)?.call_site(),
7878
})
7979
})
80-
.collect();
81-
HoverAction::GoToType(targets)
80+
.collect::<Vec<_>>();
81+
targets.is_empty().not().then_some(HoverAction::GoToType(targets))
8282
}
8383
}
8484

@@ -315,7 +315,7 @@ fn hover_simple(
315315
ast::IntNumber(num) => {
316316
res.markup = match num.value() {
317317
Ok(num) => {
318-
Markup::fenced_block_text(format_args!("{num} (0x{num:X}|0x{num:b})"))
318+
Markup::fenced_block_text(format_args!("{num} (0x{num:X}|0b{num:b})"))
319319
},
320320
Err(e) => {
321321
Markup::fenced_block_text(format_args!("{e}"))
@@ -365,25 +365,67 @@ fn hover_ranged(
365365
})
366366
}
367367

368+
// FIXME: Why is this pub(crate)?
368369
pub(crate) fn hover_for_definition(
369370
sema: &Semantics<'_, RootDatabase>,
370371
file_id: FileId,
371-
definition: Definition,
372+
def: Definition,
372373
scope_node: &SyntaxNode,
373374
config: &HoverConfig,
374375
) -> Option<HoverResult> {
375-
let famous_defs = match &definition {
376+
let famous_defs = match &def {
376377
Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(scope_node)?.krate())),
377378
_ => None,
378379
};
379-
render::definition(sema.db, definition, famous_defs.as_ref(), config).map(|markup| {
380+
381+
let db = sema.db;
382+
let def_ty = match def {
383+
Definition::Local(it) => Some(it.ty(db)),
384+
Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)),
385+
Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)),
386+
Definition::Field(field) => Some(field.ty(db)),
387+
Definition::TupleField(it) => Some(it.ty(db)),
388+
Definition::Function(it) => Some(it.ty(db)),
389+
Definition::Adt(it) => Some(it.ty(db)),
390+
Definition::Const(it) => Some(it.ty(db)),
391+
Definition::Static(it) => Some(it.ty(db)),
392+
Definition::TypeAlias(it) => Some(it.ty(db)),
393+
Definition::BuiltinType(it) => Some(it.ty(db)),
394+
_ => None,
395+
};
396+
let notable_traits = def_ty
397+
.map(|ty| {
398+
db.notable_traits_in_deps(ty.krate(db).into())
399+
.iter()
400+
.flat_map(|it| &**it)
401+
.filter_map(move |&trait_| {
402+
let trait_ = trait_.into();
403+
ty.impls_trait(db, trait_, &[]).then(|| {
404+
(
405+
trait_,
406+
trait_
407+
.items(db)
408+
.into_iter()
409+
.filter_map(hir::AssocItem::as_type_alias)
410+
.map(|alias| {
411+
(ty.normalize_trait_assoc_type(db, &[], alias), alias.name(db))
412+
})
413+
.collect::<Vec<_>>(),
414+
)
415+
})
416+
})
417+
.collect::<Vec<_>>()
418+
})
419+
.unwrap_or_default();
420+
421+
render::definition(sema.db, def, famous_defs.as_ref(), &notable_traits, config).map(|markup| {
380422
HoverResult {
381-
markup: render::process_markup(sema.db, definition, &markup, config),
423+
markup: render::process_markup(sema.db, def, &markup, config),
382424
actions: [
383-
show_implementations_action(sema.db, definition),
384-
show_fn_references_action(sema.db, definition),
385-
runnable_action(sema, definition, file_id),
386-
goto_type_action_for_def(sema.db, definition),
425+
show_implementations_action(sema.db, def),
426+
show_fn_references_action(sema.db, def),
427+
runnable_action(sema, def, file_id),
428+
goto_type_action_for_def(sema.db, def, &notable_traits),
387429
]
388430
.into_iter()
389431
.flatten()
@@ -446,14 +488,25 @@ fn runnable_action(
446488
}
447489
}
448490

449-
fn goto_type_action_for_def(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
491+
fn goto_type_action_for_def(
492+
db: &RootDatabase,
493+
def: Definition,
494+
notable_traits: &[(hir::Trait, Vec<(Option<hir::Type>, hir::Name)>)],
495+
) -> Option<HoverAction> {
450496
let mut targets: Vec<hir::ModuleDef> = Vec::new();
451497
let mut push_new_def = |item: hir::ModuleDef| {
452498
if !targets.contains(&item) {
453499
targets.push(item);
454500
}
455501
};
456502

503+
for &(trait_, ref assocs) in notable_traits {
504+
push_new_def(trait_.into());
505+
assocs.iter().filter_map(|(ty, _)| ty.as_ref()).for_each(|ty| {
506+
walk_and_push_ty(db, ty, &mut push_new_def);
507+
});
508+
}
509+
457510
if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
458511
let krate = it.module(db).krate();
459512
let sized_trait =
@@ -469,13 +522,13 @@ fn goto_type_action_for_def(db: &RootDatabase, def: Definition) -> Option<HoverA
469522
Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
470523
Definition::Field(field) => field.ty(db),
471524
Definition::Function(function) => function.ret_type(db),
472-
_ => return None,
525+
_ => return HoverAction::goto_type_from_targets(db, targets),
473526
};
474527

475528
walk_and_push_ty(db, &ty, &mut push_new_def);
476529
}
477530

478-
Some(HoverAction::goto_type_from_targets(db, targets))
531+
HoverAction::goto_type_from_targets(db, targets)
479532
}
480533

481534
fn walk_and_push_ty(

crates/ide/src/hover/render.rs

Lines changed: 45 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use std::{mem, ops::Not};
33

44
use either::Either;
55
use hir::{
6-
db::DefDatabase, Adt, AsAssocItem, AssocItem, CaptureKind, HasCrate, HasSource, HirDisplay,
7-
Layout, LayoutError, Semantics, TypeInfo,
6+
Adt, AsAssocItem, CaptureKind, HasSource, HirDisplay, Layout, LayoutError, Name, Semantics,
7+
Trait, Type, TypeInfo,
88
};
99
use ide_db::{
1010
base_db::SourceDatabase,
@@ -117,7 +117,9 @@ pub(super) fn try_expr(
117117
};
118118
walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def);
119119
walk_and_push_ty(sema.db, &body_ty, &mut push_new_def);
120-
res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
120+
if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets) {
121+
res.actions.push(actions);
122+
}
121123

122124
let inner_ty = inner_ty.display(sema.db).to_string();
123125
let body_ty = body_ty.display(sema.db).to_string();
@@ -195,7 +197,9 @@ pub(super) fn deref_expr(
195197
)
196198
.into()
197199
};
198-
res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
200+
if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets) {
201+
res.actions.push(actions);
202+
}
199203

200204
Some(res)
201205
}
@@ -302,7 +306,9 @@ pub(super) fn struct_rest_pat(
302306

303307
Markup::fenced_block(&s)
304308
};
305-
res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
309+
if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets) {
310+
res.actions.push(actions);
311+
}
306312
res
307313
}
308314

@@ -388,6 +394,7 @@ pub(super) fn definition(
388394
db: &RootDatabase,
389395
def: Definition,
390396
famous_defs: Option<&FamousDefs<'_, '_>>,
397+
notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
391398
config: &HoverConfig,
392399
) -> Option<Markup> {
393400
let mod_path = definition_mod_path(db, &def);
@@ -464,58 +471,35 @@ pub(super) fn definition(
464471
_ => None,
465472
};
466473

467-
let def_ty = match def {
468-
Definition::Local(it) => Some(it.ty(db)),
469-
Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)),
470-
Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)),
471-
Definition::Field(field) => Some(field.ty(db)),
472-
Definition::TupleField(it) => Some(it.ty(db)),
473-
Definition::Function(it) => Some(it.ty(db)),
474-
Definition::Adt(it) => Some(it.ty(db)),
475-
Definition::Const(it) => Some(it.ty(db)),
476-
Definition::Static(it) => Some(it.ty(db)),
477-
Definition::TypeAlias(it) => Some(it.ty(db)),
478-
Definition::BuiltinType(it) => Some(it.ty(db)),
479-
_ => None,
480-
};
481-
let notable_traits = def_ty.and_then(|ty| {
474+
let notable_traits = {
482475
let mut desc = String::new();
483476
let mut needs_impl_header = true;
484-
for &trait_ in db.notable_traits_in_deps(ty.krate(db).into()).iter().flat_map(|it| &**it) {
485-
let trait_ = trait_.into();
486-
if ty.impls_trait(db, trait_, &[]) {
487-
let aliases: Vec<_> = trait_
488-
.items(db)
489-
.into_iter()
490-
.filter_map(AssocItem::as_type_alias)
491-
.map(|alias| (ty.normalize_trait_assoc_type(db, &[], alias), alias.name(db)))
492-
.collect();
493-
desc.push_str(if mem::take(&mut needs_impl_header) {
494-
" // notable traits impls: "
495-
} else {
496-
", "
497-
});
498-
format_to!(desc, "{}", trait_.name(db).display(db),);
499-
if !aliases.is_empty() {
500-
desc.push('<');
501-
format_to!(
502-
desc,
503-
"{}",
504-
aliases.into_iter().format_with(", ", |(ty, name), f| {
505-
f(&name.display(db))?;
506-
f(&" = ")?;
507-
match ty {
508-
Some(ty) => f(&ty.display(db)),
509-
None => f(&"?"),
510-
}
511-
})
512-
);
513-
desc.push('>');
514-
}
477+
for (trait_, assoc_types) in notable_traits {
478+
desc.push_str(if mem::take(&mut needs_impl_header) {
479+
" // notable traits implemented: "
480+
} else {
481+
", "
482+
});
483+
format_to!(desc, "{}", trait_.name(db).display(db),);
484+
if !assoc_types.is_empty() {
485+
desc.push('<');
486+
format_to!(
487+
desc,
488+
"{}",
489+
assoc_types.into_iter().format_with(", ", |(ty, name), f| {
490+
f(&name.display(db))?;
491+
f(&" = ")?;
492+
match ty {
493+
Some(ty) => f(&ty.display(db)),
494+
None => f(&"?"),
495+
}
496+
})
497+
);
498+
desc.push('>');
515499
}
516500
}
517501
desc.is_empty().not().then(|| desc)
518-
});
502+
};
519503

520504
let mut desc = String::new();
521505
if let Some(notable_traits) = notable_traits {
@@ -569,7 +553,9 @@ fn type_info(
569553
} else {
570554
Markup::fenced_block(&original.display(sema.db))
571555
};
572-
res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
556+
if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets) {
557+
res.actions.push(actions);
558+
}
573559
Some(res)
574560
}
575561

@@ -629,7 +615,9 @@ fn closure_ty(
629615
);
630616

631617
let mut res = HoverResult::default();
632-
res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
618+
if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets) {
619+
res.actions.push(actions);
620+
}
633621
res.markup = markup.into();
634622
Some(res)
635623
}
@@ -783,7 +771,9 @@ fn keyword_hints(
783771
KeywordHint {
784772
description,
785773
keyword_mod,
786-
actions: vec![HoverAction::goto_type_from_targets(sema.db, targets)],
774+
actions: HoverAction::goto_type_from_targets(sema.db, targets)
775+
.into_iter()
776+
.collect(),
787777
}
788778
}
789779
_ => KeywordHint {

0 commit comments

Comments
 (0)