From eeee5b698fd554f0b2d7e30b6fa5eff21fbdba9a Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Fri, 31 Aug 2018 18:48:29 +0300 Subject: [PATCH] IDEA-77478 diff: support by-character diff highlighting --- .../preview/PreviewDiffPanel.java | 4 +- .../diff/comparison/InnerFragmentsPolicy.java | 8 ++ .../com/intellij/diff/comparison/ByChar.java | 28 +++++++ .../comparison/ComparisonManagerImpl.java | 70 +++++++++++++---- .../diff/tools/util/base/HighlightPolicy.java | 22 +++++- .../util/text/SimpleTextDiffProvider.java | 15 ++-- .../util/text/SmartTextDiffProvider.java | 9 ++- .../diff/comparison/ComparisonUtilAutoTest.kt | 75 ++++++++++++------- .../comparison/IgnoreComparisonUtilTest.kt | 8 +- 9 files changed, 184 insertions(+), 55 deletions(-) create mode 100644 platform/diff-api/src/com/intellij/diff/comparison/InnerFragmentsPolicy.java diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethod/preview/PreviewDiffPanel.java b/java/java-impl/src/com/intellij/refactoring/extractMethod/preview/PreviewDiffPanel.java index df15246958e3..ed4dc94344f9 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethod/preview/PreviewDiffPanel.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethod/preview/PreviewDiffPanel.java @@ -5,6 +5,7 @@ import com.intellij.diff.DiffContentFactory; import com.intellij.diff.DiffManager; import com.intellij.diff.DiffRequestPanel; import com.intellij.diff.comparison.ComparisonManagerImpl; +import com.intellij.diff.comparison.InnerFragmentsPolicy; import com.intellij.diff.contents.DocumentContent; import com.intellij.diff.fragments.LineFragment; import com.intellij.diff.tools.fragmented.UnifiedDiffTool; @@ -389,13 +390,14 @@ class PreviewDiffPanel extends BorderLayoutPanel implements Disposable, PreviewT @NotNull private static DiffUserDataKeysEx.DiffComputer getDiffComputer(@NotNull Collection ranges) { return (text1, text2, policy, innerChanges, indicator) -> { + InnerFragmentsPolicy fragmentsPolicy = innerChanges ? InnerFragmentsPolicy.WORDS : InnerFragmentsPolicy.NONE; LineOffsets offsets1 = LineOffsetsUtil.create(text1); LineOffsets offsets2 = LineOffsetsUtil.create(text2); List result = new ArrayList<>(); ComparisonManagerImpl comparisonManager = ComparisonManagerImpl.getInstanceImpl(); for (Range range : ranges) { - result.addAll(comparisonManager.compareLinesInner(range, text1, text2, offsets1, offsets2, policy, innerChanges, indicator)); + result.addAll(comparisonManager.compareLinesInner(range, text1, text2, offsets1, offsets2, policy, fragmentsPolicy, indicator)); } return result; }; diff --git a/platform/diff-api/src/com/intellij/diff/comparison/InnerFragmentsPolicy.java b/platform/diff-api/src/com/intellij/diff/comparison/InnerFragmentsPolicy.java new file mode 100644 index 000000000000..98491d2205a4 --- /dev/null +++ b/platform/diff-api/src/com/intellij/diff/comparison/InnerFragmentsPolicy.java @@ -0,0 +1,8 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.diff.comparison; + +public enum InnerFragmentsPolicy { + NONE, + WORDS, + CHARS +} diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/ByChar.java b/platform/diff-impl/src/com/intellij/diff/comparison/ByChar.java index d742b45ee1dd..b21901e1e9eb 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/ByChar.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/ByChar.java @@ -16,7 +16,9 @@ package com.intellij.diff.comparison; import com.intellij.diff.comparison.iterables.DiffIterable; +import com.intellij.diff.comparison.iterables.DiffIterableUtil; import com.intellij.diff.comparison.iterables.FairDiffIterable; +import com.intellij.diff.util.IntPair; import com.intellij.diff.util.Range; import com.intellij.openapi.progress.ProgressIndicator; import gnu.trove.TIntArrayList; @@ -55,6 +57,32 @@ public class ByChar { return matchAdjustmentSpaces(chars1, chars2, text1, text2, nonSpaceChanges, indicator); } + @NotNull + public static DiffIterable compareTrimWhitespaces(@NotNull CharSequence text1, + @NotNull CharSequence text2, + @NotNull ProgressIndicator indicator) { + FairDiffIterable iterable = compareTwoStep(text1, text2, indicator); + + IntPair trim1 = TrimUtil.trim(text1, 0, text1.length()); + IntPair trim2 = TrimUtil.trim(text2, 0, text2.length()); + + List ranges = new ArrayList<>(); + for (Range ch : iterable.iterateChanges()) { + int start1 = Math.max(ch.start1, trim1.val1); + int end1 = Math.min(ch.end1, trim1.val2); + int start2 = Math.max(ch.start2, trim2.val1); + int end2 = Math.min(ch.end2, trim2.val2); + + if (end1 < start1) start1 = end1 = ch.start1; + if (end2 < start2) start2 = end2 = ch.start2; + + if (start1 != end1 || start2 != end2) { + ranges.add(new Range(start1, end1, start2, end2)); + } + } + return DiffIterableUtil.create(ranges, text1.length(), text2.length()); + } + @NotNull public static DiffIterable compareIgnoreWhitespaces(@NotNull CharSequence text1, @NotNull CharSequence text2, diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java b/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java index 9d63dc27f43f..1bf3e855c565 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java @@ -127,7 +127,7 @@ public class ComparisonManagerImpl extends ComparisonManager { @NotNull ComparisonPolicy policy, @NotNull ProgressIndicator indicator) throws DiffTooBigException { List lineFragments = compareLines(text1, text2, policy, indicator); - return createInnerFragments(lineFragments, text1, text2, policy, indicator); + return createInnerFragments(lineFragments, text1, text2, policy, InnerFragmentsPolicy.WORDS, indicator); } @NotNull @@ -136,11 +136,11 @@ public class ComparisonManagerImpl extends ComparisonManager { @NotNull LineOffsets lineOffsets1, @NotNull LineOffsets lineOffsets2, @NotNull ComparisonPolicy policy, - boolean innerFragments, + @NotNull InnerFragmentsPolicy fragmentsPolicy, @NotNull ProgressIndicator indicator) throws DiffTooBigException { List lineFragments = compareLines(text1, text2, lineOffsets1, lineOffsets2, policy, indicator); - if (innerFragments) { - return createInnerFragments(lineFragments, text1, text2, policy, indicator); + if (fragmentsPolicy != InnerFragmentsPolicy.NONE) { + return createInnerFragments(lineFragments, text1, text2, policy, fragmentsPolicy, indicator); } else { return lineFragments; @@ -154,11 +154,11 @@ public class ComparisonManagerImpl extends ComparisonManager { @NotNull LineOffsets lineOffsets1, @NotNull LineOffsets lineOffsets2, @NotNull ComparisonPolicy policy, - boolean innerFragments, + @NotNull InnerFragmentsPolicy fragmentsPolicy, @NotNull ProgressIndicator indicator) throws DiffTooBigException { List lineFragments = compareLines(range, text1, text2, lineOffsets1, lineOffsets2, policy, indicator); - if (innerFragments) { - return createInnerFragments(lineFragments, text1, text2, policy, indicator); + if (fragmentsPolicy != InnerFragmentsPolicy.NONE) { + return createInnerFragments(lineFragments, text1, text2, policy, fragmentsPolicy, indicator); } else { return lineFragments; @@ -169,6 +169,7 @@ public class ComparisonManagerImpl extends ComparisonManager { @NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull ComparisonPolicy policy, + @NotNull InnerFragmentsPolicy fragmentsPolicy, @NotNull ProgressIndicator indicator) { List result = new ArrayList<>(lineFragments.size()); @@ -179,7 +180,7 @@ public class ComparisonManagerImpl extends ComparisonManager { try { // Do not try to build fine blocks after few fails boolean tryComputeDifferences = tooBigChunksCount < FilesTooBigForDiffException.MAX_BAD_LINES; - result.addAll(createInnerFragments(fragment, text1, text2, policy, indicator, tryComputeDifferences)); + result.addAll(createInnerFragments(fragment, text1, text2, policy, fragmentsPolicy, indicator, tryComputeDifferences)); } catch (DiffTooBigException e) { result.add(fragment); @@ -195,8 +196,13 @@ public class ComparisonManagerImpl extends ComparisonManager { @NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull ComparisonPolicy policy, + @NotNull InnerFragmentsPolicy fragmentsPolicy, @NotNull ProgressIndicator indicator, boolean tryComputeDifferences) throws DiffTooBigException { + if (fragmentsPolicy == InnerFragmentsPolicy.NONE) { + return singletonList(fragment); + } + CharSequence subSequence1 = text1.subSequence(fragment.getStartOffset1(), fragment.getEndOffset1()); CharSequence subSequence2 = text2.subSequence(fragment.getStartOffset2(), fragment.getEndOffset2()); @@ -212,6 +218,23 @@ public class ComparisonManagerImpl extends ComparisonManager { if (!tryComputeDifferences) return singletonList(fragment); + if (fragmentsPolicy == InnerFragmentsPolicy.WORDS) { + return createInnerWordFragments(fragment, subSequence1, subSequence2, policy, indicator); + } + else if (fragmentsPolicy == InnerFragmentsPolicy.CHARS) { + return createInnerCharFragments(fragment, subSequence1, subSequence2, policy, indicator); + } + else { + throw new IllegalArgumentException(fragmentsPolicy.name()); + } + } + + @NotNull + private static List createInnerWordFragments(@NotNull LineFragment fragment, + @NotNull CharSequence subSequence1, + @NotNull CharSequence subSequence2, + @NotNull ComparisonPolicy policy, + @NotNull ProgressIndicator indicator) throws DiffTooBigException { List lineBlocks = ByWord.compareAndSplit(subSequence1, subSequence2, policy, indicator); assert lineBlocks.size() != 0; @@ -241,6 +264,27 @@ public class ComparisonManagerImpl extends ComparisonManager { return chunks; } + @NotNull + private static List createInnerCharFragments(@NotNull LineFragment fragment, + @NotNull CharSequence subSequence1, + @NotNull CharSequence subSequence2, + @NotNull ComparisonPolicy policy, + @NotNull ProgressIndicator indicator) throws DiffTooBigException { + DiffIterable iterable; + if (policy == ComparisonPolicy.DEFAULT) { + iterable = ByChar.compareTwoStep(subSequence1, subSequence2, indicator); + } + else if (policy == ComparisonPolicy.TRIM_WHITESPACES) { + iterable = ByChar.compareTrimWhitespaces(subSequence1, subSequence2, indicator); + } + else { + iterable = ByChar.compareIgnoreWhitespaces(subSequence1, subSequence2, indicator); + } + + List innerChanges = convertIntoDiffFragments(iterable); + return singletonList(new LineFragmentImpl(fragment, innerChanges)); + } + @NotNull @Override @Deprecated @@ -532,12 +576,12 @@ public class ComparisonManagerImpl extends ComparisonManager { @NotNull LineOffsets lineOffsets2, @NotNull BitSet ignored1, @NotNull BitSet ignored2, - boolean innerFragments, + @NotNull InnerFragmentsPolicy fragmentsPolicy, @NotNull ProgressIndicator indicator) throws DiffTooBigException { Range range = new Range(0, lineOffsets1.getLineCount(), 0, lineOffsets2.getLineCount()); return compareLinesWithIgnoredRanges(range, text1, text2, lineOffsets1, lineOffsets2, ignored1, ignored2, - innerFragments, indicator); + fragmentsPolicy, indicator); } /** @@ -551,7 +595,7 @@ public class ComparisonManagerImpl extends ComparisonManager { @NotNull LineOffsets lineOffsets2, @NotNull BitSet ignored1, @NotNull BitSet ignored2, - boolean innerFragments, + @NotNull InnerFragmentsPolicy fragmentsPolicy, @NotNull ProgressIndicator indicator) throws DiffTooBigException { List lineTexts1 = getNotIgnoredLineContents(range.start1, range.end1, text1, lineOffsets1, ignored1); List lineTexts2 = getNotIgnoredLineContents(range.start2, range.end2, text2, lineOffsets2, ignored2); @@ -566,8 +610,8 @@ public class ComparisonManagerImpl extends ComparisonManager { List lineFragments = convertIntoLineFragments(range, lineOffsets1, lineOffsets2, trimmedIterable); - if (innerFragments) { - lineFragments = createInnerFragments(lineFragments, text1, text2, ComparisonPolicy.DEFAULT, indicator); + if (fragmentsPolicy != InnerFragmentsPolicy.NONE) { + lineFragments = createInnerFragments(lineFragments, text1, text2, ComparisonPolicy.DEFAULT, fragmentsPolicy, indicator); } return ContainerUtil.mapNotNull(lineFragments, fragment -> { diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/base/HighlightPolicy.java b/platform/diff-impl/src/com/intellij/diff/tools/util/base/HighlightPolicy.java index 59f82e4ea0cf..d70c0276fe2c 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/base/HighlightPolicy.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/base/HighlightPolicy.java @@ -15,12 +15,14 @@ */ package com.intellij.diff.tools.util.base; +import com.intellij.diff.comparison.InnerFragmentsPolicy; import org.jetbrains.annotations.NotNull; public enum HighlightPolicy { BY_LINE("Highlight lines"), BY_WORD("Highlight words"), BY_WORD_SPLIT("Highlight split changes"), + BY_CHAR("Highlight symbols"), DO_NOT_HIGHLIGHT("Do not highlight"); @NotNull private final String myText; @@ -39,10 +41,26 @@ public enum HighlightPolicy { } public boolean isFineFragments() { - return this == BY_WORD || this == BY_WORD_SPLIT; + return getFragmentsPolicy() != InnerFragmentsPolicy.NONE; } public boolean isShouldSquash() { - return this == BY_WORD || this == BY_LINE; + return this != BY_WORD_SPLIT; + } + + @NotNull + public InnerFragmentsPolicy getFragmentsPolicy() { + switch (this) { + case BY_WORD: + case BY_WORD_SPLIT: + return InnerFragmentsPolicy.WORDS; + case BY_CHAR: + return InnerFragmentsPolicy.CHARS; + case BY_LINE: + case DO_NOT_HIGHLIGHT: + return InnerFragmentsPolicy.NONE; + default: + throw new IllegalArgumentException(this.name()); + } } } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleTextDiffProvider.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleTextDiffProvider.java index d8e38043f588..2b21dad1ab48 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleTextDiffProvider.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleTextDiffProvider.java @@ -17,6 +17,7 @@ package com.intellij.diff.tools.util.text; import com.intellij.diff.comparison.ComparisonManagerImpl; import com.intellij.diff.comparison.ComparisonPolicy; +import com.intellij.diff.comparison.InnerFragmentsPolicy; import com.intellij.diff.fragments.LineFragment; import com.intellij.diff.tools.util.base.HighlightPolicy; import com.intellij.diff.tools.util.base.IgnorePolicy; @@ -41,7 +42,8 @@ public class SimpleTextDiffProvider extends TwosideTextDiffProviderBase implemen private static final Logger LOG = Logger.getInstance(SimpleTextDiffProvider.class); private static final IgnorePolicy[] IGNORE_POLICIES = {DEFAULT, TRIM_WHITESPACES, IGNORE_WHITESPACES, IGNORE_WHITESPACES_CHUNKS}; - private static final HighlightPolicy[] HIGHLIGHT_POLICIES = {BY_LINE, BY_WORD, BY_WORD_SPLIT, DO_NOT_HIGHLIGHT}; + private static final HighlightPolicy[] HIGHLIGHT_POLICIES = {BY_LINE, BY_WORD, BY_WORD_SPLIT, BY_CHAR, DO_NOT_HIGHLIGHT}; + private static final HighlightPolicy[] CUSTOM_COMPUTER_HIGHLIGHT_POLICIES = {BY_LINE, BY_WORD, BY_WORD_SPLIT, DO_NOT_HIGHLIGHT}; @Nullable private final DiffComputer myDiffComputer; @@ -55,7 +57,8 @@ public class SimpleTextDiffProvider extends TwosideTextDiffProviderBase implemen @NotNull Runnable rediff, @NotNull Disposable disposable, @Nullable DiffComputer diffComputer) { - this(settings, rediff, disposable, diffComputer, IGNORE_POLICIES, HIGHLIGHT_POLICIES); + this(settings, rediff, disposable, diffComputer, IGNORE_POLICIES, + diffComputer != null ? CUSTOM_COMPUTER_HIGHLIGHT_POLICIES : HIGHLIGHT_POLICIES); } private SimpleTextDiffProvider(@NotNull TextDiffSettings settings, @@ -92,7 +95,7 @@ public class SimpleTextDiffProvider extends TwosideTextDiffProviderBase implemen @NotNull HighlightPolicy highlightPolicy, @NotNull ProgressIndicator indicator) { ComparisonPolicy policy = ignorePolicy.getComparisonPolicy(); - boolean innerFragments = highlightPolicy.isFineFragments(); + InnerFragmentsPolicy fragmentsPolicy = highlightPolicy.getFragmentsPolicy(); if (diffComputer != null && linesRanges != null) { LOG.error(new Throwable("Unsupported operation: ranged diff with custom DiffComputer - " + diffComputer)); @@ -101,12 +104,12 @@ public class SimpleTextDiffProvider extends TwosideTextDiffProviderBase implemen ComparisonManagerImpl comparisonManager = ComparisonManagerImpl.getInstanceImpl(); if (linesRanges == null) { if (diffComputer != null) { - List fragments = diffComputer.compute(text1, text2, policy, innerFragments, indicator); + List fragments = diffComputer.compute(text1, text2, policy, fragmentsPolicy != InnerFragmentsPolicy.NONE, indicator); return Collections.singletonList(fragments); } else { List fragments = comparisonManager.compareLinesInner(text1, text2, lineOffsets1, lineOffsets2, - policy, innerFragments, indicator); + policy, fragmentsPolicy, indicator); return Collections.singletonList(fragments); } } @@ -114,7 +117,7 @@ public class SimpleTextDiffProvider extends TwosideTextDiffProviderBase implemen List> result = new ArrayList<>(); for (Range range : linesRanges) { result.add(comparisonManager.compareLinesInner(range, text1, text2, lineOffsets1, lineOffsets2, - policy, innerFragments, indicator)); + policy, fragmentsPolicy, indicator)); } return result; } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/SmartTextDiffProvider.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/SmartTextDiffProvider.java index f25cdba15325..e816b6edf6b3 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/text/SmartTextDiffProvider.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/SmartTextDiffProvider.java @@ -16,6 +16,7 @@ package com.intellij.diff.tools.util.text; import com.intellij.diff.comparison.ComparisonManagerImpl; +import com.intellij.diff.comparison.InnerFragmentsPolicy; import com.intellij.diff.contents.DiffContent; import com.intellij.diff.fragments.LineFragment; import com.intellij.diff.lang.DiffIgnoredRangeProvider; @@ -44,7 +45,7 @@ import static com.intellij.diff.tools.util.base.IgnorePolicy.*; public class SmartTextDiffProvider extends TwosideTextDiffProviderBase implements TwosideTextDiffProvider { private static final IgnorePolicy[] IGNORE_POLICIES = {DEFAULT, TRIM_WHITESPACES, IGNORE_WHITESPACES, IGNORE_WHITESPACES_CHUNKS, FORMATTING}; - private static final HighlightPolicy[] HIGHLIGHT_POLICIES = {BY_LINE, BY_WORD, BY_WORD_SPLIT, DO_NOT_HIGHLIGHT}; + private static final HighlightPolicy[] HIGHLIGHT_POLICIES = {BY_LINE, BY_WORD, BY_WORD_SPLIT, BY_CHAR, DO_NOT_HIGHLIGHT}; @Nullable private final Project myProject; @NotNull private final DiffContent myContent1; @@ -137,7 +138,7 @@ public class SmartTextDiffProvider extends TwosideTextDiffProviderBase implement @Nullable List linesRanges, @NotNull HighlightPolicy highlightPolicy, @NotNull ProgressIndicator indicator) { - boolean innerFragments = highlightPolicy.isFineFragments(); + InnerFragmentsPolicy fragmentsPolicy = highlightPolicy.getFragmentsPolicy(); List ignoredRanges1 = myProvider.getIgnoredRanges(myProject, text1, myContent1); List ignoredRanges2 = myProvider.getIgnoredRanges(myProject, text2, myContent2); @@ -148,14 +149,14 @@ public class SmartTextDiffProvider extends TwosideTextDiffProviderBase implement ComparisonManagerImpl comparisonManager = ComparisonManagerImpl.getInstanceImpl(); if (linesRanges == null) { List fragments = comparisonManager.compareLinesWithIgnoredRanges(text1, text2, lineOffsets1, lineOffsets2, - ignored1, ignored2, innerFragments, indicator); + ignored1, ignored2, fragmentsPolicy, indicator); return Collections.singletonList(fragments); } else { List> result = new ArrayList<>(); for (Range range : linesRanges) { result.add(comparisonManager.compareLinesWithIgnoredRanges(range, text1, text2, lineOffsets1, lineOffsets2, - ignored1, ignored2, innerFragments, indicator)); + ignored1, ignored2, fragmentsPolicy, indicator)); } return result; } diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt index c3b7af7da7e8..8e29cf39c091 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/ComparisonUtilAutoTest.kt @@ -23,6 +23,7 @@ import com.intellij.diff.fragments.MergeLineFragment import com.intellij.diff.fragments.MergeWordFragment import com.intellij.diff.tools.util.base.HighlightPolicy import com.intellij.diff.tools.util.base.IgnorePolicy +import com.intellij.diff.tools.util.text.LineOffsetsUtil import com.intellij.diff.util.DiffUtil import com.intellij.diff.util.Range import com.intellij.diff.util.ThreeSide @@ -69,78 +70,90 @@ class ComparisonUtilAutoTest : HeavyDiffTestCase() { } private fun doTestLine(seed: Long, runs: Int, maxLength: Int) { - val policies = listOf(ComparisonPolicy.DEFAULT, ComparisonPolicy.TRIM_WHITESPACES, ComparisonPolicy.IGNORE_WHITESPACES) + val ignorePolicies = listOf(ComparisonPolicy.DEFAULT, ComparisonPolicy.TRIM_WHITESPACES, ComparisonPolicy.IGNORE_WHITESPACES) + val fragmentsPolicies = listOf(InnerFragmentsPolicy.WORDS, InnerFragmentsPolicy.CHARS) - doTest(seed, runs, maxLength, policies) { text1, text2, policy, debugData -> + doTest(seed, runs, maxLength, ignorePolicies, fragmentsPolicies) { text1, text2, ignorePolicy, fragmentsPolicy, debugData -> val sequence1 = text1.charsSequence val sequence2 = text2.charsSequence - val fragments = MANAGER.compareLinesInner(sequence1, sequence2, policy, INDICATOR) + val lineOffsets1 = LineOffsetsUtil.create(sequence1) + val lineOffsets2 = LineOffsetsUtil.create(sequence2) + + val fragments = MANAGER.compareLinesInner(sequence1, sequence2, lineOffsets1, lineOffsets2, ignorePolicy, fragmentsPolicy, INDICATOR) debugData.put("Fragments", fragments) - checkResultLine(text1, text2, fragments, policy, true) + checkResultLine(text1, text2, fragments, ignorePolicy, true) } } private fun doTestLineSquashed(seed: Long, runs: Int, maxLength: Int) { - val policies = listOf(ComparisonPolicy.DEFAULT, ComparisonPolicy.TRIM_WHITESPACES, ComparisonPolicy.IGNORE_WHITESPACES) + val ignorePolicies = listOf(ComparisonPolicy.DEFAULT, ComparisonPolicy.TRIM_WHITESPACES, ComparisonPolicy.IGNORE_WHITESPACES) + val fragmentsPolicies = listOf(InnerFragmentsPolicy.WORDS, InnerFragmentsPolicy.CHARS) - doTest(seed, runs, maxLength, policies) { text1, text2, policy, debugData -> + doTest(seed, runs, maxLength, ignorePolicies, fragmentsPolicies) { text1, text2, ignorePolicy, fragmentsPolicy, debugData -> val sequence1 = text1.charsSequence val sequence2 = text2.charsSequence - val fragments = MANAGER.compareLinesInner(sequence1, sequence2, policy, INDICATOR) + val lineOffsets1 = LineOffsetsUtil.create(sequence1) + val lineOffsets2 = LineOffsetsUtil.create(sequence2) + + val fragments = MANAGER.compareLinesInner(sequence1, sequence2, lineOffsets1, lineOffsets2, ignorePolicy, fragmentsPolicy, INDICATOR) debugData.put("Fragments", fragments) val squashedFragments = MANAGER.squash(fragments) debugData.put("Squashed Fragments", squashedFragments) - checkResultLine(text1, text2, squashedFragments, policy, false) + checkResultLine(text1, text2, squashedFragments, ignorePolicy, false) } } private fun doTestLineTrimSquashed(seed: Long, runs: Int, maxLength: Int) { - val policies = listOf(ComparisonPolicy.DEFAULT, ComparisonPolicy.TRIM_WHITESPACES, ComparisonPolicy.IGNORE_WHITESPACES) + val ignorePolicies = listOf(ComparisonPolicy.DEFAULT, ComparisonPolicy.TRIM_WHITESPACES, ComparisonPolicy.IGNORE_WHITESPACES) + val fragmentsPolicies = listOf(InnerFragmentsPolicy.WORDS, InnerFragmentsPolicy.CHARS) - doTest(seed, runs, maxLength, policies) { text1, text2, policy, debugData -> + doTest(seed, runs, maxLength, ignorePolicies, fragmentsPolicies) { text1, text2, ignorePolicy, fragmentsPolicy, debugData -> val sequence1 = text1.charsSequence val sequence2 = text2.charsSequence - val fragments = MANAGER.compareLinesInner(sequence1, sequence2, policy, INDICATOR) + val lineOffsets1 = LineOffsetsUtil.create(sequence1) + val lineOffsets2 = LineOffsetsUtil.create(sequence2) + + val fragments = MANAGER.compareLinesInner(sequence1, sequence2, lineOffsets1, lineOffsets2, ignorePolicy, fragmentsPolicy, INDICATOR) debugData.put("Fragments", fragments) - val processed = MANAGER.processBlocks(fragments, sequence1, sequence2, policy, true, true) + val processed = MANAGER.processBlocks(fragments, sequence1, sequence2, ignorePolicy, true, true) debugData.put("Processed Fragments", processed) - checkResultLine(text1, text2, processed, policy, false) + checkResultLine(text1, text2, processed, ignorePolicy, false) } } private fun doTestChar(seed: Long, runs: Int, maxLength: Int) { - val policies = listOf(ComparisonPolicy.DEFAULT, ComparisonPolicy.IGNORE_WHITESPACES) + val ignorePolicies = listOf(ComparisonPolicy.DEFAULT, ComparisonPolicy.IGNORE_WHITESPACES) - doTest(seed, runs, maxLength, policies) { text1, text2, policy, debugData -> + doTest(seed, runs, maxLength, ignorePolicies) { text1, text2, ignorePolicy, debugData -> val sequence1 = text1.charsSequence val sequence2 = text2.charsSequence - val fragments = MANAGER.compareChars(sequence1, sequence2, policy, INDICATOR) + val fragments = MANAGER.compareChars(sequence1, sequence2, ignorePolicy, INDICATOR) debugData.put("Fragments", fragments) - checkResultChar(sequence1, sequence2, fragments, policy) + checkResultChar(sequence1, sequence2, fragments, ignorePolicy) } } private fun doTestWord(seed: Long, runs: Int, maxLength: Int) { - val policies = listOf(ComparisonPolicy.DEFAULT, ComparisonPolicy.TRIM_WHITESPACES, ComparisonPolicy.IGNORE_WHITESPACES) + val ignorePolicies = listOf(ComparisonPolicy.DEFAULT, ComparisonPolicy.TRIM_WHITESPACES, ComparisonPolicy.IGNORE_WHITESPACES) - doTest(seed, runs, maxLength, policies) { text1, text2, policy, debugData -> + doTest(seed, runs, maxLength, ignorePolicies) { text1, text2, ignorePolicy, debugData -> val sequence1 = text1.charsSequence val sequence2 = text2.charsSequence - val fragments = MANAGER.compareWords(sequence1, sequence2, policy, INDICATOR) + val fragments = MANAGER.compareWords(sequence1, sequence2, ignorePolicy, INDICATOR) debugData.put("Fragments", fragments) - checkResultWord(sequence1, sequence2, fragments, policy) + checkResultWord(sequence1, sequence2, fragments, ignorePolicy) } } @@ -218,12 +231,24 @@ class ComparisonUtilAutoTest : HeavyDiffTestCase() { } - private fun doTest(seed: Long, runs: Int, maxLength: Int, policies: List, + private fun doTest(seed: Long, runs: Int, maxLength: Int, + ignorePolicies: List, fragmentsPolicies: List, + test: (Document, Document, ComparisonPolicy, InnerFragmentsPolicy, DiffTestCase.DebugData) -> Unit) { + doTest(seed, runs, maxLength, ignorePolicies) { text1, text2, ignorePolicy, debugData -> + for (fragmentsPolicy in fragmentsPolicies) { + debugData.put("Inner Policy", fragmentsPolicy) + test(text1, text2, ignorePolicy, fragmentsPolicy, debugData) + } + } + } + + private fun doTest(seed: Long, runs: Int, maxLength: Int, + ignorePolicies: List, test: (Document, Document, ComparisonPolicy, DiffTestCase.DebugData) -> Unit) { doTest(seed, runs, maxLength) { text1, text2, debugData -> - for (comparisonPolicy in policies) { - debugData.put("Policy", comparisonPolicy) - test(text1, text2, comparisonPolicy, debugData) + for (ignorePolicy in ignorePolicies) { + debugData.put("Ignore Policy", ignorePolicy) + test(text1, text2, ignorePolicy, debugData) } } } diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/IgnoreComparisonUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/IgnoreComparisonUtilTest.kt index 9b9cfcd107c0..25dfd635be0e 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/IgnoreComparisonUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/IgnoreComparisonUtilTest.kt @@ -557,13 +557,13 @@ class IgnoreComparisonUtilTest : DiffTestCase() { val result1: String = result1.filterNot { it == '.' } val result2: String = result2.filterNot { it == '.' } - private var inner = true + private var innerPolicy = InnerFragmentsPolicy.WORDS private var changedLinesNumber: IntPair? = null private var range: Range? = null private var changedLines: List>? = null fun noInnerChanges(): Test { - inner = false + innerPolicy = InnerFragmentsPolicy.NONE return this } @@ -601,10 +601,10 @@ class IgnoreComparisonUtilTest : DiffTestCase() { val lineOffsets2 = LineOffsetsUtil.create(text2) val result = if (range != null) { - MANAGER.compareLinesWithIgnoredRanges(range!!, text1, text2, lineOffsets1, lineOffsets2, ignored1, ignored2, inner, INDICATOR) + MANAGER.compareLinesWithIgnoredRanges(range!!, text1, text2, lineOffsets1, lineOffsets2, ignored1, ignored2, innerPolicy, INDICATOR) } else { - MANAGER.compareLinesWithIgnoredRanges(text1, text2, lineOffsets1, lineOffsets2, ignored1, ignored2, inner, INDICATOR) + MANAGER.compareLinesWithIgnoredRanges(text1, text2, lineOffsets1, lineOffsets2, ignored1, ignored2, innerPolicy, INDICATOR) } val expected = Couple(parseExpected(result1), parseExpected(result2))