8
8
import { ITextModel } from 'vs/editor/common/editorCommon' ;
9
9
import { FoldingMarkers } from 'vs/editor/common/modes/languageConfiguration' ;
10
10
11
- export class IndentRange {
12
- _indentRangeBrand : void ;
13
- startLineNumber : number ;
14
- endLineNumber : number ;
15
- indent : number ;
16
- marker : boolean ;
17
-
18
- constructor ( startLineNumber : number , endLineNumber : number , indent : number , marker ?: boolean ) {
19
- this . startLineNumber = startLineNumber ;
20
- this . endLineNumber = endLineNumber ;
21
- this . indent = indent ;
22
- this . marker = marker ;
23
- }
24
-
25
- public static deepCloneArr ( indentRanges : IndentRange [ ] ) : IndentRange [ ] {
26
- let result : IndentRange [ ] = [ ] ;
27
- for ( let i = 0 , len = indentRanges . length ; i < len ; i ++ ) {
28
- let r = indentRanges [ i ] ;
29
- result [ i ] = new IndentRange ( r . startLineNumber , r . endLineNumber , r . indent ) ;
11
+ export const MAX_FOLDING_REGIONS = 0xFFFF ;
12
+
13
+ const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000 ;
14
+ const MASK_LINE_NUMBER = 0xFFFFFF ;
15
+ const MASK_INDENT = 0xFF000000 ;
16
+
17
+ export class IndentRanges {
18
+ private _startIndexes : Uint32Array ;
19
+ private _endIndexes : Uint32Array ;
20
+ private _model : ITextModel ;
21
+ private _indentLimit : number ;
22
+
23
+ constructor ( startIndexes : Uint32Array , endIndexes : Uint32Array , model : ITextModel , indentLimit : number ) {
24
+ if ( startIndexes . length !== endIndexes . length || startIndexes . length > MAX_FOLDING_REGIONS ) {
25
+ throw new Error ( 'invalid startIndexes or endIndexes size' ) ;
26
+ }
27
+ this . _startIndexes = startIndexes ;
28
+ this . _endIndexes = endIndexes ;
29
+ this . _model = model ;
30
+ this . _indentLimit = indentLimit ;
31
+ this . _computeParentIndices ( ) ;
32
+ }
33
+
34
+ private _computeParentIndices ( ) {
35
+ let parentIndexes = [ ] ;
36
+ let isInsideLast = ( startLineNumber : number , endLineNumber : number ) => {
37
+ let index = parentIndexes [ parentIndexes . length - 1 ] ;
38
+ return this . getStartLineNumber ( index ) <= startLineNumber && this . getEndLineNumber ( index ) >= endLineNumber ;
39
+ } ;
40
+ for ( let i = 0 , len = this . _startIndexes . length ; i < len ; i ++ ) {
41
+ let startLineNumber = this . _startIndexes [ i ] ;
42
+ let endLineNumber = this . _endIndexes [ i ] ;
43
+ if ( startLineNumber > MASK_LINE_NUMBER || endLineNumber > MASK_LINE_NUMBER ) {
44
+ throw new Error ( 'startLineNumber or endLineNumber must not exceed ' + MASK_LINE_NUMBER ) ;
45
+ }
46
+ while ( parentIndexes . length > 0 && ! isInsideLast ( startLineNumber , endLineNumber ) ) {
47
+ parentIndexes . pop ( ) ;
48
+ }
49
+ let parentIndex = parentIndexes . length > 0 ? parentIndexes [ parentIndexes . length - 1 ] : - 1 ;
50
+ parentIndexes . push ( i ) ;
51
+ this . _startIndexes [ i ] = startLineNumber + ( ( parentIndex & 0xFF ) << 24 ) ;
52
+ this . _endIndexes [ i ] = endLineNumber + ( ( parentIndex & 0xFF00 ) << 16 ) ;
53
+ }
54
+ }
55
+
56
+ public get length ( ) : number {
57
+ return this . _startIndexes . length ;
58
+ }
59
+
60
+ public getStartLineNumber ( index : number ) : number {
61
+ return this . _startIndexes [ index ] & MASK_LINE_NUMBER ;
62
+ }
63
+
64
+ public getEndLineNumber ( index : number ) : number {
65
+ return this . _endIndexes [ index ] & MASK_LINE_NUMBER ;
66
+ }
67
+
68
+ public getParentIndex ( index : number ) {
69
+ let parent = ( ( this . _startIndexes [ index ] & MASK_INDENT ) >>> 24 ) + ( ( this . _endIndexes [ index ] & MASK_INDENT ) >>> 16 ) ;
70
+ if ( parent === MAX_FOLDING_REGIONS ) {
71
+ return - 1 ;
72
+ }
73
+ return parent ;
74
+ }
75
+
76
+ public get indentLimit ( ) {
77
+ return this . _indentLimit ;
78
+ }
79
+
80
+ public getIndent ( index : number ) {
81
+ return this . _model . getIndentLevel ( this . getStartLineNumber ( index ) ) ;
82
+ }
83
+
84
+ public contains ( index : number , line : number ) {
85
+ return this . getStartLineNumber ( index ) <= line && this . getEndLineNumber ( index ) >= line ;
86
+ }
87
+
88
+ private findIndex ( line : number ) {
89
+ let low = 0 , high = this . _startIndexes . length ;
90
+ if ( high === 0 ) {
91
+ return - 1 ; // no children
92
+ }
93
+ while ( low < high ) {
94
+ let mid = Math . floor ( ( low + high ) / 2 ) ;
95
+ if ( line < this . getStartLineNumber ( mid ) ) {
96
+ high = mid ;
97
+ } else {
98
+ low = mid + 1 ;
99
+ }
30
100
}
31
- return result ;
101
+ return low - 1 ;
102
+ }
103
+
104
+
105
+ public findRange ( line : number ) : number {
106
+ let index = this . findIndex ( line ) ;
107
+ if ( index >= 0 ) {
108
+ let endLineNumber = this . getEndLineNumber ( index ) ;
109
+ if ( endLineNumber >= line ) {
110
+ return index ;
111
+ }
112
+ index = this . getParentIndex ( index ) ;
113
+ while ( index !== - 1 ) {
114
+ if ( this . contains ( index , line ) ) {
115
+ return index ;
116
+ }
117
+ index = this . getParentIndex ( index ) ;
118
+ }
119
+ }
120
+ return - 1 ;
32
121
}
33
122
}
123
+ // public only for testing
124
+ export class RangesCollector {
125
+ private _startIndexes : Uint32Array ;
126
+ private _endIndexes : Uint32Array ;
127
+ private _indentOccurrences : number [ ] = [ ] ;
128
+ private _length : number ;
129
+ private _foldingRegionsLimit : number ;
130
+
131
+ constructor ( foldingRegionsLimit = MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT ) {
132
+ this . _startIndexes = new Uint32Array ( 64 ) ;
133
+ this . _endIndexes = new Uint32Array ( 64 ) ;
134
+ this . _indentOccurrences = [ ] ;
135
+ this . _length = 0 ;
136
+ this . _foldingRegionsLimit = foldingRegionsLimit ;
137
+ }
138
+
139
+ private expand ( arr : Uint32Array ) : Uint32Array {
140
+ let data = new Uint32Array ( arr . length * 2 ) ;
141
+ data . set ( arr , 0 ) ;
142
+ return data ;
143
+ }
144
+
145
+ public insertFirst ( startLineNumber : number , endLineNumber : number , indent : number ) {
146
+ if ( startLineNumber > MASK_LINE_NUMBER || endLineNumber > MASK_LINE_NUMBER ) {
147
+ return ;
148
+ }
149
+ let index = this . _length ;
150
+ if ( index >= this . _startIndexes . length ) {
151
+ this . _startIndexes = this . expand ( this . _startIndexes ) ;
152
+ this . _endIndexes = this . expand ( this . _endIndexes ) ;
153
+ }
154
+ this . _startIndexes [ index ] = startLineNumber ;
155
+ this . _endIndexes [ index ] = endLineNumber ;
156
+ this . _length ++ ;
157
+ if ( indent < 1000 ) {
158
+ this . _indentOccurrences [ indent ] = ( this . _indentOccurrences [ indent ] || 0 ) + 1 ;
159
+ }
160
+ }
161
+
162
+ private _computeMaxIndent ( ) {
163
+ let maxEntries = this . _foldingRegionsLimit ;
164
+ let maxIndent = this . _indentOccurrences . length ;
165
+ for ( let i = 0 ; i < this . _indentOccurrences . length ; i ++ ) {
166
+ if ( this . _indentOccurrences [ i ] ) {
167
+ maxEntries -= this . _indentOccurrences [ i ] ;
168
+ if ( maxEntries < 0 ) {
169
+ maxIndent = i ;
170
+ break ;
171
+ }
172
+ }
173
+ }
174
+ return maxIndent ;
175
+ }
176
+
177
+ public toIndentRanges ( model : ITextModel ) {
178
+ // reverse and create arrays of the exact length
179
+ let startIndexes = new Uint32Array ( this . _length ) ;
180
+ let endIndexes = new Uint32Array ( this . _length ) ;
181
+ for ( let i = this . _length - 1 , k = 0 ; i >= 0 ; i -- , k ++ ) {
182
+ startIndexes [ k ] = this . _startIndexes [ i ] ;
183
+ endIndexes [ k ] = this . _endIndexes [ i ] ;
184
+ }
185
+ return new IndentRanges ( startIndexes , endIndexes , model , this . _computeMaxIndent ( ) ) ;
186
+ }
187
+ }
188
+
34
189
35
190
interface PreviousRegion { indent : number ; line : number ; marker : boolean ; } ;
36
191
37
- export function computeRanges ( model : ITextModel , offSide : boolean , markers ?: FoldingMarkers , minimumRangeSize : number = 1 ) : IndentRange [ ] {
192
+ export function computeRanges ( model : ITextModel , offSide : boolean , markers ?: FoldingMarkers , minimumRangeSize : number = 1 ) : IndentRanges {
38
193
39
- let result : IndentRange [ ] = [ ] ;
194
+ let result = new RangesCollector ( ) ;
40
195
41
196
let pattern = void 0 ;
42
197
if ( markers ) {
@@ -70,7 +225,7 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol
70
225
previous = previousRegions [ i ] ;
71
226
72
227
// new folding range from pattern, includes the end line
73
- result . push ( new IndentRange ( line , previous . line , indent , true ) ) ;
228
+ result . insertFirst ( line , previous . line , indent ) ;
74
229
previous . marker = false ;
75
230
previous . indent = indent ;
76
231
previous . line = line ;
@@ -93,7 +248,7 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol
93
248
// new folding range
94
249
let endLineNumber = previous . line - 1 ;
95
250
if ( endLineNumber - line >= minimumRangeSize ) {
96
- result . push ( new IndentRange ( line , endLineNumber , indent ) ) ;
251
+ result . insertFirst ( line , endLineNumber , indent ) ;
97
252
}
98
253
}
99
254
if ( previous . indent === indent ) {
@@ -103,6 +258,5 @@ export function computeRanges(model: ITextModel, offSide: boolean, markers?: Fol
103
258
previousRegions . push ( { indent, line, marker : false } ) ;
104
259
}
105
260
}
106
-
107
- return result . reverse ( ) ;
261
+ return result . toIndentRanges ( model ) ;
108
262
}
0 commit comments