@@ -9,9 +9,11 @@ import { CancellationToken, CancellationTokenSource } from '../../../../../base/
9
9
import { Codicon } from '../../../../../base/common/codicons.js' ;
10
10
import { BugIndicatingError } from '../../../../../base/common/errors.js' ;
11
11
import { Emitter , Event } from '../../../../../base/common/event.js' ;
12
+ import { Iterable } from '../../../../../base/common/iterator.js' ;
12
13
import { Disposable , DisposableStore , IDisposable , toDisposable } from '../../../../../base/common/lifecycle.js' ;
14
+ import { LinkedList } from '../../../../../base/common/linkedList.js' ;
13
15
import { ResourceMap } from '../../../../../base/common/map.js' ;
14
- import { derived , IObservable , observableValue , runOnChange , ValueWithChangeEventFromObservable } from '../../../../../base/common/observable.js' ;
16
+ import { derived , IObservable , observableValue , observableValueOpts , runOnChange , ValueWithChangeEventFromObservable } from '../../../../../base/common/observable.js' ;
15
17
import { compare } from '../../../../../base/common/strings.js' ;
16
18
import { ThemeIcon } from '../../../../../base/common/themables.js' ;
17
19
import { isString } from '../../../../../base/common/types.js' ;
@@ -37,6 +39,7 @@ import { ChatContextKeys } from '../../common/chatContextKeys.js';
37
39
import { applyingChatEditsContextKey , applyingChatEditsFailedContextKey , CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME , chatEditingAgentSupportsReadonlyReferencesContextKey , chatEditingMaxFileAssignmentName , chatEditingResourceContextKey , ChatEditingSessionState , decidedChatEditingResourceContextKey , defaultChatEditingMaxFileLimit , hasAppliedChatEditsContextKey , hasUndecidedChatEditingResourceContextKey , IChatEditingService , IChatEditingSession , IChatEditingSessionStream , IChatRelatedFile , IChatRelatedFilesProvider , IModifiedFileEntry , inChatEditingSessionContextKey , WorkingSetEntryState } from '../../common/chatEditingService.js' ;
38
40
import { IChatResponseModel , IChatTextEditGroup } from '../../common/chatModel.js' ;
39
41
import { IChatService } from '../../common/chatService.js' ;
42
+ import { ChatEditingModifiedFileEntry } from './chatEditingModifiedFileEntry.js' ;
40
43
import { ChatEditingSession } from './chatEditingSession.js' ;
41
44
import { ChatEditingSnapshotTextModelContentProvider , ChatEditingTextModelContentProvider } from './chatEditingTextModelContentProviders.js' ;
42
45
@@ -50,6 +53,17 @@ export class ChatEditingService extends Disposable implements IChatEditingServic
50
53
private readonly _currentSessionObs = observableValue < ChatEditingSession | null > ( this , null ) ;
51
54
private readonly _currentSessionDisposables = this . _register ( new DisposableStore ( ) ) ;
52
55
56
+ private readonly _adhocSessionsObs = observableValueOpts < LinkedList < ChatEditingSession > > ( { equalsFn : ( a , b ) => false } , new LinkedList ( ) ) ;
57
+
58
+ readonly editingSessionsObs : IObservable < readonly IChatEditingSession [ ] > = derived ( r => {
59
+ const result = Array . from ( this . _adhocSessionsObs . read ( r ) ) ;
60
+ const globalSession = this . _currentSessionObs . read ( r ) ;
61
+ if ( globalSession ) {
62
+ result . push ( globalSession ) ;
63
+ }
64
+ return result ;
65
+ } ) ;
66
+
53
67
private readonly _currentAutoApplyOperationObs = observableValue < CancellationTokenSource | null > ( this , null ) ;
54
68
get currentAutoApplyOperation ( ) : CancellationTokenSource | null {
55
69
return this . _currentAutoApplyOperationObs . get ( ) ;
@@ -63,9 +77,6 @@ export class ChatEditingService extends Disposable implements IChatEditingServic
63
77
return this . _currentSessionObs ;
64
78
}
65
79
66
- private readonly _onDidChangeEditingSession = this . _register ( new Emitter < void > ( ) ) ;
67
- public readonly onDidChangeEditingSession = this . _onDidChangeEditingSession . event ;
68
-
69
80
private _editingSessionFileLimitPromise : Promise < number > ;
70
81
private _editingSessionFileLimit : number | undefined ;
71
82
get editingSessionFileLimit ( ) {
@@ -111,13 +122,14 @@ export class ChatEditingService extends Disposable implements IChatEditingServic
111
122
return decidedEntries . map ( entry => entry . entryId ) ;
112
123
} ) ) ;
113
124
this . _register ( bindContextKey ( hasUndecidedChatEditingResourceContextKey , contextKeyService , ( reader ) => {
114
- const currentSession = this . _currentSessionObs . read ( reader ) ;
115
- if ( ! currentSession ) {
116
- return ;
125
+
126
+ for ( const session of this . editingSessionsObs . read ( reader ) ) {
127
+ const entries = session . entries . read ( reader ) ;
128
+ const decidedEntries = entries . filter ( entry => entry . state . read ( reader ) === WorkingSetEntryState . Modified ) ;
129
+ return decidedEntries . length > 0 ;
117
130
}
118
- const entries = currentSession . entries . read ( reader ) ;
119
- const decidedEntries = entries . filter ( entry => entry . state . read ( reader ) === WorkingSetEntryState . Modified ) ;
120
- return decidedEntries . length > 0 ;
131
+
132
+ return false ;
121
133
} ) ) ;
122
134
this . _register ( bindContextKey ( hasAppliedChatEditsContextKey , contextKeyService , ( reader ) => {
123
135
const currentSession = this . _currentSessionObs . read ( reader ) ;
@@ -211,14 +223,26 @@ export class ChatEditingService extends Disposable implements IChatEditingServic
211
223
}
212
224
213
225
226
+ private _lookupEntry ( uri : URI ) : ChatEditingModifiedFileEntry | undefined {
227
+
228
+ for ( const item of Iterable . concat ( this . editingSessionsObs . get ( ) ) ) {
229
+ const candidate = item . getEntry ( uri ) ;
230
+ if ( candidate instanceof ChatEditingModifiedFileEntry ) {
231
+ // make sure to ref-count this object
232
+ return candidate . acquire ( ) ;
233
+ }
234
+ }
235
+ return undefined ;
236
+ }
237
+
214
238
private async _createEditingSession ( chatSessionId : string ) : Promise < IChatEditingSession > {
215
239
if ( this . _currentSessionObs . get ( ) ) {
216
240
throw new BugIndicatingError ( 'Cannot have more than one active editing session' ) ;
217
241
}
218
242
219
243
this . _currentSessionDisposables . clear ( ) ;
220
244
221
- const session = this . _instantiationService . createInstance ( ChatEditingSession , chatSessionId , this . _editingSessionFileLimitPromise ) ;
245
+ const session = this . _instantiationService . createInstance ( ChatEditingSession , chatSessionId , this . _editingSessionFileLimitPromise , this . _lookupEntry . bind ( this ) ) ;
222
246
await session . init ( ) ;
223
247
224
248
// listen for completed responses, run the code mapper and apply the edits to this edit session
@@ -227,14 +251,33 @@ export class ChatEditingService extends Disposable implements IChatEditingServic
227
251
this . _currentSessionDisposables . add ( session . onDidDispose ( ( ) => {
228
252
this . _currentSessionDisposables . clear ( ) ;
229
253
this . _currentSessionObs . set ( null , undefined ) ;
230
- this . _onDidChangeEditingSession . fire ( ) ;
231
- } ) ) ;
232
- this . _currentSessionDisposables . add ( session . onDidChange ( ( ) => {
233
- this . _onDidChangeEditingSession . fire ( ) ;
234
254
} ) ) ;
235
255
236
256
this . _currentSessionObs . set ( session , undefined ) ;
237
- this . _onDidChangeEditingSession . fire ( ) ;
257
+ return session ;
258
+ }
259
+
260
+ async createAdhocEditingSession ( chatSessionId : string ) : Promise < IChatEditingSession & IDisposable > {
261
+ const session = this . _instantiationService . createInstance ( ChatEditingSession , chatSessionId , this . _editingSessionFileLimitPromise , this . _lookupEntry . bind ( this ) ) ;
262
+ await session . init ( ) ;
263
+
264
+ const list = this . _adhocSessionsObs . get ( ) ;
265
+ const removeSession = list . unshift ( session ) ;
266
+
267
+ const store = new DisposableStore ( ) ;
268
+ this . _store . add ( store ) ;
269
+
270
+ store . add ( this . installAutoApplyObserver ( session ) ) ;
271
+
272
+ store . add ( session . onDidDispose ( e => {
273
+ removeSession ( ) ;
274
+ this . _adhocSessionsObs . set ( list , undefined ) ;
275
+ this . _store . deleteAndLeak ( store ) ;
276
+ store . dispose ( ) ;
277
+ } ) ) ;
278
+
279
+ this . _adhocSessionsObs . set ( list , undefined ) ;
280
+
238
281
return session ;
239
282
}
240
283
0 commit comments