Merge pull request #245216 from microsoft/joh/noble-guanaco

clean up working set remains
This commit is contained in:
Johannes Rieken
2025-04-01 12:49:36 +02:00
committed by GitHub
5 changed files with 20 additions and 48 deletions
@@ -28,7 +28,7 @@ import { GroupsOrder, IEditorGroupsService } from '../../../../services/editor/c
import { IEditorService } from '../../../../services/editor/common/editorService.js';
import { isChatViewTitleActionContext } from '../../common/chatActions.js';
import { ChatContextKeys } from '../../common/chatContextKeys.js';
import { applyingChatEditsFailedContextKey, CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, chatEditingResourceContextKey, chatEditingWidgetFileStateContextKey, decidedChatEditingResourceContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, WorkingSetEntryRemovalReason, ModifiedFileEntryState } from '../../common/chatEditingService.js';
import { applyingChatEditsFailedContextKey, CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, chatEditingResourceContextKey, chatEditingWidgetFileStateContextKey, decidedChatEditingResourceContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, ModifiedFileEntryState } from '../../common/chatEditingService.js';
import { IChatService } from '../../common/chatService.js';
import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js';
import { ChatAgentLocation, ChatMode } from '../../common/constants.js';
@@ -146,7 +146,7 @@ registerAction2(class RemoveFileFromWorkingSet extends WorkingSetAction {
// Remove from working set
await currentEditingSession.reject(...uris);
currentEditingSession.remove(WorkingSetEntryRemovalReason.User, ...uris);
currentEditingSession.remove(...uris);
// Remove from chat input part
for (const uri of uris) {
@@ -354,7 +354,7 @@ export class ChatEditingRemoveAllFilesAction extends EditingSessionAction {
override async runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession, chatWidget: IChatWidget, ...args: any[]): Promise<void> {
// Remove all files from working set
const uris = [...editingSession.entries.get()].map((e) => e.modifiedURI);
editingSession.remove(WorkingSetEntryRemovalReason.User, ...uris);
editingSession.remove(...uris);
// Remove all file attachments
const fileAttachments = chatWidget.attachmentModel ? chatWidget.attachmentModel.fileAttachments : [];
@@ -38,7 +38,7 @@ import { editorSelectionBackground } from '../../../../../platform/theme/common/
import { IUndoRedoElement, IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js';
import { SaveReason, IEditorPane } from '../../../../common/editor.js';
import { IFilesConfigurationService } from '../../../../services/filesConfiguration/common/filesConfigurationService.js';
import { IResolvedTextFileEditorModel, ITextFileService, stringToSnapshot } from '../../../../services/textfile/common/textfiles.js';
import { isTextFileEditorModel, ITextFileService, stringToSnapshot } from '../../../../services/textfile/common/textfiles.js';
import { ICellEditOperation } from '../../../notebook/common/notebookCommon.js';
import { IModifiedFileEntry, ChatEditKind, ModifiedFileEntryState, IModifiedFileEntryEditorIntegration } from '../../common/chatEditingService.js';
import { IChatResponseModel } from '../../common/chatModel.js';
@@ -76,7 +76,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie
private readonly originalModel: ITextModel;
private readonly modifiedModel: ITextModel;
readonly docFileEditorModel: IResolvedTextFileEditorModel;
private readonly _docFileEditorModel: IResolvedTextEditorModel;
private _edit: OffsetEdit = OffsetEdit.empty;
private _isEditFromUs: boolean = false;
@@ -128,7 +128,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie
instantiationService
);
this.docFileEditorModel = this._register(resourceRef).object as IResolvedTextFileEditorModel;
this._docFileEditorModel = this._register(resourceRef).object;
this.modifiedModel = resourceRef.object.textEditorModel;
this.originalURI = ChatEditingTextModelContentProvider.getFileURI(telemetryInfo.sessionId, this.entryId, this.modifiedURI.path);
@@ -451,15 +451,17 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie
protected override async _doReject(tx: ITransaction | undefined): Promise<void> {
if (this.createdInRequestId === this._telemetryInfo.requestId) {
await this.docFileEditorModel.revert({ soft: true });
await this._fileService.del(this.modifiedURI);
if (isTextFileEditorModel(this._docFileEditorModel)) {
await this._docFileEditorModel.revert({ soft: true });
await this._fileService.del(this.modifiedURI);
}
this._onDidDelete.fire();
} else {
this._setDocValue(this.originalModel.getValue());
if (this._allEditsAreFromUs) {
if (this._allEditsAreFromUs && isTextFileEditorModel(this._docFileEditorModel)) {
// save the file after discarding so that the dirty indicator goes away
// and so that an intermediate saved state gets reverted
await this.docFileEditorModel.save({ reason: SaveReason.EXPLICIT, skipSaveParticipants: true });
await this._docFileEditorModel.save({ reason: SaveReason.EXPLICIT, skipSaveParticipants: true });
}
await this._collapse(tx);
}
@@ -50,7 +50,7 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im
protected readonly _onDidDelete = this._register(new Emitter<void>());
readonly onDidDelete = this._onDidDelete.event;
protected readonly _stateObs = observableValue<ModifiedFileEntryState>(this, ModifiedFileEntryState.Attached);
protected readonly _stateObs = observableValue<ModifiedFileEntryState>(this, ModifiedFileEntryState.Modified);
readonly state: IObservable<ModifiedFileEntryState> = this._stateObs;
protected readonly _isCurrentlyBeingModifiedByObs = observableValue<IChatResponseModel | undefined>(this, undefined);
@@ -41,7 +41,7 @@ import { MultiDiffEditor } from '../../../multiDiffEditor/browser/multiDiffEdito
import { MultiDiffEditorInput } from '../../../multiDiffEditor/browser/multiDiffEditorInput.js';
import { CellUri, ICellEditOperation } from '../../../notebook/common/notebookCommon.js';
import { INotebookService } from '../../../notebook/common/notebookService.js';
import { ChatEditingSessionChangeType, ChatEditingSessionState, ChatEditKind, getMultiDiffSourceUri, IChatEditingSession, IEditSessionEntryDiff, IModifiedFileEntry, IStreamingEdits, ModifiedFileEntryState, WorkingSetDisplayMetadata, WorkingSetEntryRemovalReason } from '../../common/chatEditingService.js';
import { ChatEditingSessionChangeType, ChatEditingSessionState, ChatEditKind, getMultiDiffSourceUri, IChatEditingSession, IEditSessionEntryDiff, IModifiedFileEntry, IStreamingEdits, ModifiedFileEntryState, WorkingSetDisplayMetadata } from '../../common/chatEditingService.js';
import { ICellTextEditOperation, IChatRequestDisablement, IChatResponseModel, isCellTextEditOperation } from '../../common/chatModel.js';
import { IChatService } from '../../common/chatService.js';
import { ChatEditingModifiedDocumentEntry } from './chatEditingModifiedDocumentEntry.js';
@@ -138,8 +138,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
return this._entriesObs;
}
private _workingSet = new ResourceMap<WorkingSetDisplayMetadata>();
private _editorPane: MultiDiffEditor | undefined;
get state(): IObservable<ChatEditingSessionState> {
@@ -384,9 +382,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
public createSnapshot(requestId: string, undoStop: string | undefined): void {
const snapshot = this._createSnapshot(requestId, undoStop);
for (const [uri, _] of this._workingSet) {
this._workingSet.set(uri, { state: ModifiedFileEntryState.Sent });
}
const linearHistoryPtr = this._linearHistoryIndex.get();
const newLinearHistory: IChatEditingSessionSnapshot[] = [];
@@ -413,7 +408,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
}
private _createSnapshot(requestId: string | undefined, undoStop: string | undefined): IChatEditingSessionStop {
const workingSet = new ResourceMap<WorkingSetDisplayMetadata>(this._workingSet);
const entries = new ResourceMap<ISnapshotEntry>();
for (const entry of this._entriesObs.get()) {
entries.set(entry.modifiedURI, entry.createSnapshot(requestId, undoStop));
@@ -421,7 +416,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
return {
stopId: undoStop,
workingSet,
entries,
};
}
@@ -472,8 +466,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
}
}
private async _restoreSnapshot({ workingSet, entries }: IChatEditingSessionStop, tx: ITransaction | undefined, restoreResolvedToDisk = true): Promise<void> {
this._workingSet = new ResourceMap(workingSet);
private async _restoreSnapshot({ entries }: IChatEditingSessionStop, tx: ITransaction | undefined, restoreResolvedToDisk = true): Promise<void> {
// Reset all the files which are modified in this session state
// but which are not found in the snapshot
@@ -497,7 +490,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
this._entriesObs.set(entriesArr, tx);
}
remove(reason: WorkingSetEntryRemovalReason, ...uris: URI[]): void {
remove(...uris: URI[]): void {
this._assertNotDisposed();
let didRemoveUris = false;
@@ -511,10 +504,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
didRemoveUris = true;
}
const state = this._workingSet.get(uri);
if (state !== undefined) {
didRemoveUris = this._workingSet.delete(uri) || didRemoveUris;
}
}
if (!didRemoveUris) {
@@ -905,7 +894,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
const listener = entry.onDidDelete(() => {
const newEntries = this._entriesObs.get().filter(e => !isEqual(e.modifiedURI, entry.modifiedURI));
this._entriesObs.set(newEntries, undefined);
this._workingSet.delete(entry.modifiedURI);
this._editorService.closeEditors(this._editorService.findEditors(entry.modifiedURI));
if (!existingExternalEntry) {
@@ -996,12 +984,6 @@ class ChatEditingSessionStorage {
}
return readPromise;
};
const deserializeResourceMap = <T>(resourceMap: ResourceMapDTO<T>, deserialize: (value: any) => T, result: ResourceMap<T>): ResourceMap<T> => {
resourceMap.forEach(([resourceURI, value]) => {
result.set(URI.parse(resourceURI), deserialize(value));
});
return result;
};
const deserializeSnapshotEntriesDTO = async (dtoEntries: ISnapshotEntryDTO[]): Promise<ResourceMap<ISnapshotEntry>> => {
const entries = new ResourceMap<ISnapshotEntry>();
for (const entryDTO of dtoEntries) {
@@ -1012,14 +994,13 @@ class ChatEditingSessionStorage {
};
const deserializeChatEditingStopDTO = async (stopDTO: IChatEditingSessionStopDTO | IChatEditingSessionSnapshotDTO): Promise<IChatEditingSessionStop> => {
const entries = await deserializeSnapshotEntriesDTO(stopDTO.entries);
const workingSet = deserializeResourceMap(stopDTO.workingSet, (value) => value, new ResourceMap());
return { stopId: 'stopId' in stopDTO ? stopDTO.stopId : undefined, workingSet, entries };
return { stopId: 'stopId' in stopDTO ? stopDTO.stopId : undefined, entries };
};
const normalizeSnapshotDtos = (snapshot: IChatEditingSessionSnapshotDTO | IChatEditingSessionSnapshotDTO2): IChatEditingSessionSnapshotDTO2 => {
if ('stops' in snapshot) {
return snapshot;
}
return { requestId: snapshot.requestId, stops: [{ stopId: undefined, entries: snapshot.entries, workingSet: snapshot.workingSet }], postEdit: undefined };
return { requestId: snapshot.requestId, stops: [{ stopId: undefined, entries: snapshot.entries }], postEdit: undefined };
};
const deserializeChatEditingSessionSnapshot = async (startIndex: number, snapshot: IChatEditingSessionSnapshotDTO2): Promise<IChatEditingSessionSnapshot> => {
const stops = await Promise.all(snapshot.stops.map(deserializeChatEditingStopDTO));
@@ -1115,7 +1096,6 @@ class ChatEditingSessionStorage {
const serializeChatEditingSessionStop = (stop: IChatEditingSessionStop): IChatEditingSessionStopDTO => {
return {
stopId: stop.stopId,
workingSet: serializeResourceMap(stop.workingSet, value => value),
entries: Array.from(stop.entries.values()).map(serializeSnapshotEntry)
};
};
@@ -1201,13 +1181,11 @@ interface IChatEditingSessionStop {
/** Edit stop ID, first for a request is always undefined. */
stopId: string | undefined;
readonly workingSet: ResourceMap<WorkingSetDisplayMetadata>;
readonly entries: ResourceMap<ISnapshotEntry>;
}
interface IChatEditingSessionStopDTO {
readonly stopId: string | undefined;
readonly workingSet: ResourceMapDTO<WorkingSetDisplayMetadata>;
readonly entries: ISnapshotEntryDTO[];
}
@@ -85,7 +85,7 @@ export interface IChatEditingSession extends IDisposable {
readonly state: IObservable<ChatEditingSessionState>;
readonly entries: IObservable<readonly IModifiedFileEntry[]>;
show(): Promise<void>;
remove(reason: WorkingSetEntryRemovalReason, ...uris: URI[]): void;
remove(...uris: URI[]): void;
accept(...uris: URI[]): Promise<void>;
reject(...uris: URI[]): Promise<void>;
getEntry(uri: URI): IModifiedFileEntry | undefined;
@@ -140,18 +140,10 @@ export interface IEditSessionEntryDiff {
removed: number;
}
export const enum WorkingSetEntryRemovalReason {
User,
Programmatic
}
export const enum ModifiedFileEntryState {
Modified,
Accepted,
Rejected,
Attached, // TODO@joyceerhl remove this
Sent, // TODO@joyceerhl remove this
}
export const enum ChatEditingSessionChangeType {