merge: fix "MagicResolve" with ignored whitespaces

* Do not ignore differences in "unchanged" fragments.
This commit is contained in:
Aleksey Pivovarov
2018-06-07 19:35:20 +03:00
parent 2f9703b142
commit 0f3d853ddd
3 changed files with 131 additions and 22 deletions
@@ -113,23 +113,60 @@ private class SimpleHelper(val leftText: CharSequence, val baseText: CharSequenc
}
private fun appendBase(range: MergeRange) {
newContent.append(baseText, range.start2, range.end2)
if (range.isEmpty) return
val policy = ComparisonPolicy.DEFAULT
if (isUnchangedRange(range, policy)) {
append(range, ThreeSide.BASE)
}
else {
val type = getConflictType(range, policy)
if (type.isChange(Side.LEFT)) {
append(range, ThreeSide.LEFT)
}
else if (type.isChange(Side.RIGHT)) {
append(range, ThreeSide.RIGHT)
}
else {
append(range, ThreeSide.BASE)
}
}
}
private fun appendConflict(range: MergeRange, policy: ComparisonPolicy): Boolean {
val type = DiffUtil.getWordMergeType(MergeWordFragmentImpl(range), texts, policy)
if (type.diffType == TextDiffType.CONFLICT) return false;
val type = getConflictType(range, policy)
if (type.diffType == TextDiffType.CONFLICT) return false
if (type.isChange(Side.LEFT)) {
newContent.append(leftText, range.start1, range.end1)
append(range, ThreeSide.LEFT)
}
else {
newContent.append(rightText, range.start3, range.end3)
append(range, ThreeSide.RIGHT)
}
return true
}
private fun append(range: MergeRange, side: ThreeSide) {
when (side) {
ThreeSide.LEFT -> newContent.append(leftText, range.start1, range.end1)
ThreeSide.BASE -> newContent.append(baseText, range.start2, range.end2)
ThreeSide.RIGHT -> newContent.append(rightText, range.start3, range.end3)
}
}
private fun getConflictType(range: MergeRange, policy: ComparisonPolicy): MergeConflictType {
return DiffUtil.getWordMergeType(MergeWordFragmentImpl(range), texts, policy)
}
private fun isUnchangedRange(range: MergeRange, policy: ComparisonPolicy): Boolean {
return DiffUtil.compareWordMergeContents(MergeWordFragmentImpl(range), texts, policy, ThreeSide.BASE, ThreeSide.LEFT) &&
DiffUtil.compareWordMergeContents(MergeWordFragmentImpl(range), texts, policy, ThreeSide.BASE, ThreeSide.RIGHT)
}
}
private class GreedyHelper(val leftText: CharSequence, val baseText: CharSequence, val rightText: CharSequence) {
private val newContent = StringBuilder()
@@ -1293,11 +1293,11 @@ public class DiffUtil {
BooleanGetter.FALSE);
}
private static boolean compareWordMergeContents(@NotNull MergeWordFragment fragment,
@NotNull List<? extends CharSequence> texts,
@NotNull ComparisonPolicy policy,
@NotNull ThreeSide side1,
@NotNull ThreeSide side2) {
public static boolean compareWordMergeContents(@NotNull MergeWordFragment fragment,
@NotNull List<? extends CharSequence> texts,
@NotNull ComparisonPolicy policy,
@NotNull ThreeSide side1,
@NotNull ThreeSide side2) {
int start1 = fragment.getStartOffset(side1);
int end1 = fragment.getEndOffset(side1);
int start2 = fragment.getStartOffset(side2);
@@ -192,26 +192,98 @@ class MergeResolveUtilTest : DiffTestCase() {
"i\n",
"i",
"\ni",
"i\n",
"i",
"i"
)
testSimple(
"Y X Y",
"Y C\nX\nC Y",
"Y \nX\n Y",
"Y \nC\nX\nC Y"
)
test(
"""
public static class ChangeData {
@NotNull public final ChangeKind kind;
public final int otherPath;
public ChangeData(@NotNull ChangeKind kind, int otherPath) {
this.kind = kind;
this.otherPath = otherPath;
}
public boolean isRename() {
return kind.equals(ChangeKind.RENAMED_FROM) || kind.equals(ChangeKind.RENAMED_TO);
}
@Override
public int hashCode() {
return Objects.hash(kind, otherPath);
}
}
private enum ChangeKind {
MODIFIED((byte)0),
RENAMED_FROM((byte)1),
RENAMED_TO((byte)2);""",
"""
public enum ChangeKind {
NOT_CHANGED((byte)-1),
MODIFIED((byte)0),
ADDED((byte)1),
REMOVED((byte)2);""",
"""
public static class ChangeData {
@NotNull public final ChangeKind kind;
public final int otherPath;
public ChangeData(@NotNull ChangeKind kind, int otherPath) {
this.kind = kind;
this.otherPath = otherPath;
}
public boolean isRename() {
return kind.equals(ChangeKind.RENAMED_FROM) || kind.equals(ChangeKind.RENAMED_TO);
}
@Override
public int hashCode() {
return Objects.hash(kind, otherPath);
}
}
private enum ChangeKind {
MODIFIED((byte)0),
RENAMED_FROM((byte)1),
RENAMED_TO((byte)2);""",
"""
public enum ChangeKind {
NOT_CHANGED((byte)-1),
MODIFIED((byte)0),
ADDED((byte)1),
REMOVED((byte)2);"""
)
}
private fun testGreedy(base: String, left: String, right: String, expected: String?) {
test(base, left, right, expected, true)
private fun testSimple(base: String, left: String, right: String, expected: String?) {
val simpleResult = MergeResolveUtil.tryResolve(left, base, right)
assertEquals(expected, simpleResult)
}
private fun test(base: String, left: String, right: String, expected: String?, isGreedy: Boolean = false) {
val expectedSimple = if (isGreedy) null else expected
val expectedGreedy = expected
test(base, left, right, expectedSimple, expectedGreedy)
private fun testGreedy(base: String, left: String, right: String, expected: String?) {
val greedyResult = MergeResolveUtil.tryGreedyResolve(left, base, right)
assertEquals(expected, greedyResult)
}
private fun test(base: String, left: String, right: String, expected: String?) {
testSimple(base, left, right, expected)
testGreedy(base, left, right, expected)
}
private fun test(base: String, left: String, right: String, expectedSimple: String?, expectedGreedy: String?) {
val simpleResult = MergeResolveUtil.tryResolve(left, base, right)
val greedyResult = MergeResolveUtil.tryGreedyResolve(left, base, right)
assertEquals(expectedSimple, simpleResult, "Simple")
assertEquals(expectedGreedy, greedyResult, "Greedy")
testSimple(base, left, right, expectedSimple)
testGreedy(base, left, right, expectedGreedy)
}
}