@@ -5,8 +5,8 @@ use ra_db::SourceDatabase;
5
5
use ra_ide_db:: { defs:: NameDefinition , RootDatabase } ;
6
6
use ra_prof:: profile;
7
7
use ra_syntax:: {
8
- ast, AstNode , Direction , SyntaxElement , SyntaxKind , SyntaxKind :: * , SyntaxToken , TextRange ,
9
- WalkEvent , T ,
8
+ ast, AstNode , Direction , NodeOrToken , SyntaxElement , SyntaxKind , SyntaxKind :: * , SyntaxToken ,
9
+ TextRange , WalkEvent , T ,
10
10
} ;
11
11
use rustc_hash:: FxHashMap ;
12
12
@@ -67,8 +67,13 @@ fn is_control_keyword(kind: SyntaxKind) -> bool {
67
67
}
68
68
}
69
69
70
- pub ( crate ) fn highlight ( db : & RootDatabase , file_id : FileId ) -> Vec < HighlightedRange > {
70
+ pub ( crate ) fn highlight (
71
+ db : & RootDatabase ,
72
+ file_id : FileId ,
73
+ range : Option < TextRange > ,
74
+ ) -> Vec < HighlightedRange > {
71
75
let _p = profile ( "highlight" ) ;
76
+
72
77
let parse = db. parse ( file_id) ;
73
78
let root = parse. tree ( ) . syntax ( ) . clone ( ) ;
74
79
@@ -79,22 +84,56 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
79
84
80
85
let mut in_macro_call = None ;
81
86
87
+ // Determine the root based on the given range.
88
+ let ( root, highlight_range) = if let Some ( range) = range {
89
+ let root = match root. covering_element ( range) {
90
+ NodeOrToken :: Node ( node) => node,
91
+ NodeOrToken :: Token ( token) => token. parent ( ) ,
92
+ } ;
93
+ ( root, range)
94
+ } else {
95
+ ( root. clone ( ) , root. text_range ( ) )
96
+ } ;
97
+
82
98
for event in root. preorder_with_tokens ( ) {
83
99
match event {
84
- WalkEvent :: Enter ( node) => match node. kind ( ) {
85
- MACRO_CALL => {
86
- in_macro_call = Some ( node. clone ( ) ) ;
87
- if let Some ( range) = highlight_macro ( InFile :: new ( file_id. into ( ) , node) ) {
88
- res. push ( HighlightedRange { range, tag : tags:: MACRO , binding_hash : None } ) ;
89
- }
100
+ WalkEvent :: Enter ( node) => {
101
+ if node. text_range ( ) . intersection ( & highlight_range) . is_none ( ) {
102
+ continue ;
90
103
}
91
- _ if in_macro_call. is_some ( ) => {
92
- if let Some ( token) = node. as_token ( ) {
93
- if let Some ( ( tag, binding_hash) ) = highlight_token_tree (
104
+
105
+ match node. kind ( ) {
106
+ MACRO_CALL => {
107
+ in_macro_call = Some ( node. clone ( ) ) ;
108
+ if let Some ( range) = highlight_macro ( InFile :: new ( file_id. into ( ) , node) ) {
109
+ res. push ( HighlightedRange {
110
+ range,
111
+ tag : tags:: MACRO ,
112
+ binding_hash : None ,
113
+ } ) ;
114
+ }
115
+ }
116
+ _ if in_macro_call. is_some ( ) => {
117
+ if let Some ( token) = node. as_token ( ) {
118
+ if let Some ( ( tag, binding_hash) ) = highlight_token_tree (
119
+ & mut sb,
120
+ & analyzer,
121
+ & mut bindings_shadow_count,
122
+ InFile :: new ( file_id. into ( ) , token. clone ( ) ) ,
123
+ ) {
124
+ res. push ( HighlightedRange {
125
+ range : node. text_range ( ) ,
126
+ tag,
127
+ binding_hash,
128
+ } ) ;
129
+ }
130
+ }
131
+ }
132
+ _ => {
133
+ if let Some ( ( tag, binding_hash) ) = highlight_node (
94
134
& mut sb,
95
- & analyzer,
96
135
& mut bindings_shadow_count,
97
- InFile :: new ( file_id. into ( ) , token . clone ( ) ) ,
136
+ InFile :: new ( file_id. into ( ) , node . clone ( ) ) ,
98
137
) {
99
138
res. push ( HighlightedRange {
100
139
range : node. text_range ( ) ,
@@ -104,17 +143,12 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
104
143
}
105
144
}
106
145
}
107
- _ => {
108
- if let Some ( ( tag, binding_hash) ) = highlight_node (
109
- & mut sb,
110
- & mut bindings_shadow_count,
111
- InFile :: new ( file_id. into ( ) , node. clone ( ) ) ,
112
- ) {
113
- res. push ( HighlightedRange { range : node. text_range ( ) , tag, binding_hash } ) ;
114
- }
115
- }
116
- } ,
146
+ }
117
147
WalkEvent :: Leave ( node) => {
148
+ if node. text_range ( ) . intersection ( & highlight_range) . is_none ( ) {
149
+ continue ;
150
+ }
151
+
118
152
if let Some ( m) = in_macro_call. as_ref ( ) {
119
153
if * m == node {
120
154
in_macro_call = None ;
@@ -265,7 +299,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
265
299
)
266
300
}
267
301
268
- let mut ranges = highlight ( db, file_id) ;
302
+ let mut ranges = highlight ( db, file_id, None ) ;
269
303
ranges. sort_by_key ( |it| it. range . start ( ) ) ;
270
304
// quick non-optimal heuristic to intersect token ranges and highlighted ranges
271
305
let mut frontier = 0 ;
@@ -374,7 +408,10 @@ mod tests {
374
408
375
409
use test_utils:: { assert_eq_text, project_dir, read_text} ;
376
410
377
- use crate :: mock_analysis:: { single_file, MockAnalysis } ;
411
+ use crate :: {
412
+ mock_analysis:: { single_file, MockAnalysis } ,
413
+ FileRange , TextRange ,
414
+ } ;
378
415
379
416
#[ test]
380
417
fn test_highlighting ( ) {
@@ -475,4 +512,26 @@ fn bar() {
475
512
let _ = host. analysis ( ) . highlight ( file_id) . unwrap ( ) ;
476
513
// eprintln!("elapsed: {:?}", t.elapsed());
477
514
}
515
+
516
+ #[ test]
517
+ fn test_ranges ( ) {
518
+ let ( analysis, file_id) = single_file (
519
+ r#"
520
+ #[derive(Clone, Debug)]
521
+ struct Foo {
522
+ pub x: i32,
523
+ pub y: i32,
524
+ }"# ,
525
+ ) ;
526
+
527
+ // The "x"
528
+ let highlights = & analysis
529
+ . highlight_range ( FileRange {
530
+ file_id,
531
+ range : TextRange :: offset_len ( 82 . into ( ) , 1 . into ( ) ) ,
532
+ } )
533
+ . unwrap ( ) ;
534
+
535
+ assert_eq ! ( highlights[ 0 ] . tag, "field" ) ;
536
+ }
478
537
}
0 commit comments