Skip to content

Commit feaeb97

Browse files
committed
Move document highlighting computation from rust-analyzer to ide
1 parent 4e2ec91 commit feaeb97

File tree

4 files changed

+214
-39
lines changed

4 files changed

+214
-39
lines changed

crates/ide/src/document_highlight.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
use hir::Semantics;
2+
use ide_db::{
3+
base_db::FilePosition,
4+
defs::Definition,
5+
search::{FileReference, ReferenceAccess, SearchScope},
6+
RootDatabase,
7+
};
8+
use syntax::{AstNode, TextRange};
9+
10+
use crate::{display::TryToNav, references, NavigationTarget};
11+
12+
pub struct DocumentHighlight {
13+
pub range: TextRange,
14+
pub access: Option<ReferenceAccess>,
15+
}
16+
17+
// Feature: Document highlight
18+
//
19+
// Highlights the definition and its all references of the item at the cursor location in the current file.
20+
pub(crate) fn document_highlight(
21+
sema: &Semantics<RootDatabase>,
22+
position: FilePosition,
23+
) -> Option<Vec<DocumentHighlight>> {
24+
let _p = profile::span("document_highlight");
25+
let syntax = sema.parse(position.file_id).syntax().clone();
26+
let def = references::find_def(sema, &syntax, position)?;
27+
let usages = def.usages(sema).set_scope(Some(SearchScope::single_file(position.file_id))).all();
28+
29+
let declaration = match def {
30+
Definition::ModuleDef(hir::ModuleDef::Module(module)) => {
31+
Some(NavigationTarget::from_module_to_decl(sema.db, module))
32+
}
33+
def => def.try_to_nav(sema.db),
34+
}
35+
.filter(|decl| decl.file_id == position.file_id)
36+
.and_then(|decl| {
37+
let range = decl.focus_range?;
38+
let access = references::decl_access(&def, &syntax, range);
39+
Some(DocumentHighlight { range, access })
40+
});
41+
42+
let file_refs = usages.references.get(&position.file_id).map_or(&[][..], Vec::as_slice);
43+
let mut res = Vec::with_capacity(file_refs.len() + 1);
44+
res.extend(declaration);
45+
res.extend(
46+
file_refs
47+
.iter()
48+
.map(|&FileReference { access, range, .. }| DocumentHighlight { range, access }),
49+
);
50+
Some(res)
51+
}
52+
53+
#[cfg(test)]
54+
mod tests {
55+
use crate::fixture;
56+
57+
use super::*;
58+
59+
fn check(ra_fixture: &str) {
60+
let (analysis, pos, annotations) = fixture::annotations(ra_fixture);
61+
let hls = analysis.highlight_document(pos).unwrap().unwrap();
62+
63+
let mut expected = annotations
64+
.into_iter()
65+
.map(|(r, access)| (r.range, (!access.is_empty()).then(|| access)))
66+
.collect::<Vec<_>>();
67+
68+
let mut actual = hls
69+
.into_iter()
70+
.map(|hl| {
71+
(
72+
hl.range,
73+
hl.access.map(|it| {
74+
match it {
75+
ReferenceAccess::Read => "read",
76+
ReferenceAccess::Write => "write",
77+
}
78+
.to_string()
79+
}),
80+
)
81+
})
82+
.collect::<Vec<_>>();
83+
actual.sort_by_key(|(range, _)| range.start());
84+
expected.sort_by_key(|(range, _)| range.start());
85+
86+
assert_eq!(expected, actual);
87+
}
88+
89+
#[test]
90+
fn test_hl_module() {
91+
check(
92+
r#"
93+
//- /lib.rs
94+
mod foo$0;
95+
// ^^^
96+
//- /foo.rs
97+
struct Foo;
98+
"#,
99+
);
100+
}
101+
102+
#[test]
103+
fn test_hl_self_in_crate_root() {
104+
check(
105+
r#"
106+
//- /lib.rs
107+
use self$0;
108+
"#,
109+
);
110+
}
111+
112+
#[test]
113+
fn test_hl_self_in_module() {
114+
check(
115+
r#"
116+
//- /lib.rs
117+
mod foo;
118+
//- /foo.rs
119+
use self$0;
120+
"#,
121+
);
122+
}
123+
124+
#[test]
125+
fn test_hl_local() {
126+
check(
127+
r#"
128+
//- /lib.rs
129+
fn foo() {
130+
let mut bar = 3;
131+
// ^^^ write
132+
bar$0;
133+
// ^^^ read
134+
}
135+
"#,
136+
);
137+
}
138+
}

