From 3403baba453145394dcd3d6d69ff7f7734848a15 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Thu, 6 Mar 2014 17:52:32 +0400 Subject: [PATCH] IDEA-12609 diff: do not consider equal changes as conflict --- .../impl/incrementalMerge/ConflictChange.java | 88 ++------- .../impl/incrementalMerge/MergeConflict.java | 118 ++---------- .../diff/impl/incrementalMerge/MergeList.java | 7 +- .../incrementalMerge/MergeNoConflict.java | 16 ++ .../incrementalMerge/NoConflictChange.java | 27 +++ .../impl/incrementalMerge/TwoSideChange.java | 181 ++++++++++++++++++ 6 files changed, 253 insertions(+), 184 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeNoConflict.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/NoConflictChange.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/TwoSideChange.java diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java index aae51e02cb23..172f24e88633 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/ConflictChange.java @@ -15,120 +15,56 @@ */ package com.intellij.openapi.diff.impl.incrementalMerge; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; -import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.util.TextRange; import org.jetbrains.annotations.NotNull; /** * One of two conflicting changes. + * * @see MergeConflict */ -class ConflictChange extends Change implements DiffRangeMarker.RangeInvalidListener { +class ConflictChange extends TwoSideChange.SideChange { private static final Logger LOG = Logger.getInstance(ConflictChange.class); - private SimpleChangeSide myOriginalSide; - private MergeConflict myConflict; - @NotNull private final ChangeList myChangeList; - private ChangeType myType; private boolean mySemiApplied; public ConflictChange(@NotNull MergeConflict conflict, @NotNull FragmentSide mergeSide, - @NotNull TextRange range, + @NotNull TextRange versionRange, @NotNull ChangeList changeList) { - myConflict = conflict; - myChangeList = changeList; - myOriginalSide = new SimpleChangeSide(mergeSide, new DiffRangeMarker((DocumentEx)conflict.getOriginalDocument(mergeSide), range, this)); - myType = ChangeType.CONFLICT; - } - - @Override - protected void changeSide(ChangeSide sideToChange, DiffRangeMarker newRange) { - myConflict.setRange(newRange); - } - - protected void removeFromList() { - myConflict.conflictRemoved(); - myConflict = null; - } - - @NotNull - public ChangeSide getChangeSide(@NotNull FragmentSide side) { - return isBranch(side) ? myOriginalSide : myConflict; - } - - private static boolean isBranch(@NotNull FragmentSide side) { - return MergeList.BRANCH_SIDE == side; - } - - public SimpleChangeSide getOriginalSide() { - return myOriginalSide; - } - - public ChangeType getType() { - return myType; - } - - public ChangeList getChangeList() { - return myConflict.getMergeList().getChanges(myOriginalSide.getFragmentSide()); + super(conflict, changeList, ChangeType.CONFLICT, mergeSide, versionRange); } @Override public void onApplied() { - apply(); + markApplied(); // update the other variant of the conflict to point to the bottom if (!mySemiApplied) { - ConflictChange otherChange = myConflict.getOtherChange(this); - LOG.assertTrue(otherChange != null, String.format("Other change is null. This change: %s Merge conflict: %s", this, myConflict)); + ConflictChange otherChange = myTwoSideChange.getOtherChange(this); + LOG.assertTrue(otherChange != null, String.format("Other change is null. This change: %s Merge conflict: %s", this, myTwoSideChange)); otherChange.mySemiApplied = true; otherChange.updateOtherSideOnConflictApply(); - myConflict.removeOtherChange(this); + myTwoSideChange.removeOtherChange(this); } } private void updateOtherSideOnConflictApply() { if (myOriginalSide.getStart() == myOriginalSide.getEnd()) { - apply(); + markApplied(); return; } - int startOffset = myConflict.getRange().getEndOffset(); + int startOffset = myTwoSideChange.getRange().getEndOffset(); TextRange emptyRange = new TextRange(startOffset, startOffset); ConflictChange leftChange = isBranch(myOriginalSide.getFragmentSide()) ? null : this; ConflictChange rightChange = isBranch(myOriginalSide.getFragmentSide()) ? this : null; - myConflict = myConflict.deriveSideForNotAppliedChange(emptyRange, leftChange, rightChange); + myTwoSideChange = myTwoSideChange.deriveSideForNotAppliedChange(emptyRange, leftChange, rightChange); myOriginalSide.getHighlighterHolder().updateHighlighter(myOriginalSide, myType); - myConflict.getHighlighterHolder().updateHighlighter(myConflict, myType); + myTwoSideChange.getHighlighterHolder().updateHighlighter(myTwoSideChange, myType); myChangeList.fireOnChangeApplied(); } - - private void apply() { - myType = ChangeType.deriveApplied(myType); - myChangeList.apply(this); - - myOriginalSide.getHighlighterHolder().updateHighlighter(myOriginalSide, myType); - myOriginalSide.getHighlighterHolder().setActions(new AnAction[0]); - - // display, what one side of the conflict was resolved to - myConflict.getHighlighterHolder().updateHighlighter(myConflict, myType); - } - - public void onRemovedFromList() { - myOriginalSide.getRange().removeListener(this); - myConflict = null; - myOriginalSide = null; - } - - public boolean isValid() { - return myConflict != null; - } - - public void onRangeInvalidated() { - removeFromList(); - } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java index 097d68a19d0d..93c046e7fe5c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeConflict.java @@ -16,8 +16,6 @@ package com.intellij.openapi.diff.impl.incrementalMerge; import com.intellij.openapi.diff.impl.highlighting.FragmentSide; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.util.TextRange; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,30 +23,22 @@ import org.jetbrains.annotations.Nullable; /** * Represents a merge conflict, i.e. two {@link ConflictChange conflicting changes}, one from left, another from right. */ -class MergeConflict extends ChangeSide implements DiffRangeMarker.RangeInvalidListener { - - @NotNull private final MergeList myMergeList; - @NotNull private DiffRangeMarker myCommonRange; - @Nullable private ConflictChange myLeftChange; - @Nullable private ConflictChange myRightChange; - @NotNull private final ChangeHighlighterHolder myCommonHighlighterHolder; - - MergeConflict(@NotNull TextRange commonRange, - @NotNull MergeList mergeList, - @NotNull TextRange leftMarker, - @NotNull TextRange rightMarker) { - myCommonRange = new DiffRangeMarker((DocumentEx)mergeList.getBaseDocument(), commonRange, this); - myMergeList = mergeList; - myCommonHighlighterHolder = new ChangeHighlighterHolder(); - myLeftChange = new ConflictChange(this, FragmentSide.SIDE1, leftMarker, mergeList.getLeftChangeList()); - myRightChange = new ConflictChange(this, FragmentSide.SIDE2, rightMarker, mergeList.getRightChangeList()); +class MergeConflict extends TwoSideChange { + MergeConflict(@NotNull TextRange baseRange, + @NotNull TextRange leftRange, + @NotNull TextRange rightRange, + @NotNull MergeList mergeList) { + super(baseRange, mergeList, new ChangeHighlighterHolder()); + myLeftChange = new ConflictChange(this, FragmentSide.SIDE1, leftRange, mergeList.getLeftChangeList()); + myRightChange = new ConflictChange(this, FragmentSide.SIDE2, rightRange, mergeList.getRightChangeList()); } - private MergeConflict(@NotNull TextRange commonRange, @NotNull MergeList mergeList, @NotNull ChangeHighlighterHolder highlighterHolder, - @Nullable ConflictChange leftChange, @Nullable ConflictChange rightChange) { - myCommonRange = new DiffRangeMarker((DocumentEx)mergeList.getBaseDocument(), commonRange, this); - myMergeList = mergeList; - myCommonHighlighterHolder = highlighterHolder; + private MergeConflict(@NotNull TextRange baseRange, + @Nullable ConflictChange leftChange, + @Nullable ConflictChange rightChange, + @NotNull MergeList mergeList, + @NotNull ChangeHighlighterHolder highlighterHolder) { + super(baseRange, mergeList, highlighterHolder); myLeftChange = leftChange; myRightChange = rightChange; } @@ -58,86 +48,8 @@ class MergeConflict extends ChangeSide implements DiffRangeMarker.RangeInvalidLi @Nullable ConflictChange leftChange, @Nullable ConflictChange rightChange) { ChangeHighlighterHolder highlighterHolder = new ChangeHighlighterHolder(); - MergeConflict mergeConflict = new MergeConflict(baseRange, myMergeList, highlighterHolder, leftChange, rightChange); + MergeConflict mergeConflict = new MergeConflict(baseRange, leftChange, rightChange, myMergeList, highlighterHolder); highlighterHolder.highlight(mergeConflict, myCommonHighlighterHolder.getEditor(), ChangeType.CONFLICT); return mergeConflict; } - - @NotNull - public ChangeHighlighterHolder getHighlighterHolder() { - return myCommonHighlighterHolder; - } - - @NotNull - public DiffRangeMarker getRange() { - return myCommonRange; - } - - @Nullable - public ConflictChange getLeftChange() { - return myLeftChange; - } - - @Nullable - public ConflictChange getRightChange() { - return myRightChange; - } - - @Nullable - ConflictChange getOtherChange(@NotNull ConflictChange change) { - if (change == myLeftChange) { - return myRightChange; - } - else if (change == myRightChange) { - return myLeftChange; - } - else { - throw new IllegalStateException("Unexpected change: " + change); - } - } - - public void conflictRemoved() { - removeHighlighters(myLeftChange); - removeHighlighters(myRightChange); - myCommonHighlighterHolder.removeHighlighters(); - myMergeList.removeChanges(myLeftChange, myRightChange); - myCommonRange.removeListener(this); - } - - public void setRange(@NotNull DiffRangeMarker range) { - myCommonRange = range; - } - - public void removeOtherChange(@NotNull ConflictChange change) { - if (change == myLeftChange) { - myRightChange = null; - } - else if (change == myRightChange) { - myLeftChange = null; - } - else { - throw new IllegalStateException("Unexpected change: " + change); - } - } - - private static void removeHighlighters(@Nullable ConflictChange change) { - if (change != null) { - change.getOriginalSide().getHighlighterHolder().removeHighlighters(); - } - } - - @NotNull - public Document getOriginalDocument(FragmentSide mergeSide) { - return myMergeList.getChanges(mergeSide).getDocument(MergeList.BRANCH_SIDE); - } - - public void onRangeInvalidated() { - conflictRemoved(); - } - - @NotNull - public MergeList getMergeList() { - return myMergeList; - } - } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java index b4099473b8ff..5f7141d9b4d0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeList.java @@ -114,14 +114,11 @@ public class MergeList implements UserDataHolder { TextRange rightRange = mergeFragment.getRight(); if (compareSubstring(leftText, leftRange, rightText, rightRange)) { - MergeConflict conflict = new MergeConflict(baseRange, mergeList, leftRange, rightRange); + MergeNoConflict conflict = new MergeNoConflict(baseRange, leftRange, rightRange, mergeList); assert conflict.getLeftChange() != null; assert conflict.getRightChange() != null; leftChanges.add(conflict.getLeftChange()); rightChanges.add(conflict.getRightChange()); - // TODO: no conflict - //rightChanges.add(SimpleChange.fromRanges(baseRange, rightRange, mergeList.myBaseToRightChangeList)); - //leftChanges.add(SimpleChange.fromRanges(baseRange, leftRange, mergeList.myBaseToLeftChangeList)); } else if (compareSubstring(baseText, baseRange, leftText, leftRange)) { rightChanges.add(SimpleChange.fromRanges(baseRange, rightRange, mergeList.myBaseToRightChangeList)); @@ -130,7 +127,7 @@ public class MergeList implements UserDataHolder { leftChanges.add(SimpleChange.fromRanges(baseRange, leftRange, mergeList.myBaseToLeftChangeList)); } else { - MergeConflict conflict = new MergeConflict(baseRange, mergeList, leftRange, rightRange); + MergeConflict conflict = new MergeConflict(baseRange, leftRange, rightRange, mergeList); assert conflict.getLeftChange() != null; assert conflict.getRightChange() != null; leftChanges.add(conflict.getLeftChange()); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeNoConflict.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeNoConflict.java new file mode 100644 index 000000000000..7c53f9ef1eb0 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/MergeNoConflict.java @@ -0,0 +1,16 @@ +package com.intellij.openapi.diff.impl.incrementalMerge; + +import com.intellij.openapi.diff.impl.highlighting.FragmentSide; +import com.intellij.openapi.util.TextRange; +import org.jetbrains.annotations.NotNull; + +public class MergeNoConflict extends TwoSideChange { + MergeNoConflict(@NotNull TextRange baseRange, + @NotNull TextRange leftRange, + @NotNull TextRange rightRange, + @NotNull MergeList mergeList) { + super(baseRange, mergeList, new ChangeHighlighterHolder()); + myLeftChange = new NoConflictChange(this, FragmentSide.SIDE1, baseRange, leftRange, mergeList.getLeftChangeList()); + myRightChange = new NoConflictChange(this, FragmentSide.SIDE2, baseRange, rightRange, mergeList.getRightChangeList()); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/NoConflictChange.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/NoConflictChange.java new file mode 100644 index 000000000000..5ebbe9161a1c --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/NoConflictChange.java @@ -0,0 +1,27 @@ +package com.intellij.openapi.diff.impl.incrementalMerge; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.diff.impl.highlighting.FragmentSide; +import com.intellij.openapi.util.TextRange; +import org.jetbrains.annotations.NotNull; + +public class NoConflictChange extends TwoSideChange.SideChange { + private static final Logger LOG = Logger.getInstance(NoConflictChange.class); + + public NoConflictChange(@NotNull MergeNoConflict twoSideChange, + @NotNull FragmentSide mergeSide, + @NotNull TextRange baseRange, + @NotNull TextRange versionRange, + @NotNull ChangeList changeList) { + super(twoSideChange, changeList, ChangeType.fromRanges(baseRange, versionRange), mergeSide, versionRange); + } + + @Override + public void onApplied() { + markApplied(); + + NoConflictChange otherChange = myTwoSideChange.getOtherChange(this); + LOG.assertTrue(otherChange != null, String.format("Other change is null. This change: %s Merge conflict: %s", this, myTwoSideChange)); + otherChange.markApplied(); + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/TwoSideChange.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/TwoSideChange.java new file mode 100644 index 000000000000..aecc0294d4e7 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/TwoSideChange.java @@ -0,0 +1,181 @@ +package com.intellij.openapi.diff.impl.incrementalMerge; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.diff.impl.highlighting.FragmentSide; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.ex.DocumentEx; +import com.intellij.openapi.util.TextRange; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class TwoSideChange extends ChangeSide implements DiffRangeMarker.RangeInvalidListener { + @NotNull protected final MergeList myMergeList; + @NotNull protected DiffRangeMarker myBaseRangeMarker; + protected T myLeftChange; + protected T myRightChange; + @NotNull protected final ChangeHighlighterHolder myCommonHighlighterHolder; + + protected TwoSideChange(@NotNull TextRange baseRange, + @NotNull MergeList mergeList, + @NotNull ChangeHighlighterHolder highlighterHolder) { + myBaseRangeMarker = new DiffRangeMarker((DocumentEx)mergeList.getBaseDocument(), baseRange, this); + myMergeList = mergeList; + myCommonHighlighterHolder = highlighterHolder; + } + + @NotNull + public ChangeHighlighterHolder getHighlighterHolder() { + return myCommonHighlighterHolder; + } + + @NotNull + public DiffRangeMarker getRange() { + return myBaseRangeMarker; + } + + @Nullable + public Change getLeftChange() { + return myLeftChange; + } + + @Nullable + public Change getRightChange() { + return myRightChange; + } + + public void setRange(@NotNull DiffRangeMarker range) { + myBaseRangeMarker = range; + } + + @Nullable + T getOtherChange(@NotNull T change) { + if (change == myLeftChange) { + return myRightChange; + } + else if (change == myRightChange) { + return myLeftChange; + } + else { + throw new IllegalStateException("Unexpected change: " + change); + } + } + + public void removeOtherChange(@NotNull T change) { + if (change == myLeftChange) { + myRightChange = null; + } + else if (change == myRightChange) { + myLeftChange = null; + } + else { + throw new IllegalStateException("Unexpected change: " + change); + } + } + + public void conflictRemoved() { + removeHighlighters(myLeftChange); + removeHighlighters(myRightChange); + myCommonHighlighterHolder.removeHighlighters(); + myMergeList.removeChanges(myLeftChange, myRightChange); + myBaseRangeMarker.removeListener(this); + } + + private static void removeHighlighters(@Nullable T change) { + if (change != null) { + change.getOriginalSide().getHighlighterHolder().removeHighlighters(); + } + } + + @NotNull + public Document getOriginalDocument(FragmentSide mergeSide) { + return myMergeList.getChanges(mergeSide).getDocument(MergeList.BRANCH_SIDE); + } + + public void onRangeInvalidated() { + conflictRemoved(); + } + + @NotNull + public MergeList getMergeList() { + return myMergeList; + } + + protected static abstract class SideChange extends Change implements DiffRangeMarker.RangeInvalidListener { + protected V myTwoSideChange; + @NotNull protected final ChangeList myChangeList; + + protected SimpleChangeSide myOriginalSide; + @NotNull protected ChangeType myType; + + protected SideChange(@NotNull V twoSideChange, + @NotNull ChangeList changeList, + @NotNull ChangeType type, + @NotNull FragmentSide mergeSide, + @NotNull TextRange versionRange) { + myTwoSideChange = twoSideChange; + myChangeList = changeList; + myOriginalSide = + new SimpleChangeSide(mergeSide, new DiffRangeMarker((DocumentEx)twoSideChange.getOriginalDocument(mergeSide), versionRange, this)); + myType = type; + } + + @NotNull + public ChangeType getType() { + return myType; + } + + public SimpleChangeSide getOriginalSide() { + return myOriginalSide; + } + + protected void markApplied() { + myType = ChangeType.deriveApplied(myType); + myChangeList.apply(this); + + myOriginalSide.getHighlighterHolder().updateHighlighter(myOriginalSide, myType); + myOriginalSide.getHighlighterHolder().setActions(new AnAction[0]); + + // display, what one side of the conflict was resolved to + myTwoSideChange.getHighlighterHolder().updateHighlighter(myTwoSideChange, myType); + } + + public ChangeList getChangeList() { + return myTwoSideChange.getMergeList().getChanges(myOriginalSide.getFragmentSide()); + } + + @Override + protected void changeSide(ChangeSide sideToChange, DiffRangeMarker newRange) { + myTwoSideChange.setRange(newRange); + } + + @NotNull + @Override + public ChangeSide getChangeSide(@NotNull FragmentSide side) { + return isBranch(side) ? myOriginalSide : myTwoSideChange; + } + + protected static boolean isBranch(@NotNull FragmentSide side) { + return MergeList.BRANCH_SIDE == side; + } + + protected void removeFromList() { + myTwoSideChange.conflictRemoved(); + myTwoSideChange = null; + } + + public boolean isValid() { + return myTwoSideChange != null; + } + + public void onRemovedFromList() { + myOriginalSide.getRange().removeListener(this); + myTwoSideChange = null; + myOriginalSide = null; + } + + public void onRangeInvalidated() { + removeFromList(); + } + } +}