diff --git a/src/ClaudeDev.ts b/src/ClaudeDev.ts index c9fd877..eb23c4e 100644 --- a/src/ClaudeDev.ts +++ b/src/ClaudeDev.ts @@ -383,7 +383,19 @@ export class ClaudeDev { }) .join("") - vscode.window.showTextDocument(vscode.Uri.file(filePath), { preview: false }) + // Create virtual document with new file, then open diff editor + const fileName = path.basename(filePath) + vscode.commands.executeCommand( + "vscode.diff", + vscode.Uri.file(filePath), + // to create a virtual doc we use a uri scheme registered in extension.ts, which then converts this base64 content into a text document + // (providing file name with extension in the uri lets vscode know the language of the file and apply syntax highlighting) + vscode.Uri.parse(`claude-dev-diff:${fileName}`).with({ + query: Buffer.from(newContent).toString("base64"), + }), + `${fileName}: Original ↔ Suggested Changes` + ) + const { response, text } = await this.ask( "tool", JSON.stringify({ @@ -392,6 +404,10 @@ export class ClaudeDev { diff: diffRepresentation, } as ClaudeSayTool) ) + // close the diff view if it's open + if (vscode.window.activeTextEditor?.document.uri.scheme === "claude-dev-diff") { + await vscode.commands.executeCommand("workbench.action.closeActiveEditor") + } if (response !== "yesButtonTapped") { if (response === "textResponse" && text) { await this.say("user_feedback", text) @@ -401,12 +417,29 @@ export class ClaudeDev { } await fs.writeFile(filePath, newContent) + // Finish by opening the edited file in the editor + vscode.window.showTextDocument(vscode.Uri.file(filePath), { preview: false }) return `Changes applied to ${filePath}:\n${diffResult}` } else { + const fileName = path.basename(filePath) + vscode.commands.executeCommand( + "vscode.diff", + vscode.Uri.parse(`claude-dev-diff:${fileName}`).with({ + query: Buffer.from("").toString("base64"), + }), + vscode.Uri.parse(`claude-dev-diff:${fileName}`).with({ + query: Buffer.from(newContent).toString("base64"), + }), + `${fileName}: New File` + ) + const { response, text } = await this.ask( "tool", JSON.stringify({ tool: "newFileCreated", path: filePath, content: newContent } as ClaudeSayTool) ) + if (vscode.window.activeTextEditor?.document.uri.scheme === "claude-dev-diff") { + await vscode.commands.executeCommand("workbench.action.closeActiveEditor") + } if (response !== "yesButtonTapped") { if (response === "textResponse" && text) { await this.say("user_feedback", text) diff --git a/src/extension.ts b/src/extension.ts index 9aec3dd..bb254b7 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -86,6 +86,22 @@ export function activate(context: vscode.ExtensionContext) { sidebarProvider.postMessageToWebview({ type: "action", action: "settingsButtonTapped" }) }) ) + + /* + We use the text document content provider API to show a diff view for new files/edits by creating a virtual document for the new content. + + - This API allows you to create readonly documents in VSCode from arbitrary sources, and works by claiming an uri-scheme for which your provider then returns text contents. The scheme must be provided when registering a provider and cannot change afterwards. + - Note how the provider doesn't create uris for virtual documents - its role is to provide contents given such an uri. In return, content providers are wired into the open document logic so that providers are always considered. + https://code.visualstudio.com/api/extension-guides/virtual-documents + */ + const diffContentProvider = new (class implements vscode.TextDocumentContentProvider { + provideTextDocumentContent(uri: vscode.Uri): string { + return Buffer.from(uri.query, "base64").toString("utf-8") + } + })() + context.subscriptions.push( + vscode.workspace.registerTextDocumentContentProvider("claude-dev-diff", diffContentProvider) + ) } // This method is called when your extension is deactivated