crates/ide/src/lib.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,33 @@ mod display;
2424

2525
mod annotations;
2626
mod call_hierarchy;
27+
mod doc_links;
28+
mod document_highlight;
2729
mod expand_macro;
2830
mod extend_selection;
2931
mod file_structure;
32+
mod fn_references;
3033
mod folding_ranges;
3134
mod goto_definition;
3235
mod goto_implementation;
3336
mod goto_type_definition;
34-
mod view_hir;
3537
mod hover;
3638
mod inlay_hints;
3739
mod join_lines;
40+
mod markdown_remove;
3841
mod matching_brace;
3942
mod move_item;
4043
mod parent_module;
4144
mod references;
4245
mod rename;
43-
mod fn_references;
4446
mod runnables;
4547
mod ssr;
4648
mod status;
4749
mod syntax_highlighting;
4850
mod syntax_tree;
4951
mod typing;
50-
mod markdown_remove;
51-
mod doc_links;
5252
mod view_crate_graph;
53+
mod view_hir;
5354
mod view_item_tree;
5455

5556
use std::sync::Arc;
@@ -72,6 +73,7 @@ pub use crate::{
7273
annotations::{Annotation, AnnotationConfig, AnnotationKind},
7374
call_hierarchy::CallItem,
7475
display::navigation_target::NavigationTarget,
76+
document_highlight::DocumentHighlight,
7577
expand_macro::ExpandedMacro,
7678
file_structure::{StructureNode, StructureNodeKind},
7779
folding_ranges::{Fold, FoldKind},
@@ -483,6 +485,14 @@ impl Analysis {
483485
self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
484486
}
485487

488+
/// Computes all ranges to highlight for a given item in a file.
489+
pub fn highlight_document(
490+
&self,
491+
position: FilePosition,
492+
) -> Cancellable<Option<Vec<DocumentHighlight>>> {
493+
self.with_db(|db| document_highlight::document_highlight(&Semantics::new(db), position))
494+
}
495+
486496
/// Computes syntax highlighting for the given file range.
487497
pub fn highlight_range(&self, frange: FileRange) -> Cancellable<Vec<HlRange>> {
488498
self.with_db(|db| {

crates/ide/src/references.rs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,13 @@ pub(crate) fn find_all_refs(
9090
_ => {}
9191
}
9292
}
93-
let declaration = def.try_to_nav(sema.db).map(|nav| {
93+
let declaration = match def {
94+
Definition::ModuleDef(hir::ModuleDef::Module(module)) => {
95+
Some(NavigationTarget::from_module_to_decl(sema.db, module))
96+
}
97+
def => def.try_to_nav(sema.db),
98+
}
99+
.map(|nav| {
94100
let decl_range = nav.focus_or_full_range();
95101
Declaration { nav, access: decl_access(&def, &syntax, decl_range) }
96102
});
@@ -104,7 +110,7 @@ pub(crate) fn find_all_refs(
104110
Some(ReferenceSearchResult { declaration, references })
105111
}
106112

107-
fn find_def(
113+
pub(crate) fn find_def(
108114
sema: &Semantics<RootDatabase>,
109115
syntax: &SyntaxNode,
110116
position: FilePosition,
@@ -126,7 +132,11 @@ fn find_def(
126132
Some(def)
127133
}
128134

129-
fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
135+
pub(crate) fn decl_access(
136+
def: &Definition,
137+
syntax: &SyntaxNode,
138+
range: TextRange,
139+
) -> Option<ReferenceAccess> {
130140
match def {
131141
Definition::Local(_) | Definition::Field(_) => {}
132142
_ => return None,
@@ -658,9 +668,6 @@ fn f() {
658668
);
659669
}
660670

661-
// `mod foo;` is not in the results because `foo` is an `ast::Name`.
662-
// So, there are two references: the first one is a definition of the `foo` module,
663-
// which is the whole `foo.rs`, and the second one is in `use foo::Foo`.
664671
#[test]
665672
fn test_find_all_refs_decl_module() {
666673
check(
@@ -680,13 +687,44 @@ pub struct Foo {
680687
}
681688
"#,
682689
expect![[r#"
683-
foo Module FileId(1) 0..35
690+
foo Module FileId(0) 0..8 4..7
684691
685692
FileId(0) 14..17
686693
"#]],
687694
);
688695
}
689696

697+
#[test]
698+
fn test_find_all_refs_decl_module_on_self() {
699+
check(
700+
r#"
701+
//- /lib.rs
702+
mod foo;
703+
704+
//- /foo.rs
705+
use self$0;
706+
"#,
707+
expect![[r#"
708+
foo Module FileId(0) 0..8 4..7
709+
710+
"#]],
711+
);
712+
}
713+
714+
#[test]
715+
fn test_find_all_refs_decl_module_on_self_crate_root() {
716+
check(
717+
r#"
718+
//- /lib.rs
719+
use self$0;
720+
"#,
721+
expect![[r#"
722+
Module FileId(0) 0..10
723+
724+
"#]],
725+
);
726+
}
727+
690728
#[test]
691729
fn test_find_all_refs_super_mod_vis() {
692730
check(

crates/rust-analyzer/src/handlers.rs

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,21 @@ use std::{
99

1010
use ide::{
1111
AnnotationConfig, AssistKind, AssistResolveStrategy, FileId, FilePosition, FileRange,
12-
HoverAction, HoverGotoTypeData, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
13-
SingleResolve, SourceChange, TextEdit,
12+
HoverAction, HoverGotoTypeData, Query, RangeInfo, Runnable, RunnableKind, SingleResolve,
13+
SourceChange, TextEdit,
1414
};
1515
use ide_db::SymbolKind;
1616
use itertools::Itertools;
1717
use lsp_server::ErrorCode;
1818
use lsp_types::{
1919
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
2020
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
21-
CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams,
22-
DocumentHighlight, FoldingRange, FoldingRangeParams, HoverContents, Location, NumberOrString,
23-
Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams,
24-
SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams,
25-
SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag,
26-
TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkspaceEdit,
21+
CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams, FoldingRange,
22+
FoldingRangeParams, HoverContents, Location, NumberOrString, Position, PrepareRenameResponse,
23+
Range, RenameParams, SemanticTokensDeltaParams, SemanticTokensFullDeltaResult,
24+
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
25+
SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier,
26+
TextDocumentPositionParams, Url, WorkspaceEdit,
2727
};
2828
use project_model::TargetKind;
2929
use serde::{Deserialize, Serialize};
@@ -1163,33 +1163,22 @@ pub(crate) fn handle_code_lens_resolve(
11631163
pub(crate) fn handle_document_highlight(
11641164
snap: GlobalStateSnapshot,
11651165
params: lsp_types::DocumentHighlightParams,
1166-
) -> Result<Option<Vec<DocumentHighlight>>> {
1166+
) -> Result<Option<Vec<lsp_types::DocumentHighlight>>> {
11671167
let _p = profile::span("handle_document_highlight");
11681168
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
11691169
let line_index = snap.file_line_index(position.file_id)?;
11701170

1171-
let refs = match snap
1172-
.analysis
1173-
.find_all_refs(position, Some(SearchScope::single_file(position.file_id)))?
1174-
{
1171+
let refs = match snap.analysis.highlight_document(position)? {
11751172
None => return Ok(None),
11761173
Some(refs) => refs,
11771174
};
1178-
1179-
let decl = refs.declaration.filter(|decl| decl.nav.file_id == position.file_id).map(|decl| {
1180-
DocumentHighlight {
1181-
range: to_proto::range(&line_index, decl.nav.focus_or_full_range()),
1182-
kind: decl.access.map(to_proto::document_highlight_kind),
1183-
}
1184-
});
1185-
1186-
let file_refs = refs.references.get(&position.file_id).map_or(&[][..], Vec::as_slice);
1187-
let mut res = Vec::with_capacity(file_refs.len() + 1);
1188-
res.extend(decl);
1189-
res.extend(file_refs.iter().map(|&(range, access)| DocumentHighlight {
1190-
range: to_proto::range(&line_index, range),
1191-
kind: access.map(to_proto::document_highlight_kind),
1192-
}));
1175+
let res = refs
1176+
.into_iter()
1177+
.map(|ide::DocumentHighlight { range, access }| lsp_types::DocumentHighlight {
1178+
range: to_proto::range(&line_index, range),
1179+
kind: access.map(to_proto::document_highlight_kind),
1180+
})
1181+
.collect();
11931182
Ok(Some(res))
11941183
}
11951184

0 commit comments

Comments
 (0)