@@ -31,6 +31,80 @@ pub struct HighlightedRange {
31
31
pub binding_hash : Option < u64 > ,
32
32
}
33
33
34
+ #[ derive( Debug ) ]
35
+ struct HighlightedRangeStack {
36
+ stack : Vec < Vec < HighlightedRange > > ,
37
+ }
38
+
39
+ /// We use a stack to implement the flattening logic for the highlighted
40
+ /// syntax ranges.
41
+ impl HighlightedRangeStack {
42
+ fn new ( ) -> Self {
43
+ Self { stack : vec ! [ Vec :: new( ) ] }
44
+ }
45
+
46
+ fn push ( & mut self ) {
47
+ self . stack . push ( Vec :: new ( ) ) ;
48
+ }
49
+
50
+ /// Flattens the highlighted ranges.
51
+ ///
52
+ /// For example `#[cfg(feature = "foo")]` contains the nested ranges:
53
+ /// 1) parent-range: Attribute [0, 23)
54
+ /// 2) child-range: String [16, 21)
55
+ ///
56
+ /// The following code implements the flattening, for our example this results to:
57
+ /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
58
+ fn pop ( & mut self ) {
59
+ let children = self . stack . pop ( ) . unwrap ( ) ;
60
+ let prev = self . stack . last_mut ( ) . unwrap ( ) ;
61
+ let needs_flattening = !children. is_empty ( )
62
+ && !prev. is_empty ( )
63
+ && children. first ( ) . unwrap ( ) . range . is_subrange ( & prev. last ( ) . unwrap ( ) . range ) ;
64
+ if !needs_flattening {
65
+ prev. extend ( children) ;
66
+ } else {
67
+ let mut parent = prev. pop ( ) . unwrap ( ) ;
68
+ for ele in children {
69
+ assert ! ( ele. range. is_subrange( & parent. range) ) ;
70
+ let mut cloned = parent. clone ( ) ;
71
+ parent. range = TextRange :: from_to ( parent. range . start ( ) , ele. range . start ( ) ) ;
72
+ cloned. range = TextRange :: from_to ( ele. range . end ( ) , cloned. range . end ( ) ) ;
73
+ if !parent. range . is_empty ( ) {
74
+ prev. push ( parent) ;
75
+ }
76
+ prev. push ( ele) ;
77
+ parent = cloned;
78
+ }
79
+ if !parent. range . is_empty ( ) {
80
+ prev. push ( parent) ;
81
+ }
82
+ }
83
+ }
84
+
85
+ fn add ( & mut self , range : HighlightedRange ) {
86
+ self . stack
87
+ . last_mut ( )
88
+ . expect ( "during DFS traversal, the stack must not be empty" )
89
+ . push ( range)
90
+ }
91
+
92
+ fn flattened ( mut self ) -> Vec < HighlightedRange > {
93
+ assert_eq ! (
94
+ self . stack. len( ) ,
95
+ 1 ,
96
+ "after DFS traversal, the stack should only contain a single element"
97
+ ) ;
98
+ let res = self . stack . pop ( ) . unwrap ( ) ;
99
+ // Check that ranges are sorted and disjoint
100
+ assert ! ( res
101
+ . iter( )
102
+ . zip( res. iter( ) . skip( 1 ) )
103
+ . all( |( left, right) | left. range. end( ) <= right. range. start( ) ) ) ;
104
+ res
105
+ }
106
+ }
107
+
34
108
pub ( crate ) fn highlight (
35
109
db : & RootDatabase ,
36
110
file_id : FileId ,
@@ -57,52 +131,17 @@ pub(crate) fn highlight(
57
131
let mut bindings_shadow_count: FxHashMap < Name , u32 > = FxHashMap :: default ( ) ;
58
132
// We use a stack for the DFS traversal below.
59
133
// When we leave a node, the we use it to flatten the highlighted ranges.
60
- let mut res : Vec < Vec < HighlightedRange > > = vec ! [ Vec :: new( ) ] ;
134
+ let mut stack = HighlightedRangeStack :: new ( ) ;
61
135
62
136
let mut current_macro_call: Option < ast:: MacroCall > = None ;
63
137
64
138
// Walk all nodes, keeping track of whether we are inside a macro or not.
65
139
// If in macro, expand it first and highlight the expanded code.
66
140
for event in root. preorder_with_tokens ( ) {
67
141
match & event {
68
- WalkEvent :: Enter ( _) => res. push ( Vec :: new ( ) ) ,
69
- WalkEvent :: Leave ( _) => {
70
- /* Flattens the highlighted ranges.
71
- *
72
- * For example `#[cfg(feature = "foo")]` contains the nested ranges:
73
- * 1) parent-range: Attribute [0, 23)
74
- * 2) child-range: String [16, 21)
75
- *
76
- * The following code implements the flattening, for our example this results to:
77
- * `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
78
- */
79
- let children = res. pop ( ) . unwrap ( ) ;
80
- let prev = res. last_mut ( ) . unwrap ( ) ;
81
- let needs_flattening = !children. is_empty ( )
82
- && !prev. is_empty ( )
83
- && children. first ( ) . unwrap ( ) . range . is_subrange ( & prev. last ( ) . unwrap ( ) . range ) ;
84
- if !needs_flattening {
85
- prev. extend ( children) ;
86
- } else {
87
- let mut parent = prev. pop ( ) . unwrap ( ) ;
88
- for ele in children {
89
- assert ! ( ele. range. is_subrange( & parent. range) ) ;
90
- let mut cloned = parent. clone ( ) ;
91
- parent. range = TextRange :: from_to ( parent. range . start ( ) , ele. range . start ( ) ) ;
92
- cloned. range = TextRange :: from_to ( ele. range . end ( ) , cloned. range . end ( ) ) ;
93
- if !parent. range . is_empty ( ) {
94
- prev. push ( parent) ;
95
- }
96
- prev. push ( ele) ;
97
- parent = cloned;
98
- }
99
- if !parent. range . is_empty ( ) {
100
- prev. push ( parent) ;
101
- }
102
- }
103
- }
142
+ WalkEvent :: Enter ( _) => stack. push ( ) ,
143
+ WalkEvent :: Leave ( _) => stack. pop ( ) ,
104
144
} ;
105
- let current = res. last_mut ( ) . expect ( "during DFS traversal, the stack must not be empty" ) ;
106
145
107
146
let event_range = match & event {
108
147
WalkEvent :: Enter ( it) => it. text_range ( ) ,
@@ -119,7 +158,7 @@ pub(crate) fn highlight(
119
158
WalkEvent :: Enter ( Some ( mc) ) => {
120
159
current_macro_call = Some ( mc. clone ( ) ) ;
121
160
if let Some ( range) = macro_call_range ( & mc) {
122
- current . push ( HighlightedRange {
161
+ stack . add ( HighlightedRange {
123
162
range,
124
163
highlight : HighlightTag :: Macro . into ( ) ,
125
164
binding_hash : None ,
@@ -161,27 +200,19 @@ pub(crate) fn highlight(
161
200
162
201
if let Some ( token) = element. as_token ( ) . cloned ( ) . and_then ( ast:: RawString :: cast) {
163
202
let expanded = element_to_highlight. as_token ( ) . unwrap ( ) . clone ( ) ;
164
- if highlight_injection ( current , & sema, token, expanded) . is_some ( ) {
203
+ if highlight_injection ( & mut stack , & sema, token, expanded) . is_some ( ) {
165
204
continue ;
166
205
}
167
206
}
168
207
169
208
if let Some ( ( highlight, binding_hash) ) =
170
209
highlight_element ( & sema, & mut bindings_shadow_count, element_to_highlight)
171
210
{
172
- current . push ( HighlightedRange { range, highlight, binding_hash } ) ;
211
+ stack . add ( HighlightedRange { range, highlight, binding_hash } ) ;
173
212
}
174
213
}
175
214
176
- assert_eq ! ( res. len( ) , 1 , "after DFS traversal, the stack should only contain a single element" ) ;
177
- let mut res = res. pop ( ) . unwrap ( ) ;
178
- res. sort_by_key ( |range| range. range . start ( ) ) ;
179
- // Check that ranges are sorted and disjoint
180
- assert ! ( res
181
- . iter( )
182
- . zip( res. iter( ) . skip( 1 ) )
183
- . all( |( left, right) | left. range. end( ) <= right. range. start( ) ) ) ;
184
- res
215
+ stack. flattened ( )
185
216
}
186
217
187
218
fn macro_call_range ( macro_call : & ast:: MacroCall ) -> Option < TextRange > {
@@ -358,7 +389,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
358
389
}
359
390
360
391
fn highlight_injection (
361
- acc : & mut Vec < HighlightedRange > ,
392
+ acc : & mut HighlightedRangeStack ,
362
393
sema : & Semantics < RootDatabase > ,
363
394
literal : ast:: RawString ,
364
395
expanded : SyntaxToken ,
@@ -373,7 +404,7 @@ fn highlight_injection(
373
404
let ( analysis, tmp_file_id) = Analysis :: from_single_file ( value) ;
374
405
375
406
if let Some ( range) = literal. open_quote_text_range ( ) {
376
- acc. push ( HighlightedRange {
407
+ acc. add ( HighlightedRange {
377
408
range,
378
409
highlight : HighlightTag :: StringLiteral . into ( ) ,
379
410
binding_hash : None ,
@@ -383,12 +414,12 @@ fn highlight_injection(
383
414
for mut h in analysis. highlight ( tmp_file_id) . unwrap ( ) {
384
415
if let Some ( r) = literal. map_range_up ( h. range ) {
385
416
h. range = r;
386
- acc. push ( h)
417
+ acc. add ( h)
387
418
}
388
419
}
389
420
390
421
if let Some ( range) = literal. close_quote_text_range ( ) {
391
- acc. push ( HighlightedRange {
422
+ acc. add ( HighlightedRange {
392
423
range,
393
424
highlight : HighlightTag :: StringLiteral . into ( ) ,
394
425
binding_hash : None ,
0 commit comments