@@ -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,26 +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 res = res. pop ( ) . unwrap ( ) ;
178
- // Check that ranges are sorted and disjoint
179
- assert ! ( res
180
- . iter( )
181
- . zip( res. iter( ) . skip( 1 ) )
182
- . all( |( left, right) | left. range. end( ) <= right. range. start( ) ) ) ;
183
- res
215
+ stack. flattened ( )
184
216
}
185
217
186
218
fn macro_call_range ( macro_call : & ast:: MacroCall ) -> Option < TextRange > {
@@ -357,7 +389,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
357
389
}
358
390
359
391
fn highlight_injection (
360
- acc : & mut Vec < HighlightedRange > ,
392
+ acc : & mut HighlightedRangeStack ,
361
393
sema : & Semantics < RootDatabase > ,
362
394
literal : ast:: RawString ,
363
395
expanded : SyntaxToken ,
@@ -372,7 +404,7 @@ fn highlight_injection(
372
404
let ( analysis, tmp_file_id) = Analysis :: from_single_file ( value) ;
373
405
374
406
if let Some ( range) = literal. open_quote_text_range ( ) {
375
- acc. push ( HighlightedRange {
407
+ acc. add ( HighlightedRange {
376
408
range,
377
409
highlight : HighlightTag :: StringLiteral . into ( ) ,
378
410
binding_hash : None ,
@@ -382,12 +414,12 @@ fn highlight_injection(
382
414
for mut h in analysis. highlight ( tmp_file_id) . unwrap ( ) {
383
415
if let Some ( r) = literal. map_range_up ( h. range ) {
384
416
h. range = r;
385
- acc. push ( h)
417
+ acc. add ( h)
386
418
}
387
419
}
388
420
389
421
if let Some ( range) = literal. close_quote_text_range ( ) {
390
- acc. push ( HighlightedRange {
422
+ acc. add ( HighlightedRange {
391
423
range,
392
424
highlight : HighlightTag :: StringLiteral . into ( ) ,
393
425
binding_hash : None ,
0 commit comments