Skip to content

Commit 78cd390

Browse files
committed
Make equal-segment cursor recovery robust
1 parent e08b98b commit 78cd390

2 files changed

Lines changed: 37 additions & 6 deletions

File tree

Sources/TextDiff/AppKit/DiffRevertActionResolver.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,20 @@ enum DiffRevertActionResolver {
5757
case .equal:
5858
originalRange = NSRange(location: originalCursor, length: textLength)
5959
updatedRange = NSRange(location: updatedCursor, length: textLength)
60-
if textMatches(segment.text, source: originalNSString, at: originalCursor) {
61-
originalCursor += textLength
62-
}
63-
if textMatches(segment.text, source: updatedNSString, at: updatedCursor) {
64-
updatedCursor += textLength
65-
}
60+
let originalMatches = textMatches(segment.text, source: originalNSString, at: originalCursor)
61+
let updatedMatches = textMatches(segment.text, source: updatedNSString, at: updatedCursor)
62+
#if !TESTING
63+
assert(
64+
originalMatches,
65+
"Equal segment text mismatch in original at \(originalCursor) for segment \(index): \(segment.text)"
66+
)
67+
assert(
68+
updatedMatches,
69+
"Equal segment text mismatch in updated at \(updatedCursor) for segment \(index): \(segment.text)"
70+
)
71+
#endif
72+
originalCursor += textLength
73+
updatedCursor += textLength
6674
case .delete:
6775
originalRange = NSRange(location: originalCursor, length: textLength)
6876
updatedRange = NSRange(location: updatedCursor, length: 0)

Tests/TextDiffTests/DiffRevertActionResolverTests.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,29 @@ func candidatesBuildPairedReplacementForAdjacentDeleteInsert() throws {
2525
#expect(action.resultingUpdated == "old")
2626
}
2727

28+
@Test
29+
func indexedSegmentsAdvancePastEqualSegmentsEvenWhenTextValidationFails() {
30+
let segments = [
31+
DiffSegment(kind: .equal, tokenKind: .word, text: "abc"),
32+
DiffSegment(kind: .delete, tokenKind: .word, text: "X"),
33+
DiffSegment(kind: .insert, tokenKind: .word, text: "Y")
34+
]
35+
36+
let indexed = DiffRevertActionResolver.indexedSegments(
37+
from: segments,
38+
original: "zzzX",
39+
updated: "zzzY"
40+
)
41+
42+
#expect(indexed.count == 3)
43+
#expect(indexed[0].originalRange == NSRange(location: 0, length: 3))
44+
#expect(indexed[0].updatedRange == NSRange(location: 0, length: 3))
45+
#expect(indexed[1].originalRange == NSRange(location: 3, length: 1))
46+
#expect(indexed[1].updatedRange == NSRange(location: 3, length: 0))
47+
#expect(indexed[2].originalRange == NSRange(location: 4, length: 0))
48+
#expect(indexed[2].updatedRange == NSRange(location: 3, length: 1))
49+
}
50+
2851
@Test
2952
func candidatesDoNotPairWhenAnySegmentExistsBetweenDeleteAndInsert() {
3053
let segments = [

0 commit comments

Comments
 (0)