From a738ca0b2c553616ea411a07d090e8cdf57ab699 Mon Sep 17 00:00:00 2001 From: mp Date: Fri, 25 Oct 2024 21:47:00 -0700 Subject: [PATCH] Reafactor DiffProvider for ctrl+k --- extensions/void/src/DiffProvider.ts | 461 ++++++++++++++++++ extensions/void/src/DisplayChangesProvider.ts | 314 ------------ extensions/void/src/common/ctrlL.ts | 9 +- .../void/src/common/readFileContentOfUri.ts | 6 + extensions/void/src/common/shared_types.ts | 29 +- extensions/void/src/extension.ts | 21 +- extensions/void/src/findDiffs.ts | 20 +- 7 files changed, 498 insertions(+), 362 deletions(-) create mode 100644 extensions/void/src/DiffProvider.ts delete mode 100644 extensions/void/src/DisplayChangesProvider.ts create mode 100644 extensions/void/src/common/readFileContentOfUri.ts diff --git a/extensions/void/src/DiffProvider.ts b/extensions/void/src/DiffProvider.ts new file mode 100644 index 00000000..0218984d --- /dev/null +++ b/extensions/void/src/DiffProvider.ts @@ -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 = new vscode.EventEmitter(); // signals a UI refresh on .fire() events + public readonly onDidChangeCodeLenses: vscode.Event = this._onDidChangeCodeLenses.event; + + // used internally by vscode + public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult { + 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, 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 = ` + + Hello World! + + `; + + } + + // 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() { + + } + + + +} + + +*/ \ No newline at end of file diff --git a/extensions/void/src/DisplayChangesProvider.ts b/extensions/void/src/DisplayChangesProvider.ts deleted file mode 100644 index e6159256..00000000 --- a/extensions/void/src/DisplayChangesProvider.ts +++ /dev/null @@ -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 = new vscode.EventEmitter(); // signals a UI refresh on .fire() events - public readonly onDidChangeCodeLenses: vscode.Event = this._onDidChangeCodeLenses.event; - - // used internally by vscode - public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult { - 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) - } -} diff --git a/extensions/void/src/common/ctrlL.ts b/extensions/void/src/common/ctrlL.ts index 02b0723d..393ba6b2 100644 --- a/extensions/void/src/common/ctrlL.ts +++ b/extensions/void/src/common/ctrlL.ts @@ -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 = ((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 diff --git a/extensions/void/src/common/readFileContentOfUri.ts b/extensions/void/src/common/readFileContentOfUri.ts new file mode 100644 index 00000000..d0f2e990 --- /dev/null +++ b/extensions/void/src/common/readFileContentOfUri.ts @@ -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 +} \ No newline at end of file diff --git a/extensions/void/src/common/shared_types.ts b/extensions/void/src/common/shared_types.ts index f312b128..10ad01dd 100644 --- a/extensions/void/src/common/shared_types.ts +++ b/extensions/void/src/common/shared_types.ts @@ -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, diff --git a/extensions/void/src/extension.ts b/extensions/void/src/extension.ts index f4a99736..c0f82a68 100644 --- a/extensions/void/src/extension.ts +++ b/extensions/void/src/extension.ts @@ -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 = { 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') { diff --git a/extensions/void/src/findDiffs.ts b/extensions/void/src/findDiffs.ts index d230a29e..a3d7f739 100644 --- a/extensions/void/src/findDiffs.ts +++ b/extensions/void/src/findDiffs.ts @@ -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), }); }