Skip to content

Commit e3924b0

Browse files
committed
implemented first version of inline merge diff
1 parent 4c03ec4 commit e3924b0

File tree

2 files changed

+157
-141
lines changed

2 files changed

+157
-141
lines changed

src/main/java/difflib/text/DiffRowGenerator.java

Lines changed: 105 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public class DiffRowGenerator {
5656
private final String inlineNewCssClass;
5757
private final int columnWidth;
5858
private final Equalizer<String> equalizer;
59+
private final boolean mergeInline;
5960

6061
/**
6162
* This class used for building the DiffRowGenerator.
@@ -72,6 +73,7 @@ public static class Builder {
7273
private String inlineOldCssClass = "editOldInline";
7374
private String inlineNewCssClass = "editNewInline";
7475
private int columnWidth = 80;
76+
private boolean mergeInline = false;
7577

7678
private Builder() {
7779
}
@@ -163,6 +165,12 @@ public Builder columnWidth(int width) {
163165
public DiffRowGenerator build() {
164166
return new DiffRowGenerator(this);
165167
}
168+
169+
public Builder mergeInline(boolean mergeInline) {
170+
this.mergeInline = mergeInline;
171+
return this;
172+
}
173+
166174
}
167175

168176
public static Builder create() {
@@ -171,12 +179,13 @@ public static Builder create() {
171179

172180
private DiffRowGenerator(Builder builder) {
173181
showInlineDiffs = builder.showInlineDiffs;
174-
ignoreWhiteSpaces = builder.ignoreWhiteSpaces; //
182+
ignoreWhiteSpaces = builder.ignoreWhiteSpaces;
175183
inlineOldTag = builder.inlineOldTag;
176184
inlineNewTag = builder.inlineNewTag;
177185
inlineOldCssClass = builder.inlineOldCssClass;
178186
inlineNewCssClass = builder.inlineNewCssClass;
179-
columnWidth = builder.columnWidth; //
187+
columnWidth = builder.columnWidth;
188+
mergeInline = builder.mergeInline;
180189
equalizer = new Equalizer<String>() {
181190
@Override
182191
public boolean equals(String original, String revised) {
@@ -202,9 +211,21 @@ public List<DiffRow> generateDiffRows(List<String> original, List<String> revise
202211
}
203212

204213
private DiffRow buildDiffRow(Tag type, String orgline, String newline) {
205-
return new DiffRow(type,
206-
StringUtils.wrapText(StringUtils.normalize(orgline), columnWidth),
207-
StringUtils.wrapText(StringUtils.normalize(newline), columnWidth));
214+
String wrapOrg = StringUtils.wrapText(StringUtils.normalize(orgline), columnWidth);
215+
if (showInlineDiffs && Tag.DELETE == type) {
216+
wrapOrg = createOpenTag(inlineOldTag, inlineOldCssClass) + wrapOrg + createCloseTag(inlineOldTag);
217+
}
218+
String wrapNew = StringUtils.wrapText(StringUtils.normalize(newline), columnWidth);
219+
if (showInlineDiffs && Tag.INSERT == type) {
220+
wrapOrg = createOpenTag(inlineNewTag, inlineNewCssClass) + wrapNew + createCloseTag(inlineNewTag);
221+
}
222+
return new DiffRow(type, wrapOrg, wrapNew);
223+
}
224+
225+
private DiffRow buildDiffRowWithoutNormalizing(Tag type, String orgline, String newline) {
226+
return new DiffRow(type,
227+
StringUtils.wrapText(orgline, columnWidth),
228+
StringUtils.wrapText(newline, columnWidth));
208229
}
209230

210231
/**
@@ -217,14 +238,6 @@ private DiffRow buildDiffRow(Tag type, String orgline, String newline) {
217238
* @return the DiffRows between original and revised texts
218239
*/
219240
public List<DiffRow> generateDiffRows(final List<String> original, Patch<String> patch) throws DiffException {
220-
// normalize the lines (expand tabs, escape html entities)
221-
//List<String> original = originalText;
222-
//List<String> revised = StringUtils.normalize(revisedText);
223-
224-
// wrap to the column width
225-
// TODO: we want to process original text und not wrapped text
226-
//original = StringUtils.wrapText(original, this.columnWidth);
227-
//revised = StringUtils.wrapText(revised, this.columnWidth);
228241
List<DiffRow> diffRows = new ArrayList<>();
229242
int endPos = 0;
230243
final List<Delta<String>> deltaList = patch.getDeltas();
@@ -233,15 +246,6 @@ public List<DiffRow> generateDiffRows(final List<String> original, Patch<String>
233246
Chunk<String> orig = delta.getOriginal();
234247
Chunk<String> rev = delta.getRevised();
235248

236-
// We should normalize and wrap lines in deltas too.
237-
// TODO: not in Deltas
238-
//orig.setLines(StringUtils.normalize((List<String>) orig.getLines()));
239-
//rev.setLines(StringUtils.normalize((List<String>) rev.getLines()));
240-
241-
//TODO: no we shouldnt: Deltas should not be used for display purposes
242-
//orig.setLines(StringUtils.wrapText((List<String>) orig.getLines(), this.columnWidth));
243-
//rev.setLines(StringUtils.wrapText((List<String>) rev.getLines(), this.columnWidth));
244-
// catch the equal prefix for each chunk
245249
for (String line : original.subList(endPos, orig.getPosition())) {
246250
diffRows.add(buildDiffRow(Tag.EQUAL, line, line));
247251
}
@@ -265,23 +269,12 @@ public List<DiffRow> generateDiffRows(final List<String> original, Patch<String>
265269
}
266270

267271
if (showInlineDiffs) {
268-
diffRows.addAll(generateInlineDiffs(delta, false));
272+
diffRows.addAll(generateInlineDiffs(delta));
269273
} else {
270-
if (orig.size() == rev.size()) {
271-
for (int j = 0; j < orig.size(); j++) {
272-
diffRows.add(buildDiffRow(Tag.CHANGE, (String) orig.getLines().get(j),
273-
(String) rev.getLines().get(j)));
274-
}
275-
} else if (orig.size() > rev.size()) {
276-
for (int j = 0; j < orig.size(); j++) {
277-
diffRows.add(buildDiffRow(Tag.CHANGE, (String) orig.getLines().get(j), rev
278-
.getLines().size() > j ? (String) rev.getLines().get(j) : ""));
279-
}
280-
} else {
281-
for (int j = 0; j < rev.size(); j++) {
282-
diffRows.add(buildDiffRow(Tag.CHANGE, orig.getLines().size() > j ? (String) orig
283-
.getLines().get(j) : "", (String) rev.getLines().get(j)));
284-
}
274+
for (int j = 0; j < Math.max(orig.size(), rev.size()); j++) {
275+
diffRows.add(buildDiffRow(Tag.CHANGE,
276+
orig.getLines().size() > j ? orig.getLines().get(j) : "",
277+
rev.getLines().size() > j ? rev.getLines().get(j) : ""));
285278
}
286279
}
287280
endPos = orig.last() + 1;
@@ -299,60 +292,73 @@ public List<DiffRow> generateDiffRows(final List<String> original, Patch<String>
299292
*
300293
* @param delta the given delta
301294
*/
302-
private List<DiffRow> generateInlineDiffs(Delta<String> delta, boolean mergeToTarget) throws DiffException {
303-
if (delta.getType()!=Delta.DeltaType.CHANGE)
304-
throw new IllegalArgumentException("only for change deltas allowed");
305-
// List<String> orig = delta.getOriginal().getLines();
306-
// List<String> rev = delta.getRevised().getLines();
307-
// LinkedList<String> origList = new LinkedList<>();
308-
// for (Character character : String.join("\n", orig).toCharArray()) {
309-
// origList.add(character.toString());
310-
// }
311-
// LinkedList<String> revList = new LinkedList<>();
312-
// for (Character character : String.join("\n", rev).toCharArray()) {
313-
// revList.add(character.toString());
314-
// }
315-
// List<Delta<String>> inlineDeltas = DiffUtils.diff(origList, revList).getDeltas();
316-
Patch<String> patch = DiffUtils.diffInline(String.join("\n", delta.getOriginal().getLines()), String.join("\n", delta.getRevised().getLines()));
317-
if (patch.getDeltas().size() < 3) {
318-
List<Delta<String>> deltas = new ArrayList<>(patch.getDeltas());
319-
Collections.reverse(deltas);
320-
321-
for (Delta<String> inlineDelta : deltas) {
322-
System.out.println(inlineDelta);
295+
private List<DiffRow> generateInlineDiffs(Delta<String> delta) throws DiffException {
296+
List<String> orig = StringUtils.normalize(delta.getOriginal().getLines());
297+
List<String> rev = StringUtils.normalize(delta.getRevised().getLines());
298+
LinkedList<String> origList = new LinkedList<>();
299+
for (Character character : String.join("\n", orig).toCharArray()) {
300+
origList.add(character.toString());
301+
}
302+
LinkedList<String> revList = new LinkedList<>();
303+
for (Character character : String.join("\n", rev).toCharArray()) {
304+
revList.add(character.toString());
305+
}
306+
List<Delta<String>> inlineDeltas = DiffUtils.diff(origList, revList).getDeltas();
307+
308+
Collections.reverse(inlineDeltas);
309+
for (Delta<String> inlineDelta : inlineDeltas) {
310+
Chunk<String> inlineOrig = inlineDelta.getOriginal();
311+
Chunk<String> inlineRev = inlineDelta.getRevised();
312+
if (inlineDelta instanceof DeleteDelta) {
313+
wrapInTag(origList, inlineOrig.getPosition(), inlineOrig
314+
.getPosition()
315+
+ inlineOrig.size() + 1, this.inlineOldTag, this.inlineOldCssClass);
316+
} else if (inlineDelta instanceof InsertDelta) {
317+
if (mergeInline) {
318+
origList.addAll(inlineOrig.getPosition(),
319+
revList.subList(inlineRev.getPosition(), inlineRev.getPosition()
320+
+ inlineRev.size()));
321+
wrapInTag(origList, inlineOrig.getPosition(), inlineOrig.getPosition()
322+
+ inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass);
323+
} else {
324+
wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition()
325+
+ inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass);
326+
}
327+
} else if (inlineDelta instanceof ChangeDelta) {
328+
if (mergeInline) {
329+
origList.addAll(inlineOrig.getPosition(),
330+
revList.subList(inlineRev.getPosition(), inlineRev.getPosition()
331+
+ inlineRev.size()));
332+
wrapInTag(origList, inlineOrig.getPosition(), inlineOrig.getPosition()
333+
+ inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass);
334+
} else {
335+
wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition()
336+
+ inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass);
337+
}
338+
wrapInTag(origList, inlineOrig.getPosition(), inlineOrig
339+
.getPosition()
340+
+ inlineOrig.size() + 1, this.inlineOldTag, this.inlineOldCssClass);
323341
}
324-
325-
326-
return Collections.EMPTY_LIST;
327-
// Collections.reverse(inlineDeltas);
328-
// for (Delta<String> inlineDelta : inlineDeltas) {
329-
// Chunk<String> inlineOrig = inlineDelta.getOriginal();
330-
// Chunk<String> inlineRev = inlineDelta.getRevised();
331-
// if (inlineDelta instanceof DeleteDelta) {
332-
// origList = wrapInTag(origList, inlineOrig.getPosition(), inlineOrig
333-
// .getPosition()
334-
// + inlineOrig.size() + 1, this.inlineOldTag, this.inlineOldCssClass);
335-
// } else if (inlineDelta instanceof InsertDelta) {
336-
// revList = wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition()
337-
// + inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass);
338-
// } else if (inlineDelta instanceof ChangeDelta) {
339-
// origList = wrapInTag(origList, inlineOrig.getPosition(), inlineOrig
340-
// .getPosition()
341-
// + inlineOrig.size() + 1, this.inlineOldTag, this.inlineOldCssClass);
342-
// revList = wrapInTag(revList, inlineRev.getPosition(), inlineRev.getPosition()
343-
// + inlineRev.size() + 1, this.inlineNewTag, this.inlineNewCssClass);
344-
// }
345-
// }
346-
// StringBuilder origResult = new StringBuilder(), revResult = new StringBuilder();
347-
// for (String character : origList) {
348-
// origResult.append(character);
349-
// }
350-
// for (String character : revList) {
351-
// revResult.append(character);
352-
// }
353-
// delta.getOriginal().setLines(Arrays.asList(origResult.toString().split("\n")));
354-
// delta.getRevised().setLines(Arrays.asList(revResult.toString().split("\n")));
355-
} else return Collections.emptyList();
342+
}
343+
StringBuilder origResult = new StringBuilder();
344+
StringBuilder revResult = new StringBuilder();
345+
for (String character : origList) {
346+
origResult.append(character);
347+
}
348+
for (String character : revList) {
349+
revResult.append(character);
350+
}
351+
352+
List<String> original = Arrays.asList(origResult.toString().split("\n"));
353+
List<String> revised = Arrays.asList(revResult.toString().split("\n"));
354+
List<DiffRow> diffRows = new ArrayList<>();
355+
for (int j = 0; j < Math.max(original.size(), revised.size()); j++) {
356+
diffRows.
357+
add(buildDiffRowWithoutNormalizing(Tag.CHANGE,
358+
original.size() > j ? original.get(j) : "",
359+
revised.size() > j ? revised.get(j) : ""));
360+
}
361+
return diffRows;
356362
}
357363

358364
/**
@@ -364,59 +370,17 @@ private List<DiffRow> generateInlineDiffs(Delta<String> delta, boolean mergeToTa
364370
* @param tag the tag name without angle brackets, just a word
365371
* @param cssClass the optional css class
366372
*/
367-
public static LinkedList<String> wrapInTag(LinkedList<String> sequence, int startPosition,
373+
public static void wrapInTag(LinkedList<String> sequence, int startPosition,
368374
int endPosition, String tag, String cssClass) {
369-
LinkedList<String> result = (LinkedList<String>) sequence.clone();
370-
StringBuilder tagBuilder = new StringBuilder();
371-
tagBuilder.append("<");
372-
tagBuilder.append(tag);
373-
if (cssClass != null) {
374-
tagBuilder.append(" class=\"");
375-
tagBuilder.append(cssClass);
376-
tagBuilder.append("\"");
377-
}
378-
tagBuilder.append(">");
379-
String startTag = tagBuilder.toString();
380-
381-
tagBuilder.delete(0, tagBuilder.length());
382-
383-
tagBuilder.append("</");
384-
tagBuilder.append(tag);
385-
tagBuilder.append(">");
386-
String endTag = tagBuilder.toString();
387-
388-
result.add(startPosition, startTag);
389-
result.add(endPosition, endTag);
390-
return result;
375+
sequence.add(startPosition, createOpenTag(tag, cssClass));
376+
sequence.add(endPosition, createCloseTag(tag));
391377
}
392378

393-
/**
394-
* Wrap the given line with the given tag
395-
*
396-
* @param line the given line
397-
* @param tag the tag name without angle brackets, just a word
398-
* @param cssClass the optional css class
399-
* @return the wrapped string
400-
*/
401-
public static String wrapInTag(String line, String tag, String cssClass) {
402-
StringBuilder tagBuilder = new StringBuilder();
403-
tagBuilder.append("<");
404-
tagBuilder.append(tag);
405-
if (cssClass != null) {
406-
tagBuilder.append(" class=\"");
407-
tagBuilder.append(cssClass);
408-
tagBuilder.append("\"");
409-
}
410-
tagBuilder.append(">");
411-
String startTag = tagBuilder.toString();
412-
413-
tagBuilder.delete(0, tagBuilder.length());
414-
415-
tagBuilder.append("</");
416-
tagBuilder.append(tag);
417-
tagBuilder.append(">");
418-
String endTag = tagBuilder.toString();
379+
private static String createCloseTag(String tag) {
380+
return "</" + tag + ">";
381+
}
419382

420-
return startTag + line + endTag;
383+
private static String createOpenTag(String tag, String cssClass) {
384+
return "<" + tag + (cssClass != null ? " class=\"" + cssClass + "\"" : "") + ">";
421385
}
422386
}

src/test/java/difflib/text/DiffRowGeneratorTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,56 @@ public void testGeneratorWithWordWrap() throws DiffException {
8686
assertEquals("[CHANGE, ,]", rows.get(1).toString());
8787
assertEquals("[EQUAL,other,other]", rows.get(2).toString());
8888
}
89+
90+
@Test
91+
public void testGeneratorWithInlineMerge() throws DiffException {
92+
String first = "anything \n \nother";
93+
String second = "anything\n\nother";
94+
95+
DiffRowGenerator generator = DiffRowGenerator.create()
96+
.showInlineDiffs(true)
97+
.mergeInline(true)
98+
.build();
99+
List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
100+
print(rows);
101+
102+
assertEquals(3, rows.size());
103+
assertEquals("[CHANGE,anything<span class=\"editOldInline\"> </span>,anything]", rows.get(0).toString());
104+
assertEquals("[CHANGE,<span class=\"editOldInline\"> </span>,]", rows.get(1).toString());
105+
assertEquals("[EQUAL,other,other]", rows.get(2).toString());
106+
}
107+
108+
@Test
109+
public void testGeneratorWithInlineMerge2() throws DiffException {
110+
DiffRowGenerator generator = DiffRowGenerator.create()
111+
.showInlineDiffs(true)
112+
.mergeInline(true)
113+
.build();
114+
List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("Test"),Arrays.asList("ester"));
115+
print(rows);
116+
117+
assertEquals(1, rows.size());
118+
assertEquals("[CHANGE,<span class=\"editOldInline\">T</span>est<span class=\"editNewInline\">er</span>,ester]", rows.get(0).toString());
119+
}
120+
121+
@Test
122+
public void testGeneratorWithInlineMerge3() throws DiffException {
123+
String first = "test\nanything \n \nother";
124+
String second = "anything\n\nother\ntest\ntest2";
125+
126+
DiffRowGenerator generator = DiffRowGenerator.create()
127+
.showInlineDiffs(true)
128+
.mergeInline(true)
129+
.build();
130+
List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
131+
print(rows);
132+
133+
assertEquals(6, rows.size());
134+
assertEquals("[CHANGE,<span class=\"editOldInline\">test,anything]", rows.get(0).toString());
135+
assertEquals("[CHANGE,</span>anything<span class=\"editOldInline\"> </span>,]", rows.get(1).toString());
136+
assertEquals("[CHANGE,<span class=\"editOldInline\"> </span>,]", rows.get(2).toString());
137+
assertEquals("[EQUAL,other,other]", rows.get(3).toString());
138+
assertEquals("[INSERT,<span class=\"editNewInline\">test</span>,test]", rows.get(4).toString());
139+
assertEquals("[INSERT,<span class=\"editNewInline\">test2</span>,test2]", rows.get(5).toString());
140+
}
89141
}

0 commit comments

Comments
 (0)