refactor(calculateDiff): optimize LCS algorithm for improved diff calculation

This commit is contained in:
Haileyesus
2026-02-12 20:53:34 +03:00
parent 60a9f3dc32
commit f6ed3cbd6d

View File

@@ -29,35 +29,54 @@ export const calculateDiff = (oldStr: string, newStr: string): DiffLine[] => {
const oldLines = oldStr.split('\n'); const oldLines = oldStr.split('\n');
const newLines = newStr.split('\n'); const newLines = newStr.split('\n');
// Use LCS alignment so insertions/deletions don't cascade into a full-file "changed" diff.
const lcsTable: number[][] = Array.from({ length: oldLines.length + 1 }, () =>
new Array<number>(newLines.length + 1).fill(0),
);
for (let oldIndex = oldLines.length - 1; oldIndex >= 0; oldIndex -= 1) {
for (let newIndex = newLines.length - 1; newIndex >= 0; newIndex -= 1) {
if (oldLines[oldIndex] === newLines[newIndex]) {
lcsTable[oldIndex][newIndex] = lcsTable[oldIndex + 1][newIndex + 1] + 1;
} else {
lcsTable[oldIndex][newIndex] = Math.max(
lcsTable[oldIndex + 1][newIndex],
lcsTable[oldIndex][newIndex + 1],
);
}
}
}
const diffLines: DiffLine[] = []; const diffLines: DiffLine[] = [];
let oldIndex = 0; let oldIndex = 0;
let newIndex = 0; let newIndex = 0;
while (oldIndex < oldLines.length || newIndex < newLines.length) { while (oldIndex < oldLines.length && newIndex < newLines.length) {
const oldLine = oldLines[oldIndex]; const oldLine = oldLines[oldIndex];
const newLine = newLines[newIndex]; const newLine = newLines[newIndex];
if (oldIndex >= oldLines.length) {
diffLines.push({ type: 'added', content: newLine, lineNum: newIndex + 1 });
newIndex += 1;
continue;
}
if (newIndex >= newLines.length) {
diffLines.push({ type: 'removed', content: oldLine, lineNum: oldIndex + 1 });
oldIndex += 1;
continue;
}
if (oldLine === newLine) { if (oldLine === newLine) {
oldIndex += 1; oldIndex += 1;
newIndex += 1; newIndex += 1;
continue; continue;
} }
diffLines.push({ type: 'removed', content: oldLine, lineNum: oldIndex + 1 }); if (lcsTable[oldIndex + 1][newIndex] >= lcsTable[oldIndex][newIndex + 1]) {
diffLines.push({ type: 'removed', content: oldLine, lineNum: oldIndex + 1 });
oldIndex += 1;
continue;
}
diffLines.push({ type: 'added', content: newLine, lineNum: newIndex + 1 }); diffLines.push({ type: 'added', content: newLine, lineNum: newIndex + 1 });
newIndex += 1;
}
while (oldIndex < oldLines.length) {
diffLines.push({ type: 'removed', content: oldLines[oldIndex], lineNum: oldIndex + 1 });
oldIndex += 1; oldIndex += 1;
}
while (newIndex < newLines.length) {
diffLines.push({ type: 'added', content: newLines[newIndex], lineNum: newIndex + 1 });
newIndex += 1; newIndex += 1;
} }