diff --git a/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/unifieddiff/internal/UnifiedDiffManager.java b/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/unifieddiff/internal/UnifiedDiffManager.java index e97de479e77..7b3789feee7 100644 --- a/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/unifieddiff/internal/UnifiedDiffManager.java +++ b/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/unifieddiff/internal/UnifiedDiffManager.java @@ -870,14 +870,17 @@ private static List computeDiffs(IDocument leftDocument, IDocument case RangeDifference.NOCHANGE: break; case RangeDifference.CHANGE: - var diff = new UnifiedDiff(leftDocument, leftStart, leftEnd, leftDiffSource, rightDocument, - rightStart, rightEnd, rightDiffSource, unifiedDiffs, mode); - unifiedDiffs.add(diff); - // line based fine granular diff via DocumentMerger#simpleTokenDiff ITokenComparator l = createTokenComparator(leftDiffSource, tokenComparatorFactory); ITokenComparator r = createTokenComparator(rightDiffSource, tokenComparatorFactory); RangeDifference[] detailedDiffs = RangeDifferencer.findRanges((IRangeComparator) null, l, r); + if (!hasDetailedChanges(detailedDiffs)) { + break; + } + var diff = new UnifiedDiff(leftDocument, leftStart, leftEnd, leftDiffSource, rightDocument, + rightStart, rightEnd, rightDiffSource, unifiedDiffs, mode); + unifiedDiffs.add(diff); + for (RangeDifference detailedDiff : detailedDiffs) { if (detailedDiff.kind() == RangeDifference.NOCHANGE) { continue; @@ -917,6 +920,15 @@ private static List computeDiffs(IDocument leftDocument, IDocument return unifiedDiffs; } + private static boolean hasDetailedChanges(RangeDifference[] detailedDiffs) { + for (RangeDifference detailedDiff : detailedDiffs) { + if (detailedDiff.kind() != RangeDifference.NOCHANGE) { + return true; + } + } + return false; + } + public static void error(Exception e) { Platform.getLog(UnifiedDiffManager.class).error(e.getMessage(), e); } diff --git a/team/tests/org.eclipse.team.tests.core/src/org/eclipse/team/tests/ui/UnifiedDiffManagerTest.java b/team/tests/org.eclipse.team.tests.core/src/org/eclipse/team/tests/ui/UnifiedDiffManagerTest.java index 85dc2071aef..31291c915c8 100644 --- a/team/tests/org.eclipse.team.tests.core/src/org/eclipse/team/tests/ui/UnifiedDiffManagerTest.java +++ b/team/tests/org.eclipse.team.tests.core/src/org/eclipse/team/tests/ui/UnifiedDiffManagerTest.java @@ -27,6 +27,8 @@ import java.util.Iterator; import java.util.List; +import org.eclipse.compare.contentmergeviewer.ITokenComparator; +import org.eclipse.compare.rangedifferencer.IRangeComparator; import org.eclipse.compare.unifieddiff.UnifiedDiff; import org.eclipse.compare.unifieddiff.UnifiedDiffMode; import org.eclipse.compare.unifieddiff.internal.UnifiedDiffManager; @@ -324,6 +326,17 @@ public void testWhitespaceOnlyChangeIsReportedWhenNotIgnored() { "with ignoreWhiteSpace(false) the changed indentation is a diff"); } + @Test + public void testTokenComparatorCanIgnoreALineChange() { + setEditorContent("line one\n"); + + assertTrue(UnifiedDiff.create(editor, "LINE ONE\n", UnifiedDiffMode.OVERLAY_MODE) + .ignoreWhiteSpace(false).tokenComparatorFactory(CaseInsensitiveTokenComparator::new).open().isOK()); + + assertTrue(UnifiedDiffManager.get(viewer()).isEmpty(), + "a change ignored by the token comparator must not create a parent diff"); + } + /** * A diff at the very end of the document is shown as a code mining. Only a line * header mining reserves its height in the text widget, so an emptied file must @@ -395,6 +408,39 @@ private void assertReplaceModeYields(String left, String right) { assertEquals(right, document().get(), "REPLACE_MODE must transform the document into the compared source"); } + private static final class CaseInsensitiveTokenComparator implements ITokenComparator { + private final String text; + + CaseInsensitiveTokenComparator(String text) { + this.text = text; + } + + @Override + public int getRangeCount() { + return 1; + } + + @Override + public int getTokenStart(int index) { + return index == 0 ? 0 : text.length(); + } + + @Override + public int getTokenLength(int index) { + return index == 0 ? text.length() : 0; + } + + @Override + public boolean rangesEqual(int thisIndex, IRangeComparator other, int otherIndex) { + return other instanceof CaseInsensitiveTokenComparator comparator && text.equalsIgnoreCase(comparator.text); + } + + @Override + public boolean skipRangeComparison(int length, int maxLength, IRangeComparator other) { + return false; + } + } + private ITextViewer viewer() { ITextViewer viewer = editor.getAdapter(ITextViewer.class); assertNotNull(viewer, "editor must adapt to ITextViewer");