Skip to content

Commit c8df698

Browse files
committed
Shift diff up to get to boundaries (microsoft#30087)
1 parent a2c47ca commit c8df698

2 files changed

Lines changed: 135 additions & 1 deletion

File tree

src/vs/base/common/diff/diff.ts

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,7 @@ export class LcsDiff {
799799
do {
800800
mergedDiffs = false;
801801

802-
// Shift all the changes first
802+
// Shift all the changes down first
803803
for (let i = 0; i < changes.length; i++) {
804804
const change = changes[i];
805805
const originalStop = (i < changes.length - 1) ? changes[i + 1].originalStart : this.OriginalSequence.getLength();
@@ -834,9 +834,105 @@ export class LcsDiff {
834834
changes = result;
835835
} while (mergedDiffs);
836836

837+
// Shift changes back up until we hit empty or whitespace-only lines
838+
for (let i = changes.length - 1; i >= 0; i--) {
839+
const change = changes[i];
840+
841+
let originalStop = 0;
842+
let modifiedStop = 0;
843+
if (i > 0) {
844+
const prevChange = changes[i - 1];
845+
if (prevChange.originalLength > 0) {
846+
originalStop = prevChange.originalStart + prevChange.originalLength;
847+
}
848+
if (prevChange.modifiedLength > 0) {
849+
modifiedStop = prevChange.modifiedStart + prevChange.modifiedLength;
850+
}
851+
}
852+
853+
const checkOriginal = change.originalLength > 0;
854+
const checkModified = change.modifiedLength > 0;
855+
856+
let bestDelta = 0;
857+
let bestScore = this._boundaryScore(change.originalStart, change.originalLength, change.modifiedStart, change.modifiedLength);
858+
859+
for (let delta = 1; ; delta++) {
860+
let originalStart = change.originalStart - delta;
861+
let modifiedStart = change.modifiedStart - delta;
862+
863+
if (originalStart < originalStop || modifiedStart < modifiedStart) {
864+
break;
865+
}
866+
867+
if (checkOriginal && !this.OriginalElementsAreEqual(originalStart, originalStart + change.originalLength)) {
868+
break;
869+
}
870+
871+
if (checkModified && !this.ModifiedElementsAreEqual(modifiedStart, modifiedStart + change.modifiedLength)) {
872+
break;
873+
}
874+
875+
let score = this._boundaryScore(originalStart, change.originalLength, modifiedStart, change.modifiedLength);
876+
877+
if (score > bestScore) {
878+
bestScore = score;
879+
bestDelta = delta;
880+
}
881+
}
882+
883+
change.originalStart -= bestDelta;
884+
change.modifiedStart -= bestDelta;
885+
}
886+
837887
return changes;
838888
}
839889

890+
private _OriginalIsBoundary(index: number): boolean {
891+
if (index <= 0 || index >= this.OriginalSequence.getLength() - 1) {
892+
return true;
893+
}
894+
return /^\s*$/.test(this.OriginalSequence.getElementHash(index));
895+
}
896+
897+
private _OriginalRegionIsBoundary(originalStart: number, originalLength: number): boolean {
898+
if (this._OriginalIsBoundary(originalStart) || this._OriginalIsBoundary(originalStart - 1)) {
899+
return true;
900+
}
901+
if (originalLength > 0) {
902+
let originalEnd = originalStart + originalLength;
903+
if (this._OriginalIsBoundary(originalEnd - 1) || this._OriginalIsBoundary(originalEnd)) {
904+
return true;
905+
}
906+
}
907+
return false;
908+
}
909+
910+
private _ModifiedIsBoundary(index: number): boolean {
911+
if (index <= 0 || index >= this.ModifiedSequence.getLength() - 1) {
912+
return true;
913+
}
914+
return /^\s*$/.test(this.ModifiedSequence.getElementHash(index));
915+
}
916+
917+
private _ModifiedRegionIsBoundary(modifiedStart: number, modifiedLength: number): boolean {
918+
if (this._ModifiedIsBoundary(modifiedStart) || this._ModifiedIsBoundary(modifiedStart - 1)) {
919+
return true;
920+
}
921+
if (modifiedLength > 0) {
922+
let modifiedEnd = modifiedStart + modifiedLength;
923+
if (this._ModifiedIsBoundary(modifiedEnd - 1) || this._ModifiedIsBoundary(modifiedEnd)) {
924+
return true;
925+
}
926+
}
927+
return false;
928+
}
929+
930+
private _boundaryScore(originalStart: number, originalLength: number, modifiedStart: number, modifiedLength: number): number {
931+
let originalScore = (this._OriginalRegionIsBoundary(originalStart, originalLength) ? 1 : 0);
932+
let modifiedScore = (this._ModifiedRegionIsBoundary(modifiedStart, modifiedLength) ? 1 : 0);
933+
return (originalScore + modifiedScore);
934+
}
935+
840936
/**
841937
* Concatenates the two input DiffChange lists and returns the resulting
842938
* list.

src/vs/editor/test/common/diff/diffComputer.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,4 +539,42 @@ suite('Editor Diff - DiffComputer', () => {
539539
];
540540
assertDiff(original, modified, expected, false, true);
541541
});
542+
543+
test('pretty diff 3', () => {
544+
var original = [
545+
'class A {',
546+
' /**',
547+
' * m1',
548+
' */',
549+
' method1() {}',
550+
'',
551+
' /**',
552+
' * m3',
553+
' */',
554+
' method3() {}',
555+
'}',
556+
];
557+
var modified = [
558+
'class A {',
559+
' /**',
560+
' * m1',
561+
' */',
562+
' method1() {}',
563+
'',
564+
' /**',
565+
' * m2',
566+
' */',
567+
' method2() {}',
568+
'',
569+
' /**',
570+
' * m3',
571+
' */',
572+
' method3() {}',
573+
'}',
574+
];
575+
var expected = [
576+
createLineInsertion(7, 11, 6)
577+
];
578+
assertDiff(original, modified, expected, false, true);
579+
});
542580
});

0 commit comments

Comments
 (0)