mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
Throttle edit requests, refactor
This commit is contained in:
parent
1e4c932313
commit
097f279ecb
6 changed files with 252 additions and 295 deletions
14
extensions/void/package-lock.json
generated
14
extensions/void/package-lock.json
generated
|
|
@ -19,6 +19,7 @@
|
|||
"@types/diff": "^5.2.2",
|
||||
"@types/diff-match-patch": "^1.0.36",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/lodash": "^4.17.12",
|
||||
"@types/mocha": "^10.0.8",
|
||||
"@types/node": "^22.5.1",
|
||||
"@types/react": "^18.3.4",
|
||||
|
|
@ -37,6 +38,7 @@
|
|||
"eslint-plugin-react": "^7.35.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"globals": "^15.9.0",
|
||||
"lodash": "^4.17.21",
|
||||
"marked": "^14.1.0",
|
||||
"ollama": "^0.5.9",
|
||||
"postcss": "^8.4.41",
|
||||
|
|
@ -710,6 +712,12 @@
|
|||
"pretty-format": "^29.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.12.tgz",
|
||||
"integrity": "sha512-sviUmCE8AYdaF/KIHLDJBQgeYzPBI0vf/17NaYehBJfYD1j6/L95Slh07NlyK2iNyBNaEkb3En2jRt+a8y3xZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/mdast": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
|
||||
|
|
@ -4549,6 +4557,12 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@
|
|||
"@types/diff": "^5.2.2",
|
||||
"@types/diff-match-patch": "^1.0.36",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/lodash": "^4.17.12",
|
||||
"@types/mocha": "^10.0.8",
|
||||
"@types/node": "^22.5.1",
|
||||
"@types/react": "^18.3.4",
|
||||
|
|
@ -130,6 +131,7 @@
|
|||
"eslint-plugin-react": "^7.35.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"globals": "^15.9.0",
|
||||
"lodash": "^4.17.21",
|
||||
"marked": "^14.1.0",
|
||||
"ollama": "^0.5.9",
|
||||
"postcss": "^8.4.41",
|
||||
|
|
|
|||
|
|
@ -154,16 +154,16 @@ export class DisplayChangesProvider implements vscode.CodeLensProvider {
|
|||
this.addDiffs(editor.document.uri, diffs, diffArea)
|
||||
|
||||
// // print diffs
|
||||
// console.log('!CodeBefore:', JSON.stringify(diffArea.originalCode))
|
||||
// console.log('!CodeAfter:', JSON.stringify(currentCode))
|
||||
// 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,)
|
||||
// }
|
||||
|
||||
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,)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,16 +3,53 @@ import { OnFinalMessage, OnText, sendLLMMessage, SetAbort } from "./sendLLMMessa
|
|||
import { VoidConfig } from '../sidebar/contextForConfig';
|
||||
import { findDiffs } from '../findDiffs';
|
||||
import { searchDiffChunkInstructions, writeFileWithDiffInstructions } from './systemPrompts';
|
||||
import { throttle } from 'lodash';
|
||||
|
||||
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 writeFileWithDiffUntilMatchup = ({ fileUri, originalFileStr, unfinishedFileStr, diffStr, voidConfig, setAbort }: { fileUri: vscode.Uri, originalFileStr: string, unfinishedFileStr: string, diffStr: string, voidConfig: VoidConfig, setAbort: SetAbort }) => {
|
||||
const THRTOTLE_TIME = 100 // minimum time between edits
|
||||
const LINES_PER_CHUNK = 20 // number of lines to search at a time
|
||||
|
||||
const applyCtrlLChangesToFile = throttle(
|
||||
({ fileUri, newCurrentLine, oldCurrentLine, fullCompletedStr, oldFileStr, debug }: { fileUri: vscode.Uri, newCurrentLine: number, oldCurrentLine: number, fullCompletedStr: string, oldFileStr: string, debug?: string }) => {
|
||||
|
||||
console.log('DEBUG: ', debug)
|
||||
console.log('oldNext: ', oldCurrentLine)
|
||||
console.log('newNext: ', newCurrentLine)
|
||||
console.log('WRITE_TO_FILE1: ', fullCompletedStr.split('\n').slice(0, newCurrentLine + 1).join('\n'))
|
||||
console.log('WRITE_TO_FILE2: ', oldFileStr.split('\n').slice(oldCurrentLine + 1).join('\n'))
|
||||
|
||||
// write the change to the file
|
||||
const WRITE_TO_FILE = (
|
||||
fullCompletedStr.split('\n').slice(0, newCurrentLine + 1).join('\n') // newFile[:newCurrentLine+1]
|
||||
+ oldFileStr.split('\n').slice(oldCurrentLine + 1).join('\n') // oldFile[oldCurrentLine+1:]
|
||||
)
|
||||
const workspaceEdit = new vscode.WorkspaceEdit()
|
||||
workspaceEdit.replace(fileUri, new vscode.Range(0, 0, Number.MAX_SAFE_INTEGER, 0), WRITE_TO_FILE)
|
||||
vscode.workspace.applyEdit(workspaceEdit)
|
||||
|
||||
// highlight the `newCurrentLine` in white
|
||||
// highlight the remaining part of the file in gray
|
||||
|
||||
},
|
||||
THRTOTLE_TIME, { trailing: true }
|
||||
)
|
||||
|
||||
|
||||
// `next` is the line after the completed text
|
||||
// `oldNext` is the same line but in the original file
|
||||
type CompetedReturn = { isFinished: true, next?: undefined, oldNext?: undefined, } | { isFinished?: undefined, next: number, oldNext: number, }
|
||||
const generateFileUsingDiffUntilMatchup = ({ fileUri, oldFileStr, completedStr, oldNext, next, diffStr, voidConfig, setAbort }: { fileUri: vscode.Uri, oldFileStr: string, completedStr: string, oldNext: number, next: number, diffStr: string, voidConfig: VoidConfig, setAbort: SetAbort }) => {
|
||||
|
||||
const NUM_MATCHUP_TOKENS = 20
|
||||
|
||||
const promptContent = `ORIGINAL_FILE
|
||||
\`\`\`
|
||||
${originalFileStr}
|
||||
${oldFileStr}
|
||||
\`\`\`
|
||||
|
||||
DIFF
|
||||
|
|
@ -25,83 +62,80 @@ Please finish writing the new file \`NEW_FILE\`. Return ONLY the completion of t
|
|||
|
||||
NEW_FILE
|
||||
\`\`\`
|
||||
${unfinishedFileStr}
|
||||
${completedStr}
|
||||
\`\`\`
|
||||
`
|
||||
// create a promise that can be awaited
|
||||
let res: Res<{ deltaStr: string, matchupLine: number | undefined }> = () => { }
|
||||
const promise = new Promise<{ deltaStr: string, matchupLine: number | undefined }>((resolve, reject) => { res = resolve })
|
||||
let res: Res<CompetedReturn> = () => { }
|
||||
const promise = new Promise<CompetedReturn>((resolve, reject) => { res = resolve })
|
||||
|
||||
// get the abort method
|
||||
let _abort = () => { }
|
||||
let did_abort = false
|
||||
|
||||
// make LLM complete the file to include the diff
|
||||
sendLLMMessage({
|
||||
messages: [{ role: 'system', content: writeFileWithDiffInstructions, }, { role: 'user', content: promptContent, }],
|
||||
onText: (tokenStr, deltaStr) => {
|
||||
|
||||
const newFileStr = unfinishedFileStr + deltaStr
|
||||
if (did_abort) return;
|
||||
|
||||
// 1. Apply the edit and modify highlighting
|
||||
|
||||
console.log('EDIT START')
|
||||
|
||||
const workspaceEdit = new vscode.WorkspaceEdit()
|
||||
workspaceEdit.replace(fileUri, new vscode.Range(0, 0, Number.MAX_SAFE_INTEGER, 0), newFileStr)
|
||||
vscode.workspace.applyEdit(workspaceEdit)
|
||||
|
||||
// 2. Check for matchup with original file
|
||||
const fullCompletedStr = completedStr + deltaStr
|
||||
|
||||
// diff `originalFileStr` and `newFileStr`
|
||||
const diffs = findDiffs(originalFileStr, 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
|
||||
// create a representation of both files with all spaces removed from each line
|
||||
const oldFileAfterLastDiff = originalFileStr.split('\n').slice(oldLineAfterLastDiff).map(line => line.replace(/\s/g, '')).join('\n')
|
||||
const newFileAfterLastDiff = newFileStr.split('\n').slice(newLineAfterLastDiff).map(line => line.replace(/\s/g, '')).join('\n')
|
||||
|
||||
// find where the matchup starts in `oldLinesAfterLastDiff`
|
||||
const targetStr = newFileAfterLastDiff.slice(-NUM_MATCHUP_TOKENS)
|
||||
// check if we've generated a diff
|
||||
const didGenerateDiff = newLineAfterLastDiff > next
|
||||
|
||||
// return if not enough tokens to match
|
||||
if (targetStr.length < NUM_MATCHUP_TOKENS) return;
|
||||
// return if no matchup found
|
||||
const matchupIdx = oldFileAfterLastDiff.indexOf(targetStr)
|
||||
if (matchupIdx === -1) return;
|
||||
// get the line we are currently generating `newCurrentLine`; make sure it never goes past the last diff we've generated
|
||||
// - if `deltaStr` contains a diff, then _next = newLineAfterLastDiff - 1
|
||||
// - if it does not contain a diff, then _next = next + deltaStr.split('\n').length - 1
|
||||
const newCurrentLine = didGenerateDiff ? newLineAfterLastDiff - 1 : next + deltaStr.split('\n').length - 1
|
||||
const oldCurrentLine = didGenerateDiff ? oldLineAfterLastDiff - 1 : oldNext + (newCurrentLine - next)
|
||||
|
||||
console.log('MATCHUP')
|
||||
// 1. Apply the changes and modify highlighting
|
||||
|
||||
applyCtrlLChangesToFile({ fileUri, newCurrentLine, oldCurrentLine, fullCompletedStr, oldFileStr })
|
||||
|
||||
// 2. Check for early stopping
|
||||
// the conditions for early stopping are:
|
||||
// - we have generated a diff
|
||||
// - there is matchup with the original file after the diff
|
||||
const isMatchupAfterDiff = fullCompletedStr.split('\n').slice(newLineAfterLastDiff).join('\n').length > NUM_MATCHUP_TOKENS
|
||||
if (didGenerateDiff && isMatchupAfterDiff) {
|
||||
|
||||
// resolve the promise
|
||||
res({ next: newCurrentLine + 1, oldNext: oldCurrentLine + 1, });
|
||||
|
||||
// abort the LLM call
|
||||
_abort()
|
||||
did_abort = true
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
// resolve the promise with the delta, up to first matchup
|
||||
res({
|
||||
matchupLine: oldLineAfterLastDiff,
|
||||
deltaStr: newFileStr.split('\n').splice(0, newLineAfterLastDiff).join('\n'),
|
||||
});
|
||||
|
||||
// abort the LLM call
|
||||
_abort()
|
||||
|
||||
},
|
||||
onFinalMessage: (finalMessage) => {
|
||||
onFinalMessage: (deltaStr) => {
|
||||
|
||||
const newFileStr = unfinishedFileStr + finalMessage
|
||||
const newCompletedStr = completedStr + deltaStr
|
||||
|
||||
const workspaceEdit = new vscode.WorkspaceEdit()
|
||||
workspaceEdit.replace(fileUri, new vscode.Range(0, 0, Number.MAX_SAFE_INTEGER, 0), newFileStr)
|
||||
vscode.workspace.applyEdit(workspaceEdit)
|
||||
applyCtrlLChangesToFile({ fileUri, newCurrentLine: Number.MAX_SAFE_INTEGER, oldCurrentLine: Number.MAX_SAFE_INTEGER, fullCompletedStr: newCompletedStr, oldFileStr, debug: 'FINAL' })
|
||||
|
||||
|
||||
console.log('FINAL MESSAGE', finalMessage)
|
||||
|
||||
|
||||
res({ deltaStr: finalMessage, matchupLine: undefined });
|
||||
res({ isFinished: true });
|
||||
},
|
||||
onError: (e) => {
|
||||
res({ deltaStr: '', matchupLine: undefined });
|
||||
res({ isFinished: true });
|
||||
console.error('Error rewriting file with diff', e);
|
||||
},
|
||||
voidConfig,
|
||||
setAbort: (a) => { setAbort(a); _abort = a },
|
||||
setAbort: (a) => { setAbort(a); _abort = a; },
|
||||
})
|
||||
|
||||
return promise
|
||||
|
|
@ -163,57 +197,58 @@ Return \`true\` if ANY part of the chunk should be modified, and \`false\` if it
|
|||
|
||||
// lazily applies the diff to the file
|
||||
// we chunk the text in the file, and ask an LLM whether it should edit each chunk
|
||||
const applyDiffLazily = async ({ fileUri, fileStr, diffStr, voidConfig, setAbort }: { fileUri: vscode.Uri, fileStr: string, diffStr: string, voidConfig: VoidConfig, setAbort: SetAbort }) => {
|
||||
const applyDiffLazily = async ({ fileUri, oldFileStr, diffStr, voidConfig, setAbort }: { fileUri: vscode.Uri, oldFileStr: string, diffStr: string, voidConfig: VoidConfig, setAbort: SetAbort }) => {
|
||||
|
||||
console.log('apply diff lazily')
|
||||
|
||||
const LINES_PER_CHUNK = 20 // number of lines to search at a time
|
||||
// stateful variables
|
||||
let next = 0
|
||||
let oldNext = 0
|
||||
|
||||
// read file content
|
||||
const fileLines = fileStr.split('\n')
|
||||
const completedLines = []
|
||||
while (next < oldFileStr.split('\n').length) {
|
||||
|
||||
// search the file chunk-by-chunk
|
||||
let chunkStart: number | undefined = 0
|
||||
while (chunkStart !== undefined && chunkStart < fileLines.length) {
|
||||
|
||||
console.log('chunkStartLine: ', chunkStart)
|
||||
console.log('next line: ', next)
|
||||
|
||||
// get the chunk
|
||||
const chunkLines = fileLines.slice(chunkStart, chunkStart + LINES_PER_CHUNK)
|
||||
const chunkStr = chunkLines.join('\n');
|
||||
|
||||
const chunkStr = oldFileStr.split('\n').slice(next, next + LINES_PER_CHUNK).join('\n')
|
||||
|
||||
// ask LLM if we should apply the diff to the chunk
|
||||
const __start = new Date().getTime()
|
||||
let shouldApplyDiff = await shouldApplyDiffFn({ fileStr, speculationStr: chunkStr, diffStr, voidConfig, setAbort })
|
||||
|
||||
let shouldApplyDiff = await shouldApplyDiffFn({ fileStr: oldFileStr, speculationStr: chunkStr, diffStr, voidConfig, setAbort })
|
||||
|
||||
const __end = new Date().getTime()
|
||||
|
||||
if (!shouldApplyDiff) { // should not change the chunk
|
||||
console.log('KEEP CHUNK time: ', __end - __start)
|
||||
completedLines.push(chunkStr);
|
||||
chunkStart += chunkLines.length
|
||||
// TODO update highlighting here
|
||||
|
||||
next += LINES_PER_CHUNK
|
||||
oldNext += LINES_PER_CHUNK
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// ask LLM to rewrite file with diff (if there is significant matchup with the original file, we stop rewriting)
|
||||
// make vscode read uri = 'asdasd'
|
||||
|
||||
const ___start = new Date().getTime()
|
||||
const { deltaStr, matchupLine } = await writeFileWithDiffUntilMatchup({
|
||||
originalFileStr: fileStr,
|
||||
unfinishedFileStr: completedLines.join('\n'),
|
||||
diffStr,
|
||||
fileUri,
|
||||
voidConfig,
|
||||
// TODO! update highlighting here
|
||||
setAbort,
|
||||
})
|
||||
|
||||
|
||||
const completedStr = (await readFileContentOfUri(fileUri)).split('\n').slice(0, next).join('\n');
|
||||
const result = await generateFileUsingDiffUntilMatchup({ fileUri, oldFileStr, completedStr, oldNext, next, diffStr, voidConfig, setAbort, })
|
||||
|
||||
const ___end = new Date().getTime()
|
||||
console.log('EDIT CHUNK time: ', ___end - ___start)
|
||||
|
||||
console.log('EDIT CHUNK time: ', ___end - ___start);
|
||||
|
||||
// if we are finished, stop the loop
|
||||
if (result.isFinished) {
|
||||
break;
|
||||
}
|
||||
|
||||
next = result.next
|
||||
oldNext = result.oldNext
|
||||
|
||||
completedLines.push(deltaStr)
|
||||
chunkStart = matchupLine
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
let abort = () => { } // TODO this is unused
|
||||
|
||||
// apply the change
|
||||
applyDiffLazily({ fileUri, fileStr, diffStr: m.code, voidConfig, setAbort: (a) => { abort = a } })
|
||||
applyDiffLazily({ fileUri, oldFileStr: fileStr, diffStr: m.code, voidConfig, setAbort: (a) => { abort = a } })
|
||||
|
||||
// set the file equal to the change
|
||||
// await editor.edit(editBuilder => {
|
||||
|
|
|
|||
|
|
@ -2,34 +2,118 @@
|
|||
import * as vscode from 'vscode';
|
||||
// import { diffLines, Change } from 'diff';
|
||||
import { BaseDiff } from './shared_types';
|
||||
|
||||
import { diff_match_patch } from 'diff-match-patch';
|
||||
import { diffLines } from 'diff';
|
||||
|
||||
|
||||
const diffLines = (text1: string, text2: string) => {
|
||||
var dmp = new diff_match_patch();
|
||||
var a = dmp.diff_linesToChars_(text1, text2);
|
||||
var lineText1 = a.chars1;
|
||||
var lineText2 = a.chars2;
|
||||
var lineArray = a.lineArray;
|
||||
var diffs = dmp.diff_main(lineText1, lineText2, false);
|
||||
dmp.diff_charsToLines_(diffs, lineArray);
|
||||
// dmp.diff_cleanupSemantic(diffs);
|
||||
return diffs;
|
||||
}
|
||||
// const diffLinesOld = (text1: string, text2: string) => {
|
||||
// var dmp = new diff_match_patch();
|
||||
// var a = dmp.diff_linesToChars_(text1, text2);
|
||||
// var lineText1 = a.chars1;
|
||||
// var lineText2 = a.chars2;
|
||||
// var lineArray = a.lineArray;
|
||||
// var diffs = dmp.diff_main(lineText1, lineText2, false);
|
||||
// dmp.diff_charsToLines_(diffs, lineArray);
|
||||
// // dmp.diff_cleanupSemantic(diffs);
|
||||
// return diffs;
|
||||
// }
|
||||
|
||||
|
||||
// // TODO use a better diff algorithm
|
||||
// export const findDiffsOld = (oldText: string, newText: string): BaseDiff[] => {
|
||||
|
||||
// const diffs = diffLinesOld(oldText, newText);
|
||||
|
||||
// const blocks: BaseDiff[] = [];
|
||||
// let reprBlock: string[] = [];
|
||||
// let deletedBlock: string[] = [];
|
||||
// let insertedBlock: string[] = [];
|
||||
// let insertedLine = 0;
|
||||
// let deletedLine = 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 = insertedLine;
|
||||
// insertedLine += lines.length - 1; // Update only 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 = deletedLine;
|
||||
// deletedLine += lines.length - 1; // Update only the line count for old text
|
||||
// deletedBlock.push(text);
|
||||
// reprBlock.push(lines.map(line => `- ${line}`).join('\n'));
|
||||
// break;
|
||||
|
||||
// // no change
|
||||
// case 0:
|
||||
// // If we have a pending block, add it 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, deletedLine, Number.MAX_SAFE_INTEGER),
|
||||
// insertedRange: new vscode.Range(insertedStart, 0, insertedLine, Number.MAX_SAFE_INTEGER),
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Reset the block variables
|
||||
// reprBlock = [];
|
||||
// deletedBlock = [];
|
||||
// insertedBlock = [];
|
||||
|
||||
// // Update line counts for unchanged text
|
||||
// insertedLine += lines.length - 1;
|
||||
// deletedLine += 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, deletedLine, Number.MAX_SAFE_INTEGER),
|
||||
// insertedRange: new vscode.Range(insertedStart, 0, insertedLine, Number.MAX_SAFE_INTEGER),
|
||||
// });
|
||||
// }
|
||||
|
||||
// return blocks;
|
||||
// };
|
||||
|
||||
|
||||
// TODO use a better diff algorithm
|
||||
export const findDiffs = (oldText: string, newText: string): BaseDiff[] => {
|
||||
|
||||
const diffs = diffLines(oldText, newText);
|
||||
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 insertedLine = 0;
|
||||
let deletedLine = 0;
|
||||
let newFileLine = 0;
|
||||
let oldFileLine = 0;
|
||||
let insertedStart = 0;
|
||||
let deletedStart = 0;
|
||||
|
||||
|
|
@ -42,8 +126,8 @@ export const findDiffs = (oldText: string, newText: string): BaseDiff[] => {
|
|||
// insertion
|
||||
case 1:
|
||||
if (reprBlock.length === 0) { reprBlock.push('@@@@'); }
|
||||
if (insertedBlock.length === 0) insertedStart = insertedLine;
|
||||
insertedLine += lines.length - 1; // Update only the line count for new text
|
||||
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;
|
||||
|
|
@ -51,33 +135,33 @@ export const findDiffs = (oldText: string, newText: string): BaseDiff[] => {
|
|||
// deletion
|
||||
case -1:
|
||||
if (reprBlock.length === 0) { reprBlock.push('@@@@'); }
|
||||
if (deletedBlock.length === 0) deletedStart = deletedLine;
|
||||
deletedLine += lines.length - 1; // Update only the line count for old text
|
||||
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:
|
||||
// If we have a pending block, add it to the blocks array
|
||||
// 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, deletedLine, Number.MAX_SAFE_INTEGER),
|
||||
insertedRange: new vscode.Range(insertedStart, 0, insertedLine, Number.MAX_SAFE_INTEGER),
|
||||
deletedRange: new vscode.Range(deletedStart, 0, oldFileLine, Number.MAX_SAFE_INTEGER),
|
||||
insertedRange: new vscode.Range(insertedStart, 0, newFileLine, Number.MAX_SAFE_INTEGER),
|
||||
});
|
||||
}
|
||||
|
||||
// Reset the block variables
|
||||
// update variables
|
||||
reprBlock = [];
|
||||
deletedBlock = [];
|
||||
insertedBlock = [];
|
||||
|
||||
// Update line counts for unchanged text
|
||||
insertedLine += lines.length - 1;
|
||||
deletedLine += lines.length - 1;
|
||||
deletedStart += lines.length - 1;
|
||||
insertedStart += lines.length - 1;
|
||||
newFileLine += lines.length - 1;
|
||||
oldFileLine += lines.length - 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -89,189 +173,11 @@ export const findDiffs = (oldText: string, newText: string): BaseDiff[] => {
|
|||
code: reprBlock.join(''),
|
||||
deletedCode: deletedBlock.join(''),
|
||||
insertedCode: insertedBlock.join(''),
|
||||
deletedRange: new vscode.Range(deletedStart, 0, deletedLine, Number.MAX_SAFE_INTEGER),
|
||||
insertedRange: new vscode.Range(insertedStart, 0, insertedLine, Number.MAX_SAFE_INTEGER),
|
||||
deletedRange: new vscode.Range(deletedStart, 0, oldFileLine, Number.MAX_SAFE_INTEGER),
|
||||
insertedRange: new vscode.Range(insertedStart, 0, newFileLine, Number.MAX_SAFE_INTEGER),
|
||||
});
|
||||
}
|
||||
|
||||
return blocks;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// export const findDiffs = (oldText: string, newText: string): DiffBlock[] => {
|
||||
|
||||
// const diffs = diffLines(oldText, newText);
|
||||
|
||||
// const blocks: DiffBlock[] = [];
|
||||
|
||||
// let reprBlock: string[] = [];
|
||||
// let deletedBlock: string[] = [];
|
||||
// let insertedBlock: string[] = [];
|
||||
|
||||
// let insertedEnd = 0;
|
||||
// let deletedEnd = 0;
|
||||
// let insertedStart = 0;
|
||||
// let deletedStart = 0;
|
||||
|
||||
// diffs.forEach(part => {
|
||||
|
||||
// part.count = part.count ?? 0
|
||||
|
||||
// // if the part is an addition or deletion, add it to the current block
|
||||
// if (part.added || part.removed) {
|
||||
// if (reprBlock.length === 0) { reprBlock.push('@@@@'); }
|
||||
// if (part.added) {
|
||||
// if (insertedBlock.length === 0) insertedStart = insertedEnd;
|
||||
// insertedEnd += part.count
|
||||
// insertedBlock.push(part.value);
|
||||
// reprBlock.push(part.value.split('\n').map(line => `+ ${line}`).join('\n'));
|
||||
// }
|
||||
// if (part.removed) {
|
||||
// if (deletedBlock.length === 0) deletedStart = deletedEnd;
|
||||
// deletedEnd += part.count
|
||||
// deletedBlock.push(part.value);
|
||||
// reprBlock.push(part.value.split('\n').map(line => `- ${line}`).join('\n'));
|
||||
// }
|
||||
// }
|
||||
|
||||
// // if the part is unchanged, finalize the block and add it to the array
|
||||
// else {
|
||||
// // if the block is not null, add it to the array
|
||||
// if (insertedBlock.length > 0 || deletedBlock.length > 0) {
|
||||
// blocks.push({
|
||||
// code: reprBlock.join('\n'),
|
||||
// deletedCode: deletedBlock.join(''),
|
||||
// insertedCode: insertedBlock.join(''),
|
||||
// deletedRange: new vscode.Range(deletedStart, 0, deletedEnd, Number.MAX_SAFE_INTEGER),
|
||||
// insertedRange: new vscode.Range(insertedStart, 0, insertedEnd, Number.MAX_SAFE_INTEGER),
|
||||
// });
|
||||
// }
|
||||
|
||||
// // update block variables
|
||||
// reprBlock = [];
|
||||
// deletedBlock = [];
|
||||
// insertedBlock = [];
|
||||
// insertedEnd += part.count;
|
||||
// deletedEnd += part.count;
|
||||
|
||||
// }
|
||||
|
||||
// })
|
||||
|
||||
// // finally, add the last block to the array
|
||||
// if (insertedBlock.length > 0 || deletedBlock.length > 0) {
|
||||
// blocks.push({
|
||||
// code: reprBlock.join('\n'),
|
||||
// deletedCode: deletedBlock.join(''),
|
||||
// insertedCode: insertedBlock.join(''),
|
||||
// deletedRange: new vscode.Range(deletedStart, 0, deletedEnd, Number.MAX_SAFE_INTEGER),
|
||||
// insertedRange: new vscode.Range(insertedStart, 0, insertedEnd, Number.MAX_SAFE_INTEGER),
|
||||
// });
|
||||
// }
|
||||
|
||||
// return blocks;
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// import { diffLines, Change } from 'diff';
|
||||
|
||||
// export type SuggestedEdit = {
|
||||
// // start/end of current file
|
||||
// startLine: number;
|
||||
// endLine: number;
|
||||
|
||||
// // start/end of original file
|
||||
// originalStartLine: number,
|
||||
// originalEndLine: number,
|
||||
|
||||
// // original content (originalfile[originalStart...originalEnd])
|
||||
// originalContent: string;
|
||||
// newContent: string;
|
||||
// }
|
||||
|
||||
// export function getDiffedLines(oldStr: string, newStr: string) {
|
||||
// // an ordered list of every original line, line added to the new file, and line removed from the old file (order is unambiguous, think about it)
|
||||
// const lineByLineChanges: Change[] = diffLines(oldStr, newStr);
|
||||
// console.debug('Line by line changes', lineByLineChanges)
|
||||
|
||||
// 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) {
|
||||
// // if we were on a streak, add it
|
||||
// if (streakStartInNewFile !== undefined) {
|
||||
|
||||
// const startLine = streakStartInNewFile
|
||||
// const endLine = newFileLineNum - 1 // don't include current line, the edit was up to this line but not including it
|
||||
// const newContent = newStrLines.slice(startLine, endLine + 1).join('\n')
|
||||
|
||||
// const originalStartLine = streakStartInOldFile!
|
||||
// const originalEndLine = oldFileLineNum - 1 // don't include current line, the edit was up to this line but not including it
|
||||
// const originalContent = oldStrLines.slice(originalStartLine, originalEndLine + 1).join('\n')
|
||||
|
||||
// const replacement: SuggestedEdit = { startLine, endLine, newContent, originalStartLine, originalEndLine, originalContent }
|
||||
|
||||
// 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
|
||||
|
||||
// }
|
||||
Loading…
Reference in a new issue