Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export function getAccessibilityHelpText(type: 'panelChat' | 'inlineChat' | 'age
}
content.push(localize('chatEditing.helpfulCommands', 'Some helpful commands include:'));
content.push(localize('workbench.action.chat.undoEdits', '- Undo Edits{0}.', '<keybinding:workbench.action.chat.undoEdits>'));
content.push(localize('workbench.action.chat.restoreLastCheckpoint', '- Restore to Last Checkpoint{0}.', '<keybinding:workbench.action.chat.restoreLastCheckpoint>'));
content.push(localize('workbench.action.chat.editing.attachFiles', '- Attach Files{0}.', '<keybinding:workbench.action.chat.editing.attachFiles>'));
content.push(localize('chatEditing.removeFileFromWorkingSet', '- Remove File from Working Set{0}.', '<keybinding:chatEditing.removeFileFromWorkingSet>'));
content.push(localize('chatEditing.acceptFile', '- Keep{0} and Undo File{1}.', '<keybinding:chatEditing.acceptFile>', '<keybinding:chatEditing.discardFile>'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { CancellationToken } from '../../../../../base/common/cancellation.js';
import { Codicon } from '../../../../../base/common/codicons.js';
import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js';
import { alert } from '../../../../../base/browser/ui/aria/aria.js';
import { basename } from '../../../../../base/common/resources.js';
import { URI, UriComponents } from '../../../../../base/common/uri.js';
import { isCodeEditor } from '../../../../../editor/browser/editorBrowser.js';
Expand Down Expand Up @@ -419,13 +420,13 @@ export class ViewAllSessionChangesAction extends Action2 {
}
registerAction2(ViewAllSessionChangesAction);

async function restoreSnapshotWithConfirmation(accessor: ServicesAccessor, item: ChatTreeItem): Promise<void> {
async function restoreSnapshotWithConfirmationByRequestId(accessor: ServicesAccessor, sessionResource: URI, requestId: string): Promise<void> {
const configurationService = accessor.get(IConfigurationService);
const dialogService = accessor.get(IDialogService);
const chatWidgetService = accessor.get(IChatWidgetService);
const widget = chatWidgetService.getWidgetBySessionResource(item.sessionResource);
const widget = chatWidgetService.getWidgetBySessionResource(sessionResource);
const chatService = accessor.get(IChatService);
const chatModel = chatService.getSession(item.sessionResource);
const chatModel = chatService.getSession(sessionResource);
if (!chatModel) {
return;
}
Expand All @@ -435,59 +436,69 @@ async function restoreSnapshotWithConfirmation(accessor: ServicesAccessor, item:
return;
}

const requestId = isRequestVM(item) ? item.id :
isResponseVM(item) ? item.requestId : undefined;
const chatRequests = chatModel.getRequests();
const itemIndex = chatRequests.findIndex(request => request.id === requestId);
if (itemIndex === -1) {
return;
}

if (requestId) {
const chatRequests = chatModel.getRequests();
const itemIndex = chatRequests.findIndex(request => request.id === requestId);
const editsToUndo = chatRequests.length - itemIndex;

const requestsToRemove = chatRequests.slice(itemIndex);
const requestIdsToRemove = new Set(requestsToRemove.map(request => request.id));
const entriesModifiedInRequestsToRemove = session.entries.get().filter((entry) => requestIdsToRemove.has(entry.lastModifyingRequestId)) ?? [];
const shouldPrompt = entriesModifiedInRequestsToRemove.length > 0 && configurationService.getValue('chat.editing.confirmEditRequestRemoval') === true;

let message: string;
if (editsToUndo === 1) {
if (entriesModifiedInRequestsToRemove.length === 1) {
message = localize('chat.removeLast.confirmation.message2', "This will remove your last request and undo the edits made to {0}. Do you want to proceed?", basename(entriesModifiedInRequestsToRemove[0].modifiedURI));
} else {
message = localize('chat.removeLast.confirmation.multipleEdits.message', "This will remove your last request and undo edits made to {0} files in your working set. Do you want to proceed?", entriesModifiedInRequestsToRemove.length);
}
const editsToUndo = chatRequests.length - itemIndex;

const requestsToRemove = chatRequests.slice(itemIndex);
const requestIdsToRemove = new Set(requestsToRemove.map(request => request.id));
const entriesModifiedInRequestsToRemove = session.entries.get().filter((entry) => requestIdsToRemove.has(entry.lastModifyingRequestId)) ?? [];
const shouldPrompt = entriesModifiedInRequestsToRemove.length > 0 && configurationService.getValue('chat.editing.confirmEditRequestRemoval') === true;

let message: string;
if (editsToUndo === 1) {
if (entriesModifiedInRequestsToRemove.length === 1) {
message = localize('chat.removeLast.confirmation.message2', "This will remove your last request and undo the edits made to {0}. Do you want to proceed?", basename(entriesModifiedInRequestsToRemove[0].modifiedURI));
} else {
if (entriesModifiedInRequestsToRemove.length === 1) {
message = localize('chat.remove.confirmation.message2', "This will remove all subsequent requests and undo edits made to {0}. Do you want to proceed?", basename(entriesModifiedInRequestsToRemove[0].modifiedURI));
} else {
message = localize('chat.remove.confirmation.multipleEdits.message', "This will remove all subsequent requests and undo edits made to {0} files in your working set. Do you want to proceed?", entriesModifiedInRequestsToRemove.length);
}
message = localize('chat.removeLast.confirmation.multipleEdits.message', "This will remove your last request and undo edits made to {0} files in your working set. Do you want to proceed?", entriesModifiedInRequestsToRemove.length);
}
} else {
if (entriesModifiedInRequestsToRemove.length === 1) {
message = localize('chat.remove.confirmation.message2', "This will remove all subsequent requests and undo edits made to {0}. Do you want to proceed?", basename(entriesModifiedInRequestsToRemove[0].modifiedURI));
} else {
message = localize('chat.remove.confirmation.multipleEdits.message', "This will remove all subsequent requests and undo edits made to {0} files in your working set. Do you want to proceed?", entriesModifiedInRequestsToRemove.length);
}
}

const confirmation = shouldPrompt
? await dialogService.confirm({
title: editsToUndo === 1
? localize('chat.removeLast.confirmation.title', "Do you want to undo your last edit?")
: localize('chat.remove.confirmation.title', "Do you want to undo {0} edits?", editsToUndo),
message: message,
primaryButton: localize('chat.remove.confirmation.primaryButton', "Yes"),
checkbox: { label: localize('chat.remove.confirmation.checkbox', "Don't ask again"), checked: false },
type: 'info'
})
: { confirmed: true };
const confirmation = shouldPrompt
? await dialogService.confirm({
title: editsToUndo === 1
? localize('chat.removeLast.confirmation.title', "Do you want to undo your last edit?")
: localize('chat.remove.confirmation.title', "Do you want to undo {0} edits?", editsToUndo),
message: message,
primaryButton: localize('chat.remove.confirmation.primaryButton', "Yes"),
checkbox: { label: localize('chat.remove.confirmation.checkbox', "Don't ask again"), checked: false },
type: 'info'
})
: { confirmed: true };

if (!confirmation.confirmed) {
widget?.viewModel?.model.setCheckpoint(undefined);
return;
}
if (!confirmation.confirmed) {
widget?.viewModel?.model.setCheckpoint(undefined);
return;
}

if (confirmation.checkboxChecked) {
await configurationService.updateValue('chat.editing.confirmEditRequestRemoval', false);
}
if (confirmation.checkboxChecked) {
await configurationService.updateValue('chat.editing.confirmEditRequestRemoval', false);
}

// Restore the snapshot to what it was before the request(s) that we deleted
const snapshotRequestId = chatRequests[itemIndex].id;
await session.restoreSnapshot(snapshotRequestId, undefined);
// Restore the snapshot to what it was before the request(s) that we deleted
const snapshotRequestId = chatRequests[itemIndex].id;
await session.restoreSnapshot(snapshotRequestId, undefined);
}

async function restoreSnapshotWithConfirmation(accessor: ServicesAccessor, item: ChatTreeItem): Promise<void> {
const requestId = isRequestVM(item) ? item.id :
isResponseVM(item) ? item.requestId : undefined;

if (!requestId) {
return;
}

await restoreSnapshotWithConfirmationByRequestId(accessor, item.sessionResource, requestId);
}

registerAction2(class RemoveAction extends Action2 {
Expand Down Expand Up @@ -593,9 +604,14 @@ registerAction2(class RestoreLastCheckpoint extends Action2 {
super({
id: 'workbench.action.chat.restoreLastCheckpoint',
title: localize2('chat.restoreLastCheckpoint.label', "Restore to Last Checkpoint"),
f1: false,
f1: true,
category: CHAT_CATEGORY,
icon: Codicon.discard,
precondition: ContextKeyExpr.and(
ChatContextKeys.inChatSession,
ContextKeyExpr.equals(`config.${ChatConfiguration.CheckpointsEnabled}`, true),
ChatContextKeys.lockedToCodingAgent.negate()
),
menu: [
{
id: MenuId.ChatMessageFooter,
Expand All @@ -616,30 +632,27 @@ registerAction2(class RestoreLastCheckpoint extends Action2 {
item = widget?.getFocus();
}

if (!item) {
const sessionResource = widget?.viewModel?.sessionResource ?? (isChatTreeItem(item) ? item.sessionResource : undefined);
if (!sessionResource) {
return;
}

const chatModel = chatService.getSession(item.sessionResource);
if (!chatModel) {
const chatModel = chatService.getSession(sessionResource);
if (!chatModel?.editingSession) {
return;
}

const session = chatModel.editingSession;
if (!session) {
const checkpointRequest = chatModel.checkpoint;
if (!checkpointRequest) {
alert(localize('chat.restoreCheckpoint.none', 'There is no checkpoint to restore.'));
return;
}

await restoreSnapshotWithConfirmation(accessor, item);
widget?.viewModel?.model.setCheckpoint(checkpointRequest.id);
widget?.focusInput();
widget?.input.setValue(checkpointRequest.message.text, false);

if (isResponseVM(item)) {
widget?.viewModel?.model.setCheckpoint(item.requestId);
const request = chatModel.getRequests().find(request => request.id === item.requestId);
if (request) {
widget?.focusInput();
widget?.input.setValue(request.message.text, false);
}
}
await restoreSnapshotWithConfirmationByRequestId(accessor, sessionResource, checkpointRequest.id);
}
});

Expand Down
Loading