diff --git a/README.md b/README.md index 70dfa521..06c39d76 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Just add the code below to your maven dependencies: io.github.java-diff-utils java-diff-utils - 4.12 + 4.15 ``` diff --git a/java-diff-utils-jgit/pom.xml b/java-diff-utils-jgit/pom.xml index 6d0fbdd2..5b01d2c5 100644 --- a/java-diff-utils-jgit/pom.xml +++ b/java-diff-utils-jgit/pom.xml @@ -4,7 +4,7 @@ io.github.java-diff-utils java-diff-utils-parent - 4.13-SNAPSHOT + 4.16-SNAPSHOT java-diff-utils-jgit java-diff-utils-jgit @@ -18,6 +18,7 @@ test + org.eclipse.jgit org.eclipse.jgit 5.13.3.202401111512-r diff --git a/java-diff-utils-jgit/src/main/java/com/github/difflib/algorithm/jgit/HistogramDiff.java b/java-diff-utils-jgit/src/main/java/com/github/difflib/algorithm/jgit/HistogramDiff.java index e3f6cd5b..6d15f609 100644 --- a/java-diff-utils-jgit/src/main/java/com/github/difflib/algorithm/jgit/HistogramDiff.java +++ b/java-diff-utils-jgit/src/main/java/com/github/difflib/algorithm/jgit/HistogramDiff.java @@ -28,7 +28,7 @@ import org.eclipse.jgit.diff.SequenceComparator; /** - * HistorgramDiff using JGit - Library. This one is much more performant than the orginal Myers + * HistorgramDiff using JGit - Library. This one is much more performant than the original Myers * implementation. * * @author toben diff --git a/java-diff-utils/pom.xml b/java-diff-utils/pom.xml index c18e88b0..95560b01 100644 --- a/java-diff-utils/pom.xml +++ b/java-diff-utils/pom.xml @@ -7,7 +7,7 @@ io.github.java-diff-utils java-diff-utils-parent - 4.13-SNAPSHOT + 4.16-SNAPSHOT UTF-8 diff --git a/java-diff-utils/src/main/java/com/github/difflib/DiffUtils.java b/java-diff-utils/src/main/java/com/github/difflib/DiffUtils.java index 8917772b..e0ac521f 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/DiffUtils.java +++ b/java-diff-utils/src/main/java/com/github/difflib/DiffUtils.java @@ -42,7 +42,7 @@ public final class DiffUtils { /** * Sets the default diff algorithm factory to be used by all diff routines. * - * @param factory a {@link DiffAlgorithmFactory} represnting the new default diff algorithm factory. + * @param factory a {@link DiffAlgorithmFactory} representing the new default diff algorithm factory. */ public static void withDefaultDiffAlgorithmFactory(DiffAlgorithmFactory factory) { DEFAULT_DIFF = factory; @@ -52,9 +52,9 @@ public static void withDefaultDiffAlgorithmFactory(DiffAlgorithmFactory factory) * Computes the difference between two sequences of elements using the default diff algorithm. * * @param a generic representing the type of the elements to be compared. - * @param original a {@link List} represnting the original sequence of elements. Must not be {@code null}. - * @param revised a {@link List} represnting the revised sequence of elements. Must not be {@code null}. - * @param progress a {@link DiffAlgorithmListener} represnting the progress listener. Can be {@code null}. + * @param original a {@link List} representing the original sequence of elements. Must not be {@code null}. + * @param revised a {@link List} representing the revised sequence of elements. Must not be {@code null}. + * @param progress a {@link DiffAlgorithmListener} representing the progress listener. Can be {@code null}. * @return The patch describing the difference between the original and revised sequences. Never {@code null}. */ public static Patch diff(List original, List revised, DiffAlgorithmListener progress) { @@ -65,8 +65,8 @@ public static Patch diff(List original, List revised, DiffAlgorithm * Computes the difference between two sequences of elements using the default diff algorithm. * * @param a generic representing the type of the elements to be compared. - * @param original a {@link List} represnting the original sequence of elements. Must not be {@code null}. - * @param revised a {@link List} represnting the revised sequence of elements. Must not be {@code null}. + * @param original a {@link List} representing the original sequence of elements. Must not be {@code null}. + * @param revised a {@link List} representing the revised sequence of elements. Must not be {@code null}. * @return The patch describing the difference between the original and revised sequences. Never {@code null}. */ public static Patch diff(List original, List revised) { @@ -77,9 +77,9 @@ public static Patch diff(List original, List revised) { * Computes the difference between two sequences of elements using the default diff algorithm. * * @param a generic representing the type of the elements to be compared. - * @param original a {@link List} represnting the original sequence of elements. Must not be {@code null}. - * @param revised a {@link List} represnting the revised sequence of elements. Must not be {@code null}. - * @param includeEqualParts a {@link boolean} represnting whether to include equal parts in the resulting patch. + * @param original a {@link List} representing the original sequence of elements. Must not be {@code null}. + * @param revised a {@link List} representing the revised sequence of elements. Must not be {@code null}. + * @param includeEqualParts a {@link boolean} representing whether to include equal parts in the resulting patch. * @return The patch describing the difference between the original and revised sequences. Never {@code null}. */ public static Patch diff(List original, List revised, boolean includeEqualParts) { @@ -89,9 +89,9 @@ public static Patch diff(List original, List revised, boolean inclu /** * Computes the difference between two strings using the default diff algorithm. * - * @param sourceText a {@link String} represnting the original string. Must not be {@code null}. - * @param targetText a {@link String} represnting the revised string. Must not be {@code null}. - * @param progress a {@link DiffAlgorithmListener} represnting the progress listener. Can be {@code null}. + * @param sourceText a {@link String} representing the original string. Must not be {@code null}. + * @param targetText a {@link String} representing the revised string. Must not be {@code null}. + * @param progress a {@link DiffAlgorithmListener} representing the progress listener. Can be {@code null}. * @return The patch describing the difference between the original and revised strings. Never {@code null}. */ public static Patch diff(String sourceText, String targetText, @@ -105,9 +105,9 @@ public static Patch diff(String sourceText, String targetText, * Computes the difference between the original and revised list of elements * with default diff algorithm * - * @param source a {@link List} represnting the original text. Must not be {@code null}. - * @param target a {@link List} represnting the revised text. Must not be {@code null}. - * @param equalizer a {@link BiPredicate} represnting the equalizer object to replace the default compare + * @param source a {@link List} representing the original text. Must not be {@code null}. + * @param target a {@link List} representing the revised text. Must not be {@code null}. + * @param equalizer a {@link BiPredicate} representing the equalizer object to replace the default compare * algorithm (Object.equals). If {@code null} the default equalizer of the * default algorithm is used. * @return The patch describing the difference between the original and @@ -131,10 +131,10 @@ public static Patch diff(List original, List revised, * Computes the difference between the original and revised list of elements * with default diff algorithm * - * @param original a {@link List} represnting the original text. Must not be {@code null}. - * @param revised a {@link List} represnting the revised text. Must not be {@code null}. - * @param algorithm a {@link DiffAlgorithmI} represnting the diff algorithm. Must not be {@code null}. - * @param progress a {@link DiffAlgorithmListener} represnting the diff algorithm listener. + * @param original a {@link List} representing the original text. Must not be {@code null}. + * @param revised a {@link List} representing the revised text. Must not be {@code null}. + * @param algorithm a {@link DiffAlgorithmI} representing the diff algorithm. Must not be {@code null}. + * @param progress a {@link DiffAlgorithmListener} representing the diff algorithm listener. * @param includeEqualParts Include equal data parts into the patch. * @return The patch describing the difference between the original and * revised sequences. Never {@code null}. @@ -154,9 +154,9 @@ public static Patch diff(List original, List revised, * Computes the difference between the original and revised list of elements * with default diff algorithm * - * @param original a {@link List} represnting the original text. Must not be {@code null}. - * @param revised a {@link List} represnting the revised text. Must not be {@code null}. - * @param algorithm a {@link DiffAlgorithmI} represnting the diff algorithm. Must not be {@code null}. + * @param original a {@link List} representing the original text. Must not be {@code null}. + * @param revised a {@link List} representing the revised text. Must not be {@code null}. + * @param algorithm a {@link DiffAlgorithmI} representing the diff algorithm. Must not be {@code null}. * @return The patch describing the difference between the original and * revised sequences. Never {@code null}. */ @@ -169,8 +169,8 @@ public static Patch diff(List original, List revised, DiffAlgorithm * "trick" to make out of texts lists of characters, like DiffRowGenerator * does and merges those changes at the end together again. * - * @param original a {@link String} represnting the original text. Must not be {@code null}. - * @param revised a {@link String} represnting the revised text. Must not be {@code null}. + * @param original a {@link String} representing the original text. Must not be {@code null}. + * @param revised a {@link String} representing the revised text. Must not be {@code null}. * @return The patch describing the difference between the original and * revised sequences. Never {@code null}. */ @@ -194,8 +194,8 @@ public static Patch diffInline(String original, String revised) { /** * Applies the given patch to the original list and returns the revised list. * - * @param original a {@link List} represnting the original list. - * @param patch a {@link List} represnting the patch to apply. + * @param original a {@link List} representing the original list. + * @param patch a {@link List} representing the patch to apply. * @return the revised list. * @throws PatchFailedException if the patch cannot be applied. */ @@ -207,8 +207,8 @@ public static List patch(List original, Patch patch) /** * Applies the given patch to the revised list and returns the original list. * - * @param revised a {@link List} represnting the revised list. - * @param patch a {@link Patch} represnting the patch to apply. + * @param revised a {@link List} representing the revised list. + * @param patch a {@link Patch} representing the patch to apply. * @return the original list. * @throws PatchFailedException if the patch cannot be applied. */ diff --git a/java-diff-utils/src/main/java/com/github/difflib/UnifiedDiffUtils.java b/java-diff-utils/src/main/java/com/github/difflib/UnifiedDiffUtils.java index 727008db..94786b6c 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/UnifiedDiffUtils.java +++ b/java-diff-utils/src/main/java/com/github/difflib/UnifiedDiffUtils.java @@ -36,7 +36,7 @@ public final class UnifiedDiffUtils { private static final Pattern UNIFIED_DIFF_CHUNK_REGEXP = Pattern - .compile("^@@\\s+-(?:(\\d+)(?:,(\\d+))?)\\s+\\+(?:(\\d+)(?:,(\\d+))?)\\s+@@$"); + .compile("^@@\\s+-(\\d+)(?:,(\\d+))?\\s+\\+(\\d+)(?:,(\\d+))?\\s+@@.*$"); private static final String NULL_FILE_INDICATOR = "/dev/null"; /** @@ -352,7 +352,7 @@ public static List generateOriginalAndDiff(List original, List generateOriginalAndDiff(List original, List revised, String originalFileName, String revisedFileName) { String originalFileNameTemp = originalFileName; - String revisedFileNameTemp = originalFileName; + String revisedFileNameTemp = revisedFileName; if (originalFileNameTemp == null) { originalFileNameTemp = "original"; } diff --git a/java-diff-utils/src/main/java/com/github/difflib/algorithm/DiffAlgorithmListener.java b/java-diff-utils/src/main/java/com/github/difflib/algorithm/DiffAlgorithmListener.java index 37d51813..a141d7be 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/algorithm/DiffAlgorithmListener.java +++ b/java-diff-utils/src/main/java/com/github/difflib/algorithm/DiffAlgorithmListener.java @@ -24,7 +24,7 @@ public interface DiffAlgorithmListener { /** * This is a step within the diff algorithm. Due to different implementations the value - * is not strict incrementing to the max and is not garantee to reach the max. It could + * is not strict incrementing to the max and is not guarantee to reach the max. It could * stop before. * @param value * @param max diff --git a/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/MyersDiff.java b/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/MyersDiff.java index 54ffed91..2517de46 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/MyersDiff.java +++ b/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/MyersDiff.java @@ -187,13 +187,13 @@ public static DiffAlgorithmFactory factory() { @Override public DiffAlgorithmI create() { - return new MyersDiff(); + return new MyersDiff<>(); } @Override public DiffAlgorithmI create(BiPredicate < T, T > equalizer) { - return new MyersDiff(equalizer); + return new MyersDiff<>(equalizer); } }; } diff --git a/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/MyersDiffWithLinearSpace.java b/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/MyersDiffWithLinearSpace.java index 6d4451b0..ca4114c4 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/MyersDiffWithLinearSpace.java +++ b/java-diff-utils/src/main/java/com/github/difflib/algorithm/myers/MyersDiffWithLinearSpace.java @@ -231,13 +231,13 @@ public static DiffAlgorithmFactory factory() { @Override public DiffAlgorithmI create() { - return new MyersDiffWithLinearSpace(); + return new MyersDiffWithLinearSpace<>(); } @Override public DiffAlgorithmI create(BiPredicate < T, T > equalizer) { - return new MyersDiffWithLinearSpace(equalizer); + return new MyersDiffWithLinearSpace<>(equalizer); } }; } diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java index 7e55ac0d..50054074 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Chunk.java @@ -26,7 +26,7 @@ * *

* Text is represented as Object[] because the diff engine is - * capable of handling more than plain ascci. In fact, arrays or lists of any + * capable of handling more than plain ascii. In fact, arrays or lists of any * type that implements {@link java.lang.Object#hashCode hashCode()} and * {@link java.lang.Object#equals equals()} correctly can be subject to * differencing using this library. diff --git a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java index aaff7d94..305f2de7 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java +++ b/java-diff-utils/src/main/java/com/github/difflib/patch/Patch.java @@ -224,7 +224,7 @@ private int findPositionWithFuzzAndMoreDelta(PatchApplyingContext ctx, Abstra private ConflictOutput conflictOutput = CONFLICT_PRODUCES_EXCEPTION; /** - * Alter normal conflict output behaviour to e.g. inclide some conflict + * Alter normal conflict output behaviour to e.g. include some conflict * statements in the result, like git does it. */ public Patch withConflictOutput(ConflictOutput conflictOutput) { diff --git a/java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java b/java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java index 3711bfb6..9ec50a84 100644 --- a/java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java +++ b/java-diff-utils/src/main/java/com/github/difflib/text/DiffRowGenerator.java @@ -24,6 +24,8 @@ import com.github.difflib.patch.InsertDelta; import com.github.difflib.patch.Patch; import com.github.difflib.text.DiffRow.Tag; +import com.github.difflib.text.deltamerge.DeltaMergeUtils; +import com.github.difflib.text.deltamerge.InlineDeltaMergeInfo; import java.util.*; import java.util.function.BiFunction; import java.util.function.BiPredicate; @@ -75,6 +77,14 @@ public final class DiffRowGenerator { public static final Function> SPLITTER_BY_WORD = line -> splitStringPreserveDelimiter(line, SPLIT_BY_WORD_PATTERN); public static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+"); + public static final Function>> DEFAULT_INLINE_DELTA_MERGER = InlineDeltaMergeInfo::getDeltas; + + /** + * Merge diffs which are separated by equalities consisting of whitespace only. + */ + public static final Function>> WHITESPACE_EQUALITIES_MERGER = deltaMergeInfo -> DeltaMergeUtils + .mergeInlineDeltas(deltaMergeInfo, equalities -> equalities.stream().allMatch(s -> s==null || s.replaceAll("\\s+", "").equals(""))); + public static Builder create() { return new Builder(); } @@ -170,6 +180,7 @@ static void wrapInTag(List sequence, int startPosition, private final boolean reportLinesUnchanged; private final Function lineNormalizer; private final Function processDiffs; + private final Function>> inlineDeltaMerger; private final boolean showInlineDiffs; private final boolean replaceOriginalLinefeedInChangesWithSpaces; @@ -194,11 +205,13 @@ private DiffRowGenerator(Builder builder) { reportLinesUnchanged = builder.reportLinesUnchanged; lineNormalizer = builder.lineNormalizer; processDiffs = builder.processDiffs; + inlineDeltaMerger = builder.inlineDeltaMerger; replaceOriginalLinefeedInChangesWithSpaces = builder.replaceOriginalLinefeedInChangesWithSpaces; Objects.requireNonNull(inlineDiffSplitter); Objects.requireNonNull(lineNormalizer); + Objects.requireNonNull(inlineDeltaMerger); } /** @@ -370,7 +383,10 @@ private List generateInlineDiffs(AbstractDelta delta) { origList = inlineDiffSplitter.apply(joinedOrig); revList = inlineDiffSplitter.apply(joinedRev); - List> inlineDeltas = DiffUtils.diff(origList, revList, equalizer).getDeltas(); + List> originalInlineDeltas = DiffUtils.diff(origList, revList, equalizer) + .getDeltas(); + List> inlineDeltas = inlineDeltaMerger + .apply(new InlineDeltaMergeInfo(originalInlineDeltas, origList, revList)); Collections.reverse(inlineDeltas); for (AbstractDelta inlineDelta : inlineDeltas) { @@ -465,6 +481,7 @@ public static class Builder { private Function processDiffs = null; private BiPredicate equalizer = null; private boolean replaceOriginalLinefeedInChangesWithSpaces = false; + private Function>> inlineDeltaMerger = DEFAULT_INLINE_DELTA_MERGER; private Builder() { } @@ -673,5 +690,17 @@ public Builder replaceOriginalLinefeedInChangesWithSpaces(boolean replace) { this.replaceOriginalLinefeedInChangesWithSpaces = replace; return this; } + + /** + * Provide an inline delta merger for use case specific delta optimizations. + * + * @param inlineDeltaMerger + * @return + */ + public Builder inlineDeltaMerger( + Function>> inlineDeltaMerger) { + this.inlineDeltaMerger = inlineDeltaMerger; + return this; + } } } diff --git a/java-diff-utils/src/main/java/com/github/difflib/text/deltamerge/DeltaMergeUtils.java b/java-diff-utils/src/main/java/com/github/difflib/text/deltamerge/DeltaMergeUtils.java new file mode 100644 index 00000000..b2580957 --- /dev/null +++ b/java-diff-utils/src/main/java/com/github/difflib/text/deltamerge/DeltaMergeUtils.java @@ -0,0 +1,79 @@ +/* + * Copyright 2009-2024 java-diff-utils. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.difflib.text.deltamerge; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +import com.github.difflib.patch.AbstractDelta; +import com.github.difflib.patch.ChangeDelta; +import com.github.difflib.patch.Chunk; + +/** + * Provides utility features for merge inline deltas + * + * @author Christian Meier + */ +final public class DeltaMergeUtils { + + public static List> mergeInlineDeltas(InlineDeltaMergeInfo deltaMergeInfo, + Predicate> replaceEquality) { + final List> originalDeltas = deltaMergeInfo.getDeltas(); + if (originalDeltas.size() < 2) { + return originalDeltas; + } + + final List> newDeltas = new ArrayList<>(); + newDeltas.add(originalDeltas.get(0)); + for (int i = 1; i < originalDeltas.size(); i++) { + final AbstractDelta previousDelta = newDeltas.get(newDeltas.size()-1); + final AbstractDelta currentDelta = originalDeltas.get(i); + + final List equalities = deltaMergeInfo.getOrigList().subList( + previousDelta.getSource().getPosition() + previousDelta.getSource().size(), + currentDelta.getSource().getPosition()); + + if (replaceEquality.test(equalities)) { + // Merge the previous delta, the equality and the current delta into one + // ChangeDelta and replace the previous delta by this new ChangeDelta. + final List allSourceLines = new ArrayList<>(); + allSourceLines.addAll(previousDelta.getSource().getLines()); + allSourceLines.addAll(equalities); + allSourceLines.addAll(currentDelta.getSource().getLines()); + + final List allTargetLines = new ArrayList<>(); + allTargetLines.addAll(previousDelta.getTarget().getLines()); + allTargetLines.addAll(equalities); + allTargetLines.addAll(currentDelta.getTarget().getLines()); + + final ChangeDelta replacement = new ChangeDelta<>( + new Chunk<>(previousDelta.getSource().getPosition(), allSourceLines), + new Chunk<>(previousDelta.getTarget().getPosition(), allTargetLines)); + + newDeltas.remove(newDeltas.size()-1); + newDeltas.add(replacement); + } else { + newDeltas.add(currentDelta); + } + } + + return newDeltas; + } + + private DeltaMergeUtils() { + } +} diff --git a/java-diff-utils/src/main/java/com/github/difflib/text/deltamerge/InlineDeltaMergeInfo.java b/java-diff-utils/src/main/java/com/github/difflib/text/deltamerge/InlineDeltaMergeInfo.java new file mode 100644 index 00000000..cc6b399a --- /dev/null +++ b/java-diff-utils/src/main/java/com/github/difflib/text/deltamerge/InlineDeltaMergeInfo.java @@ -0,0 +1,51 @@ +/* + * Copyright 2009-2024 java-diff-utils. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.difflib.text.deltamerge; + +import java.util.List; + +import com.github.difflib.patch.AbstractDelta; + +/** + * Holds the information required to merge deltas originating from an inline + * diff + * + * @author Christian Meier + */ +public final class InlineDeltaMergeInfo { + + private final List> deltas; + private final List origList; + private final List revList; + + public InlineDeltaMergeInfo(List> deltas, List origList, List revList) { + this.deltas = deltas; + this.origList = origList; + this.revList = revList; + } + + public List> getDeltas() { + return deltas; + } + + public List getOrigList() { + return origList; + } + + public List getRevList() { + return revList; + } +} diff --git a/java-diff-utils/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java b/java-diff-utils/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java index e13d41aa..b8667818 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/GenerateUnifiedDiffTest.java @@ -51,8 +51,14 @@ public void testGenerateUnifiedWithOneDelta() throws IOException { @Test public void testGenerateUnifiedDiffWithoutAnyDeltas() { List test = Arrays.asList("abc"); - Patch patch = DiffUtils.diff(test, test); - UnifiedDiffUtils.generateUnifiedDiff("abc", "abc", test, patch, 0); + List testRevised = Arrays.asList("abc2"); + Patch patch = DiffUtils.diff(test, testRevised); + String unifiedDiffTxt = String.join("\n", UnifiedDiffUtils.generateUnifiedDiff("abc1", "abc2", test, patch, 0)); + System.out.println(unifiedDiffTxt); + + assertThat(unifiedDiffTxt) + .as("original filename should be abc1").contains("--- abc1") + .as("revised filename should be abc2").contains("+++ abc2"); } @Test diff --git a/java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java b/java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java index 897a37f9..beccad8c 100644 --- a/java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java +++ b/java-diff-utils/src/test/java/com/github/difflib/text/DiffRowGeneratorTest.java @@ -1,12 +1,18 @@ package com.github.difflib.text; +import com.github.difflib.DiffUtils; +import com.github.difflib.algorithm.myers.MyersDiffWithLinearSpace; import java.io.File; import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.function.Function; import java.util.regex.Pattern; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; @@ -15,6 +21,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; +import com.github.difflib.patch.AbstractDelta; +import com.github.difflib.text.deltamerge.DeltaMergeUtils; +import com.github.difflib.text.deltamerge.InlineDeltaMergeInfo; + public class DiffRowGeneratorTest { @Test @@ -721,7 +731,7 @@ public void testIssue129WithDeltaDecompression() { assertThat(txt).isEqualTo("EQUAL EQUAL EQUAL CHANGE INSERT INSERT EQUAL EQUAL EQUAL"); } - + @Test public void testIssue129SkipDeltaDecompression() { List lines1 = Arrays.asList( @@ -743,17 +753,17 @@ public void testIssue129SkipDeltaDecompression() { "banana2", "banana3"); int[] entry = {1}; - String txt = - DiffRowGenerator.create() - .showInlineDiffs(true) - .decompressDeltas(false) - .oldTag((tag, isOpening) -> isOpening ? "==old" + tag + "==>" : "<==old==") - .newTag((tag, isOpening) -> isOpening ? "==new" + tag + "==>" : "<==new==") - .build() - .generateDiffRows(lines1, lines2) - .stream() - .map(row -> row.getTag().toString()) - .collect(joining(" ")); + String txt + = DiffRowGenerator.create() + .showInlineDiffs(true) + .decompressDeltas(false) + .oldTag((tag, isOpening) -> isOpening ? "==old" + tag + "==>" : "<==old==") + .newTag((tag, isOpening) -> isOpening ? "==new" + tag + "==>" : "<==new==") + .build() + .generateDiffRows(lines1, lines2) + .stream() + .map(row -> row.getTag().toString()) + .collect(joining(" ")); // .forEachOrdered(row -> { // System.out.printf("%4d %-8s %-80s %-80s\n", entry[0]++, // row.getTag(), row.getOldLine(), row.getNewLine()); @@ -761,7 +771,7 @@ public void testIssue129SkipDeltaDecompression() { assertThat(txt).isEqualTo("EQUAL EQUAL EQUAL CHANGE CHANGE CHANGE EQUAL EQUAL EQUAL"); } - + @Test public void testIssue129SkipWhitespaceChanges() throws IOException { String original = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue129_1.txt")).collect(joining("\n")); @@ -780,9 +790,74 @@ public void testIssue129SkipWhitespaceChanges() throws IOException { Arrays.asList(revised.split("\n"))); assertThat(rows).hasSize(13); - + rows.stream() .filter(item -> item.getTag() != DiffRow.Tag.EQUAL) .forEach(System.out::println); } + + @Test + public void testGeneratorWithWhitespaceDeltaMerge() { + final DiffRowGenerator generator = DiffRowGenerator.create().showInlineDiffs(true).mergeOriginalRevised(true) + .inlineDiffByWord(true).oldTag(f -> "~").newTag(f -> "**") // + .lineNormalizer(StringUtils::htmlEntites) // do not replace tabs + .inlineDeltaMerger(DiffRowGenerator.WHITESPACE_EQUALITIES_MERGER).build(); + + assertInlineDiffResult(generator, "No diff", "No diff", "No diff"); + assertInlineDiffResult(generator, " x whitespace before diff", " y whitespace before diff", + " ~x~**y** whitespace before diff"); + assertInlineDiffResult(generator, "Whitespace after diff x ", "Whitespace after diff y ", + "Whitespace after diff ~x~**y** "); + assertInlineDiffResult(generator, "Diff x x between", "Diff y y between", "Diff ~x x~**y y** between"); + assertInlineDiffResult(generator, "Hello \t world", "Hi \t universe", "~Hello \t world~**Hi \t universe**"); + assertInlineDiffResult(generator, "The quick brown fox jumps over the lazy dog", "A lazy dog jumps over a fox", + "~The quick brown fox ~**A lazy dog **jumps over ~the lazy dog~**a fox**"); + } + + @Test + public void testGeneratorWithMergingDeltasForShortEqualities() { + final Function>> shortEqualitiesMerger = deltaMergeInfo -> DeltaMergeUtils + .mergeInlineDeltas(deltaMergeInfo, + equalities -> equalities.stream().mapToInt(String::length).sum() < 6); + + final DiffRowGenerator generator = DiffRowGenerator.create().showInlineDiffs(true).mergeOriginalRevised(true) + .inlineDiffByWord(true).oldTag(f -> "~").newTag(f -> "**").inlineDeltaMerger(shortEqualitiesMerger) + .build(); + + assertInlineDiffResult(generator, "No diff", "No diff", "No diff"); + assertInlineDiffResult(generator, "aaa bbb ccc", "xxx bbb zzz", "~aaa bbb ccc~**xxx bbb zzz**"); + assertInlineDiffResult(generator, "aaa bbbb ccc", "xxx bbbb zzz", "~aaa~**xxx** bbbb ~ccc~**zzz**"); + } + + private void assertInlineDiffResult(DiffRowGenerator generator, String original, String revised, String expected) { + final List rows = generator.generateDiffRows(Arrays.asList(original), Arrays.asList(revised)); + print(rows); + + assertEquals(1, rows.size()); + assertEquals(expected, rows.get(0).getOldLine().toString()); + } + + @Test + public void testIssue188HangOnExamples() throws IOException, URISyntaxException { + try (FileSystem zipFs = FileSystems.newFileSystem(Paths.get("target/test-classes/com/github/difflib/text/test.zip"), (ClassLoader) null);) { + List original = Files.readAllLines(zipFs.getPath("old.html")); + List revised = Files.readAllLines(zipFs.getPath("new.html")); + + DiffRowGenerator generator = DiffRowGenerator.create() + .lineNormalizer(line -> line) + .showInlineDiffs(true) + .mergeOriginalRevised(true) + .inlineDiffByWord(true) + .decompressDeltas(true) + .oldTag(f -> f ? "" : "") + .newTag(f -> f ? "" : "") + .build(); + + //List rows = generator.generateDiffRows(original, revised); + List rows = generator.generateDiffRows(original, DiffUtils.diff(original, revised, new MyersDiffWithLinearSpace<>() )); + + System.out.println(rows); + } + } } + diff --git a/java-diff-utils/src/test/resources/com/github/difflib/text/test.zip b/java-diff-utils/src/test/resources/com/github/difflib/text/test.zip new file mode 100644 index 00000000..7b071fcd Binary files /dev/null and b/java-diff-utils/src/test/resources/com/github/difflib/text/test.zip differ diff --git a/pom.xml b/pom.xml index 52b653da..aa1deae3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 io.github.java-diff-utils java-diff-utils-parent - 4.13-SNAPSHOT + 4.16-SNAPSHOT java-diff-utils-parent pom @@ -65,13 +65,13 @@ org.junit.jupiter junit-jupiter - 5.7.1 + 5.11.4 test org.assertj assertj-core - 3.19.0 + 3.27.3 test @@ -86,12 +86,13 @@ true false forked-path + sign-release-artifacts org.apache.felix maven-bundle-plugin - 3.3.0 + 3.5.1 bundle-manifest @@ -122,7 +123,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.1.0 + 3.6.0 verify-style @@ -176,7 +177,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M4 + 3.5.2 **/LR*.java