Skip to content

Commit 9508be8

Browse files
authored
edits undo: chat entries are no longer hidden after reload (microsoft#234345)
edits: chat entries are no longer hidden after reload
1 parent 268d4cf commit 9508be8

File tree

4 files changed

+45
-52
lines changed

4 files changed

+45
-52
lines changed

src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -265,15 +265,11 @@ export function registerNewChatActions() {
265265

266266
async run(accessor: ServicesAccessor, ...args: any[]) {
267267
const chatEditingService = accessor.get(IChatEditingService);
268-
const chatWidgetService = accessor.get(IChatWidgetService);
269268
const currentEditingSession = chatEditingService.currentEditingSession;
270269
if (!currentEditingSession) {
271270
return;
272271
}
273-
274-
const widget = chatWidgetService.getWidgetBySessionId(currentEditingSession.chatSessionId);
275272
await currentEditingSession.undoInteraction();
276-
widget?.viewModel?.model.disableRequests(currentEditingSession.hiddenRequestIds.get());
277273
}
278274
});
279275

@@ -297,15 +293,11 @@ export function registerNewChatActions() {
297293

298294
async run(accessor: ServicesAccessor, ...args: any[]) {
299295
const chatEditingService = accessor.get(IChatEditingService);
300-
const chatWidgetService = accessor.get(IChatWidgetService);
301296
const currentEditingSession = chatEditingService.currentEditingSession;
302297
if (!currentEditingSession) {
303298
return;
304299
}
305-
306-
const widget = chatWidgetService.getWidgetBySessionId(currentEditingSession.chatSessionId);
307300
await chatEditingService.currentEditingSession?.redoInteraction();
308-
widget?.viewModel?.model.disableRequests(currentEditingSession.hiddenRequestIds.get());
309301
}
310302
});
311303

src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class ChatEditingModifiedFileEntry extends Disposable implements IModifie
4040
public readonly entryId = `${ChatEditingModifiedFileEntry.scheme}::${++ChatEditingModifiedFileEntry.lastEntryId}`;
4141

4242
private readonly docSnapshot: ITextModel;
43-
private readonly originalContent;
43+
public readonly initialContent: string;
4444
private readonly doc: ITextModel;
4545
private readonly docFileEditorModel: IResolvedTextFileEditorModel;
4646
private _allEditsAreFromUs: boolean = true;
@@ -121,7 +121,7 @@ export class ChatEditingModifiedFileEntry extends Disposable implements IModifie
121121
private readonly _multiDiffEntryDelegate: { collapse: (transaction: ITransaction | undefined) => void },
122122
private _telemetryInfo: IModifiedEntryTelemetryInfo,
123123
kind: ChatEditKind,
124-
originalContent: string | undefined,
124+
initialContent: string | undefined,
125125
@IModelService modelService: IModelService,
126126
@ITextModelService textModelService: ITextModelService,
127127
@ILanguageService languageService: ILanguageService,
@@ -137,10 +137,10 @@ export class ChatEditingModifiedFileEntry extends Disposable implements IModifie
137137
this.docFileEditorModel = this._register(resourceRef).object as IResolvedTextFileEditorModel;
138138
this.doc = resourceRef.object.textEditorModel;
139139

140-
this.originalContent = originalContent ?? this.doc.getValue();
140+
this.initialContent = initialContent ?? this.doc.getValue();
141141
const docSnapshot = this.docSnapshot = this._register(
142142
modelService.createModel(
143-
createTextBufferFactoryFromSnapshot(originalContent ? stringToSnapshot(originalContent) : this.doc.createSnapshot()),
143+
createTextBufferFactoryFromSnapshot(initialContent ? stringToSnapshot(initialContent) : this.doc.createSnapshot()),
144144
languageService.createById(this.doc.getLanguageId()),
145145
ChatEditingTextModelContentProvider.getFileURI(this.entryId, this.modifiedURI.path),
146146
false
@@ -192,16 +192,15 @@ export class ChatEditingModifiedFileEntry extends Disposable implements IModifie
192192
telemetryInfo: this._telemetryInfo
193193
};
194194
}
195-
196195
restoreFromSnapshot(snapshot: ISnapshotEntry) {
197196
this._stateObs.set(snapshot.state, undefined);
198197
this.docSnapshot.setValue(snapshot.original);
199198
this._setDocValue(snapshot.current);
200199
this._edit = snapshot.originalToCurrentEdit;
201200
}
202201

