mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
Refactor and prepare to create addNewChanges
This commit is contained in:
parent
d64e9c93f5
commit
f4a407d80f
9 changed files with 74 additions and 44 deletions
|
|
@ -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"
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<void> = new vscode.EventEmitter<void>(); // signals a UI refresh on .fire() events
|
||||
|
||||
private _weAreEditing: boolean = false
|
||||
|
||||
// used internally by vscode
|
||||
private _onDidChangeCodeLenses: vscode.EventEmitter<void> = new vscode.EventEmitter<void>(); // signals a UI refresh on .fire() events
|
||||
public readonly onDidChangeCodeLenses: vscode.Event<void> = 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
|
||||
|
|
@ -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') {
|
||||
|
|
|
|||
|
|
@ -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') {
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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[] }
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export const BlockCode = ({ text, disableApplyButton = false }: { text: string,
|
|||
return <div className='py-1'>
|
||||
{disableApplyButton ? null : <div className='text-sm'>
|
||||
<button className='btn btn-secondary px-3 py-1 text-sm rounded-t-sm'
|
||||
onClick={async () => { getVSCodeAPI().postMessage({ type: 'applyCode', code: text }) }}>Apply</button>
|
||||
onClick={async () => { getVSCodeAPI().postMessage({ type: 'applyChanges', code: text }) }}>Apply</button>
|
||||
</div>}
|
||||
<div className={`overflow-x-auto rounded-sm text-vscode-editor-fg bg-vscode-editor-bg ${disableApplyButton ? '' : 'rounded-tl-none'}`}>
|
||||
<pre className='p-3'>
|
||||
|
|
|
|||
|
|
@ -186,10 +186,13 @@ const Sidebar = () => {
|
|||
|
||||
|
||||
const formRef = useRef<HTMLFormElement | null>(null)
|
||||
|
||||
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
|
||||
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
|
||||
: (
|
||||
<div className="relative">
|
||||
<button
|
||||
<button
|
||||
onClick={clearSelection}
|
||||
className="absolute top-2 right-2 text-white hover:text-gray-300 z-10"
|
||||
>
|
||||
|
|
@ -274,7 +285,7 @@ const Sidebar = () => {
|
|||
</button>
|
||||
<BlockCode text={selection.selectionStr} disableApplyButton={true} />
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
<form
|
||||
ref={formRef}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Command, WebviewMessage } from "../shared_types";
|
|||
// message -> res[]
|
||||
const awaiting: { [c in Command]: ((res: any) => void)[] } = {
|
||||
"ctrl+l": [],
|
||||
"applyCode": [],
|
||||
"applyChanges": [],
|
||||
"requestFiles": [],
|
||||
"files": [],
|
||||
"apiConfig": [],
|
||||
|
|
|
|||
Loading…
Reference in a new issue