mirror of
https://github.com/voideditor/void
synced 2026-05-23 17:38:23 +00:00
case analysis on insert, delete, and replace
This commit is contained in:
parent
315c5a65f2
commit
aec1434a0c
3 changed files with 70 additions and 79 deletions
|
|
@ -1,32 +1,18 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { SuggestedEdit } from './getDiffedLines';
|
||||
|
||||
export type SuggestedEdit = {
|
||||
// start/end of current file
|
||||
startLine: number;
|
||||
endLine: number;
|
||||
|
||||
// start/end of original file
|
||||
originalStartLine: number,
|
||||
originalEndLine: number,
|
||||
|
||||
// original content (originalfile[originalStart...originalEnd])
|
||||
originalContent: string;
|
||||
newContent: string;
|
||||
}
|
||||
|
||||
|
||||
// stored for later use
|
||||
// each diff on the user's screen right now
|
||||
type DiffType = {
|
||||
diffid: number, // unique id
|
||||
range: vscode.Range, // current range
|
||||
originalCode: string, // original code in case user wants to revert this
|
||||
diffid: number,
|
||||
lenses: vscode.CodeLens[],
|
||||
greenRange: vscode.Range,
|
||||
originalCode: string, // If a revert happens, we replace the greenRange with this content.
|
||||
}
|
||||
|
||||
// TODO in theory this should be disposed
|
||||
const greenDecoration = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: 'rgba(0 255 51 / 0.2)',
|
||||
isWholeLine: true, // after: { contentText: ' [original]', color: 'rgba(0 255 60 / 0.5)' } // hoverMessage: originalText // this applies to hovering over after:...
|
||||
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 {
|
||||
|
|
@ -49,6 +35,7 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider {
|
|||
return this._computedLensesOfDocument[docUriStr]
|
||||
}
|
||||
|
||||
// declared by us, registered with vscode.languages.registerCodeLensProvider()
|
||||
constructor() {
|
||||
// this acts as a useEffect. Every time text changes, clear the diffs in this editor
|
||||
vscode.workspace.onDidChangeTextDocument((e) => {
|
||||
|
|
@ -66,6 +53,13 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider {
|
|||
})
|
||||
}
|
||||
|
||||
// used by us only
|
||||
private refreshLenses = (editor: vscode.TextEditor, docUriStr: string) => {
|
||||
editor.setDecorations(greenDecoration, this._diffsOfDocument[docUriStr].map(diff => diff.greenRange)) // refresh highlighting
|
||||
this._computedLensesOfDocument[docUriStr] = this._diffsOfDocument[docUriStr].flatMap(diff => diff.lenses) // recompute _computedLensesOfDocument (can optimize this later)
|
||||
this._onDidChangeCodeLenses.fire() // fire event for vscode to refresh lenses
|
||||
}
|
||||
|
||||
// used by us only
|
||||
public async addNewApprovals(editor: vscode.TextEditor, suggestedEdits: SuggestedEdit[]) {
|
||||
|
||||
|
|
@ -77,62 +71,55 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider {
|
|||
if (!this._computedLensesOfDocument[docUriStr])
|
||||
this._computedLensesOfDocument[docUriStr] = []
|
||||
|
||||
// 0. create a diff for each suggested edit
|
||||
const diffs: DiffType[] = []
|
||||
for (let suggestedEdit of suggestedEdits) {
|
||||
|
||||
// TODO we need to account for this case (deletions)
|
||||
if (suggestedEdit.startLine > suggestedEdit.endLine)
|
||||
continue
|
||||
|
||||
const selectedRange = new vscode.Range(suggestedEdit.startLine, 0, suggestedEdit.endLine, Number.MAX_SAFE_INTEGER)
|
||||
|
||||
// if any other codelens intersects with the selection, ignore this edit
|
||||
for (let { range } of this._diffsOfDocument[docUriStr]) {
|
||||
if (range.intersection(selectedRange)) {
|
||||
vscode.window.showWarningMessage(`Changes have already been applied to this location. Please accept/reject them before applying new changes.`)
|
||||
return // do not make any edits
|
||||
}
|
||||
}
|
||||
|
||||
diffs.push({ diffid: this._diffidPool, range: selectedRange, originalCode: suggestedEdit.originalContent, lenses: [] })
|
||||
this._diffidPool += 1
|
||||
}
|
||||
|
||||
this._diffsOfDocument[docUriStr].push(...diffs);
|
||||
|
||||
// 1. apply each diff to the document
|
||||
// 1. convert suggested edits (which are described using line numbers) into actual edits (described using vscode.Range, vscode.Uri)
|
||||
// must do this before adding codelenses or highlighting so that codelens and highlights will apply to the fresh code and not the old code
|
||||
// apply changes in reverse order so additions don't push down the line numbers of the next edit
|
||||
let workspaceEdit = new vscode.WorkspaceEdit();
|
||||
for (let i = suggestedEdits.length - 1; i > -1; i--) {
|
||||
for (let i = suggestedEdits.length - 1; i > -1; i -= 1) {
|
||||
let suggestedEdit = suggestedEdits[i]
|
||||
const originalRange = new vscode.Range(suggestedEdit.originalStartLine, 0, suggestedEdit.originalEndLine, Number.MAX_SAFE_INTEGER)
|
||||
workspaceEdit.replace(docUri, originalRange, suggestedEdit.newContent);
|
||||
|
||||
let greenRange: vscode.Range
|
||||
|
||||
// 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
|
||||
greenRange = new vscode.Range(suggestedEdit.startLine, 0, suggestedEdit.endLine + 1, 0)
|
||||
}
|
||||
// DELETIONS
|
||||
else if (suggestedEdit.startLine > suggestedEdit.endLine) {
|
||||
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
|
||||
}
|
||||
// REPLACEMENTS
|
||||
else {
|
||||
const originalRange = new vscode.Range(suggestedEdit.originalStartLine, 0, suggestedEdit.originalEndLine, Number.MAX_SAFE_INTEGER)
|
||||
workspaceEdit.replace(docUri, originalRange, suggestedEdit.newContent)
|
||||
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,
|
||||
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 }] })
|
||||
]
|
||||
});
|
||||
this._diffidPool += 1
|
||||
}
|
||||
|
||||
this._weAreEditing = true
|
||||
await vscode.workspace.applyEdit(workspaceEdit)
|
||||
await vscode.workspace.save(docUri)
|
||||
this._weAreEditing = false
|
||||
|
||||
// 2. add the Yes/No codelenses
|
||||
for (let diff of diffs) {
|
||||
const { range, diffid, lenses: codeLenses } = diff
|
||||
|
||||
let approveLens = new vscode.CodeLens(range, { title: 'Accept', command: 'void.approveDiff', arguments: [{ diffid }] })
|
||||
let discardLens = new vscode.CodeLens(range, { title: 'Reject', command: 'void.discardDiff', arguments: [{ diffid }] })
|
||||
|
||||
codeLenses.push(discardLens, approveLens)
|
||||
}
|
||||
|
||||
// 3. apply green highlighting for each (+) diff
|
||||
editor.setDecorations(greenDecoration, this._diffsOfDocument[docUriStr].map(diff => diff.range))
|
||||
|
||||
// recompute _computedLensesOfDocument (can optimize this later)
|
||||
this._computedLensesOfDocument[docUriStr] = this._diffsOfDocument[docUriStr].flatMap(diff => diff.lenses)
|
||||
|
||||
// refresh
|
||||
this._onDidChangeCodeLenses.fire()
|
||||
this.refreshLenses(editor, docUriStr)
|
||||
|
||||
console.log('diffs after added:', this._diffsOfDocument[docUriStr])
|
||||
}
|
||||
|
|
@ -156,14 +143,8 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider {
|
|||
// remove this diff from the diffsOfDocument[docStr] (can change this behavior in future if add something like history)
|
||||
this._diffsOfDocument[docUriStr].splice(index, 1)
|
||||
|
||||
// clear the decoration in this diff's range
|
||||
editor.setDecorations(greenDecoration, this._diffsOfDocument[docUriStr].map(diff => diff.range))
|
||||
|
||||
// recompute _computedLensesOfDocument (can optimize this later)
|
||||
this._computedLensesOfDocument[docUriStr] = this._diffsOfDocument[docUriStr].flatMap(diff => diff.lenses)
|
||||
|
||||
// refresh
|
||||
this._onDidChangeCodeLenses.fire()
|
||||
this.refreshLenses(editor, docUriStr)
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -183,13 +164,13 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider {
|
|||
return
|
||||
}
|
||||
|
||||
const { range, lenses, originalCode } = this._diffsOfDocument[docUriStr][index] // do this before we splice and mess up index
|
||||
const { greenRange: range, lenses, originalCode } = this._diffsOfDocument[docUriStr][index] // do this before we splice and mess up index
|
||||
|
||||
// remove this diff from the diffsOfDocument[docStr] (can change this behavior in future if add something like history)
|
||||
this._diffsOfDocument[docUriStr].splice(index, 1)
|
||||
|
||||
// clear the decoration in this diffs range
|
||||
editor.setDecorations(greenDecoration, this._diffsOfDocument[docUriStr].map(diff => diff.range))
|
||||
editor.setDecorations(greenDecoration, this._diffsOfDocument[docUriStr].map(diff => diff.greenRange))
|
||||
|
||||
// REVERT THE CHANGE (this is the only part that's different from approveDiff)
|
||||
let workspaceEdit = new vscode.WorkspaceEdit();
|
||||
|
|
@ -199,10 +180,7 @@ export class ApprovalCodeLensProvider implements vscode.CodeLensProvider {
|
|||
await vscode.workspace.save(docUri)
|
||||
this._weAreEditing = false
|
||||
|
||||
// recompute _computedLensesOfDocument (can optimize this later)
|
||||
this._computedLensesOfDocument[docUriStr] = this._diffsOfDocument[docUriStr].flatMap(diff => diff.lenses)
|
||||
|
||||
// refresh
|
||||
this._onDidChangeCodeLenses.fire()
|
||||
this.refreshLenses(editor, docUriStr)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import * as vscode from 'vscode';
|
|||
import { WebviewMessage } from './shared_types';
|
||||
import { CtrlKCodeLensProvider } from './CtrlKCodeLensProvider';
|
||||
import { getDiffedLines } from './getDiffedLines';
|
||||
import { ApprovalCodeLensProvider, SuggestedEdit } from './ApprovalCodeLensProvider';
|
||||
import { ApprovalCodeLensProvider } from './ApprovalCodeLensProvider';
|
||||
import { SidebarWebviewProvider } from './SidebarWebviewProvider';
|
||||
import { ApiConfig } from './common/sendLLMMessage';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,18 @@
|
|||
import { diffLines, Change } from 'diff';
|
||||
import { SuggestedEdit } from './ApprovalCodeLensProvider';
|
||||
|
||||
export type SuggestedEdit = {
|
||||
// start/end of current file
|
||||
startLine: number;
|
||||
endLine: number;
|
||||
|
||||
// start/end of original file
|
||||
originalStartLine: number,
|
||||
originalEndLine: number,
|
||||
|
||||
// original content (originalfile[originalStart...originalEnd])
|
||||
originalContent: string;
|
||||
newContent: string;
|
||||
}
|
||||
|
||||
export function getDiffedLines(oldStr: string, newStr: string) {
|
||||
// an ordered list of every original line, line added to the new file, and line removed from the old file (order is unambiguous, think about it)
|
||||
|
|
|
|||
Loading…
Reference in a new issue