diff --git a/extensions/void/package.json b/extensions/void/package.json index 0f9105b2..809329dd 100644 --- a/extensions/void/package.json +++ b/extensions/void/package.json @@ -72,11 +72,11 @@ "title": "Show Selection Lens" }, { - "command": "void.approveDiff", + "command": "void.acceptDiff", "title": "Approve Diff" }, { - "command": "void.discardDiff", + "command": "void.rejectDiff", "title": "Discard Diff" }, { diff --git a/extensions/void/src/ApprovalCodeLensProvider.ts b/extensions/void/src/DisplayChangesProvider.ts similarity index 85% rename from extensions/void/src/ApprovalCodeLensProvider.ts rename to extensions/void/src/DisplayChangesProvider.ts index 80386fa1..31c2a28a 100644 --- a/extensions/void/src/ApprovalCodeLensProvider.ts +++ b/extensions/void/src/DisplayChangesProvider.ts @@ -15,17 +15,17 @@ const greenDecoration = vscode.window.createTextEditorDecorationType({ isWholeLine: false, // after: { contentText: ' [original]', color: 'rgba(0 255 60 / 0.5)' } // hoverMessage: originalText // this applies to hovering over after:... }) -export class ApprovalCodeLensProvider implements vscode.CodeLensProvider { + +// responsible for displaying diffs and showing accept/reject buttons +export class ApplyChangesProvider implements vscode.CodeLensProvider { private _diffsOfDocument: { [docUriStr: string]: DiffType[] } = {}; private _computedLensesOfDocument: { [docUriStr: string]: vscode.CodeLens[] } = {} // computed from diffsOfDocument[docUriStr].lenses private _diffidPool = 0 - - private _onDidChangeCodeLenses: vscode.EventEmitter = new vscode.EventEmitter(); // signals a UI refresh on .fire() events - private _weAreEditing: boolean = false // used internally by vscode + private _onDidChangeCodeLenses: vscode.EventEmitter = new vscode.EventEmitter(); // signals a UI refresh on .fire() events public readonly onDidChangeCodeLenses: vscode.Event = this._onDidChangeCodeLenses.event; @@ -40,16 +40,19 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider { // this acts as a useEffect. Every time text changes, clear the diffs in this editor vscode.workspace.onDidChangeTextDocument((e) => { const editor = vscode.window.activeTextEditor + if (!editor) return if (this._weAreEditing) return + const docUri = editor.document.uri const docUriStr = docUri.toString() this._diffsOfDocument[docUriStr].splice(0) // clear diffs editor.setDecorations(greenDecoration, []) // clear decorations - this._computedLensesOfDocument[docUriStr] = this._diffsOfDocument[docUriStr].flatMap(diff => diff.lenses) // recompute - this._onDidChangeCodeLenses.fire() // refresh + + this._computedLensesOfDocument[docUriStr] = this._diffsOfDocument[docUriStr].flatMap(diff => diff.lenses) // recompute codelenses + this._onDidChangeCodeLenses.fire() // rerender codelenses }) } @@ -61,13 +64,15 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider { } // used by us only - public async addNewApprovals(editor: vscode.TextEditor, suggestedEdits: SuggestedEdit[]) { + public async addNewChanges(editor: vscode.TextEditor, suggestedEdits: SuggestedEdit[]) { const docUri = editor.document.uri const docUriStr = docUri.toString() + // if no diffs, set diffs to [] if (!this._diffsOfDocument[docUriStr]) this._diffsOfDocument[docUriStr] = [] + // if no codelenses, set codelenses to [] if (!this._computedLensesOfDocument[docUriStr]) this._computedLensesOfDocument[docUriStr] = [] @@ -84,7 +89,7 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider { // INSERTIONS (e.g. {originalStartLine: 0, originalEndLine: -1}) if (suggestedEdit.originalStartLine > suggestedEdit.originalEndLine) { const originalPosition = new vscode.Position(suggestedEdit.originalStartLine, 0) - workspaceEdit.insert(docUri, originalPosition, suggestedEdit.newContent + '\n') // add back in the line we deleted when we made the startline->endline range go negative + workspaceEdit.insert(docUri, originalPosition, suggestedEdit.afterCode + '\n') // add back in the line we deleted when we made the startline->endline range go negative greenRange = new vscode.Range(suggestedEdit.startLine, 0, suggestedEdit.endLine + 1, 0) } // DELETIONS @@ -92,22 +97,22 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider { const deleteRange = new vscode.Range(suggestedEdit.originalStartLine, 0, suggestedEdit.originalEndLine + 1, 0) workspaceEdit.delete(docUri, deleteRange) greenRange = new vscode.Range(suggestedEdit.startLine, 0, suggestedEdit.startLine, 0) - suggestedEdit.originalContent += '\n' // add back in the line we deleted when we made the startline->endline range go negative + suggestedEdit.beforeCode += '\n' // add back in the line we deleted when we made the startline->endline range go negative } // REPLACEMENTS else { const originalRange = new vscode.Range(suggestedEdit.originalStartLine, 0, suggestedEdit.originalEndLine, Number.MAX_SAFE_INTEGER) - workspaceEdit.replace(docUri, originalRange, suggestedEdit.newContent) + workspaceEdit.replace(docUri, originalRange, suggestedEdit.afterCode) greenRange = new vscode.Range(suggestedEdit.startLine, 0, suggestedEdit.endLine, Number.MAX_SAFE_INTEGER) } this._diffsOfDocument[docUriStr].push({ diffid: this._diffidPool, greenRange: greenRange, - originalCode: suggestedEdit.originalContent, + originalCode: suggestedEdit.beforeCode, lenses: [ - new vscode.CodeLens(greenRange, { title: 'Accept', command: 'void.approveDiff', arguments: [{ diffid: this._diffidPool }] }), - new vscode.CodeLens(greenRange, { title: 'Reject', command: 'void.discardDiff', arguments: [{ diffid: this._diffidPool }] }) + new vscode.CodeLens(greenRange, { title: 'Accept', command: 'void.acceptDiff', arguments: [{ diffid: this._diffidPool }] }), + new vscode.CodeLens(greenRange, { title: 'Reject', command: 'void.rejectDiff', arguments: [{ diffid: this._diffidPool }] }) ] }); this._diffidPool += 1 @@ -124,12 +129,13 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider { console.log('diffs after added:', this._diffsOfDocument[docUriStr]) } - // called on void.approveDiff - public async approveDiff({ diffid }: { diffid: number }) { + // called on void.acceptDiff + public async acceptDiff({ diffid }: { diffid: number }) { const editor = vscode.window.activeTextEditor if (!editor) return + // get document uri const docUri = editor.document.uri const docUriStr = docUri.toString() @@ -148,8 +154,8 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider { } - // called on void.discardDiff - public async discardDiff({ diffid }: { diffid: number }) { + // called on void.rejectDiff + public async rejectDiff({ diffid }: { diffid: number }) { const editor = vscode.window.activeTextEditor if (!editor) return @@ -172,7 +178,7 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider { // clear the decoration in this diffs range editor.setDecorations(greenDecoration, this._diffsOfDocument[docUriStr].map(diff => diff.greenRange)) - // REVERT THE CHANGE (this is the only part that's different from approveDiff) + // REVERT THE CHANGE (this is the only part that's different from acceptDiff) let workspaceEdit = new vscode.WorkspaceEdit(); workspaceEdit.replace(docUri, range, originalCode); this._weAreEditing = true diff --git a/extensions/void/src/common/sendLLMMessage.ts b/extensions/void/src/common/sendLLMMessage.ts index 9e47a80a..c0e1a8b4 100644 --- a/extensions/void/src/common/sendLLMMessage.ts +++ b/extensions/void/src/common/sendLLMMessage.ts @@ -64,9 +64,14 @@ const sendClaudeMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFinal const anthropic = new Anthropic({ apiKey: apiConfig.anthropic.apikey, dangerouslyAllowBrowser: true }); // defaults to process.env["ANTHROPIC_API_KEY"] + console.log('max_tokens:' + apiConfig.anthropic.maxTokens) + + let max_tokens = parseInt(apiConfig.anthropic.maxTokens) + if (isNaN(max_tokens)) { max_tokens = 4000 } // TODO make a default max_tokens + const stream = anthropic.messages.stream({ model: apiConfig.anthropic.model, - max_tokens: parseInt(apiConfig.anthropic.maxTokens), + max_tokens: max_tokens, messages: messages, }); @@ -221,6 +226,8 @@ const sendGreptileMsg: SendLLMMessageFnTypeInternal = ({ messages, onText, onFin export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ messages, onText, onFinalMessage, apiConfig }) => { if (!apiConfig) return { abort: () => { } } + console.log(`void: sending LLMMessage to ${apiConfig.whichApi}`) + const whichApi = apiConfig.whichApi if (whichApi === 'anthropic') { diff --git a/extensions/void/src/extension.ts b/extensions/void/src/extension.ts index 2d7e85a1..ab12b252 100644 --- a/extensions/void/src/extension.ts +++ b/extensions/void/src/extension.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import { WebviewMessage } from './shared_types'; import { CtrlKCodeLensProvider } from './CtrlKCodeLensProvider'; import { getDiffedLines } from './getDiffedLines'; -import { ApprovalCodeLensProvider } from './ApprovalCodeLensProvider'; +import { ApplyChangesProvider as DisplayChangesProvider } from './DisplayChangesProvider'; import { SidebarWebviewProvider } from './SidebarWebviewProvider'; import { ApiConfig } from './common/sendLLMMessage'; @@ -71,22 +71,23 @@ export function activate(context: vscode.ExtensionContext) { ); // 3. Show an approve/reject codelens above each change - const approvalCodeLensProvider = new ApprovalCodeLensProvider(); - context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', approvalCodeLensProvider)); + const displayChangesProvider = new DisplayChangesProvider(); + console.log(`void: Creating DisplayChangesProvider`) + context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', displayChangesProvider)); // 4. Add approve/reject commands - context.subscriptions.push(vscode.commands.registerCommand('void.approveDiff', async (params) => { - approvalCodeLensProvider.approveDiff(params) + context.subscriptions.push(vscode.commands.registerCommand('void.acceptDiff', async (params) => { + displayChangesProvider.acceptDiff(params) })); - context.subscriptions.push(vscode.commands.registerCommand('void.discardDiff', async (params) => { - approvalCodeLensProvider.discardDiff(params) + context.subscriptions.push(vscode.commands.registerCommand('void.rejectDiff', async (params) => { + displayChangesProvider.rejectDiff(params) })); context.subscriptions.push(vscode.commands.registerCommand('void.openSettings', async () => { vscode.commands.executeCommand('workbench.action.openSettings', '@ext:void.void'); })); - // 5. + // 5. Receive messages from sidebar webviewProvider.webview.then( webview => { @@ -112,16 +113,21 @@ export function activate(context: vscode.ExtensionContext) { // send contents to webview webview.postMessage({ type: 'files', files, } satisfies WebviewMessage) - } else if (m.type === 'applyCode') { + } else if (m.type === 'applyChanges') { const editor = vscode.window.activeTextEditor if (!editor) { vscode.window.showInformationMessage('No active editor!') return } - const oldContents = await readFileContentOfUri(editor.document.uri) - const suggestedEdits = getDiffedLines(oldContents, m.code) - await approvalCodeLensProvider.addNewApprovals(editor, suggestedEdits) + + const beforeCode = await readFileContentOfUri(editor.document.uri) + + // TODO change this to be animated + const suggestedEdits = getDiffedLines(beforeCode, m.code) + + // when changes have been created + await displayChangesProvider.addNewChanges(editor, suggestedEdits) } else if (m.type === 'getApiConfig') { diff --git a/extensions/void/src/getDiffedLines.ts b/extensions/void/src/getDiffedLines.ts index 56fa119e..7533b20e 100644 --- a/extensions/void/src/getDiffedLines.ts +++ b/extensions/void/src/getDiffedLines.ts @@ -2,16 +2,16 @@ import { diffLines, Change } from 'diff'; export type SuggestedEdit = { // start/end of current file - startLine: number; - endLine: number; + startLine: number, + endLine: number, // start/end of original file originalStartLine: number, originalEndLine: number, // original content (originalfile[originalStart...originalEnd]) - originalContent: string; - newContent: string; + beforeCode: string; + afterCode: string; } export function getDiffedLines(oldStr: string, newStr: string) { @@ -46,7 +46,7 @@ export function getDiffedLines(oldStr: string, newStr: string) { const originalEndLine = oldFileLineNum - 1 // don't include current line, the edit was up to this line but not including it const originalContent = oldStrLines.slice(originalStartLine, originalEndLine + 1).join('\n') - const replacement: SuggestedEdit = { startLine, endLine, newContent, originalStartLine, originalEndLine, originalContent } + const replacement: SuggestedEdit = { beforeCode: originalContent, afterCode: newContent, startLine, endLine, originalStartLine, originalEndLine, } replacements.push(replacement) streakStartInNewFile = undefined diff --git a/extensions/void/src/shared_types.ts b/extensions/void/src/shared_types.ts index 4d7c0dc9..81efbe71 100644 --- a/extensions/void/src/shared_types.ts +++ b/extensions/void/src/shared_types.ts @@ -13,7 +13,7 @@ type WebviewMessage = ( | { type: 'ctrl+l', selection: Selection } // user presses ctrl+l in the editor // sidebar -> editor - | { type: 'applyCode', code: string } // user clicks "apply" in the sidebar + | { type: 'applyChanges', code: string } // user clicks "apply" in the sidebar // sidebar -> editor | { type: 'requestFiles', filepaths: vscode.Uri[] } diff --git a/extensions/void/src/sidebar/MarkdownRender.tsx b/extensions/void/src/sidebar/MarkdownRender.tsx index e9cc2b96..ebc9a2a7 100644 --- a/extensions/void/src/sidebar/MarkdownRender.tsx +++ b/extensions/void/src/sidebar/MarkdownRender.tsx @@ -8,7 +8,7 @@ export const BlockCode = ({ text, disableApplyButton = false }: { text: string, return
{disableApplyButton ? null :
+ onClick={async () => { getVSCodeAPI().postMessage({ type: 'applyChanges', code: text }) }}>Apply
}
diff --git a/extensions/void/src/sidebar/Sidebar.tsx b/extensions/void/src/sidebar/Sidebar.tsx
index 4cba1ccb..5cb9f9c8 100644
--- a/extensions/void/src/sidebar/Sidebar.tsx
+++ b/extensions/void/src/sidebar/Sidebar.tsx
@@ -186,10 +186,13 @@ const Sidebar = () => {
 
 
 	const formRef = useRef(null)
+
 	const onSubmit = async (e: FormEvent) => {
 
+		console.log(`11111`)
 		e.preventDefault()
 		if (isLoading) return
+		console.log(`2222222`)
 
 		setIsLoading(true)
 		setInstructions('');
@@ -197,9 +200,14 @@ const Sidebar = () => {
 		setSelection(null)
 		setFiles([])
 
+
+		console.log(`AAAAAA`)
+
 		// request file content from vscode and await response
 		getVSCodeAPI().postMessage({ type: 'requestFiles', filepaths: files })
+		console.log(`BBBBB`)
 		const relevantFiles = await awaitVSCodeResponse('files')
+		console.log(`CCCCCC`)
 
 		// add message to chat history
 		const content = userInstructionsStr(instructions, relevantFiles.files, selection)
@@ -207,7 +215,8 @@ const Sidebar = () => {
 		const newHistoryElt: ChatMessage = { role: 'user', content, displayContent: instructions, selection, files }
 		setChatHistory(chatMessageHistory => [...chatMessageHistory, newHistoryElt])
 
-		// send message to claude
+		// send message to LLM
+		console.log(`DDDDD`)
 		let { abort } = sendLLMMessage({
 			messages: [...chatMessageHistory.map(m => ({ role: m.role, content: m.content })), { role: 'user', content }],
 			onText: (newText, fullText) => setMessageStream(fullText),
@@ -223,7 +232,9 @@ const Sidebar = () => {
 			},
 			apiConfig: apiConfig
 		})
+		console.log(`EEEEE`)
 		abortFnRef.current = abort
+		console.log(`FFFF`)
 
 	}
 
@@ -266,7 +277,7 @@ const Sidebar = () => {
 					{!selection?.selectionStr ? null
 						: (
 							
-
- )} + )}
res[] const awaiting: { [c in Command]: ((res: any) => void)[] } = { "ctrl+l": [], - "applyCode": [], + "applyChanges": [], "requestFiles": [], "files": [], "apiConfig": [],