|
1 | 1 | use hir::Semantics; |
2 | 2 | use ide_db::{ |
3 | 3 | base_db::FilePosition, |
4 | | - defs::Definition, |
| 4 | + defs::{Definition, NameClass, NameRefClass}, |
5 | 5 | helpers::{for_each_break_expr, for_each_tail_expr, pick_best_token}, |
6 | 6 | search::{FileReference, ReferenceAccess, SearchScope}, |
7 | 7 | RootDatabase, |
8 | 8 | }; |
9 | 9 | use syntax::{ |
10 | 10 | ast::{self, LoopBodyOwner}, |
11 | | - match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, T, |
| 11 | + match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, T, |
12 | 12 | }; |
13 | 13 |
|
14 | 14 | use crate::{display::TryToNav, references, NavigationTarget}; |
@@ -70,35 +70,36 @@ fn highlight_references( |
70 | 70 | syntax: &SyntaxNode, |
71 | 71 | FilePosition { offset, file_id }: FilePosition, |
72 | 72 | ) -> Option<Vec<HighlightedRange>> { |
73 | | - let def = references::find_def(sema, syntax, offset)?; |
74 | | - let usages = def |
75 | | - .usages(sema) |
76 | | - .set_scope(Some(SearchScope::single_file(file_id))) |
77 | | - .include_self_refs() |
78 | | - .all(); |
79 | | - |
80 | | - let declaration = match def { |
81 | | - Definition::ModuleDef(hir::ModuleDef::Module(module)) => { |
82 | | - Some(NavigationTarget::from_module_to_decl(sema.db, module)) |
| 73 | + let defs = find_defs(sema, syntax, offset)?; |
| 74 | + let usages = defs |
| 75 | + .iter() |
| 76 | + .flat_map(|&d| { |
| 77 | + d.usages(sema) |
| 78 | + .set_scope(Some(SearchScope::single_file(file_id))) |
| 79 | + .include_self_refs() |
| 80 | + .all() |
| 81 | + .references |
| 82 | + .remove(&file_id) |
| 83 | + }) |
| 84 | + .flatten() |
| 85 | + .map(|FileReference { access, range, .. }| HighlightedRange { range, access }); |
| 86 | + |
| 87 | + let declarations = defs.iter().flat_map(|def| { |
| 88 | + match def { |
| 89 | + &Definition::ModuleDef(hir::ModuleDef::Module(module)) => { |
| 90 | + Some(NavigationTarget::from_module_to_decl(sema.db, module)) |
| 91 | + } |
| 92 | + def => def.try_to_nav(sema.db), |
83 | 93 | } |
84 | | - def => def.try_to_nav(sema.db), |
85 | | - } |
86 | | - .filter(|decl| decl.file_id == file_id) |
87 | | - .and_then(|decl| { |
88 | | - let range = decl.focus_range?; |
89 | | - let access = references::decl_access(&def, syntax, range); |
90 | | - Some(HighlightedRange { range, access }) |
| 94 | + .filter(|decl| decl.file_id == file_id) |
| 95 | + .and_then(|decl| { |
| 96 | + let range = decl.focus_range?; |
| 97 | + let access = references::decl_access(&def, syntax, range); |
| 98 | + Some(HighlightedRange { range, access }) |
| 99 | + }) |
91 | 100 | }); |
92 | 101 |
|
93 | | - let file_refs = usages.references.get(&file_id).map_or(&[][..], Vec::as_slice); |
94 | | - let mut res = Vec::with_capacity(file_refs.len() + 1); |
95 | | - res.extend(declaration); |
96 | | - res.extend( |
97 | | - file_refs |
98 | | - .iter() |
99 | | - .map(|&FileReference { access, range, .. }| HighlightedRange { range, access }), |
100 | | - ); |
101 | | - Some(res) |
| 102 | + Some(declarations.chain(usages).collect()) |
102 | 103 | } |
103 | 104 |
|
104 | 105 | fn highlight_exit_points( |
@@ -265,6 +266,35 @@ fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange |
265 | 266 | } |
266 | 267 | } |
267 | 268 |
|
| 269 | +fn find_defs( |
| 270 | + sema: &Semantics<RootDatabase>, |
| 271 | + syntax: &SyntaxNode, |
| 272 | + offset: TextSize, |
| 273 | +) -> Option<Vec<Definition>> { |
| 274 | + let defs = match sema.find_node_at_offset_with_descend(syntax, offset)? { |
| 275 | + ast::NameLike::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? { |
| 276 | + NameRefClass::Definition(def) => vec![def], |
| 277 | + NameRefClass::FieldShorthand { local_ref, field_ref } => { |
| 278 | + vec![Definition::Local(local_ref), Definition::Field(field_ref)] |
| 279 | + } |
| 280 | + }, |
| 281 | + ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? { |
| 282 | + NameClass::Definition(it) | NameClass::ConstReference(it) => vec![it], |
| 283 | + NameClass::PatFieldShorthand { local_def, field_ref } => { |
| 284 | + vec![Definition::Local(local_def), Definition::Field(field_ref)] |
| 285 | + } |
| 286 | + }, |
| 287 | + ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime) |
| 288 | + .and_then(|class| match class { |
| 289 | + NameRefClass::Definition(it) => Some(it), |
| 290 | + _ => None, |
| 291 | + }) |
| 292 | + .or_else(|| NameClass::classify_lifetime(sema, &lifetime).and_then(NameClass::defined)) |
| 293 | + .map(|it| vec![it])?, |
| 294 | + }; |
| 295 | + Some(defs) |
| 296 | +} |
| 297 | + |
268 | 298 | #[cfg(test)] |
269 | 299 | mod tests { |
270 | 300 | use crate::fixture; |
@@ -773,6 +803,22 @@ fn foo() { |
773 | 803 | ); |
774 | 804 | } |
775 | 805 |
|
| 806 | + #[test] |
| 807 | + fn test_hl_field_shorthand() { |
| 808 | + check( |
| 809 | + r#" |
| 810 | +struct Struct { field: u32 } |
| 811 | + //^^^^^ |
| 812 | +fn function(field: u32) { |
| 813 | + //^^^^^ |
| 814 | + Struct { field$0 } |
| 815 | + //^^^^^ read |
| 816 | + //^^^^^ read |
| 817 | +} |
| 818 | +"#, |
| 819 | + ); |
| 820 | + } |
| 821 | + |
776 | 822 | #[test] |
777 | 823 | fn test_hl_disabled_ref_local() { |
778 | 824 | let config = HighlightRelatedConfig { |
|
0 commit comments