3
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
+ import { binarySearch , coalesceInPlace } from '../../../../base/common/arrays.js' ;
6
7
import { Disposable , DisposableStore , toDisposable } from '../../../../base/common/lifecycle.js' ;
7
8
import { Constants } from '../../../../base/common/uint.js' ;
8
9
import { ICodeEditor , IViewZone } from '../../../../editor/browser/editorBrowser.js' ;
9
10
import { LineSource , renderLines , RenderOptions } from '../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js' ;
10
11
import { diffAddDecoration , diffDeleteDecoration , diffWholeLineAddDecoration } from '../../../../editor/browser/widget/diffEditor/registrations.contribution.js' ;
11
12
import { EditorOption } from '../../../../editor/common/config/editorOptions.js' ;
13
+ import { Range } from '../../../../editor/common/core/range.js' ;
12
14
import { IDocumentDiff } from '../../../../editor/common/diff/documentDiffProvider.js' ;
13
- import { IEditorContribution } from '../../../../editor/common/editorCommon.js' ;
15
+ import { IEditorContribution , ScrollType } from '../../../../editor/common/editorCommon.js' ;
14
16
import { IModelDeltaDecoration , ITextModel } from '../../../../editor/common/model.js' ;
15
17
import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js' ;
16
18
import { InlineDecoration , InlineDecorationType } from '../../../../editor/common/viewModel.js' ;
17
19
import { IChatEditingService , IChatEditingSession , IModifiedFileEntry , WorkingSetEntryState } from '../common/chatEditingService.js' ;
20
+ import { localize } from '../../../../nls.js' ;
21
+ import { IContextKey , IContextKeyService , RawContextKey } from '../../../../platform/contextkey/common/contextkey.js' ;
22
+
23
+ export const ctxHasEditorModification = new RawContextKey < boolean > ( 'chat.hasEditorModifications' , undefined , localize ( 'chat.hasEditorModifications' , "The current editor contains chat modifications" ) ) ;
18
24
19
25
export class ChatEditorController extends Disposable implements IEditorContribution {
20
26
21
27
public static readonly ID = 'editor.contrib.chatEditorController' ;
28
+
22
29
private readonly _sessionStore = this . _register ( new DisposableStore ( ) ) ;
23
30
private readonly _decorations = this . _editor . createDecorationsCollection ( ) ;
24
31
private _viewZones : string [ ] = [ ] ;
32
+ private readonly _ctxHasEditorModification : IContextKey < boolean > ;
33
+
34
+ static get ( editor : ICodeEditor ) : ChatEditorController | null {
35
+ const controller = editor . getContribution < ChatEditorController > ( ChatEditorController . ID ) ;
36
+ return controller ;
37
+ }
25
38
26
39
constructor (
27
40
private readonly _editor : ICodeEditor ,
28
41
@IChatEditingService private readonly _chatEditingService : IChatEditingService ,
29
- @IEditorWorkerService private readonly _editorWorkerService : IEditorWorkerService
42
+ @IEditorWorkerService private readonly _editorWorkerService : IEditorWorkerService ,
43
+ @IContextKeyService contextKeyService : IContextKeyService ,
30
44
) {
31
45
super ( ) ;
32
46
this . _register ( this . _editor . onDidChangeModel ( ( ) => this . _update ( ) ) ) ;
33
47
this . _register ( this . _chatEditingService . onDidChangeEditingSession ( ( ) => this . _updateSessionDecorations ( ) ) ) ;
34
48
this . _register ( toDisposable ( ( ) => this . _clearRendering ( ) ) ) ;
49
+
50
+ this . _ctxHasEditorModification = ctxHasEditorModification . bindTo ( contextKeyService ) ;
51
+ }
52
+
53
+ override dispose ( ) : void {
54
+ this . _clearRendering ( ) ;
55
+ super . dispose ( ) ;
35
56
}
36
57
37
58
private _update ( ) : void {
@@ -88,6 +109,7 @@ export class ChatEditorController extends Disposable implements IEditorContribut
88
109
return ;
89
110
}
90
111
112
+ this . _ctxHasEditorModification . set ( true ) ;
91
113
this . _updateWithDiff ( model , entry , diff ) ;
92
114
} ) ;
93
115
}
@@ -107,6 +129,7 @@ export class ChatEditorController extends Disposable implements IEditorContribut
107
129
} ) ;
108
130
this . _viewZones = [ ] ;
109
131
this . _decorations . clear ( ) ;
132
+ this . _ctxHasEditorModification . reset ( ) ;
110
133
}
111
134
112
135
private _updateWithDiff ( model : ITextModel , entry : IModifiedFileEntry , diff : IDocumentDiff | null ) : void {
@@ -166,4 +189,62 @@ export class ChatEditorController extends Disposable implements IEditorContribut
166
189
this . _decorations . set ( modifiedDecorations ) ;
167
190
} ) ;
168
191
}
192
+
193
+ revealNext ( ) {
194
+ this . _reveal ( true ) ;
195
+ }
196
+
197
+ revealPrevious ( ) {
198
+ this . _reveal ( false ) ;
199
+ }
200
+
201
+ private _reveal ( next : boolean ) {
202
+ const position = this . _editor . getPosition ( ) ;
203
+ if ( ! position ) {
204
+ return ;
205
+ }
206
+
207
+ const decorations : ( Range | undefined ) [ ] = this . _decorations
208
+ . getRanges ( )
209
+ . sort ( ( a , b ) => Range . compareRangesUsingStarts ( a , b ) ) ;
210
+
211
+ // TODO@jrieken this is slow and should be done smarter, e.g being able to read
212
+ // only whole range decorations because the goal is to go from change to change, skipping
213
+ // over word level changes
214
+ for ( let i = 0 ; i < decorations . length ; i ++ ) {
215
+ const decoration = decorations [ i ] ;
216
+ for ( let j = 0 ; j < decorations . length ; j ++ ) {
217
+ if ( i !== j && decoration && decorations [ j ] ?. containsRange ( decoration ) ) {
218
+ decorations [ i ] = undefined ;
219
+ break ;
220
+ }
221
+ }
222
+ }
223
+
224
+ coalesceInPlace ( decorations ) ;
225
+
226
+ if ( decorations . length === 0 ) {
227
+ return ;
228
+ }
229
+
230
+ let idx = binarySearch ( decorations , Range . fromPositions ( position ) , Range . compareRangesUsingStarts ) ;
231
+ if ( idx < 0 ) {
232
+ idx = ~ idx ;
233
+ }
234
+
235
+ let target : number ;
236
+ if ( decorations [ idx ] ?. containsPosition ( position ) ) {
237
+ target = idx + ( next ? 1 : - 1 ) ;
238
+ } else {
239
+ target = next ? idx : idx - 1 ;
240
+ }
241
+
242
+ target = ( target + decorations . length ) % decorations . length ;
243
+
244
+
245
+ const targetPosition = decorations [ target ] . getStartPosition ( ) ;
246
+ this . _editor . setPosition ( targetPosition ) ;
247
+ this . _editor . revealPosition ( targetPosition , ScrollType . Smooth ) ;
248
+ this . _editor . focus ( ) ;
249
+ }
169
250
}
0 commit comments