Skip to content

Commit 7f5c3fa

Browse files
committed
Merge origin/master
2 parents 527e83c + b5b7dac commit 7f5c3fa

File tree

5 files changed

+207
-2
lines changed

5 files changed

+207
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ Just add the code below to your maven dependencies:
125125
<dependency>
126126
<groupId>io.github.java-diff-utils</groupId>
127127
<artifactId>java-diff-utils</artifactId>
128-
<version>4.12</version>
128+
<version>4.15</version>
129129
</dependency>
130130
```
131131

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import com.github.difflib.patch.InsertDelta;
2525
import com.github.difflib.patch.Patch;
2626
import com.github.difflib.text.DiffRow.Tag;
27+
import com.github.difflib.text.deltamerge.DeltaMergeUtils;
28+
import com.github.difflib.text.deltamerge.InlineDeltaMergeInfo;
2729
import java.util.*;
2830
import java.util.function.BiFunction;
2931
import java.util.function.BiPredicate;
@@ -75,6 +77,14 @@ public final class DiffRowGenerator {
7577
public static final Function<String, List<String>> SPLITTER_BY_WORD = line -> splitStringPreserveDelimiter(line, SPLIT_BY_WORD_PATTERN);
7678
public static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
7779

80+
public static final Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> DEFAULT_INLINE_DELTA_MERGER = InlineDeltaMergeInfo::getDeltas;
81+
82+
/**
83+
* Merge diffs which are separated by equalities consisting of whitespace only.
84+
*/
85+
public static final Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> WHITESPACE_EQUALITIES_MERGER = deltaMergeInfo -> DeltaMergeUtils
86+
.mergeInlineDeltas(deltaMergeInfo, (equalities -> equalities.stream().allMatch(String::isBlank)));
87+
7888
public static Builder create() {
7989
return new Builder();
8090
}
@@ -170,6 +180,7 @@ static void wrapInTag(List<String> sequence, int startPosition,
170180
private final boolean reportLinesUnchanged;
171181
private final Function<String, String> lineNormalizer;
172182
private final Function<String, String> processDiffs;
183+
private final Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> inlineDeltaMerger;
173184

174185
private final boolean showInlineDiffs;
175186
private final boolean replaceOriginalLinefeedInChangesWithSpaces;
@@ -194,11 +205,13 @@ private DiffRowGenerator(Builder builder) {
194205
reportLinesUnchanged = builder.reportLinesUnchanged;
195206
lineNormalizer = builder.lineNormalizer;
196207
processDiffs = builder.processDiffs;
208+
inlineDeltaMerger = builder.inlineDeltaMerger;
197209

198210
replaceOriginalLinefeedInChangesWithSpaces = builder.replaceOriginalLinefeedInChangesWithSpaces;
199211

200212
Objects.requireNonNull(inlineDiffSplitter);
201213
Objects.requireNonNull(lineNormalizer);
214+
Objects.requireNonNull(inlineDeltaMerger);
202215
}
203216

204217
/**
@@ -370,7 +383,10 @@ private List<DiffRow> generateInlineDiffs(AbstractDelta<String> delta) {
370383
origList = inlineDiffSplitter.apply(joinedOrig);
371384
revList = inlineDiffSplitter.apply(joinedRev);
372385

373-
List<AbstractDelta<String>> inlineDeltas = DiffUtils.diff(origList, revList, equalizer).getDeltas();
386+
List<AbstractDelta<String>> originalInlineDeltas = DiffUtils.diff(origList, revList, equalizer)
387+
.getDeltas();
388+
List<AbstractDelta<String>> inlineDeltas = inlineDeltaMerger
389+
.apply(new InlineDeltaMergeInfo(originalInlineDeltas, origList, revList));
374390

375391
Collections.reverse(inlineDeltas);
376392
for (AbstractDelta<String> inlineDelta : inlineDeltas) {
@@ -465,6 +481,7 @@ public static class Builder {
465481
private Function<String, String> processDiffs = null;
466482
private BiPredicate<String, String> equalizer = null;
467483
private boolean replaceOriginalLinefeedInChangesWithSpaces = false;
484+
private Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> inlineDeltaMerger = DEFAULT_INLINE_DELTA_MERGER;
468485

469486
private Builder() {
470487
}
@@ -673,5 +690,17 @@ public Builder replaceOriginalLinefeedInChangesWithSpaces(boolean replace) {
673690
this.replaceOriginalLinefeedInChangesWithSpaces = replace;
674691
return this;
675692
}
693+
694+
/**
695+
* Provide an inline delta merger for use case specific delta optimizations.
696+
*
697+
* @param inlineDeltaMerger
698+
* @return
699+
*/
700+
public Builder inlineDeltaMerger(
701+
Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> inlineDeltaMerger) {
702+
this.inlineDeltaMerger = inlineDeltaMerger;
703+
return this;
704+
}
676705
}
677706
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2009-2024 java-diff-utils.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.github.difflib.text.deltamerge;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import java.util.function.Predicate;
21+
22+
import com.github.difflib.patch.AbstractDelta;
23+
import com.github.difflib.patch.ChangeDelta;
24+
import com.github.difflib.patch.Chunk;
25+
26+
/**
27+
* Provides utility features for merge inline deltas
28+
*
29+
* @author <a href="christian.meier@epictec.ch">Christian Meier</a>
30+
*/
31+
final public class DeltaMergeUtils {
32+
33+
public static List<AbstractDelta<String>> mergeInlineDeltas(InlineDeltaMergeInfo deltaMergeInfo,
34+
Predicate<List<String>> replaceEquality) {
35+
final List<AbstractDelta<String>> originalDeltas = deltaMergeInfo.getDeltas();
36+
if (originalDeltas.size() < 2) {
37+
return originalDeltas;
38+
}
39+
40+
final List<AbstractDelta<String>> newDeltas = new ArrayList<>();
41+
newDeltas.add(originalDeltas.get(0));
42+
for (int i = 1; i < originalDeltas.size(); i++) {
43+
final AbstractDelta<String> previousDelta = newDeltas.getLast();
44+
final AbstractDelta<String> currentDelta = originalDeltas.get(i);
45+
46+
final List<String> equalities = deltaMergeInfo.getOrigList().subList(
47+
previousDelta.getSource().getPosition() + previousDelta.getSource().size(),
48+
currentDelta.getSource().getPosition());
49+
50+
if (replaceEquality.test(equalities)) {
51+
// Merge the previous delta, the equality and the current delta into one
52+
// ChangeDelta and replace the previous delta by this new ChangeDelta.
53+
final List<String> allSourceLines = new ArrayList<>();
54+
allSourceLines.addAll(previousDelta.getSource().getLines());
55+
allSourceLines.addAll(equalities);
56+
allSourceLines.addAll(currentDelta.getSource().getLines());
57+
58+
final List<String> allTargetLines = new ArrayList<>();
59+
allTargetLines.addAll(previousDelta.getTarget().getLines());
60+
allTargetLines.addAll(equalities);
61+
allTargetLines.addAll(currentDelta.getTarget().getLines());
62+
63+
final ChangeDelta<String> replacement = new ChangeDelta<>(
64+
new Chunk<>(previousDelta.getSource().getPosition(), allSourceLines),
65+
new Chunk<>(previousDelta.getTarget().getPosition(), allTargetLines));
66+
67+
newDeltas.removeLast();
68+
newDeltas.add(replacement);
69+
} else {
70+
newDeltas.add(currentDelta);
71+
}
72+
}
73+
74+
return newDeltas;
75+
}
76+
77+
private DeltaMergeUtils() {
78+
}
79+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2009-2024 java-diff-utils.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.github.difflib.text.deltamerge;
17+
18+
import java.util.List;
19+
20+
import com.github.difflib.patch.AbstractDelta;
21+
22+
/**
23+
* Holds the information required to merge deltas originating from an inline
24+
* diff
25+
*
26+
* @author <a href="christian.meier@epictec.ch">Christian Meier</a>
27+
*/
28+
public final class InlineDeltaMergeInfo {
29+
30+
private final List<AbstractDelta<String>> deltas;
31+
private final List<String> origList;
32+
private final List<String> revList;
33+
34+
public InlineDeltaMergeInfo(List<AbstractDelta<String>> deltas, List<String> origList, List<String> revList) {
35+
this.deltas = deltas;
36+
this.origList = origList;
37+
this.revList = revList;
38+
}
39+
40+
public List<AbstractDelta<String>> getDeltas() {
41+
return deltas;
42+
}
43+
44+
public List<String> getOrigList() {
45+
return origList;
46+
}
47+
48+
public List<String> getRevList() {
49+
return revList;
50+
}
51+
}

java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.Arrays;
1313
import java.util.Collections;
1414
import java.util.List;
15+
import java.util.function.Function;
1516
import java.util.regex.Pattern;
1617
import static java.util.stream.Collectors.joining;
1718
import static java.util.stream.Collectors.toList;
@@ -20,6 +21,10 @@
2021
import static org.junit.jupiter.api.Assertions.assertTrue;
2122
import org.junit.jupiter.api.Test;
2223

24+
import com.github.difflib.patch.AbstractDelta;
25+
import com.github.difflib.text.deltamerge.DeltaMergeUtils;
26+
import com.github.difflib.text.deltamerge.InlineDeltaMergeInfo;
27+
2328
public class DiffRowGeneratorTest {
2429

2530
@Test
@@ -791,6 +796,47 @@ public void testIssue129SkipWhitespaceChanges() throws IOException {
791796
.forEach(System.out::println);
792797
}
793798

799+
@Test
800+
public void testGeneratorWithWhitespaceDeltaMerge() {
801+
final DiffRowGenerator generator = DiffRowGenerator.create().showInlineDiffs(true).mergeOriginalRevised(true)
802+
.inlineDiffByWord(true).oldTag(f -> "~").newTag(f -> "**") //
803+
.lineNormalizer(StringUtils::htmlEntites) // do not replace tabs
804+
.inlineDeltaMerger(DiffRowGenerator.WHITESPACE_EQUALITIES_MERGER).build();
805+
806+
assertInlineDiffResult(generator, "No diff", "No diff", "No diff");
807+
assertInlineDiffResult(generator, " x whitespace before diff", " y whitespace before diff",
808+
" ~x~**y** whitespace before diff");
809+
assertInlineDiffResult(generator, "Whitespace after diff x ", "Whitespace after diff y ",
810+
"Whitespace after diff ~x~**y** ");
811+
assertInlineDiffResult(generator, "Diff x x between", "Diff y y between", "Diff ~x x~**y y** between");
812+
assertInlineDiffResult(generator, "Hello \t world", "Hi \t universe", "~Hello \t world~**Hi \t universe**");
813+
assertInlineDiffResult(generator, "The quick brown fox jumps over the lazy dog", "A lazy dog jumps over a fox",
814+
"~The quick brown fox ~**A lazy dog **jumps over ~the lazy dog~**a fox**");
815+
}
816+
817+
@Test
818+
public void testGeneratorWithMergingDeltasForShortEqualities() {
819+
final Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> shortEqualitiesMerger = deltaMergeInfo -> DeltaMergeUtils
820+
.mergeInlineDeltas(deltaMergeInfo,
821+
(equalities -> equalities.stream().mapToInt(String::length).sum() < 6));
822+
823+
final DiffRowGenerator generator = DiffRowGenerator.create().showInlineDiffs(true).mergeOriginalRevised(true)
824+
.inlineDiffByWord(true).oldTag(f -> "~").newTag(f -> "**").inlineDeltaMerger(shortEqualitiesMerger)
825+
.build();
826+
827+
assertInlineDiffResult(generator, "No diff", "No diff", "No diff");
828+
assertInlineDiffResult(generator, "aaa bbb ccc", "xxx bbb zzz", "~aaa bbb ccc~**xxx bbb zzz**");
829+
assertInlineDiffResult(generator, "aaa bbbb ccc", "xxx bbbb zzz", "~aaa~**xxx** bbbb ~ccc~**zzz**");
830+
}
831+
832+
private void assertInlineDiffResult(DiffRowGenerator generator, String original, String revised, String expected) {
833+
final List<DiffRow> rows = generator.generateDiffRows(Arrays.asList(original), Arrays.asList(revised));
834+
print(rows);
835+
836+
assertEquals(1, rows.size());
837+
assertEquals(expected, rows.get(0).getOldLine().toString());
838+
}
839+
794840
@Test
795841
public void testIssue188HangOnExamples() throws IOException, URISyntaxException {
796842
try (FileSystem zipFs = FileSystems.newFileSystem(Paths.get("target/test-classes/com/github/difflib/text/test.zip"), null);) {

0 commit comments

Comments
 (0)