diff --git a/core/index.d.ts b/core/index.d.ts index 8e9d43dc370..21a77f194ad 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -1515,7 +1515,7 @@ export interface ShowFilePayload { export interface ApplyToFilePayload { streamId: string; - filepath?: string; + filepath: string; text: string; toolCallId?: string; isSearchAndReplace?: boolean; diff --git a/extensions/vscode/src/apply/ApplyManager.ts b/extensions/vscode/src/apply/ApplyManager.ts index de8344e1889..f40523b27ce 100644 --- a/extensions/vscode/src/apply/ApplyManager.ts +++ b/extensions/vscode/src/apply/ApplyManager.ts @@ -11,6 +11,7 @@ import { streamDiffLines } from "core/edit/streamDiffLines"; import { pruneLinesFromBottom, pruneLinesFromTop } from "core/llm/countTokens"; import { getMarkdownLanguageTagForFile } from "core/util"; import { VerticalDiffManager } from "../diff/vertical/manager"; +import { openEditorAndRevealRange } from "../util/vscode"; import { VsCodeIde } from "../VsCodeIde"; import { VsCodeWebviewProtocol } from "../webviewProtocol"; @@ -32,18 +33,14 @@ export class ApplyManager { toolCallId, isSearchAndReplace, }: ApplyToFilePayload) { - if (filepath) { - await this.ensureFileOpen(filepath); - } - - const { activeTextEditor } = vscode.window; - if (!activeTextEditor) { - void vscode.window.showErrorMessage("No active editor to apply edits to"); + const editor = await this.getOrCreateEditor(filepath); + if (!editor) { + void vscode.window.showErrorMessage("Failed to open editor for file"); return; } // Capture the original file content before applying changes - const originalFileContent = activeTextEditor.document.getText(); + const originalFileContent = editor.document.getText(); await this.webviewProtocol.request("updateApplyState", { streamId, @@ -53,42 +50,37 @@ export class ApplyManager { toolCallId, }); - const hasExistingDocument = !!activeTextEditor.document.getText().trim(); + const hasExistingDocument = !!editor.document.getText().trim(); if (hasExistingDocument) { // Currently `isSearchAndReplace` will always provide a full file rewrite // as the contents of `text`, so we can just instantly apply if (isSearchAndReplace) { await this.verticalDiffManager.instantApplyDiff( + filepath, originalFileContent, text, streamId, toolCallId, ); } else { - await this.handleExistingDocument( - activeTextEditor, - text, - streamId, - toolCallId, - ); + await this.handleExistingDocument(editor, text, streamId, toolCallId); } } else { - await this.handleEmptyDocument( - activeTextEditor, - text, - streamId, - toolCallId, - ); + await this.handleEmptyDocument(editor, text, streamId, toolCallId); } } - private async ensureFileOpen(filepath: string): Promise { + private async getOrCreateEditor( + filepath: string, + ): Promise { const fileExists = await this.ide.fileExists(filepath); if (!fileExists) { await this.ide.writeFile(filepath, ""); - await this.ide.openFile(filepath); } - await this.ide.openFile(filepath); + const uri = filepath.startsWith("file://") + ? vscode.Uri.parse(filepath) + : vscode.Uri.file(filepath); + return openEditorAndRevealRange(uri); } private modelIsTooFastForStreaming(model: string): boolean { diff --git a/extensions/vscode/src/diff/vertical/manager.ts b/extensions/vscode/src/diff/vertical/manager.ts index 059efe0aee4..4c5d9161a09 100644 --- a/extensions/vscode/src/diff/vertical/manager.ts +++ b/extensions/vscode/src/diff/vertical/manager.ts @@ -16,6 +16,7 @@ import { EDIT_MODE_STREAM_ID } from "core/edit/constants"; import { stripImages } from "core/util/messageContent"; import { getLastNPathParts } from "core/util/uri"; import { editOutcomeTracker } from "../../extension/EditOutcomeTracker"; +import { openEditorAndRevealRange } from "../../util/vscode"; import { VerticalDiffHandler, VerticalDiffHandlerOptions } from "./handler"; import { getFirstChangedLine } from "./util"; @@ -296,6 +297,7 @@ export class VerticalDiffManager { } async instantApplyDiff( + filepath: string, oldContent: string, newContent: string, streamId: string, @@ -303,17 +305,16 @@ export class VerticalDiffManager { ) { vscode.commands.executeCommand("setContext", "continue.diffVisible", true); - const editor = vscode.window.activeTextEditor; + const uri = vscode.Uri.parse(filepath); + const editor = await openEditorAndRevealRange(uri); if (!editor) { return; } - const fileUri = editor.document.uri.toString(); - const myersDiffs = myersDiff(oldContent, newContent); const diffHandler = this.createVerticalDiffHandler( - fileUri, + filepath, 0, editor.document.lineCount - 1, { @@ -324,7 +325,7 @@ export class VerticalDiffManager { status, numDiffs, fileContent, - filepath: fileUri, + filepath, toolCallId, }), streamId, @@ -347,9 +348,9 @@ export class VerticalDiffManager { await this.webviewProtocol.request("updateApplyState", { streamId, status: "done", - numDiffs: this.fileUriToCodeLens.get(fileUri)?.length ?? 0, + numDiffs: this.fileUriToCodeLens.get(filepath)?.length ?? 0, fileContent: editor.document.getText(), - filepath: fileUri, + filepath, toolCallId, }); } diff --git a/gui/src/redux/thunks/handleApplyStateUpdate.ts b/gui/src/redux/thunks/handleApplyStateUpdate.ts index a220af51f63..7b4a82356a2 100644 --- a/gui/src/redux/thunks/handleApplyStateUpdate.ts +++ b/gui/src/redux/thunks/handleApplyStateUpdate.ts @@ -10,6 +10,7 @@ import { updateEditStateApplyState } from "../slices/editState"; import { acceptToolCall, errorToolCall, + setToolGenerated, updateApplyState, updateToolCallOutput, } from "../slices/sessionSlice"; @@ -49,16 +50,22 @@ export const handleApplyStateUpdate = createAsyncThunk< applyState.toolCallId, ); - if ( - applyState.status === "done" && - toolCallState?.toolCall.function.name && - getState().ui.toolSettings[toolCallState.toolCall.function.name] === - "allowedWithoutPermission" - ) { - extra.ideMessenger.post("acceptDiff", { - streamId: applyState.streamId, - filepath: applyState.filepath, - }); + if (applyState.status === "done") { + // diff is ready for user accept/reject - set status back to "generated" + dispatch( + setToolGenerated({ toolCallId: applyState.toolCallId, tools: [] }), + ); + + if ( + toolCallState?.toolCall.function.name && + getState().ui.toolSettings[toolCallState.toolCall.function.name] === + "allowedWithoutPermission" + ) { + extra.ideMessenger.post("acceptDiff", { + streamId: applyState.streamId, + filepath: applyState.filepath, + }); + } } if (applyState.status === "closed") {