Reafactor DiffProvider for ctrl+k

This commit is contained in:
mp 2024-10-25 21:47:00 -07:00
parent 1b8e7f3643
commit a738ca0b2c
7 changed files with 498 additions and 362 deletions

View file

@ -0,0 +1,461 @@
import * as vscode from 'vscode';
import { findDiffs } from './findDiffs';
import { Diff, DiffArea, BaseDiff, } from './common/shared_types';
import { readFileContentOfUri } from './common/readFileContentOfUri';
// TODO in theory this should be disposed
const greenDecoration = vscode.window.createTextEditorDecorationType({
backgroundColor: 'rgba(0 255 51 / 0.2)',
isWholeLine: false, // after: { contentText: ' [original]', color: 'rgba(0 255 60 / 0.5)' } // hoverMessage: originalText // this applies to hovering over after:...
})
// responsible for displaying diffs and showing accept/reject buttons
export class DiffProvider implements vscode.CodeLensProvider {
private _originalFileOfDocument: { [docUriStr: string]: string } = {}
private _diffAreasOfDocument: { [docUriStr: string]: DiffArea[] } = {}
private _diffsOfDocument: { [docUriStr: string]: Diff[] } = {}
private _diffareaidPool = 0
private _diffidPool = 0
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;
// used internally by vscode
public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens[]> {
const docUriStr = document.uri.toString()
return this._diffsOfDocument[docUriStr]?.flatMap(diff => diff.lenses) ?? []
}
// declared by us, registered with vscode.languages.registerCodeLensProvider()
constructor() {
console.log('Creating DisplayChangesProvider')
// this acts as a useEffect every time text changes
vscode.workspace.onDidChangeTextDocument((e) => {
const editor = vscode.window.activeTextEditor
if (!editor) return
if (this._weAreEditing) return
const docUriStr = editor.document.uri.toString()
const changes = e.contentChanges.map(c => ({ startLine: c.range.start.line, endLine: c.range.end.line, text: c.text, }))
// on user change, grow/shrink/merge/delete diff areas
this.updateDiffAreasBasedOnChanges(docUriStr, changes, 'currentFile')
// refresh the diffAreas
this.refreshStyles(docUriStr)
})
}
// used by us only
public createDiffArea(uri: vscode.Uri, diffArea: Omit<DiffArea, 'diffareaid'>, originalFile: string) {
const uriStr = uri.toString()
this._originalFileOfDocument[uriStr] = originalFile
// make sure array is defined
if (!this._diffAreasOfDocument[uriStr]) this._diffAreasOfDocument[uriStr] = []
// remove all diffAreas that the new `diffArea` is overlapping with
this._diffAreasOfDocument[uriStr] = this._diffAreasOfDocument[uriStr].filter(da => {
const noOverlap = da.startLine > diffArea.endLine || da.endLine < diffArea.startLine
if (!noOverlap) return false
return true
})
// add `diffArea` to storage
this._diffAreasOfDocument[uriStr].push({
...diffArea,
diffareaid: this._diffareaidPool
})
this._diffareaidPool += 1
}
// used by us only
public updateDiffAreasBasedOnChanges(docUriStr: string, changes: { text: string, startLine: number, endLine: number }[], changesTo: 'originalFile' | 'currentFile') {
const diffAreas = this._diffAreasOfDocument[docUriStr] || []
let endName
let startName
if (changesTo === 'originalFile') {
endName = 'originalEndLine' as const
startName = 'originalStartLine' as const
} else {
endName = 'endLine' as const
startName = 'startLine' as const
}
for (const change of changes) {
// here, `change.range` is the range of the original file that gets replaced with `change.text`
// compute net number of newlines lines that were added/removed
const numNewLines = (change.text.match(/\n/g) || []).length
const numLineDeletions = change.endLine - change.startLine
const deltaNewlines = numNewLines - numLineDeletions
// compute overlap with each diffArea and shrink/elongate the diffArea accordingly
for (const diffArea of diffAreas) {
// if the change is fully within the diffArea, elongate it by the delta amount of newlines
if (change.startLine >= diffArea[startName] && change.endLine <= diffArea[endName]) {
diffArea[endName] += deltaNewlines
}
// check if the `diffArea` was fully deleted and remove it if so
if (diffArea[startName] > diffArea[endName]) {
//remove it
const index = diffAreas.findIndex(da => da === diffArea)
diffAreas.splice(index, 1)
}
// TODO handle other cases where eg. the change overlaps many diffAreas
}
// if a diffArea is below the last character of the change, shift the diffArea up/down by the delta amount of newlines
for (const diffArea of diffAreas) {
if (diffArea[startName] > change.endLine) {
diffArea[startName] += deltaNewlines
diffArea[endName] += deltaNewlines
}
}
// TODO merge any diffAreas if they overlap with each other as a result from the shift
}
}
// used by us only
public refreshStyles(docUriStr: string) {
const editor = vscode.window.activeTextEditor // TODO the editor should be that of `docUri` and not necessarily the current editor
if (!editor) {
console.log('Error: No active editor!')
return;
}
const originalFile = this._originalFileOfDocument[docUriStr]
if (!originalFile) {
console.log('Error: No original file!')
return;
}
const diffAreas = this._diffAreasOfDocument[docUriStr] || []
// reset all diffs (we update them below)
this._diffsOfDocument[docUriStr] = []
// for each diffArea
for (const diffArea of diffAreas) {
// get code inside of diffArea
const originalCode = originalFile.split('\n').slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
const currentCode = editor.document.getText(new vscode.Range(diffArea.startLine, 0, diffArea.endLine, Number.MAX_SAFE_INTEGER)).replace(/\r\n/g, '\n')
// compute the diffs
const diffs = findDiffs(originalCode, currentCode)
// add the diffs to `this._diffsOfDocument[docUriStr]`
this.createDiffs(editor.document.uri, diffs, diffArea)
// print diffs
console.log('!ORIGINAL FILE:', JSON.stringify(originalFile))
console.log('!NEW FILE :', JSON.stringify(editor.document.getText().replace(/\r\n/g, '\n')))
console.log('!AREA originalCode:', JSON.stringify(originalCode))
console.log('!AREA currentCode :', JSON.stringify(currentCode))
for (const diff of this._diffsOfDocument[docUriStr]) {
console.log('------------')
console.log('originalCode:', JSON.stringify(diff.originalCode))
console.log('currentCode:', JSON.stringify(diff.code))
console.log('originalRange:', diff.originalRange.start.line, diff.originalRange.end.line,)
console.log('currentRange:', diff.range.start.line, diff.range.end.line,)
}
console.log('DiffRepr: ', diffs.map(diff => diff.repr).join('\n'))
}
// update green highlighting
editor.setDecorations(
greenDecoration,
(this._diffsOfDocument[docUriStr]
.filter(diff => diff.range !== undefined)
.map(diff => diff.range)
)
);
// TODO update red highlighting
// this._diffsOfDocument[docUriStr].map(diff => diff.deletedCode)
// update code lenses
this._onDidChangeCodeLenses.fire()
}
// used by us only
public createDiffs(docUri: vscode.Uri, diffs: BaseDiff[], diffArea: DiffArea) {
const docUriStr = docUri.toString()
// if no diffs, set diffs to []
if (!this._diffsOfDocument[docUriStr])
this._diffsOfDocument[docUriStr] = []
// add each diff and its codelens to the document
for (let i = diffs.length - 1; i > -1; i -= 1) {
let suggestedDiff = diffs[i]
this._diffsOfDocument[docUriStr].push({
...suggestedDiff,
diffid: this._diffidPool,
// originalCode: suggestedDiff.deletedText,
lenses: [
new vscode.CodeLens(suggestedDiff.range, { title: 'Accept', command: 'void.acceptDiff', arguments: [{ diffid: this._diffidPool, diffareaid: diffArea.diffareaid }] }),
new vscode.CodeLens(suggestedDiff.range, { title: 'Reject', command: 'void.rejectDiff', arguments: [{ diffid: this._diffidPool, diffareaid: diffArea.diffareaid }] })
]
});
this._diffidPool += 1
}
}
// called on void.acceptDiff
public async acceptDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) {
const editor = vscode.window.activeTextEditor
if (!editor)
return
const docUriStr = editor.document.uri.toString()
const diffIdx = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid);
if (diffIdx === -1) { console.error('Error: DiffID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
const diffareaIdx = this._diffAreasOfDocument[docUriStr].findIndex(diff => diff.diffareaid === diffareaid);
if (diffareaIdx === -1) { console.error('Error: DiffAreaID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
const diff = this._diffsOfDocument[docUriStr][diffIdx]
const originalFile = this._originalFileOfDocument[docUriStr]
const currentFile = await readFileContentOfUri(editor.document.uri)
// Fixed: Handle newlines properly by splitting into lines and joining with proper newlines
const originalLines = originalFile.split('\n');
const currentLines = currentFile.split('\n');
// Get the changed lines from current file
const changedLines = currentLines.slice(diff.range.start.line, diff.range.end.line + 1);
// Create new original file content by replacing the affected lines
const newOriginalLines = [
...originalLines.slice(0, diff.originalRange.start.line),
...changedLines,
...originalLines.slice(diff.originalRange.end.line + 1)
];
this._originalFileOfDocument[docUriStr] = newOriginalLines.join('\n');
// Update diff areas based on the change
this.updateDiffAreasBasedOnChanges(docUriStr, [{
text: changedLines.join('\n'),
startLine: diff.originalRange.start.line,
endLine: diff.originalRange.end.line
}], 'originalFile')
// Check if diffArea should be removed
const diffArea = this._diffAreasOfDocument[docUriStr][diffareaIdx]
const currentArea = currentLines.slice(diffArea.startLine, diffArea.endLine + 1).join('\n')
const originalArea = newOriginalLines.slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
console.log('ACCEPT change', changedLines.join('\n'), diff.originalRange.start.line, diff.originalRange.end.line)
console.log('ACCEPT area lines', diffArea.startLine, diffArea.endLine, diffArea.originalStartLine, diffArea.originalEndLine)
console.log('ACCEPT currentArea', currentArea)
console.log('ACCEPT originalArea', originalArea)
if (originalArea === currentArea) {
const index = this._diffAreasOfDocument[docUriStr].findIndex(da => da.diffareaid === diffArea.diffareaid)
this._diffAreasOfDocument[docUriStr].splice(index, 1)
}
this.refreshStyles(docUriStr)
}
// called on void.rejectDiff
public async rejectDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) {
const editor = vscode.window.activeTextEditor
if (!editor)
return
const docUriStr = editor.document.uri.toString()
const diffIdx = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid);
if (diffIdx === -1) { console.error('Error: DiffID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
const diffareaIdx = this._diffAreasOfDocument[docUriStr].findIndex(diff => diff.diffareaid === diffareaid);
if (diffareaIdx === -1) { console.error('Error: DiffAreaID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
const diff = this._diffsOfDocument[docUriStr][diffIdx]
// Apply the rejection by replacing with original code
const workspaceEdit = new vscode.WorkspaceEdit();
workspaceEdit.replace(editor.document.uri, diff.range, diff.originalCode)
this._weAreEditing = true
await vscode.workspace.applyEdit(workspaceEdit)
this._weAreEditing = false
// Check if diffArea should be removed
const originalFile = this._originalFileOfDocument[docUriStr]
const currentFile = await readFileContentOfUri(editor.document.uri)
const diffArea = this._diffAreasOfDocument[docUriStr][diffareaIdx]
const currentLines = currentFile.split('\n');
const originalLines = originalFile.split('\n');
const currentArea = currentLines.slice(diffArea.startLine, diffArea.endLine + 1).join('\n')
const originalArea = originalLines.slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
console.log('REJECT diff lines', diff.originalRange.start.line, diff.originalRange.end.line)
console.log('REJECT area lines', diffArea.startLine, diffArea.endLine, diffArea.originalStartLine, diffArea.originalEndLine)
console.log('REJECT currentArea', currentArea)
console.log('REJECT originalArea', originalArea)
if (originalArea === currentArea) {
const index = this._diffAreasOfDocument[docUriStr].findIndex(da => da.diffareaid === diffArea.diffareaid)
this._diffAreasOfDocument[docUriStr].splice(index, 1)
}
this.refreshStyles(docUriStr)
}
}
/*
import * as vscode from 'vscode';
import { SuggestedEdit } from './findDiffs';
const greenDecoration = vscode.window.createTextEditorDecorationType({
backgroundColor: 'rgba(0 255 51 / 0.2)',
isWholeLine: false, // after: { contentText: ' [original]', color: 'rgba(0 255 60 / 0.5)' } // hoverMessage: originalText // this applies to hovering over after:...
})
export class DiffProvider {
originalCodeOfDocument: { [docUri: string]: string }
diffsOfDocument: {
[docUri: string]: {
startLine,
startCol,
endLine,
endCol,
originalText,
inset,
diffid,
}
}
// sweep
currentLine: { [docUri: string]: undefined | number }
weAreEditing: boolean = false
constructor() {
vscode.workspace.onDidChangeTextDocument((e) => {
// on user change, grow/shrink/merge/delete diff AREAS
// you dont have to do anything to the diffs here bc they all get recomputed in refresh()
// user changes only get highlighted if theyre in a diffarea
// go thru all diff areas and adjust line numbers based on the user's change
this.refreshStyles(e.document.uri.toString())
})
}
// refreshes styles on page
refreshStyles(docUriStr: string) {
if (this.weAreEditing) return
// recompute all diffs on the page
// run inset.dispose() on all diffs
// original and current code -> diffs
// originalCodeOfDocument[docUriStr]
// create new diffs
const inset = vscode.window.createWebviewTextEditorInset(editor, lineStart, height, {})
inset.webview.html = `
<html>
<body style="pointer-events:none;">Hello World!</body>
</html>
`;
}
// called on void.acceptDiff
public async acceptDiff({ diffid }: { diffid: number }) {
// update original based on the diff
// refresh()
}
// called on void.rejectDiff
public async rejectDiff({ diffid }: { diffid: number }) {
// get diffs[diffid]
// revert current file based on diff
// refresh()
}
// sweep
initializeSweep({ startLine }) {
// reject all diffs on the page
// store original code
// currentLine=start of sweep
}
onUpdateSweep(addedText) {
// update final
// refresh() ?
// currentLine += number of newlines in addedText
}
onAbortSweep() {
}
}
*/

View file

@ -1,314 +0,0 @@
import * as vscode from 'vscode';
import { findDiffs } from './findDiffs';
import { Diff, BaseDiffArea, BaseDiff, DiffArea } from './common/shared_types';
// TODO in theory this should be disposed
const greenDecoration = vscode.window.createTextEditorDecorationType({
backgroundColor: 'rgba(0 255 51 / 0.2)',
isWholeLine: false, // after: { contentText: ' [original]', color: 'rgba(0 255 60 / 0.5)' } // hoverMessage: originalText // this applies to hovering over after:...
})
// responsible for displaying diffs and showing accept/reject buttons
export class DisplayChangesProvider implements vscode.CodeLensProvider {
private _diffAreasOfDocument: { [docUriStr: string]: DiffArea[] } = {}
private _diffsOfDocument: { [docUriStr: string]: Diff[] } = {}
private _diffareaidPool = 0
private _diffidPool = 0
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;
// used internally by vscode
public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens[]> {
const docUriStr = document.uri.toString()
return this._diffsOfDocument[docUriStr]?.flatMap(diff => diff.lenses) ?? []
}
// declared by us, registered with vscode.languages.registerCodeLensProvider()
constructor() {
console.log('Creating DisplayChangesProvider')
// this acts as a useEffect. Every time text changes, run this
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()
const diffAreas = this._diffAreasOfDocument[docUriStr] || []
// loop through each change
for (const change of e.contentChanges) {
// here, `change.range` is the range of the original file that gets replaced with `change.text`
// compute net number of newlines lines that were added/removed
const numNewLines = (change.text.match(/\n/g) || []).length
const numLineDeletions = change.range.end.line - change.range.start.line
const deltaNewlines = numNewLines - numLineDeletions
// compute overlap with each diffArea and shrink/elongate the diffArea accordingly
for (const diffArea of diffAreas) {
// if the change is fully within the diffArea, elongate it by the delta amount of newlines
if (change.range.start.line >= diffArea.startLine && change.range.end.line <= diffArea.endLine) {
diffArea.endLine += deltaNewlines
}
// check if the `diffArea` was fully deleted and remove it if so
if (diffArea.startLine > diffArea.endLine) {
//remove it
const index = diffAreas.findIndex(da => da === diffArea)
diffAreas.splice(index, 1)
}
// TODO handle other cases where eg. the change overlaps many diffAreas
}
// if a diffArea is below the last character of the change, shift the diffArea up/down by the delta amount of newlines
for (const diffArea of diffAreas) {
if (diffArea.startLine > change.range.end.line) {
diffArea.startLine += deltaNewlines
diffArea.endLine += deltaNewlines
}
}
// TODO merge any diffAreas if they overlap with each other as a result from the shift
}
// refresh the diffAreas
this.refreshDiffAreas(docUri)
})
}
// used by us only
public addDiffArea(uri: vscode.Uri, diffArea: BaseDiffArea) {
const uriStr = uri.toString()
// make sure array is defined
if (!this._diffAreasOfDocument[uriStr])
this._diffAreasOfDocument[uriStr] = []
// remove all diffAreas that the new `diffArea` is overlapping with
this._diffAreasOfDocument[uriStr] = this._diffAreasOfDocument[uriStr].filter(da => {
const noOverlap = da.startLine > diffArea.endLine || da.endLine < diffArea.startLine
if (!noOverlap) return false
return true
})
// add `diffArea` to storage
this._diffAreasOfDocument[uriStr].push({
...diffArea,
diffareaid: this._diffareaidPool
})
this._diffareaidPool += 1
}
// used by us only
public refreshDiffAreas(docUri: vscode.Uri) {
const editor = vscode.window.activeTextEditor // TODO the editor should be that of `docUri` and not necessarily the current editor
if (!editor) {
console.log('Error: No active editor!')
return;
}
const docUriStr = docUri.toString()
const diffAreas = this._diffAreasOfDocument[docUriStr] || []
// reset all diffs (we update them below)
this._diffsOfDocument[docUriStr] = []
// for each diffArea
for (const diffArea of diffAreas) {
// get code inside of diffArea
const currentCode = editor.document.getText(new vscode.Range(diffArea.startLine, 0, diffArea.endLine, Number.MAX_SAFE_INTEGER)).replace(/\r\n/g, '\n')
// compute the diffs
const diffs = findDiffs(diffArea.originalCode, currentCode)
// add the diffs to `this._diffsOfDocument[docUriStr]`
this.addDiffs(editor.document.uri, diffs, diffArea)
// // print diffs
console.log('!CodeBefore:', JSON.stringify(diffArea.originalCode))
console.log('!CodeAfter:', JSON.stringify(currentCode))
console.log('DiffRepr: ', diffs.map(diff => diff.code).join('\n'))
for (const diff of this._diffsOfDocument[docUriStr]) {
console.log('------------')
console.log('deletedCode:', JSON.stringify(diff.deletedCode))
console.log('insertedCode:', JSON.stringify(diff.insertedCode))
console.log('deletedRange:', diff.deletedRange.start.line, diff.deletedRange.end.line,)
console.log('insertedRange:', diff.insertedRange.start.line, diff.insertedRange.end.line,)
}
}
// update green highlighting
editor.setDecorations(
greenDecoration,
(this._diffsOfDocument[docUriStr]
.filter(diff => diff.insertedRange !== undefined)
.map(diff => diff.insertedRange)
)
);
// TODO update red highlighting
// this._diffsOfDocument[docUriStr].map(diff => diff.deletedCode)
// update code lenses
this._onDidChangeCodeLenses.fire()
}
// used by us only
public addDiffs(docUri: vscode.Uri, diffs: BaseDiff[], diffArea: DiffArea) {
const docUriStr = docUri.toString()
// if no diffs, set diffs to []
if (!this._diffsOfDocument[docUriStr])
this._diffsOfDocument[docUriStr] = []
// add each diff and its codelens to the document
for (let i = diffs.length - 1; i > -1; i -= 1) {
let suggestedDiff = diffs[i]
this._diffsOfDocument[docUriStr].push({
...suggestedDiff,
diffid: this._diffidPool,
// originalCode: suggestedDiff.deletedText,
lenses: [
new vscode.CodeLens(suggestedDiff.insertedRange, { title: 'Accept', command: 'void.acceptDiff', arguments: [{ diffid: this._diffidPool, diffareaid: diffArea.diffareaid }] }),
new vscode.CodeLens(suggestedDiff.insertedRange, { title: 'Reject', command: 'void.rejectDiff', arguments: [{ diffid: this._diffidPool, diffareaid: diffArea.diffareaid }] })
]
});
this._diffidPool += 1
}
}
// called on void.acceptDiff
public async acceptDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) {
const editor = vscode.window.activeTextEditor
if (!editor)
return
// get document uri
const docUri = editor.document.uri
const docUriStr = docUri.toString()
// get relevant diff
// TODO speed up with hashmap
const diffIdx = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid);
if (diffIdx === -1) {
console.error('Error: DiffID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return;
}
// get relevant diffArea
const diffareaIdx = this._diffAreasOfDocument[docUriStr].findIndex(diff => diff.diffareaid === diffareaid);
if (diffareaIdx === -1) {
console.error('Error: DiffAreaID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return;
}
const diff = this._diffsOfDocument[docUriStr][diffIdx]
const diffArea = this._diffAreasOfDocument[docUriStr][diffareaIdx]
// replace `originalCode[diff.deletedRange]` with diff.insertedCode
// TODO add a history event to undo this change
const originalLines = diffArea.originalCode.split('\n');
const relativeStart = diff.deletedRange.start.line - diffArea.originalStartLine
const relativeEnd = diff.deletedRange.end.line - diffArea.originalStartLine
diffArea.originalCode = [
...originalLines.slice(0, relativeStart), // lines before the deleted range
...diff.insertedCode.split('\n'), // inserted lines
...originalLines.slice(relativeEnd + 1) // lines after the deleted range
].join('\n')
// if the diffArea has no changes, remove it
const currentDiffAreaCode = editor.document.getText()
.replace(/\r\n/g, '\n')
.split('\n')
.slice(diffArea.startLine, diffArea.endLine + 1)
.join('\n')
if (diffArea.originalCode === currentDiffAreaCode) { // if the currentDiffAreaCode === diffArea.originalCode, remove the diffArea
const index = this._diffAreasOfDocument[docUriStr].findIndex(da => da.diffareaid === diffArea.diffareaid)
this._diffAreasOfDocument[docUriStr].splice(index, 1)
}
// refresh the diff area
this.refreshDiffAreas(docUri)
}
// called on void.rejectDiff
public async rejectDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) {
const editor = vscode.window.activeTextEditor
if (!editor)
return
// get document uri
const docUri = editor.document.uri
const docUriStr = docUri.toString()
// get relevant diff
// TODO speed up with hashmap
const diffIdx = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid);
if (diffIdx === -1) {
console.error('Error: DiffID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return;
}
// get relevant diffArea
const diffareaIdx = this._diffAreasOfDocument[docUriStr].findIndex(diff => diff.diffareaid === diffareaid);
if (diffareaIdx === -1) {
console.error('Error: DiffAreaID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return;
}
const diff = this._diffsOfDocument[docUriStr][diffIdx]
const diffArea = this._diffAreasOfDocument[docUriStr][diffareaIdx]
// replace `editorCode[diff.insertedRange]` with diff.deletedCode
const workspaceEdit = new vscode.WorkspaceEdit();
workspaceEdit.replace(docUri, diff.insertedRange, diff.deletedCode)
this._weAreEditing = true
await vscode.workspace.applyEdit(workspaceEdit)
this._weAreEditing = false
// if the diffArea has no changes, remove it
const currentDiffAreaCode = editor.document.getText()
.replace(/\r\n/g, '\n')
.split('\n')
.slice(diffArea.startLine, diffArea.endLine + 1)
.join('\n')
if (diffArea.originalCode === currentDiffAreaCode) { // if the currentDiffAreaCode === diffArea.originalCode, remove the diffArea
const index = this._diffAreasOfDocument[docUriStr].findIndex(da => da.diffareaid === diffArea.diffareaid)
this._diffAreasOfDocument[docUriStr].splice(index, 1)
}
// refresh the diff area
this.refreshDiffAreas(docUri)
}
}

View file

@ -4,11 +4,8 @@ import { VoidConfig } from '../sidebar/contextForConfig';
import { findDiffs } from '../findDiffs';
import { searchDiffChunkInstructions, writeFileWithDiffInstructions } from './systemPrompts';
import { throttle } from 'lodash';
import { readFileContentOfUri } from './readFileContentOfUri';
const readFileContentOfUri = async (uri: vscode.Uri) => {
return Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf8')
.replace(/\r\n/g, '\n') // replace windows \r\n with \n
}
type Res<T> = ((value: T) => void)
const THRTOTLE_TIME = 100 // minimum time between edits
@ -85,8 +82,8 @@ ${completedStr}
// diff `originalFileStr` and `newFileStr`
const diffs = findDiffs(oldFileStr, fullCompletedStr)
const lastDiff = diffs[diffs.length - 1]
const oldLineAfterLastDiff = lastDiff.deletedRange.end.line + 1
const newLineAfterLastDiff = lastDiff.insertedRange.end.line + 1
const oldLineAfterLastDiff = lastDiff.originalRange.end.line + 1
const newLineAfterLastDiff = lastDiff.range.end.line + 1
// check if we've generated a diff
const didGenerateDiff = newLineAfterLastDiff > next

View file

@ -0,0 +1,6 @@
import * as vscode from 'vscode'
export const readFileContentOfUri = async (uri: vscode.Uri) => {
return Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf8')
.replace(/\r\n/g, '\n') // replace windows \r\n with \n
}

View file

@ -10,26 +10,19 @@ type CodeSelection = { selectionStr: string, selectionRange: vscode.Range, fileP
type File = { filepath: vscode.Uri, content: string }
// an area that is currently being diffed
type BaseDiffArea = {
// use `startLine` and `endLine` instead of `range` for mutibility
// bounds are relative to the file, inclusive
startLine: number;
endLine: number;
originalStartLine: number,
originalEndLine: number,
originalCode: string, // the original chunk of code (not necessarily the whole file)
// `newCode: string,` is not included because it is the code in the actual file, `document.text()[startline: endLine + 1]`
type DiffArea = {
diffareaid: number,
startLine: number, endLine: number,
originalStartLine: number, originalEndLine: number,
}
type DiffArea = BaseDiffArea & { diffareaid: number }
// the return type of diff creator
type BaseDiff = {
code: string; // representation of the diff in text
deletedRange: vscode.Range; // relative to the original file, inclusive
insertedRange: vscode.Range;
deletedCode: string; // relative to the new file, inclusive
insertedCode: string;
repr: string; // representation of the diff in text
originalRange: vscode.Range;
range: vscode.Range;
originalCode: string;
code: string;
}
// each diff on the user's screen
@ -92,8 +85,8 @@ type ChatMessage =
}
export {
BaseDiff, BaseDiffArea,
Diff, DiffArea,
BaseDiff, Diff,
DiffArea,
CodeSelection,
File,
MessageFromSidebar,

View file

@ -1,10 +1,11 @@
import * as vscode from 'vscode';
import { DisplayChangesProvider } from './DisplayChangesProvider';
import { BaseDiffArea, ChatThreads, MessageFromSidebar, MessageToSidebar } from './common/shared_types';
import { DiffProvider } from './DiffProvider';
import { DiffArea, ChatThreads, MessageFromSidebar, MessageToSidebar } from './common/shared_types';
import { SidebarWebviewProvider } from './SidebarWebviewProvider';
import { v4 as uuidv4 } from 'uuid'
import { applyDiffLazily } from './common/ctrlL';
import { getVoidConfig } from './sidebar/contextForConfig';
import { readFileContentOfUri } from './common/readFileContentOfUri';
// this comes from vscode.proposed.editorInsets.d.ts
declare module 'vscode' {
@ -21,13 +22,6 @@ declare module 'vscode' {
}
}
const readFileContentOfUri = async (uri: vscode.Uri) => {
return Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf8')
.replace(/\r\n/g, '\n') // replace windows \r\n with \n
}
const roundRangeToLines = (selection: vscode.Selection) => {
return new vscode.Range(selection.start.line, 0, selection.end.line, Number.MAX_SAFE_INTEGER)
}
@ -97,7 +91,7 @@ export function activate(context: vscode.ExtensionContext) {
);
// 3. Show an approve/reject codelens above each change
const displayChangesProvider = new DisplayChangesProvider();
const displayChangesProvider = new DiffProvider();
context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', displayChangesProvider));
// 4. Add approve/reject commands
@ -147,14 +141,13 @@ export function activate(context: vscode.ExtensionContext) {
// create an area to show diffs
const diffArea: BaseDiffArea = {
const diffArea: Omit<DiffArea, 'diffareaid'> = {
startLine: 0, // in ctrl+L the start and end lines are the full document
endLine: editor.document.lineCount,
originalStartLine: 0,
originalEndLine: editor.document.lineCount,
originalCode: await readFileContentOfUri(editor.document.uri),
}
displayChangesProvider.addDiffArea(editor.document.uri, diffArea)
displayChangesProvider.createDiffArea(editor.document.uri, diffArea, await readFileContentOfUri(editor.document.uri))
// write new code `m.code` to the document
@ -178,7 +171,7 @@ export function activate(context: vscode.ExtensionContext) {
// });
// rediff the changes based on the diffAreas
displayChangesProvider.refreshDiffAreas(editor.document.uri)
displayChangesProvider.refreshStyles(editor.document.uri.toString())
}
else if (m.type === 'getPartialVoidConfig') {

View file

@ -146,11 +146,11 @@ export const findDiffs = (oldText: string, newText: string): BaseDiff[] => {
// add pending block to the blocks array
if (insertedBlock.length > 0 || deletedBlock.length > 0) {
blocks.push({
code: reprBlock.join(''),
deletedCode: deletedBlock.join(''),
insertedCode: insertedBlock.join(''),
deletedRange: new vscode.Range(deletedStart, 0, oldFileLine, Number.MAX_SAFE_INTEGER),
insertedRange: new vscode.Range(insertedStart, 0, newFileLine, Number.MAX_SAFE_INTEGER),
repr: reprBlock.join(''),
originalCode: deletedBlock.join(''),
code: insertedBlock.join(''),
originalRange: new vscode.Range(deletedStart, 0, oldFileLine, Number.MAX_SAFE_INTEGER),
range: new vscode.Range(insertedStart, 0, newFileLine, Number.MAX_SAFE_INTEGER),
});
}
@ -170,11 +170,11 @@ export const findDiffs = (oldText: string, newText: string): BaseDiff[] => {
// Add any remaining blocks after the loop ends
if (insertedBlock.length > 0 || deletedBlock.length > 0) {
blocks.push({
code: reprBlock.join(''),
deletedCode: deletedBlock.join(''),
insertedCode: insertedBlock.join(''),
deletedRange: new vscode.Range(deletedStart, 0, oldFileLine, Number.MAX_SAFE_INTEGER),
insertedRange: new vscode.Range(insertedStart, 0, newFileLine, Number.MAX_SAFE_INTEGER),
repr: reprBlock.join(''),
originalCode: deletedBlock.join(''),
code: insertedBlock.join(''),
originalRange: new vscode.Range(deletedStart, 0, oldFileLine, Number.MAX_SAFE_INTEGER),
range: new vscode.Range(insertedStart, 0, newFileLine, Number.MAX_SAFE_INTEGER),
});
}