1- import type {
2- DecorationOptions ,
3- Range ,
4- TextDocument ,
5- TextEditor ,
6- TextEditorDecorationType ,
7- TextEditorSelectionChangeEvent ,
8- Uri ,
9- } from 'vscode' ;
1+ import type { TextEditor , TextEditorDecorationType , TextEditorSelectionChangeEvent } from 'vscode' ;
102import { Disposable , window } from 'vscode' ;
113import type { FileAnnotationType } from '../config' ;
4+ import type { Container } from '../container' ;
125import { setContext } from '../system/context' ;
136import { Logger } from '../system/logger' ;
147import type { GitDocumentState } from '../trackers/gitDocumentTracker' ;
158import type { TrackedDocument } from '../trackers/trackedDocument' ;
9+ import type { Decoration } from './annotations' ;
1610
1711export type AnnotationStatus = 'computing' | 'computed' ;
1812
@@ -28,23 +22,16 @@ export function getEditorCorrelationKey(editor: TextEditor | undefined): TextEdi
2822export abstract class AnnotationProviderBase < TContext extends AnnotationContext = AnnotationContext >
2923 implements Disposable
3024{
31- annotationContext : TContext | undefined ;
32- correlationKey : TextEditorCorrelationKey ;
33- document : TextDocument ;
34- status : AnnotationStatus | undefined ;
35-
36- private decorations :
37- | { decorationType : TextEditorDecorationType ; rangesOrOptions : Range [ ] | DecorationOptions [ ] } [ ]
38- | undefined ;
25+ private decorations : Decoration [ ] | undefined ;
3926 protected disposable : Disposable ;
4027
4128 constructor (
29+ protected readonly container : Container ,
4230 public readonly annotationType : FileAnnotationType ,
43- public editor : TextEditor ,
31+ editor : TextEditor ,
4432 protected readonly trackedDocument : TrackedDocument < GitDocumentState > ,
4533 ) {
46- this . correlationKey = getEditorCorrelationKey ( this . editor ) ;
47- this . document = this . editor . document ;
34+ this . editor = editor ;
4835
4936 this . disposable = Disposable . from (
5037 window . onDidChangeTextEditorSelection ( this . onTextEditorSelectionChanged , this ) ,
@@ -57,36 +44,98 @@ export abstract class AnnotationProviderBase<TContext extends AnnotationContext
5744 this . disposable . dispose ( ) ;
5845 }
5946
47+ private _annotationContext : TContext | undefined ;
48+ get annotationContext ( ) : TContext | undefined {
49+ return this . _annotationContext ;
50+ }
51+ protected set annotationContext ( value : TContext | undefined ) {
52+ this . _annotationContext = value ;
53+ }
54+
55+ private _correlationKey ! : TextEditorCorrelationKey ;
56+ get correlationKey ( ) : TextEditorCorrelationKey {
57+ return this . _correlationKey ;
58+ }
59+
60+ private _editor ! : TextEditor ;
61+ get editor ( ) : TextEditor {
62+ return this . _editor ;
63+ }
64+ protected set editor ( value : TextEditor ) {
65+ this . _editor = value ;
66+ this . _correlationKey = getEditorCorrelationKey ( value ) ;
67+ }
68+
69+ private _status : AnnotationStatus | undefined ;
70+ get status ( ) : AnnotationStatus | undefined {
71+ return this . _status ;
72+ }
73+
74+ get statusContextValue ( ) : string | undefined {
75+ return this . status != null ? `${ this . status } +${ this . annotationType } ` : undefined ;
76+ }
77+
78+ private async setStatus ( value : AnnotationStatus | undefined , editor : TextEditor | undefined ) : Promise < void > {
79+ if ( this . status === value ) return ;
80+
81+ this . _status = value ;
82+ if ( editor != null && editor === window . activeTextEditor ) {
83+ await setContext ( 'gitlens:annotationStatus' , this . statusContextValue ) ;
84+ }
85+ }
86+
6087 private onTextEditorSelectionChanged ( e : TextEditorSelectionChangeEvent ) {
61- if ( this . document !== e . textEditor . document ) return ;
88+ if ( this . editor . document !== e . textEditor . document ) return ;
6289
63- void this . selection ( { line : e . selections [ 0 ] . active . line } ) ;
90+ void this . selection ?. ( { line : e . selections [ 0 ] . active . line } ) ;
6491 }
6592
66- get editorUri ( ) : Uri | undefined {
67- return this . editor ?. document ?. uri ;
93+ canReuse ( _context ?: TContext ) : boolean {
94+ return true ;
6895 }
6996
7097 clear ( ) {
98+ const decorations = this . decorations ;
99+ this . decorations = undefined ;
71100 this . annotationContext = undefined ;
72- this . status = undefined ;
101+ void this . setStatus ( undefined , this . editor ) ;
102+
73103 if ( this . editor == null ) return ;
74104
75- if ( this . decorations ?. length ) {
76- for ( const d of this . decorations ) {
105+ if ( decorations ?. length ) {
106+ for ( const d of decorations ) {
77107 try {
78108 this . editor . setDecorations ( d . decorationType , [ ] ) ;
109+ if ( d . dispose ) {
110+ d . decorationType . dispose ( ) ;
111+ }
79112 } catch { }
80113 }
81-
82- this . decorations = undefined ;
83114 }
84115 }
85116
86- mustReopen ( _context ?: TContext ) : boolean {
117+ nextChange ?( ) : void ;
118+ previousChange ?( ) : void ;
119+
120+ async provideAnnotation ( context ?: TContext , force ?: boolean ) : Promise < boolean > {
121+ void this . setStatus ( 'computing' , this . editor ) ;
122+
123+ try {
124+ if ( await this . onProvideAnnotation ( context , force ) ) {
125+ void this . setStatus ( 'computed' , this . editor ) ;
126+ await this . selection ?.( force ? { line : this . editor . selection . active . line } : context ?. selection ) ;
127+ return true ;
128+ }
129+ } catch ( ex ) {
130+ Logger . error ( ex ) ;
131+ }
132+
133+ void this . setStatus ( undefined , this . editor ) ;
87134 return false ;
88135 }
89136
137+ protected abstract onProvideAnnotation ( context ?: TContext , force ?: boolean ) : Promise < boolean > ;
138+
90139 refresh ( replaceDecorationTypes : Map < TextEditorDecorationType , TextEditorDecorationType | null > ) {
91140 if ( this . editor == null || ! this . decorations ?. length ) return ;
92141
@@ -106,65 +155,60 @@ export abstract class AnnotationProviderBase<TContext extends AnnotationContext
106155 this . setDecorations ( this . decorations ) ;
107156 }
108157
109- async restore ( editor : TextEditor ) {
158+ restore ( editor : TextEditor , force ?: boolean ) {
110159 // If the editor isn't disposed then we don't need to do anything
111160 // Explicitly check for `false`
112161 if ( ( this . editor as any ) . _disposed === false ) return ;
113162
114- this . status = 'computing' ;
115- if ( editor === window . activeTextEditor ) {
116- await setContext ( 'gitlens:annotationStatus' , this . status ) ;
163+ if ( force || this . decorations == null ) {
164+ void this . provideAnnotation ( this . annotationContext , force ) ;
165+ return ;
117166 }
118167
168+ void this . setStatus ( 'computing' , this . editor ) ;
169+
119170 this . editor = editor ;
120- this . correlationKey = getEditorCorrelationKey ( editor ) ;
121- this . document = editor . document ;
122171
123172 if ( this . decorations ?. length ) {
124173 for ( const d of this . decorations ) {
125174 this . editor . setDecorations ( d . decorationType , d . rangesOrOptions ) ;
126175 }
127176 }
128177
129- this . status = 'computed' ;
130- if ( editor === window . activeTextEditor ) {
131- await setContext ( 'gitlens:annotationStatus' , this . status ) ;
132- }
178+ void this . setStatus ( 'computed' , this . editor ) ;
133179 }
134180
135- async provideAnnotation ( context ?: TContext ) : Promise < boolean > {
136- this . status = 'computing' ;
137- try {
138- if ( await this . onProvideAnnotation ( context ) ) {
139- this . status = 'computed' ;
140- return true ;
141- }
142- } catch ( ex ) {
143- Logger . error ( ex ) ;
144- }
145-
146- this . status = undefined ;
147- return false ;
148- }
181+ selection ?( selection ?: TContext [ 'selection' ] ) : Promise < void > ;
182+ validate ?( ) : boolean | Promise < boolean > ;
149183
150- protected abstract onProvideAnnotation ( context ?: TContext ) : Promise < boolean > ;
184+ protected setDecorations ( decorations : Decoration [ ] ) {
185+ if ( this . decorations ?. length ) {
186+ // If we have no new decorations, just completely clear the old ones
187+ if ( ! decorations ?. length ) {
188+ this . clear ( ) ;
151189
152- abstract selection ( selection ?: TContext [ 'selection' ] ) : Promise < void > ;
190+ return ;
191+ }
153192
154- protected setDecorations (
155- decorations : { decorationType : TextEditorDecorationType ; rangesOrOptions : Range [ ] | DecorationOptions [ ] } [ ] ,
156- ) {
157- if ( this . decorations ?. length ) {
158- this . clear ( ) ;
193+ // Only remove the decorations that are no longer needed
194+ const remove = this . decorations . filter (
195+ decoration => ! decorations . some ( d => d . decorationType . key === decoration . decorationType . key ) ,
196+ ) ;
197+ for ( const d of remove ) {
198+ try {
199+ this . editor . setDecorations ( d . decorationType , [ ] ) ;
200+ if ( d . dispose ) {
201+ d . decorationType . dispose ( ) ;
202+ }
203+ } catch { }
204+ }
159205 }
160206
161207 this . decorations = decorations ;
162- if ( this . decorations ?. length ) {
163- for ( const d of this . decorations ) {
208+ if ( decorations ?. length ) {
209+ for ( const d of decorations ) {
164210 this . editor . setDecorations ( d . decorationType , d . rangesOrOptions ) ;
165211 }
166212 }
167213 }
168-
169- abstract validate ( ) : Promise < boolean > ;
170214}
0 commit comments