4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
6
import { isEqual } from '../../../../../base/common/resources.js' ;
7
- import { Disposable , DisposableStore , IReference , ReferenceCollection } from '../../../../../base/common/lifecycle.js' ;
8
- import { IModifiedFileEntry } from '../../../chat/common/chatEditingService.js' ;
7
+ import { Disposable , IReference , ReferenceCollection } from '../../../../../base/common/lifecycle.js' ;
8
+ import { IChatEditingService , IModifiedFileEntry , WorkingSetEntryState } from '../../../chat/common/chatEditingService.js' ;
9
9
import { INotebookService } from '../../common/notebookService.js' ;
10
10
import { bufferToStream , VSBuffer } from '../../../../../base/common/buffer.js' ;
11
11
import { NotebookTextModel } from '../../common/model/notebookTextModel.js' ;
@@ -25,22 +25,22 @@ import { SaveReason } from '../../../../common/editor.js';
25
25
import { IChatService } from '../../../chat/common/chatService.js' ;
26
26
import { createDecorator , IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js' ;
27
27
import { INotebookOriginalModelReferenceFactory } from './notebookOriginalModelRefFactory.js' ;
28
- import { IObservable , observableValue } from '../../../../../base/common/observable.js' ;
28
+ import { autorun , autorunWithStore , derived , IObservable , observableValue } from '../../../../../base/common/observable.js' ;
29
29
30
30
31
31
export const INotebookModelSynchronizerFactory = createDecorator < INotebookModelSynchronizerFactory > ( 'INotebookModelSynchronizerFactory' ) ;
32
32
33
33
export interface INotebookModelSynchronizerFactory {
34
34
readonly _serviceBrand : undefined ;
35
- getOrCreate ( model : NotebookTextModel , entry : IModifiedFileEntry ) : IReference < NotebookModelSynchronizer > ;
35
+ getOrCreate ( model : NotebookTextModel ) : IReference < NotebookModelSynchronizer > ;
36
36
}
37
37
38
38
class NotebookModelSynchronizerReferenceCollection extends ReferenceCollection < NotebookModelSynchronizer > {
39
39
constructor ( @IInstantiationService private readonly instantiationService : IInstantiationService ) {
40
40
super ( ) ;
41
41
}
42
- protected override createReferencedObject ( _key : string , model : NotebookTextModel , entry : IModifiedFileEntry ) : NotebookModelSynchronizer {
43
- return this . instantiationService . createInstance ( NotebookModelSynchronizer , model , entry ) ;
42
+ protected override createReferencedObject ( _key : string , model : NotebookTextModel ) : NotebookModelSynchronizer {
43
+ return this . instantiationService . createInstance ( NotebookModelSynchronizer , model ) ;
44
44
}
45
45
protected override destroyReferencedObject ( _key : string , object : NotebookModelSynchronizer ) : void {
46
46
object . dispose ( ) ;
@@ -54,8 +54,8 @@ export class NotebookModelSynchronizerFactory implements INotebookModelSynchroni
54
54
this . _data = instantiationService . createInstance ( NotebookModelSynchronizerReferenceCollection ) ;
55
55
}
56
56
57
- getOrCreate ( model : NotebookTextModel , entry : IModifiedFileEntry ) : IReference < NotebookModelSynchronizer > {
58
- return this . _data . acquire ( entry . modifiedURI . toString ( ) , model , entry ) ;
57
+ getOrCreate ( model : NotebookTextModel ) : IReference < NotebookModelSynchronizer > {
58
+ return this . _data . acquire ( model . uri . toString ( ) , model ) ;
59
59
}
60
60
}
61
61
@@ -70,7 +70,7 @@ export class NotebookModelSynchronizer extends Disposable {
70
70
private isEditFromUs : boolean = false ;
71
71
constructor (
72
72
private readonly model : NotebookTextModel ,
73
- public readonly entry : IModifiedFileEntry ,
73
+ @ IChatEditingService _chatEditingService : IChatEditingService ,
74
74
@INotebookService private readonly notebookService : INotebookService ,
75
75
@IChatService chatService : IChatService ,
76
76
@INotebookLoggingService private readonly logService : INotebookLoggingService ,
@@ -81,51 +81,78 @@ export class NotebookModelSynchronizer extends Disposable {
81
81
) {
82
82
super ( ) ;
83
83
84
+ const entryObs = derived ( ( r ) => {
85
+ const session = _chatEditingService . currentEditingSessionObs . read ( r ) ;
86
+ if ( ! session ) {
87
+ return ;
88
+ }
89
+ return session . entries . read ( r ) . find ( e => isEqual ( e . modifiedURI , model . uri ) ) ;
90
+ } ) . recomputeInitiallyAndOnChange ( this . _store ) ;
91
+
92
+
84
93
this . _register ( chatService . onDidPerformUserAction ( async e => {
94
+ const entry = entryObs . read ( undefined ) ;
95
+ if ( ! entry ) {
96
+ return ;
97
+ }
85
98
if ( e . action . kind === 'chatEditingSessionAction' && ! e . action . hasRemainingEdits && isEqual ( e . action . uri , entry . modifiedURI ) ) {
86
99
if ( e . action . outcome === 'accepted' ) {
87
- await this . accept ( ) ;
100
+ await this . accept ( entry ) ;
88
101
await this . createSnapshot ( ) ;
89
102
this . _diffInfo . set ( undefined , undefined ) ;
90
103
}
91
104
else if ( e . action . outcome === 'rejected' ) {
92
- if ( await this . revert ( ) ) {
93
- this . _diffInfo . set ( undefined , undefined ) ;
94
- }
105
+ await this . revertImpl ( ) ;
95
106
}
96
107
}
97
108
} ) ) ;
98
109
99
- const cancellationTokenStore = this . _register ( new DisposableStore ( ) ) ;
100
- let cancellationToken = cancellationTokenStore . add ( new CancellationTokenSource ( ) ) ;
101
110
const updateNotebookModel = ( entry : IModifiedFileEntry , token : CancellationToken ) => {
102
111
this . throttledUpdateNotebookModel . trigger ( ( ) => this . updateNotebookModel ( entry , token ) ) ;
103
112
} ;
104
- const modifiedModel = ( entry as ChatEditingModifiedFileEntry ) . modifiedModel ;
105
- this . _register ( modifiedModel . onDidChangeContent ( async ( ) => {
106
- cancellationTokenStore . clear ( ) ;
107
- if ( ! modifiedModel . isDisposed ( ) && ! entry . originalModel . isDisposed ( ) && modifiedModel . getValue ( ) === entry . originalModel . getValue ( ) ) {
108
- if ( await this . revert ( ) ) {
109
- this . _diffInfo . set ( undefined , undefined ) ;
110
- }
113
+
114
+ this . _register ( autorun ( async ( r ) => {
115
+ const entry = entryObs . read ( r ) ;
116
+ if ( ! entry ) {
117
+ return ;
118
+ }
119
+ if ( entry . state . read ( r ) === WorkingSetEntryState . Rejected ) {
120
+ await this . revertImpl ( ) ;
121
+ }
122
+ } ) ) ;
123
+
124
+ let snapshotCreated = false ;
125
+ this . _register ( autorunWithStore ( ( r , store ) => {
126
+ const entry = entryObs . read ( r ) ;
127
+ if ( ! entry ) {
111
128
return ;
112
129
}
113
- cancellationToken = cancellationTokenStore . add ( new CancellationTokenSource ( ) ) ;
130
+ if ( ! snapshotCreated ) {
131
+ this . createSnapshot ( ) ;
132
+ snapshotCreated = true ;
133
+ }
134
+
135
+ const modifiedModel = ( entry as ChatEditingModifiedFileEntry ) . modifiedModel ;
136
+ let cancellationToken = store . add ( new CancellationTokenSource ( ) ) ;
137
+ store . add ( modifiedModel . onDidChangeContent ( async ( ) => {
138
+ if ( ! modifiedModel . isDisposed ( ) && ! entry . originalModel . isDisposed ( ) && modifiedModel . getValue ( ) !== entry . originalModel . getValue ( ) ) {
139
+ cancellationToken = store . add ( new CancellationTokenSource ( ) ) ;
140
+ updateNotebookModel ( entry , cancellationToken . token ) ;
141
+ }
142
+ } ) ) ;
143
+
114
144
updateNotebookModel ( entry , cancellationToken . token ) ;
115
145
} ) ) ;
146
+
116
147
this . _register ( model . onDidChangeContent ( ( ) => {
117
148
// Track changes from the user.
118
149
if ( ! this . isEditFromUs && this . snapshot ) {
119
150
this . snapshot . dirty = true ;
120
151
}
121
152
} ) ) ;
122
-
123
- updateNotebookModel ( entry , cancellationToken . token ) ;
124
-
125
-
126
153
}
127
154
128
- public async createSnapshot ( ) {
155
+ private async createSnapshot ( ) {
129
156
const [ serializer , ref ] = await Promise . all ( [
130
157
this . getNotebookSerializer ( ) ,
131
158
this . notebookModelResolverService . resolve ( this . model . uri )
@@ -173,12 +200,16 @@ export class NotebookModelSynchronizer extends Disposable {
173
200
}
174
201
}
175
202
176
- private async revert ( ) : Promise < boolean > {
203
+ public async revert ( ) {
204
+ await this . revertImpl ( ) ;
205
+ }
206
+
207
+ private async revertImpl ( ) : Promise < void > {
177
208
if ( ! this . snapshot ) {
178
- return false ;
209
+ return ;
179
210
}
180
211
await this . updateNotebook ( this . snapshot . bytes , ! this . snapshot . dirty ) ;
181
- return true ;
212
+ this . _diffInfo . set ( undefined , undefined ) ;
182
213
}
183
214
184
215
private async updateNotebook ( bytes : VSBuffer , save : boolean ) {
@@ -199,8 +230,8 @@ export class NotebookModelSynchronizer extends Disposable {
199
230
}
200
231
}
201
232
202
- private async accept ( ) {
203
- const modifiedModel = ( this . entry as ChatEditingModifiedFileEntry ) . modifiedModel ;
233
+ private async accept ( entry : IModifiedFileEntry ) {
234
+ const modifiedModel = ( entry as ChatEditingModifiedFileEntry ) . modifiedModel ;
204
235
const content = modifiedModel . getValue ( ) ;
205
236
await this . updateNotebook ( VSBuffer . fromString ( content ) , false ) ;
206
237
}
@@ -211,9 +242,9 @@ export class NotebookModelSynchronizer extends Disposable {
211
242
}
212
243
213
244
private _originalModel ?: Promise < NotebookTextModel > ;
214
- private async getOriginalModel ( ) : Promise < NotebookTextModel > {
245
+ private async getOriginalModel ( entry : IModifiedFileEntry ) : Promise < NotebookTextModel > {
215
246
if ( ! this . _originalModel ) {
216
- this . _originalModel = this . originalModelRefFactory . getOrCreate ( this . entry , this . model . viewType ) . then ( ref => this . _register ( ref ) . object ) ;
247
+ this . _originalModel = this . originalModelRefFactory . getOrCreate ( entry , this . model . viewType ) . then ( ref => this . _register ( ref ) . object ) ;
217
248
}
218
249
return this . _originalModel ;
219
250
}
@@ -225,7 +256,7 @@ export class NotebookModelSynchronizer extends Disposable {
225
256
if ( ! modelWithChatEdits || token . isCancellationRequested || ! currentModel ) {
226
257
return ;
227
258
}
228
- const originalModel = await this . getOriginalModel ( ) ;
259
+ const originalModel = await this . getOriginalModel ( entry ) ;
229
260
// This is the total diff from the original model to the model with chat edits.
230
261
const cellDiffInfo = ( await this . computeDiff ( originalModel , modelWithChatEdits , token ) ) ?. cellDiffInfo ;
231
262
// This is the diff from the current model to the model with chat edits.
0 commit comments