Auto merge of #14812 - Veykril:highlight-trait-assoc, r=HKalbasi
feat: Highlight used trait assoc items when cursor is on trait import or trait bound
This commit is contained in:
commit
d0768aad62
@ -4,7 +4,9 @@ use ide_db::{
|
|||||||
defs::{Definition, IdentClass},
|
defs::{Definition, IdentClass},
|
||||||
helpers::pick_best_token,
|
helpers::pick_best_token,
|
||||||
search::{FileReference, ReferenceCategory, SearchScope},
|
search::{FileReference, ReferenceCategory, SearchScope},
|
||||||
syntax_helpers::node_ext::{for_each_break_and_continue_expr, for_each_tail_expr, walk_expr},
|
syntax_helpers::node_ext::{
|
||||||
|
for_each_break_and_continue_expr, for_each_tail_expr, full_path_of_name_ref, walk_expr,
|
||||||
|
},
|
||||||
FxHashSet, RootDatabase,
|
FxHashSet, RootDatabase,
|
||||||
};
|
};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
@ -39,11 +41,13 @@ pub struct HighlightRelatedConfig {
|
|||||||
// Highlights constructs related to the thing under the cursor:
|
// Highlights constructs related to the thing under the cursor:
|
||||||
//
|
//
|
||||||
// . if on an identifier, highlights all references to that identifier in the current file
|
// . if on an identifier, highlights all references to that identifier in the current file
|
||||||
|
// .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope
|
||||||
// . if on an `async` or `await token, highlights all yield points for that async context
|
// . if on an `async` or `await token, highlights all yield points for that async context
|
||||||
// . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context
|
// . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context
|
||||||
// . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context
|
// . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context
|
||||||
|
// . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure.
|
||||||
//
|
//
|
||||||
// Note: `?` and `->` do not currently trigger this behavior in the VSCode editor.
|
// Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor.
|
||||||
pub(crate) fn highlight_related(
|
pub(crate) fn highlight_related(
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
config: HighlightRelatedConfig,
|
config: HighlightRelatedConfig,
|
||||||
@ -130,7 +134,7 @@ fn highlight_references(
|
|||||||
token: SyntaxToken,
|
token: SyntaxToken,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
) -> Option<Vec<HighlightedRange>> {
|
) -> Option<Vec<HighlightedRange>> {
|
||||||
let defs = find_defs(sema, token);
|
let defs = find_defs(sema, token.clone());
|
||||||
let usages = defs
|
let usages = defs
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|&d| {
|
.filter_map(|&d| {
|
||||||
@ -145,6 +149,59 @@ fn highlight_references(
|
|||||||
.map(|FileReference { category, range, .. }| HighlightedRange { range, category });
|
.map(|FileReference { category, range, .. }| HighlightedRange { range, category });
|
||||||
let mut res = FxHashSet::default();
|
let mut res = FxHashSet::default();
|
||||||
for &def in &defs {
|
for &def in &defs {
|
||||||
|
// highlight trait usages
|
||||||
|
if let Definition::Trait(t) = def {
|
||||||
|
let trait_item_use_scope = (|| {
|
||||||
|
let name_ref = token.parent().and_then(ast::NameRef::cast)?;
|
||||||
|
let path = full_path_of_name_ref(&name_ref)?;
|
||||||
|
let parent = path.syntax().parent()?;
|
||||||
|
match_ast! {
|
||||||
|
match parent {
|
||||||
|
ast::UseTree(it) => it.syntax().ancestors().find(|it| {
|
||||||
|
ast::SourceFile::can_cast(it.kind()) || ast::Module::can_cast(it.kind())
|
||||||
|
}),
|
||||||
|
ast::PathType(it) => it
|
||||||
|
.syntax()
|
||||||
|
.ancestors()
|
||||||
|
.nth(2)
|
||||||
|
.and_then(ast::TypeBoundList::cast)?
|
||||||
|
.syntax()
|
||||||
|
.parent()
|
||||||
|
.filter(|it| ast::WhereClause::can_cast(it.kind()) || ast::TypeParam::can_cast(it.kind()))?
|
||||||
|
.ancestors()
|
||||||
|
.find(|it| {
|
||||||
|
ast::Item::can_cast(it.kind())
|
||||||
|
}),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
if let Some(trait_item_use_scope) = trait_item_use_scope {
|
||||||
|
res.extend(
|
||||||
|
t.items_with_supertraits(sema.db)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|item| {
|
||||||
|
Definition::from(item)
|
||||||
|
.usages(sema)
|
||||||
|
.set_scope(Some(SearchScope::file_range(FileRange {
|
||||||
|
file_id,
|
||||||
|
range: trait_item_use_scope.text_range(),
|
||||||
|
})))
|
||||||
|
.include_self_refs()
|
||||||
|
.all()
|
||||||
|
.references
|
||||||
|
.remove(&file_id)
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.map(|FileReference { category, range, .. }| HighlightedRange {
|
||||||
|
range,
|
||||||
|
category,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// highlight the defs themselves
|
||||||
match def {
|
match def {
|
||||||
Definition::Local(local) => {
|
Definition::Local(local) => {
|
||||||
let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
|
let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
|
||||||
@ -1477,6 +1534,47 @@ fn f() {
|
|||||||
let c = move$0 |y| x + y;
|
let c = move$0 |y| x + y;
|
||||||
// ^ read
|
// ^ read
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trait_highlights_assoc_item_uses() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
trait Foo {
|
||||||
|
//^^^
|
||||||
|
type T;
|
||||||
|
const C: usize;
|
||||||
|
fn f() {}
|
||||||
|
fn m(&self) {}
|
||||||
|
}
|
||||||
|
impl Foo for i32 {
|
||||||
|
//^^^
|
||||||
|
type T = i32;
|
||||||
|
const C: usize = 0;
|
||||||
|
fn f() {}
|
||||||
|
fn m(&self) {}
|
||||||
|
}
|
||||||
|
fn f<T: Foo$0>(t: T) {
|
||||||
|
//^^^
|
||||||
|
let _: T::T;
|
||||||
|
//^
|
||||||
|
t.m();
|
||||||
|
//^
|
||||||
|
T::C;
|
||||||
|
//^
|
||||||
|
T::f();
|
||||||
|
//^
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f2<T: Foo>(t: T) {
|
||||||
|
//^^^
|
||||||
|
let _: T::T;
|
||||||
|
t.m();
|
||||||
|
T::C;
|
||||||
|
T::f();
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user