From d4a2ac2796483732419e3759e9418e0a0f5e3870 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 25 Oct 2024 01:22:10 -0700 Subject: [PATCH] draft --- extensions/void/src/Diff.ts | 8 - extensions/void/src/DiffProvider.ts | 115 ++++++++++++ extensions/void/src/extension.ts | 30 ++-- extensions/void/src/findDiffs.ts | 266 ++++++++++++++++++++-------- 4 files changed, 323 insertions(+), 96 deletions(-) delete mode 100644 extensions/void/src/Diff.ts create mode 100644 extensions/void/src/DiffProvider.ts diff --git a/extensions/void/src/Diff.ts b/extensions/void/src/Diff.ts deleted file mode 100644 index b26d380c..00000000 --- a/extensions/void/src/Diff.ts +++ /dev/null @@ -1,8 +0,0 @@ - -class Diff { - - constructor(){ - - } - -} \ No newline at end of file diff --git a/extensions/void/src/DiffProvider.ts b/extensions/void/src/DiffProvider.ts new file mode 100644 index 00000000..dfa4a9ad --- /dev/null +++ b/extensions/void/src/DiffProvider.ts @@ -0,0 +1,115 @@ +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() { + + } + + + +} diff --git a/extensions/void/src/extension.ts b/extensions/void/src/extension.ts index 1b63ee5d..fe7f7bff 100644 --- a/extensions/void/src/extension.ts +++ b/extensions/void/src/extension.ts @@ -1,10 +1,12 @@ import * as vscode from 'vscode'; -import { DisplayChangesProvider } from './DisplayChangesProvider'; -import { BaseDiffArea, ChatThreads, MessageFromSidebar, MessageToSidebar } from './common/shared_types'; +import { ApprovalCodeLensProvider } from './ApprovalCodeLensProvider'; +import { 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 { DiffProvider } from './DiffProvider'; +// import { getVoidConfig } from './sidebar/contextForConfig'; // this comes from vscode.proposed.editorInsets.d.ts declare module 'vscode' { @@ -88,7 +90,8 @@ export function activate(context: vscode.ExtensionContext) { ); // 3. Show an approve/reject - const displayChangesProvider = new DisplayChangesProvider(); + const displayChangesProvider = new DiffProvider(); + // context.subscriptions.push(vscode.languages.registerCodeLensProvider('*', displayChangesProvider)); // 4. Add approve/reject commands context.subscriptions.push(vscode.commands.registerCommand('void.acceptDiff', async (params) => { @@ -135,16 +138,15 @@ export function activate(context: vscode.ExtensionContext) { return } - - // create an area to show diffs - const diffArea: BaseDiffArea = { - 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) + // // create an area to show diffs + // const diffArea = { + // 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) // write new code `m.code` to the document @@ -168,7 +170,7 @@ export function activate(context: vscode.ExtensionContext) { // }); // rediff the changes based on the diffAreas - displayChangesProvider.refreshDiffAreas(editor.document.uri) + displayChangesProvider.refreshLenses(editor, 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..418e2662 100644 --- a/extensions/void/src/findDiffs.ts +++ b/extensions/void/src/findDiffs.ts @@ -1,9 +1,121 @@ -import * as vscode from 'vscode'; // import { diffLines, Change } from 'diff'; -import { diff_match_patch } from 'diff-match-patch'; -import { diffLines } from 'diff'; -import { BaseDiff } from './common/shared_types'; +import { diffLines, Change } from 'diff'; + +export type SuggestedEdit = { + // start/end of current file + startLine: number; + startCol: number; + endLine: number; + endCol: number; + + // start/end of original file + originalStartLine: number, + originalStartCol: number, + originalEndLine: number, + originalEndCol: number, + type: 'insertion' | 'deletion' | 'edit', + originalContent: string, // original content (originalfile[originalStart...originalEnd]) + newContent: string, +} + +export function findDiffs(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) + const lineByLineChanges: Change[] = diffLines(oldStr, newStr); + lineByLineChanges.push({ value: '' }) // add a dummy so we flush any streaks we haven't yet at the very end (!line.added && !line.removed) + + let oldFileLineNum: number = 0; + let newFileLineNum: number = 0; + + let streakStartInNewFile: number | undefined = undefined + let streakStartInOldFile: number | undefined = undefined + + let oldStrLines = oldStr.split('\n') + let newStrLines = newStr.split('\n') + + const replacements: SuggestedEdit[] = [] + for (let line of lineByLineChanges) { + + // no change on this line + if (!line.added && !line.removed) { + + // do nothing + + // if we were on a streak of +s and -s, end it + if (streakStartInNewFile !== undefined) { + let type: 'edit' | 'insertion' | 'deletion' = 'edit' + + let startLine = streakStartInNewFile + let endLine = newFileLineNum - 1 // don't include current line, the edit was up to this line but not including it + let startCol = 0 + let endCol = Number.MAX_SAFE_INTEGER + + let originalStartLine = streakStartInOldFile! + let originalEndLine = oldFileLineNum - 1 // don't include current line, the edit was up to this line but not including it + let originalStartCol = 0 + let originalEndCol = Number.MAX_SAFE_INTEGER + + let newContent = newStrLines.slice(startLine, endLine + 1).join('\n') + let originalContent = oldStrLines.slice(originalStartLine, originalEndLine + 1).join('\n') + + // if the range is empty, mark it as a deletion / insertion (both won't be true at once) + // DELETION + if (endLine === startLine - 1) { + type = 'deletion' + endLine = startLine + startCol = 0 + endCol = 0 + newContent += '\n' + } + + // INSERTION + else if (originalEndLine === originalStartLine - 1) { + type = 'insertion' + originalEndLine = originalStartLine + originalStartCol = 0 + originalEndCol = 0 + } + + const replacement: SuggestedEdit = { + type, + startLine, startCol, endLine, endCol, newContent, + originalStartLine, originalStartCol, originalEndLine, originalEndCol, originalContent + } as SuggestedEdit + + replacements.push(replacement) + + streakStartInNewFile = undefined + streakStartInOldFile = undefined + } + oldFileLineNum += line.count ?? 0; + newFileLineNum += line.count ?? 0; + } + + // line was removed from old file + else if (line.removed) { + // if we weren't on a streak, start one on this current line num + if (streakStartInNewFile === undefined) { + streakStartInNewFile = newFileLineNum + streakStartInOldFile = oldFileLineNum + } + oldFileLineNum += line.count ?? 0 // we processed the line so add 1 + } + + // line was added to new file + else if (line.added) { + // if we weren't on a streak, start one on this current line num + if (streakStartInNewFile === undefined) { + streakStartInNewFile = newFileLineNum + streakStartInOldFile = oldFileLineNum + } + newFileLineNum += line.count ?? 0; // we processed the line so add 1 + } + } // end for + + console.debug('Replacements', replacements) + return replacements +} + // const diffLinesOld = (text1: string, text2: string) => { @@ -98,86 +210,92 @@ import { BaseDiff } from './common/shared_types'; // }; -export const findDiffs = (oldText: string, newText: string): BaseDiff[] => { - - let diffs = diffLines(oldText, newText) - .map(diff => { - const operation = diff.added ? 1 : diff.removed ? -1 : 0; - const text = diff.value; - return [operation, text] as const; - }) - const blocks: BaseDiff[] = []; - let reprBlock: string[] = []; - let deletedBlock: string[] = []; - let insertedBlock: string[] = []; - let newFileLine = 0; - let oldFileLine = 0; - let insertedStart = 0; - let deletedStart = 0; - diffs.forEach(([operation, text]) => { - const lines = text.split('\n'); - switch (operation) { - // insertion - case 1: - if (reprBlock.length === 0) { reprBlock.push('@@@@'); } - if (insertedBlock.length === 0) insertedStart = newFileLine; - newFileLine += lines.length - 1; // update the line count for new text - insertedBlock.push(text); - reprBlock.push(lines.map(line => `+ ${line}`).join('\n')); - break; +// export const findDiffs = (oldText: string, newText: string): BaseDiff[] => { - // deletion - case -1: - if (reprBlock.length === 0) { reprBlock.push('@@@@'); } - if (deletedBlock.length === 0) deletedStart = oldFileLine; - oldFileLine += lines.length - 1; // update the line count for old text - deletedBlock.push(text); - reprBlock.push(lines.map(line => `- ${line}`).join('\n')); - break; +// let diffs = diffLines(oldText, newText) +// .map(diff => { +// const operation = diff.added ? 1 : diff.removed ? -1 : 0; +// const text = diff.value; +// return [operation, text] as const; +// }) - // no change - case 0: - // 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), - }); - } - // update variables - reprBlock = []; - deletedBlock = []; - insertedBlock = []; - deletedStart += lines.length - 1; - insertedStart += lines.length - 1; - newFileLine += lines.length - 1; - oldFileLine += lines.length - 1; +// const blocks: BaseDiff[] = []; +// let reprBlock: string[] = []; +// let deletedBlock: string[] = []; +// let insertedBlock: string[] = []; +// let newFileLine = 0; +// let oldFileLine = 0; +// let insertedStart = 0; +// let deletedStart = 0; - break; - } - }); +// diffs.forEach(([operation, text]) => { - // 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), - }); - } +// const lines = text.split('\n'); - return blocks; -}; +// switch (operation) { + +// // insertion +// case 1: +// if (reprBlock.length === 0) { reprBlock.push('@@@@'); } +// if (insertedBlock.length === 0) insertedStart = newFileLine; +// newFileLine += lines.length - 1; // update the line count for new text +// insertedBlock.push(text); +// reprBlock.push(lines.map(line => `+ ${line}`).join('\n')); +// break; + +// // deletion +// case -1: +// if (reprBlock.length === 0) { reprBlock.push('@@@@'); } +// if (deletedBlock.length === 0) deletedStart = oldFileLine; +// oldFileLine += lines.length - 1; // update the line count for old text +// deletedBlock.push(text); +// reprBlock.push(lines.map(line => `- ${line}`).join('\n')); +// break; + +// // no change +// case 0: +// // 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), +// }); +// } + +// // update variables +// reprBlock = []; +// deletedBlock = []; +// insertedBlock = []; +// deletedStart += lines.length - 1; +// insertedStart += lines.length - 1; +// newFileLine += lines.length - 1; +// oldFileLine += lines.length - 1; + +// break; +// } +// }); + +// // 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), +// }); +// } + +// return blocks; +// };