@@ -16,6 +16,8 @@ import { binarySearch } from 'vs/base/common/arrays';
16
16
import { Iterable } from 'vs/base/common/iterator' ;
17
17
import { FoldingController } from 'vs/editor/contrib/folding/browser/folding' ;
18
18
import { FoldingModel } from 'vs/editor/contrib/folding/browser/foldingModel' ;
19
+ import { URI } from 'vs/base/common/uri' ;
20
+ import { isEqual } from 'vs/base/common/resources' ;
19
21
20
22
export class StickyRange {
21
23
constructor (
@@ -33,19 +35,20 @@ export class StickyLineCandidate {
33
35
}
34
36
35
37
export class StickyLineCandidateProvider extends Disposable {
36
- private readonly onStickyScrollChangeEmitter = this . _register ( new Emitter < void > ( ) ) ;
37
- public readonly onStickyScrollChange = this . onStickyScrollChangeEmitter . event ;
38
38
39
39
static readonly ID = 'store.contrib.stickyScrollController' ;
40
+
41
+ private readonly _onDidChangeStickyScroll = this . _store . add ( new Emitter < void > ( ) ) ;
42
+ public readonly onDidChangeStickyScroll = this . _onDidChangeStickyScroll . event ;
43
+
40
44
private readonly _editor : ICodeEditor ;
41
45
private readonly _languageFeaturesService : ILanguageFeaturesService ;
42
46
private readonly _updateSoon : RunOnceScheduler ;
43
47
44
- private _cts : CancellationTokenSource | undefined ;
45
- private _outlineModel : StickyOutlineElement | undefined ;
46
48
private readonly _sessionStore : DisposableStore = new DisposableStore ( ) ;
47
- private _modelVersionId : number = 0 ;
48
- private _providerID : string | undefined = undefined ;
49
+ private _cts : CancellationTokenSource | undefined ;
50
+
51
+ private _model : StickyOutlineModel | undefined ;
49
52
50
53
constructor (
51
54
editor : ICodeEditor ,
@@ -63,67 +66,81 @@ export class StickyLineCandidateProvider extends Disposable {
63
66
this . readConfiguration ( ) ;
64
67
}
65
68
69
+ override dispose ( ) : void {
70
+ super . dispose ( ) ;
71
+ this . _sessionStore . dispose ( ) ;
72
+ }
73
+
66
74
private readConfiguration ( ) {
67
75
const options = this . _editor . getOption ( EditorOption . stickyScroll ) ;
68
76
if ( options . enabled === false ) {
69
77
this . _sessionStore . clear ( ) ;
70
78
return ;
71
79
} else {
72
80
this . _sessionStore . add ( this . _editor . onDidChangeModel ( ( ) => {
73
- this . _providerID = undefined ;
74
81
this . update ( ) ;
75
82
} ) ) ;
76
83
this . _sessionStore . add ( this . _editor . onDidChangeHiddenAreas ( ( ) => this . update ( ) ) ) ;
77
84
this . _sessionStore . add ( this . _editor . onDidChangeModelContent ( ( ) => this . _updateSoon . schedule ( ) ) ) ;
78
85
this . _sessionStore . add ( this . _languageFeaturesService . documentSymbolProvider . onDidChange ( ( ) => {
79
- this . _providerID = undefined ;
86
+
80
87
this . update ( ) ;
81
88
} ) ) ;
82
89
this . update ( ) ;
83
90
}
84
91
}
85
92
86
93
public getVersionId ( ) {
87
- return this . _modelVersionId ;
94
+ return this . _model ?. version ?? - 1 ;
88
95
}
89
96
90
97
public async update ( ) : Promise < void > {
91
98
this . _cts ?. dispose ( true ) ;
92
99
this . _cts = new CancellationTokenSource ( ) ;
93
100
await this . updateOutlineModel ( this . _cts . token ) ;
94
- this . onStickyScrollChangeEmitter . fire ( ) ;
101
+ this . _onDidChangeStickyScroll . fire ( ) ;
95
102
}
96
103
97
- private async updateOutlineModel ( token : CancellationToken ) {
98
- if ( this . _editor . hasModel ( ) ) {
99
- const model = this . _editor . getModel ( ) ;
100
- const modelVersionId = model . getVersionId ( ) ;
101
- const outlineModel = await OutlineModel . create ( this . _languageFeaturesService . documentSymbolProvider , model , token ) as OutlineModel ;
104
+ private async updateOutlineModel ( token : CancellationToken ) : Promise < void > {
105
+ if ( ! this . _editor . hasModel ( ) ) {
106
+ return ;
107
+ }
108
+
109
+ const model = this . _editor . getModel ( ) ;
110
+ const modelVersionId = model . getVersionId ( ) ;
111
+ const isDifferentModel = this . _model ? ! isEqual ( this . _model . uri , model . uri ) : false ;
112
+
113
+ // clear sticky scroll to not show stale data for too long
114
+ const resetHandle = isDifferentModel ? setTimeout ( ( ) => {
115
+ if ( ! token . isCancellationRequested ) {
116
+ this . _model = new StickyOutlineModel ( model . uri , model . getVersionId ( ) , undefined , undefined ) ;
117
+ this . _onDidChangeStickyScroll . fire ( ) ;
118
+ }
119
+ } , 75 ) : undefined ;
120
+
121
+ // get elements from outline or folding model
122
+ const outlineModel = await OutlineModel . create ( this . _languageFeaturesService . documentSymbolProvider , model , token ) ;
123
+ if ( token . isCancellationRequested ) {
124
+ return ;
125
+ }
126
+ if ( outlineModel . children . size !== 0 ) {
127
+ const { stickyOutlineElement, providerID } = StickyOutlineElement . fromOutlineModel ( outlineModel , this . _model ?. outlineProviderId ) ;
128
+ this . _model = new StickyOutlineModel ( model . uri , modelVersionId , stickyOutlineElement , providerID ) ;
129
+
130
+ } else {
131
+ const foldingController = FoldingController . get ( this . _editor ) ;
132
+ const foldingModel = await foldingController ?. getFoldingModel ( ) ;
102
133
if ( token . isCancellationRequested ) {
103
134
return ;
104
135
}
105
- if ( outlineModel . children . size !== 0 ) {
106
- const { stickyOutlineElement, providerID } = StickyOutlineElement . fromOutlineModel ( outlineModel , this . _providerID ) ;
107
- this . _outlineModel = stickyOutlineElement ;
108
- this . _providerID = providerID ;
136
+ if ( foldingModel && foldingModel . regions . length !== 0 ) {
137
+ const foldingElement = StickyOutlineElement . fromFoldingModel ( foldingModel ) ;
138
+ this . _model = new StickyOutlineModel ( model . uri , modelVersionId , foldingElement , undefined ) ;
109
139
} else {
110
- const foldingController = FoldingController . get ( this . _editor ) ;
111
- const foldingModel = await foldingController ?. getFoldingModel ( ) ;
112
- if ( token . isCancellationRequested ) {
113
- return ;
114
- }
115
- if ( foldingModel && foldingModel . regions . length !== 0 ) {
116
- this . _outlineModel = StickyOutlineElement . fromFoldingModel ( foldingModel ) ;
117
- } else {
118
- this . _outlineModel = new StickyOutlineElement (
119
- new StickyRange ( - 1 , - 1 ) ,
120
- [ ] ,
121
- undefined
122
- ) ;
123
- }
140
+ this . _model = undefined ;
124
141
}
125
- this . _modelVersionId = modelVersionId ;
126
142
}
143
+ clearTimeout ( resetHandle ) ;
127
144
}
128
145
129
146
private updateIndex ( index : number ) {
@@ -169,8 +186,11 @@ export class StickyLineCandidateProvider extends Disposable {
169
186
}
170
187
171
188
public getCandidateStickyLinesIntersecting ( range : StickyRange ) : StickyLineCandidate [ ] {
189
+ if ( ! this . _model ?. element ) {
190
+ return [ ] ;
191
+ }
172
192
let stickyLineCandidates : StickyLineCandidate [ ] = [ ] ;
173
- this . getCandidateStickyLinesIntersectingFromOutline ( range , this . _outlineModel as StickyOutlineElement , stickyLineCandidates , 0 , - 1 ) ;
193
+ this . getCandidateStickyLinesIntersectingFromOutline ( range , this . _model . element , stickyLineCandidates , 0 , - 1 ) ;
174
194
const hiddenRanges : Range [ ] | undefined = this . _editor . _getViewModel ( ) ?. getHiddenAreas ( ) ;
175
195
if ( hiddenRanges ) {
176
196
for ( const hiddenRange of hiddenRanges ) {
@@ -179,11 +199,6 @@ export class StickyLineCandidateProvider extends Disposable {
179
199
}
180
200
return stickyLineCandidates ;
181
201
}
182
-
183
- override dispose ( ) : void {
184
- super . dispose ( ) ;
185
- this . _sessionStore . dispose ( ) ;
186
- }
187
202
}
188
203
189
204
class StickyOutlineElement {
@@ -214,13 +229,12 @@ class StickyOutlineElement {
214
229
return new StickyOutlineElement ( range , children , undefined ) ;
215
230
}
216
231
217
- public static fromOutlineModel ( outlineModel : OutlineModel , providerID : string | undefined ) : { stickyOutlineElement : StickyOutlineElement ; providerID : string | undefined } {
232
+ public static fromOutlineModel ( outlineModel : OutlineModel , preferredProvider : string | undefined ) : { stickyOutlineElement : StickyOutlineElement ; providerID : string | undefined } {
218
233
219
- let ID : string | undefined = providerID ;
220
234
let outlineElements : Map < string , OutlineElement > ;
221
235
// When several possible outline providers
222
236
if ( Iterable . first ( outlineModel . children . values ( ) ) instanceof OutlineGroup ) {
223
- const provider = Iterable . find ( outlineModel . children . values ( ) , outlineGroupOfModel => outlineGroupOfModel . id === providerID ) ;
237
+ const provider = Iterable . find ( outlineModel . children . values ( ) , outlineGroupOfModel => outlineGroupOfModel . id === preferredProvider ) ;
224
238
if ( provider ) {
225
239
outlineElements = provider . children ;
226
240
} else {
@@ -235,7 +249,7 @@ class StickyOutlineElement {
235
249
tempID = outlineGroup . id ;
236
250
}
237
251
}
238
- ID = tempID ;
252
+ preferredProvider = tempID ;
239
253
outlineElements = optimalOutlineGroup ! . children ;
240
254
}
241
255
} else {
@@ -254,7 +268,7 @@ class StickyOutlineElement {
254
268
255
269
return {
256
270
stickyOutlineElement : stickyOutlineElement ,
257
- providerID : ID
271
+ providerID : preferredProvider
258
272
} ;
259
273
}
260
274
@@ -319,3 +333,12 @@ class StickyOutlineElement {
319
333
) {
320
334
}
321
335
}
336
+
337
+ class StickyOutlineModel {
338
+ constructor (
339
+ readonly uri : URI ,
340
+ readonly version : number ,
341
+ readonly element : StickyOutlineElement | undefined ,
342
+ readonly outlineProviderId : string | undefined
343
+ ) { }
344
+ }
0 commit comments