203-
resetToInitialValue(value: string) {
204-
this._setDocValue(value);
202+
resetToInitialValue() {
203+
this._setDocValue(this.initialContent);
205204
}
206205

207206
acceptStreamingEditsStart(tx: ITransaction) {
@@ -262,7 +261,7 @@ export class ChatEditingModifiedFileEntry extends Disposable implements IModifie
262261
}
263262

264263
if (!this.isCurrentlyBeingModified.get()) {
265-
const didResetToOriginalContent = this.doc.getValue() === this.originalContent;
264+
const didResetToOriginalContent = this.doc.getValue() === this.initialContent;
266265
const currentState = this._stateObs.get();
267266
switch (currentState) {
268267
case WorkingSetEntryState.Modified:
@@ -427,11 +426,11 @@ export class ChatEditingModifiedFileEntry extends Disposable implements IModifie
427426
}
428427

429428
export interface IModifiedEntryTelemetryInfo {
430-
agentId: string | undefined;
431-
command: string | undefined;
432-
sessionId: string;
433-
requestId: string;
434-
result: IChatAgentResult | undefined;
429+
readonly agentId: string | undefined;
430+
readonly command: string | undefined;
431+
readonly sessionId: string;
432+
readonly requestId: string;
433+
readonly result: IChatAgentResult | undefined;
435434
}
436435

437436
export interface ISnapshotEntry {

src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import { IEnvironmentService } from '../../../../../platform/environment/common/
4343
import { VSBuffer } from '../../../../../base/common/buffer.js';
4444
import { IOffsetEdit, ISingleOffsetEdit, OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js';
4545
import { ILogService } from '../../../../../platform/log/common/log.js';
46+
import { IChatService } from '../../common/chatService.js';
4647

4748
const STORAGE_CONTENTS_FOLDER = 'contents';
4849
const STORAGE_STATE_FILE = 'state.json';
@@ -56,8 +57,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
5657
* Contains the contents of a file when the AI first began doing edits to it.
5758
*/
5859
private readonly _initialFileContents = new ResourceMap<string>();
59-
private readonly _snapshots = new Map<string, IChatEditingSessionSnapshot>();
60-
6160
private readonly _filesToSkipCreating = new ResourceSet();
6261

6362
private readonly _entriesObs = observableValue<readonly ChatEditingModifiedFileEntry[]>(this, []);
@@ -144,6 +143,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
144143
@IChatAgentService private readonly _chatAgentService: IChatAgentService,
145144
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
146145
@ILogService private readonly _logService: ILogService,
146+
@IChatService private readonly _chatService: IChatService,
147147
) {
148148
super();
149149

@@ -211,10 +211,13 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
211211
}
212212
}
213213

214+
private _findSnapshot(requestId: string): IChatEditingSessionSnapshot | undefined {
215+
return this._linearHistory.get().find(s => s.requestId === requestId);
216+
}
217+
214218
public createSnapshot(requestId: string | undefined): void {
215219
const snapshot = this._createSnapshot(requestId);
216220
if (requestId) {
217-
this._snapshots.set(requestId, snapshot);
218221
for (const workingSetItem of this._workingSet.keys()) {
219222
this._workingSet.set(workingSetItem, { state: WorkingSetEntryState.Sent });
220223
}
@@ -248,7 +251,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
248251
}
249252

250253
public async getSnapshotModel(requestId: string, snapshotUri: URI): Promise<ITextModel | null> {
251-
const entries = this._snapshots.get(requestId)?.entries;
254+
const entries = this._findSnapshot(requestId)?.entries;
252255
if (!entries) {
253256
return null;
254257
}
@@ -262,7 +265,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
262265
}
263266

264267
public getSnapshot(requestId: string, uri: URI) {
265-
const snapshot = this._snapshots.get(requestId);
268+
const snapshot = this._findSnapshot(requestId);
266269
const snapshotEntries = snapshot?.entries;
267270
return snapshotEntries?.get(uri);
268271
}
@@ -273,7 +276,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
273276
private _pendingSnapshot: IChatEditingSessionSnapshot | undefined;
274277
public async restoreSnapshot(requestId: string | undefined): Promise<void> {
275278
if (requestId !== undefined) {
276-
const snapshot = this._snapshots.get(requestId);
279+
const snapshot = this._findSnapshot(requestId);
277280
if (snapshot) {
278281
if (!this._pendingSnapshot) {
279282
// Create and save a pending snapshot
@@ -301,10 +304,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
301304
for (const entry of this._entriesObs.get()) {
302305
const snapshotEntry = snapshot.entries.get(entry.modifiedURI);
303306
if (!snapshotEntry) {
304-
const initialContents = this._initialFileContents.get(entry.modifiedURI);
305-
if (typeof initialContents === 'string') {
306-
entry.resetToInitialValue(initialContents);
307-
}
307+
entry.resetToInitialValue();
308308
entry.dispose();
309309
}
310310
}
@@ -313,8 +313,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
313313
// Restore all entries from the snapshot
314314
for (const snapshotEntry of snapshot.entries.values()) {
315315
const entry = await this._getOrCreateModifiedFileEntry(snapshotEntry.resource, snapshotEntry.telemetryInfo);
316-
entry.restoreFromSnapshot(snapshotEntry);
317-
entriesArr.push(entry);
316+
entry.restoreFromSnapshot(snapshotEntry); entriesArr.push(entry);
318317
}
319318

320319
this._entriesObs.set(entriesArr, undefined);
@@ -412,12 +411,14 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
412411

413412
async _performStop(): Promise<void> {
414413
// Close out all open files
415-
await Promise.allSettled(this._editorGroupsService.groups.map(async (g) => {
416-
return Promise.allSettled(g.editors.map(async (e) => {
417-
if (e instanceof MultiDiffEditorInput || e instanceof DiffEditorInput && (e.original.resource?.scheme === ChatEditingModifiedFileEntry.scheme || e.original.resource?.scheme === ChatEditingTextModelContentProvider.scheme)) {
414+
const schemes = [ChatEditingModifiedFileEntry.scheme, ChatEditingTextModelContentProvider.scheme];
415+
await Promise.allSettled(this._editorGroupsService.groups.flatMap(async (g) => {
416+
return g.editors.map(async (e) => {
417+
if ((e instanceof MultiDiffEditorInput && e.initialResources?.some(r => r.originalUri && schemes.indexOf(r.originalUri.scheme) !== -1))
418+
|| (e instanceof DiffEditorInput && e.original.resource && schemes.indexOf(e.original.resource.scheme) !== -1)) {
418419
await g.closeEditor(e);
419420
}
420-
}));
421+
});
421422
}));
422423

423424
// delete the persisted editing session state
@@ -501,6 +502,8 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
501502
const previousSnapshot = linearHistory[linearHistoryIndex - 1];
502503
await this.restoreSnapshot(previousSnapshot.requestId);
503504
this._linearHistoryIndex.set(linearHistoryIndex - 1, undefined);
505+
this._updateRequestHiddenState();
506+
504507
}
505508

506509
async redoInteraction(): Promise<void> {
@@ -515,6 +518,12 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
515518
}
516519
await this.restoreSnapshot(nextSnapshot.requestId);
517520
this._linearHistoryIndex.set(linearHistoryIndex + 1, undefined);
521+
this._updateRequestHiddenState();
522+
}
523+
524+
private _updateRequestHiddenState() {
525+
const hiddenRequestIds = this._linearHistory.get().slice(this._linearHistoryIndex.get()).map(s => s.requestId).filter((r): r is string => !!r);
526+
this._chatService.getSession(this.chatSessionId)?.disableRequests(hiddenRequestIds);
518527
}
519528

520529
private async _acceptStreamingEditsStart(): Promise<void> {
@@ -578,12 +587,11 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
578587
}
579588
return existingEntry;
580589
}
581-
582-
const originalContent = this._initialFileContents.get(resource);
590+
const initialContent = this._initialFileContents.get(resource);
583591
// This gets manually disposed in .dispose() or in .restoreSnapshot()
584-
const entry = await this._createModifiedFileEntry(resource, responseModel, false, originalContent);
585-
if (!originalContent) {
586-
this._initialFileContents.set(resource, entry.modifiedModel.getValue());
592+
const entry = await this._createModifiedFileEntry(resource, responseModel, false, initialContent);
593+
if (!initialContent) {
594+
this._initialFileContents.set(resource, entry.initialContent);
587595
}
588596
// If an entry is deleted e.g. reverting a created file,
589597
// remove it from the entries and don't show it in the working set anymore
@@ -602,19 +610,19 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
602610
return entry;
603611
}
604612

605-
private async _createModifiedFileEntry(resource: URI, responseModel: IModifiedEntryTelemetryInfo, mustExist = false, originalContent: string | undefined): Promise<ChatEditingModifiedFileEntry> {
613+
private async _createModifiedFileEntry(resource: URI, responseModel: IModifiedEntryTelemetryInfo, mustExist = false, initialContent: string | undefined): Promise<ChatEditingModifiedFileEntry> {
606614
try {
607615
const ref = await this._textModelService.createModelReference(resource);
608616

609-
return this._instantiationService.createInstance(ChatEditingModifiedFileEntry, ref, { collapse: (transaction: ITransaction | undefined) => this._collapse(resource, transaction) }, responseModel, mustExist ? ChatEditKind.Created : ChatEditKind.Modified, originalContent);
617+
return this._instantiationService.createInstance(ChatEditingModifiedFileEntry, ref, { collapse: (transaction: ITransaction | undefined) => this._collapse(resource, transaction) }, responseModel, mustExist ? ChatEditKind.Created : ChatEditKind.Modified, initialContent);
610618
} catch (err) {
611619
if (mustExist) {
612620
throw err;
613621
}
614622
// this file does not exist yet, create it and try again
615623
await this._bulkEditService.apply({ edits: [{ newResource: resource }] });
616624
this._editorService.openEditor({ resource, options: { inactive: true, preserveFocus: true, pinned: true } });
617-
return this._createModifiedFileEntry(resource, responseModel, true, originalContent);
625+
return this._createModifiedFileEntry(resource, responseModel, true, initialContent);
618626
}
619627
}
620628

@@ -680,15 +688,9 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
680688

681689
this._filesToSkipCreating.clear();
682690
this._initialFileContents.clear();
683-
this._snapshots.clear();
684691
this._pendingSnapshot = undefined;
685692

686693
const snapshotsFromHistory = await Promise.all(data.linearHistory.map(deserializeChatEditingSessionSnapshot));
687-
for (const snapshot of snapshotsFromHistory) {
688-
if (snapshot.requestId) {
689-
this._snapshots.set(snapshot.requestId, snapshot);
690-
}
691-
}
692694
data.filesToSkipCreating.forEach((uriStr: string) => {
693695
this._filesToSkipCreating.add(URI.parse(uriStr));
694696
});
@@ -700,6 +702,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
700702
const pendingSnapshot = await deserializeChatEditingSessionSnapshot(data.pendingSnapshot);
701703
this._restoreSnapshot(pendingSnapshot);
702704
this._state.set(ChatEditingSessionState.Idle, undefined);
705+
this._updateRequestHiddenState();
703706
return true;
704707
} catch (e) {
705708
this._logService.error(`Error restoring chat editing session from ${storageLocation.toString()}`, e);

src/vs/workbench/contrib/chat/common/chatEditingService.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ export interface IChatEditingSession {
7474
readonly onDidDispose: Event<void>;
7575
readonly state: IObservable<ChatEditingSessionState>;
7676
readonly entries: IObservable<readonly IModifiedFileEntry[]>;
77-
readonly hiddenRequestIds: IObservable<readonly string[]>;
7877
readonly workingSet: ResourceMap<WorkingSetDisplayMetadata>;
7978
readonly isVisible: boolean;
8079
addFileToWorkingSet(uri: URI, description?: string, kind?: WorkingSetEntryState.Transient | WorkingSetEntryState.Suggested): void;

0 commit comments

Comments
 (0)