diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java b/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java index e53690a8b2d1..0d3e9b380a30 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java @@ -120,6 +120,13 @@ public class DiffIterableUtil { return new SubiterableDiffIterable(iterable, start1, end1, start2, end2); } + @NotNull + public static DiffIterable expandedIterable(@NotNull DiffIterable iterable, int offset1, int offset2, int length1, int length2) { + assert offset1 + iterable.getLength1() <= length1 && + offset2 + iterable.getLength2() <= length2; + return new ExpandedDiffIterable(iterable, offset1, offset2, length1, length2); + } + // // Misc // diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/iterables/ExpandedDiffIterable.java b/platform/diff-impl/src/com/intellij/diff/comparison/iterables/ExpandedDiffIterable.java new file mode 100644 index 000000000000..a9b2c651a770 --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/comparison/iterables/ExpandedDiffIterable.java @@ -0,0 +1,72 @@ +// Copyright 2000-2019 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.iterables; + +import com.intellij.diff.util.Range; +import org.jetbrains.annotations.NotNull; + +import java.util.Iterator; + +class ExpandedDiffIterable extends ChangeDiffIterableBase { + @NotNull private final DiffIterable myIterable; + private final int myOffset1; + private final int myOffset2; + + ExpandedDiffIterable(@NotNull DiffIterable iterable, int offset1, int offset2, int length1, int length2) { + super(length1, length2); + myIterable = iterable; + myOffset1 = offset1; + myOffset2 = offset2; + } + + @NotNull + @Override + protected ChangeIterable createChangeIterable() { + return new ShiftedChangeIterable(myIterable, myOffset1, myOffset2); + } + + private static class ShiftedChangeIterable implements ChangeIterable { + private final Iterator myIterator; + private final int myOffset1; + private final int myOffset2; + + private Range myLast; + + ShiftedChangeIterable(@NotNull DiffIterable iterable, int offset1, int offset2) { + myIterator = iterable.changes(); + myOffset1 = offset1; + myOffset2 = offset2; + + next(); + } + + @Override + public boolean valid() { + return myLast != null; + } + + @Override + public void next() { + myLast = myIterator.hasNext() ? myIterator.next() : null; + } + + @Override + public int getStart1() { + return myLast.start1 + myOffset1; + } + + @Override + public int getStart2() { + return myLast.start2 + myOffset2; + } + + @Override + public int getEnd1() { + return myLast.end1 + myOffset1; + } + + @Override + public int getEnd2() { + return myLast.end2 + myOffset2; + } + } +} diff --git a/platform/diff-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.kt b/platform/diff-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.kt index 5e8620f79ff3..034915c379c9 100644 --- a/platform/diff-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.kt +++ b/platform/diff-impl/src/com/intellij/openapi/vcs/ex/RangesBuilder.kt @@ -29,6 +29,7 @@ import com.intellij.diff.util.Side import com.intellij.openapi.editor.Document import com.intellij.openapi.progress.DumbProgressIndicator import java.util.* +import kotlin.math.max fun createRanges(current: List, vcs: List, @@ -63,9 +64,16 @@ fun compareLines(text1: CharSequence, text2: CharSequence, lineOffsets1: LineOffsets, lineOffsets2: LineOffsets): FairDiffIterable { - val lines1 = DiffUtil.getLines(text1, lineOffsets1) - val lines2 = DiffUtil.getLines(text2, lineOffsets2) - return compareLines(lines1, lines2) + val range = expand(text1, text2, 0, 0, text1.length, text2.length) + if (range.isEmpty) return fair(DiffIterableUtil.create(emptyList(), lineOffsets1.lineCount, lineOffsets2.lineCount)); + + val start = lineOffsets1.getLineNumber(range.start1) + val end1 = lineOffsets1.getLineNumber(range.end1) + val tail = max(lineOffsets1.lineCount - end1 - 1, 0) + val lineRange = Range(start, lineOffsets1.lineCount - tail, start, lineOffsets2.lineCount - tail) + + val iterable = compareLines(lineRange, text1, text2, lineOffsets1, lineOffsets2) + return fair(DiffIterableUtil.expandedIterable(iterable, start, start, lineOffsets1.lineCount, lineOffsets2.lineCount)) } fun compareLines(lineRange: Range, diff --git a/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BaseLineStatusTrackerManagerTest.kt b/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BaseLineStatusTrackerManagerTest.kt index b3e73551829b..ba55b1992908 100644 --- a/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BaseLineStatusTrackerManagerTest.kt +++ b/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BaseLineStatusTrackerManagerTest.kt @@ -1,6 +1,7 @@ // 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.openapi.vcs +import com.intellij.diff.comparison.iterables.DiffIterableUtil import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.command.CommandProcessor @@ -10,6 +11,7 @@ import com.intellij.openapi.command.undo.DocumentReferenceProvider import com.intellij.openapi.command.undo.UndoManager import com.intellij.openapi.editor.Document import com.intellij.openapi.fileEditor.FileEditor +import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.vcs.BaseLineStatusTrackerTestCase.Companion.parseInput import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager import com.intellij.openapi.vcs.ex.* @@ -28,6 +30,7 @@ abstract class BaseLineStatusTrackerManagerTest : BaseChangeListsTest() { override fun setUp() { super.setUp() + DiffIterableUtil.setVerifyEnabled(true) lstm = LineStatusTrackerManager.getInstanceImpl(getProject()) undoManager = UndoManager.getInstance(getProject()) as UndoManagerImpl shelveManager = ShelveChangesManager.getInstance(getProject()) @@ -39,6 +42,7 @@ abstract class BaseLineStatusTrackerManagerTest : BaseChangeListsTest() { .append(ThrowableRunnable { UIUtil.dispatchAllInvocationEvents() }) .append(ThrowableRunnable { lstm.resetExcludedFromCommitMarkers() }) .append(ThrowableRunnable { lstm.releaseAllTrackers() }) + .append(ThrowableRunnable { DiffIterableUtil.setVerifyEnabled(Registry.`is`("diff.verify.iterable")) }) .append(ThrowableRunnable { super.tearDown() }) .run() } diff --git a/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/LineStatusTrackerModifyDocumentTest.kt b/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/LineStatusTrackerModifyDocumentTest.kt index 3212a2bf5426..a06cff505891 100644 --- a/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/LineStatusTrackerModifyDocumentTest.kt +++ b/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/LineStatusTrackerModifyDocumentTest.kt @@ -18,6 +18,52 @@ package com.intellij.openapi.vcs import com.intellij.openapi.vcs.ex.Range class LineStatusTrackerModifyDocumentTest : BaseLineStatusTrackerTestCase() { + fun testInitialEmpty() { + test("", "") { + assertRanges() + compareRanges() + } + } + + fun testInitialEquals() { + test("1234_2345_3456", + "1234_2345_3456") { + assertRanges() + compareRanges() + } + } + + fun testInitialInsertion() { + test("1234_2345_3456", + "1234_3456") { + assertRanges(Range(1, 2, 1, 1)) + compareRanges() + } + } + + fun testInitialDeletion() { + test("1234_3456", "1234_2345_3456") { + assertRanges(Range(1, 1, 1, 2)) + compareRanges() + } + } + + fun testInitialModification1() { + test("1234_x_3456", "1234_2345_3456") { + assertRanges(Range(1, 2, 1, 2)) + compareRanges() + } + } + + fun testInitialModification2() { + test("1_3_4_5_6_7", "1_2_3_5_6_12") { + assertRanges(Range(1, 1, 1, 2), + Range(2, 3, 3, 3), + Range(5, 6, 5, 6)) + compareRanges() + } + } + fun testSimpleInsert() { test("1234_2345_3456") { "12".insertAfter("a")