@@ -16,6 +16,8 @@ import { binarySearch } from 'vs/base/common/arrays';
1616import { Iterable } from 'vs/base/common/iterator' ;
1717import { FoldingController } from 'vs/editor/contrib/folding/browser/folding' ;
1818import { FoldingModel } from 'vs/editor/contrib/folding/browser/foldingModel' ;
19+ import { URI } from 'vs/base/common/uri' ;
20+ import { isEqual } from 'vs/base/common/resources' ;
1921
2022export class StickyRange {
2123 constructor (
@@ -33,19 +35,20 @@ export class StickyLineCandidate {
3335}
3436
3537export class StickyLineCandidateProvider extends Disposable {
36- private readonly onStickyScrollChangeEmitter = this . _register ( new Emitter < void > ( ) ) ;
37- public readonly onStickyScrollChange = this . onStickyScrollChangeEmitter . event ;
3838
3939 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+
4044 private readonly _editor : ICodeEditor ;
4145 private readonly _languageFeaturesService : ILanguageFeaturesService ;
4246 private readonly _updateSoon : RunOnceScheduler ;
4347
44- private _cts : CancellationTokenSource | undefined ;
45- private _outlineModel : StickyOutlineElement | undefined ;
4648 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 ;
4952
5053 constructor (
5154 editor : ICodeEditor ,
@@ -63,67 +66,81 @@ export class StickyLineCandidateProvider extends Disposable {
6366 this . readConfiguration ( ) ;
6467 }
6568
69+ override dispose ( ) : void {
70+ super . dispose ( ) ;
71+ this . _sessionStore . dispose ( ) ;
72+ }
73+
6674 private readConfiguration ( ) {
6775 const options = this . _editor . getOption ( EditorOption . stickyScroll ) ;
6876 if ( options . enabled === false ) {
6977 this . _sessionStore . clear ( ) ;
7078 return ;
7179 } else {
7280 this . _sessionStore . add ( this . _editor . onDidChangeModel ( ( ) => {
73- this . _providerID = undefined ;
7481 this . update ( ) ;
7582 } ) ) ;
7683 this . _sessionStore . add ( this . _editor . onDidChangeHiddenAreas ( ( ) => this . update ( ) ) ) ;
7784 this . _sessionStore . add ( this . _editor . onDidChangeModelContent ( ( ) => this . _updateSoon . schedule ( ) ) ) ;
7885 this . _sessionStore . add ( this . _languageFeaturesService . documentSymbolProvider . onDidChange ( ( ) => {
79- this . _providerID = undefined ;
86+
8087 this . update ( ) ;
8188 } ) ) ;
8289 this . update ( ) ;
8390 }
8491 }
8592
8693 public getVersionId ( ) {
87- return this . _modelVersionId ;
94+ return this . _model ?. version ?? - 1 ;
8895 }
8996
9097 public async update ( ) : Promise < void > {
9198 this . _cts ?. dispose ( true ) ;
9299 this . _cts = new CancellationTokenSource ( ) ;
93100 await this . updateOutlineModel ( this . _cts . token ) ;
94- this . onStickyScrollChangeEmitter . fire ( ) ;
101+ this . _onDidChangeStickyScroll . fire ( ) ;
95102 }
96103
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 ( ) ;
102133 if ( token . isCancellationRequested ) {
103134 return ;
104135 }
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 ) ;
109139 } 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 ;
124141 }
125- this . _modelVersionId = modelVersionId ;
126142 }
143+ clearTimeout ( resetHandle ) ;
127144 }
128145
129146 private updateIndex ( index : number ) {
@@ -169,8 +186,11 @@ export class StickyLineCandidateProvider extends Disposable {
169186 }
170187
171188 public getCandidateStickyLinesIntersecting ( range : StickyRange ) : StickyLineCandidate [ ] {
189+ if ( ! this . _model ?. element ) {
190+ return [ ] ;
191+ }
172192 let stickyLineCandidates : StickyLineCandidate [ ] = [ ] ;
173- this . getCandidateStickyLinesIntersectingFromOutline ( range , this . _outlineModel as StickyOutlineElement , stickyLineCandidates , 0 , - 1 ) ;
193+ this . getCandidateStickyLinesIntersectingFromOutline ( range , this . _model . element , stickyLineCandidates , 0 , - 1 ) ;
174194 const hiddenRanges : Range [ ] | undefined = this . _editor . _getViewModel ( ) ?. getHiddenAreas ( ) ;
175195 if ( hiddenRanges ) {
176196 for ( const hiddenRange of hiddenRanges ) {
@@ -179,11 +199,6 @@ export class StickyLineCandidateProvider extends Disposable {
179199 }
180200 return stickyLineCandidates ;
181201 }
182-
183- override dispose ( ) : void {
184- super . dispose ( ) ;
185- this . _sessionStore . dispose ( ) ;
186- }
187202}
188203
189204class StickyOutlineElement {
@@ -214,13 +229,12 @@ class StickyOutlineElement {
214229 return new StickyOutlineElement ( range , children , undefined ) ;
215230 }
216231
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 } {
218233
219- let ID : string | undefined = providerID ;
220234 let outlineElements : Map < string , OutlineElement > ;
221235 // When several possible outline providers
222236 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 ) ;
224238 if ( provider ) {
225239 outlineElements = provider . children ;
226240 } else {
@@ -235,7 +249,7 @@ class StickyOutlineElement {
235249 tempID = outlineGroup . id ;
236250 }
237251 }
238- ID = tempID ;
252+ preferredProvider = tempID ;
239253 outlineElements = optimalOutlineGroup ! . children ;
240254 }
241255 } else {
@@ -254,7 +268,7 @@ class StickyOutlineElement {
254268
255269 return {
256270 stickyOutlineElement : stickyOutlineElement ,
257- providerID : ID
271+ providerID : preferredProvider
258272 } ;
259273 }
260274
@@ -319,3 +333,12 @@ class StickyOutlineElement {
319333 ) {
320334 }
321335}
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