diff --git a/extensions/void/src/DisplayChangesProvider.ts b/extensions/void/src/DisplayChangesProvider.ts index 7b83a359..4166d5b4 100644 --- a/extensions/void/src/DisplayChangesProvider.ts +++ b/extensions/void/src/DisplayChangesProvider.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import { findDiffs } from './findDiffs'; -import { Diff, DiffArea, DiffBlock } from './shared_types'; +import { Diff, BaseDiffArea, BaseDiff, DiffArea } from './shared_types'; @@ -17,6 +17,7 @@ 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 @@ -37,6 +38,7 @@ export class DisplayChangesProvider implements vscode.CodeLensProvider { // this acts as a useEffect. Every time text changes, clear the diffs in this editor vscode.workspace.onDidChangeTextDocument((e) => { + const editor = vscode.window.activeTextEditor if (!editor) @@ -97,7 +99,7 @@ export class DisplayChangesProvider implements vscode.CodeLensProvider { // used by us only - public addDiffArea(uri: vscode.Uri, diffArea: DiffArea) { + public addDiffArea(uri: vscode.Uri, diffArea: BaseDiffArea) { const uriStr = uri.toString() @@ -107,16 +109,20 @@ export class DisplayChangesProvider implements vscode.CodeLensProvider { // remove all diffAreas that the new `diffArea` is overlapping with this._diffAreasOfDocument[uriStr] = this._diffAreasOfDocument[uriStr].filter(da => { - // condition for no overlap + const noOverlap = da.startLine > diffArea.endLine || da.endLine < diffArea.startLine - // if there is overlap (ie there is `not noOverlap`), remove `da` + if (!noOverlap) return false + return true }) // add `diffArea` to storage - this._diffAreasOfDocument[uriStr].push(diffArea) - + this._diffAreasOfDocument[uriStr].push({ + ...diffArea, + diffareaid: this._diffareaidPool + }) + this._diffareaidPool += 1 } @@ -149,7 +155,7 @@ export class DisplayChangesProvider implements vscode.CodeLensProvider { console.log('!CODEAfter:', JSON.stringify(currentCode)) // add the diffs to `this._diffsOfDocument[docUriStr]` - this.addDiffs(editor.document.uri, diffs) + this.addDiffs(editor.document.uri, diffs, diffArea) for (const diff of this._diffsOfDocument[docUriStr]) { console.log('------------') @@ -180,7 +186,7 @@ export class DisplayChangesProvider implements vscode.CodeLensProvider { } // used by us only - public addDiffs(docUri: vscode.Uri, diffs: DiffBlock[]) { + public addDiffs(docUri: vscode.Uri, diffs: BaseDiff[], diffArea: DiffArea) { const docUriStr = docUri.toString() @@ -197,8 +203,8 @@ export class DisplayChangesProvider implements vscode.CodeLensProvider { diffid: this._diffidPool, // originalCode: suggestedDiff.deletedText, lenses: [ - new vscode.CodeLens(suggestedDiff.insertedRange, { title: 'Accept', command: 'void.acceptDiff', arguments: [{ diffid: this._diffidPool }] }), - new vscode.CodeLens(suggestedDiff.insertedRange, { title: 'Reject', command: 'void.rejectDiff', arguments: [{ diffid: this._diffidPool }] }) + 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 @@ -207,7 +213,7 @@ export class DisplayChangesProvider implements vscode.CodeLensProvider { } // called on void.acceptDiff - public async acceptDiff({ diffid }: { diffid: number }) { + public async acceptDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) { const editor = vscode.window.activeTextEditor if (!editor) return @@ -216,54 +222,94 @@ export class DisplayChangesProvider implements vscode.CodeLensProvider { const docUri = editor.document.uri const docUriStr = docUri.toString() - // get index of this diff in diffsOfDocument - const index = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid); - if (index === -1) { - console.error('Error: DiffID could not be found: ', diffid, this._diffsOfDocument[docUriStr]) - return + // 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; } - // remove this diff from the diffsOfDocument[docStr] (can change this behavior in future if add something like history) - this._diffsOfDocument[docUriStr].splice(index, 1) + // 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; + } - // refresh + 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 }: { diffid: number }) { + 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 index of this diff in diffsOfDocument - const index = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid); - if (index === -1) { - console.error('Void error: DiffID could not be found: ', diffid, this._diffsOfDocument[docUriStr]) - return + // 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; } - const { insertedRange: range, lenses, deletedCode } = this._diffsOfDocument[docUriStr][index] // do this before we splice and mess up index + // 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; + } - // remove this diff from the diffsOfDocument[docStr] (can change this behavior in future if add something like history) - this._diffsOfDocument[docUriStr].splice(index, 1) + const diff = this._diffsOfDocument[docUriStr][diffIdx] + const diffArea = this._diffAreasOfDocument[docUriStr][diffareaIdx] - // clear the decoration in this diffs range - // editor.setDecorations(greenDecoration, this._diffsOfDocument[docUriStr].map(diff => diff.insertionRange)) - - // REVERT THE CHANGE (this is the only part that's different from acceptDiff) - let workspaceEdit = new vscode.WorkspaceEdit(); - // workspaceEdit.replace(docUri, range, deletedCode); + // 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) - await vscode.workspace.save(docUri) this._weAreEditing = false - // refresh + // 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/extension.ts b/extensions/void/src/extension.ts index 21cc3dfb..aa5a69d1 100644 --- a/extensions/void/src/extension.ts +++ b/extensions/void/src/extension.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { DiffArea, WebviewMessage } from './shared_types'; +import { BaseDiffArea, WebviewMessage } from './shared_types'; import { CtrlKCodeLensProvider } from './CtrlKCodeLensProvider'; import { DisplayChangesProvider } from './DisplayChangesProvider'; import { SidebarWebviewProvider } from './SidebarWebviewProvider'; @@ -57,7 +57,7 @@ export function activate(context: vscode.ExtensionContext) { // vscode.commands.executeCommand('vscode.moveViewToPanel', CustomViewProvider.viewId); // move to aux bar // get the text the user is selecting - const selectionStr = editor.document.getText(editor.selection);5 + const selectionStr = editor.document.getText(editor.selection); 5 // get the range of the selection const selectionRange = editor.selection; @@ -124,9 +124,11 @@ export function activate(context: vscode.ExtensionContext) { } // create an area to show diffs - const diffArea: DiffArea = { + 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) @@ -139,12 +141,10 @@ export function activate(context: vscode.ExtensionContext) { // await vscode.workspace.save(docUri) // this._weAreEditing = false await editor.edit(editBuilder => { - editBuilder.replace(new vscode.Range(diffArea.startLine, 0, diffArea.endLine, 0), m.code); + editBuilder.replace(new vscode.Range(diffArea.startLine, 0, diffArea.endLine, Number.MAX_SAFE_INTEGER), m.code); }); - - - // rediff the changes based on the diffArea (start, end, original code, [current code]) + // rediff the changes based on the diffAreas displayChangesProvider.refreshDiffAreas(editor.document.uri) } diff --git a/extensions/void/src/findDiffs.ts b/extensions/void/src/findDiffs.ts index 5e456f85..847e16f8 100644 --- a/extensions/void/src/findDiffs.ts +++ b/extensions/void/src/findDiffs.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode'; // import { diffLines, Change } from 'diff'; -import { DiffBlock } from './shared_types'; +import { BaseDiff } from './shared_types'; import { diff_match_patch } from 'diff-match-patch'; @@ -20,11 +20,11 @@ const diffLines = (text1: string, text2: string) => { // TODO use a better diff algorithm -export const findDiffs = (oldText: string, newText: string): DiffBlock[] => { +export const findDiffs = (oldText: string, newText: string): BaseDiff[] => { const diffs = diffLines(oldText, newText); - const blocks: DiffBlock[] = []; + const blocks: BaseDiff[] = []; let reprBlock: string[] = []; let deletedBlock: string[] = []; let insertedBlock: string[] = []; diff --git a/extensions/void/src/shared_types.ts b/extensions/void/src/shared_types.ts index 0c8dc933..25843aaa 100644 --- a/extensions/void/src/shared_types.ts +++ b/extensions/void/src/shared_types.ts @@ -10,18 +10,25 @@ type CodeSelection = { selectionStr: string, selectionRange: vscode.Range, fileP type File = { filepath: vscode.Uri, content: string } // an area that is currently being diffed -type DiffArea = { - startLine: number, - endLine: number, - originalCode: string +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 = BaseDiffArea & { diffareaid: number } + // the return type of diff creator -type DiffBlock = { - code: string; - deletedRange: vscode.Range; - deletedCode: string; +type BaseDiff = { + code: string; // representation of the diff in text + deletedRange: vscode.Range; // relative to the file, inclusive insertedRange: vscode.Range; + deletedCode: string; insertedCode: string; } @@ -29,7 +36,7 @@ type DiffBlock = { type Diff = { diffid: number, lenses: vscode.CodeLens[], -} & DiffBlock +} & BaseDiff type WebviewMessage = ( @@ -57,7 +64,7 @@ type WebviewMessage = ( type Command = WebviewMessage['type'] export { - DiffBlock, + BaseDiff, BaseDiffArea, CodeSelection, File, WebviewMessage,