Skip to content

Commit 696edc4

Browse files
tusharsoni52Tushar Soni
andauthored
feat(diffrow): add processEqualities hook and ensure equalities are processed for unchanged lines (Fixes #219) (#224)
- Introduces a new protected method `processEqualities(String)` in DiffRowGenerator - Builder exposes `.processEqualities(Function<String,String>)` - Equal (unchanged) lines now invoke processEqualities() - Inline diffs remain unchanged (as expected in Option 3) - Added new test suite DiffRowGeneratorEqualitiesTest - Updated documentation and Javadoc for new extension point - Fixes #219 (HTML escaping issue when inline diff by word) Co-authored-by: Tushar Soni <tushar.soni@siemens.com>
1 parent b4b13c5 commit 696edc4

File tree

2 files changed

+128
-2
lines changed

2 files changed

+128
-2
lines changed

java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ static void wrapInTag(
189189
private final Function<String, String> lineNormalizer;
190190
private final Function<String, String> processDiffs;
191191
private final Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> inlineDeltaMerger;
192+
// processor for equal (unchanged) lines
193+
private final Function<String, String> equalityProcessor;
192194

193195
private final boolean showInlineDiffs;
194196
private final boolean replaceOriginalLinefeedInChangesWithSpaces;
@@ -214,6 +216,7 @@ private DiffRowGenerator(Builder builder) {
214216
lineNormalizer = builder.lineNormalizer;
215217
processDiffs = builder.processDiffs;
216218
inlineDeltaMerger = builder.inlineDeltaMerger;
219+
equalityProcessor = builder.equalityProcessor;
217220

218221
replaceOriginalLinefeedInChangesWithSpaces = builder.replaceOriginalLinefeedInChangesWithSpaces;
219222

@@ -262,7 +265,8 @@ public List<DiffRow> generateDiffRows(final List<String> original, Patch<String>
262265

263266
// Copy the final matching chunk if any.
264267
for (String line : original.subList(endPos, original.size())) {
265-
diffRows.add(buildDiffRow(Tag.EQUAL, line, line));
268+
String processed = processEqualities(line);
269+
diffRows.add(buildDiffRow(Tag.EQUAL, processed, processed));
266270
}
267271
return diffRows;
268272
}
@@ -276,7 +280,8 @@ private int transformDeltaIntoDiffRow(
276280
Chunk<String> rev = delta.getTarget();
277281

278282
for (String line : original.subList(endPos, orig.getPosition())) {
279-
diffRows.add(buildDiffRow(Tag.EQUAL, line, line));
283+
String processed = processEqualities(line);
284+
diffRows.add(buildDiffRow(Tag.EQUAL, processed, processed));
280285
}
281286

282287
switch (delta.getType()) {
@@ -496,6 +501,19 @@ private String preprocessLine(String line) {
496501
}
497502
}
498503

504+
/**
505+
* Hook for processing equal (unchanged) text segments.
506+
* Delegates to the builder-configured equalityProcessor if present.
507+
*
508+
* @author tusharsoni52
509+
* @param text
510+
* @return
511+
*
512+
*/
513+
protected String processEqualities(final String text) {
514+
return equalityProcessor != null ? equalityProcessor.apply(text) : text;
515+
}
516+
499517
/**
500518
* This class used for building the DiffRowGenerator.
501519
*
@@ -521,6 +539,8 @@ public static class Builder {
521539
private boolean replaceOriginalLinefeedInChangesWithSpaces = false;
522540
private Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> inlineDeltaMerger =
523541
DEFAULT_INLINE_DELTA_MERGER;
542+
// Processor for equalities
543+
private Function<String, String> equalityProcessor = null;
524544

525545
private Builder() {}
526546

@@ -613,6 +633,20 @@ public Builder processDiffs(Function<String, String> processDiffs) {
613633
return this;
614634
}
615635

636+
/**
637+
* Processor for equal (unchanged) text parts.
638+
* Allows applying the same escaping/transformation as for diffs.
639+
*
640+
* @author tusharsoni52
641+
* @param equalityProcessor
642+
* @return
643+
*
644+
*/
645+
public Builder processEqualities(Function<String, String> equalityProcessor) {
646+
this.equalityProcessor = equalityProcessor;
647+
return this;
648+
}
649+
616650
/**
617651
* Set the column width of generated lines of original and revised
618652
* texts.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package com.github.difflib.text;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.util.Arrays;
7+
import java.util.List;
8+
import org.junit.jupiter.api.Test;
9+
10+
public class DiffRowGeneratorEqualitiesTest {
11+
12+
@Test
13+
public void testDefaultEqualityProcessingLeavesTextUnchanged() {
14+
DiffRowGenerator generator =
15+
DiffRowGenerator.create().showInlineDiffs(false).build();
16+
17+
List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("hello world"), Arrays.asList("hello world"));
18+
19+
assertEquals(1, rows.size());
20+
assertEquals("hello world", rows.get(0).getOldLine());
21+
assertEquals("hello world", rows.get(0).getNewLine());
22+
assertEquals(DiffRow.Tag.EQUAL, rows.get(0).getTag());
23+
}
24+
25+
@Test
26+
public void testCustomEqualityProcessingIsApplied() {
27+
DiffRowGenerator generator = DiffRowGenerator.create()
28+
.showInlineDiffs(false)
29+
.processEqualities(text -> "[" + text + "]")
30+
.build();
31+
32+
List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("A", "B"), Arrays.asList("A", "B"));
33+
34+
assertEquals(2, rows.size());
35+
assertEquals("[A]", rows.get(0).getOldLine());
36+
assertEquals("[B]", rows.get(1).getOldLine());
37+
}
38+
39+
/**
40+
* Verifies that processEqualities can be used to HTML-escape unchanged
41+
* lines while still working together with the default HTML-oriented
42+
* lineNormalizer.
43+
*/
44+
@Test
45+
public void testHtmlEscapingEqualitiesWorksWithDefaultNormalizer() {
46+
DiffRowGenerator generator = DiffRowGenerator.create()
47+
.showInlineDiffs(true)
48+
.inlineDiffByWord(true)
49+
.processEqualities(s -> s.replace("<", "&lt;").replace(">", "&gt;"))
50+
.build();
51+
52+
// both lines are equal -> Tag.EQUAL, processEqualities is invoked
53+
List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("hello <world>"), Arrays.asList("hello <world>"));
54+
55+
DiffRow row = rows.get(0);
56+
57+
assertTrue(row.getOldLine().contains("&lt;world&gt;"));
58+
assertTrue(row.getNewLine().contains("&lt;world&gt;"));
59+
}
60+
61+
/**
62+
* Ensures equalities are processed while inline diff markup is still
63+
* present somewhere in the line.
64+
*/
65+
@Test
66+
public void testEqualitiesProcessedButInlineDiffStillPresent() {
67+
DiffRowGenerator generator = DiffRowGenerator.create()
68+
.showInlineDiffs(true)
69+
.inlineDiffByWord(true)
70+
.processEqualities(s -> "(" + s + ")")
71+
.build();
72+
73+
List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("hello world"), Arrays.asList("hello there"));
74+
75+
DiffRow row = rows.get(0);
76+
77+
System.out.println("OLD = " + row.getOldLine());
78+
System.out.println("NEW = " + row.getNewLine());
79+
80+
// Row must be CHANGE
81+
assertEquals(DiffRow.Tag.CHANGE, row.getTag());
82+
83+
// Inline diff markup must appear
84+
assertTrue(
85+
row.getOldLine().contains("span") || row.getNewLine().contains("span"),
86+
"Expected inline <span> diff markup in old or new line");
87+
88+
// Equalities inside CHANGE row must NOT be wrapped by processEqualities
89+
// Option 3 does NOT modify inline equalities
90+
assertTrue(row.getOldLine().startsWith("hello "), "Equal (unchanged) inline segment should remain unchanged");
91+
}
92+
}

0 commit comments

Comments
 (0)