merge: restore 'old-style' apply actions
authorAleksey Pivovarov <AMPivovarov@gmail.com>
Tue, 11 Aug 2015 18:23:18 +0000 (21:23 +0300)
committerAleksey Pivovarov <AMPivovarov@gmail.com>
Wed, 12 Aug 2015 16:56:27 +0000 (19:56 +0300)
* Two gutter actions on the left/right editors: 'apply aka >>' and 'ignore aka X'

* Non-conflicts
  > ignore - change is marked as resolved
  > apply  - change is applied and marked as resolved

* Conflicts
  > ignore - side marked as resolved (other side should be applied/ignored separately)
  > apply  - selected side of change is applied, side is marked as resolved
             - if the second side is empty - whole change is marked as resolved
             - 'apply' of the other side will 'append' content instead (without deleting base content). It icon in gutter will change.

* New feature: ctrl + click on gutter action will resolve whole conflict (no need to ignore second side)

platform/diff-impl/src/com/intellij/diff/merge/TextMergeChange.java
platform/diff-impl/src/com/intellij/diff/merge/TextMergeTool.java
platform/platform-resources-en/src/messages/ActionsBundle.properties
platform/platform-resources/src/idea/PlatformActions.xml

index 229311eb93dcf99cb724b107f448c16d5c0e1985..5479b0fe16ebed4742262c3ea8f6bec95a1c4d3b 100644 (file)
@@ -46,9 +46,10 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
 
   @NotNull private final List<MyGutterOperation> myOperations = new ArrayList<MyGutterOperation>();
 
-  private int[] myStartLines = new int[3];
-  private int[] myEndLines = new int[3];
+  private final int[] myStartLines = new int[3];
+  private final int[] myEndLines = new int[3];
   private final boolean[] myResolved = new boolean[2];
+  private boolean myOnesideAppliedConflict;
 
   @CalledInAwt
   public TextMergeChange(@NotNull MergeLineFragment fragment, @NotNull TextMergeTool.TextMergeViewer viewer) {
@@ -144,6 +145,14 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
     return side.select(myResolved);
   }
 
+  public boolean isOnesideAppliedConflict() {
+    return myOnesideAppliedConflict;
+  }
+
+  public void markOnesideAppliedConflict() {
+    myOnesideAppliedConflict = true;
+  }
+
   public boolean isResolved(@NotNull ThreeSide side) {
     switch (side) {
       case LEFT:
@@ -190,7 +199,7 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
 
     if (newRange.startLine == newRange.endLine && getDiffType() == TextDiffType.DELETED && !isResolved()) {
       if (oldState == null) oldState = storeState();
-      myViewer.markResolved(this);
+      myViewer.markChangeResolved(this);
     }
 
     setStartLine(ThreeSide.BASE, newRange.startLine);
@@ -204,13 +213,14 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
   //
 
   private void doInstallActionHighlighters() {
-    ContainerUtil.addIfNotNull(myOperations, createOperation(ThreeSide.LEFT));
-    ContainerUtil.addIfNotNull(myOperations, createOperation(ThreeSide.BASE));
-    ContainerUtil.addIfNotNull(myOperations, createOperation(ThreeSide.RIGHT));
+    ContainerUtil.addIfNotNull(myOperations, createOperation(ThreeSide.LEFT, OperationType.APPLY));
+    ContainerUtil.addIfNotNull(myOperations, createOperation(ThreeSide.LEFT, OperationType.IGNORE));
+    ContainerUtil.addIfNotNull(myOperations, createOperation(ThreeSide.RIGHT, OperationType.APPLY));
+    ContainerUtil.addIfNotNull(myOperations, createOperation(ThreeSide.RIGHT, OperationType.IGNORE));
   }
 
   @Nullable
-  private MyGutterOperation createOperation(@NotNull ThreeSide side) {
+  private MyGutterOperation createOperation(@NotNull ThreeSide side, @NotNull OperationType type) {
     if (isResolved(side)) return null;
 
     EditorEx editor = myViewer.getEditor(side);
@@ -223,7 +233,7 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
                                                                                HighlighterLayer.ADDITIONAL_SYNTAX,
                                                                                null,
                                                                                HighlighterTargetArea.LINES_IN_RANGE);
-    return new MyGutterOperation(side, highlighter);
+    return new MyGutterOperation(side, highlighter, type);
   }
 
   public void updateGutterActions(boolean force) {
@@ -235,13 +245,15 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
   private class MyGutterOperation {
     @NotNull private final ThreeSide mySide;
     @NotNull private final RangeHighlighter myHighlighter;
+    @NotNull private final OperationType myType;
 
     private boolean myCtrlPressed;
     private boolean myShiftPressed;
 
-    private MyGutterOperation(@NotNull ThreeSide side, @NotNull RangeHighlighter highlighter) {
+    private MyGutterOperation(@NotNull ThreeSide side, @NotNull RangeHighlighter highlighter, @NotNull OperationType type) {
       mySide = side;
       myHighlighter = highlighter;
+      myType = type;
 
       update(true);
     }
@@ -264,35 +276,37 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
 
     @Nullable
     public GutterIconRenderer createRenderer() {
+      if (mySide == ThreeSide.BASE) return null;
+      Side versionSide = mySide.select(Side.LEFT, null, Side.RIGHT);
+      assert versionSide != null;
+
       myCtrlPressed = myViewer.getModifierProvider().isCtrlPressed();
       myShiftPressed = myViewer.getModifierProvider().isShiftPressed();
 
-      if (mySide == ThreeSide.BASE) {
-        return createRevertRenderer();
-      }
-      else {
-        Side versionSide = mySide.select(Side.LEFT, null, Side.RIGHT);
-        assert versionSide != null;
-
-        if (!isChange(versionSide)) return null;
+      if (!isChange(versionSide)) return null;
 
-        if (myShiftPressed) {
-          return createRevertRenderer();
-        }
-        return createApplyRenderer(versionSide);
+      switch (myType) {
+        case APPLY:
+          return createApplyRenderer(versionSide, myCtrlPressed);
+        case IGNORE:
+          return createIgnoreRenderer(versionSide, myCtrlPressed);
+        default:
+          throw new IllegalArgumentException(myType.name());
       }
     }
   }
 
   @Nullable
-  private GutterIconRenderer createApplyRenderer(@NotNull final Side side) {
-    return createIconRenderer(DiffBundle.message("merge.dialog.apply.change.action.name"), AllIcons.Diff.Arrow, new Runnable() {
+  private GutterIconRenderer createApplyRenderer(@NotNull final Side side, final boolean modifier) {
+    if (isResolved(side)) return null;
+    Icon icon = isOnesideAppliedConflict() ? AllIcons.Diff.ArrowLeftDown : AllIcons.Diff.Arrow;
+    return createIconRenderer(DiffBundle.message("merge.dialog.apply.change.action.name"), icon, new Runnable() {
       @Override
       public void run() {
         myViewer.executeMergeCommand("Apply change", Collections.singletonList(TextMergeChange.this), new Runnable() {
           @Override
           public void run() {
-            myViewer.replaceChange(TextMergeChange.this, side);
+            myViewer.replaceChange(TextMergeChange.this, side, modifier);
           }
         });
       }
@@ -300,14 +314,15 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
   }
 
   @Nullable
-  private GutterIconRenderer createRevertRenderer() {
+  private GutterIconRenderer createIgnoreRenderer(@NotNull final Side side, final boolean modifier) {
+    if (isResolved(side)) return null;
     return createIconRenderer(DiffBundle.message("merge.dialog.ignore.change.action.name"), AllIcons.Diff.Remove, new Runnable() {
       @Override
       public void run() {
         myViewer.executeMergeCommand(null, Collections.singletonList(TextMergeChange.this), new Runnable() {
           @Override
           public void run() {
-            myViewer.markResolved(TextMergeChange.this);
+            myViewer.ignoreChange(TextMergeChange.this, side, modifier);
           }
         });
       }
@@ -363,6 +378,10 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
     };
   }
 
+  private enum OperationType {
+    APPLY, IGNORE
+  }
+
   //
   // State
   //
@@ -379,8 +398,9 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
       myEndLines[2],
 
       myResolved[0],
-      myResolved[1]
-    );
+      myResolved[1],
+
+      myOnesideAppliedConflict);
   }
 
   void restoreState(@NotNull State state) {
@@ -394,6 +414,8 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
 
     myResolved[0] = state.myResolved1;
     myResolved[1] = state.myResolved2;
+
+    myOnesideAppliedConflict = state.myOnesideAppliedConflict;
   }
 
   public static class State {
@@ -408,6 +430,8 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
     private final boolean myResolved1;
     private final boolean myResolved2;
 
+    private final boolean myOnesideAppliedConflict;
+
     public State(int startLine1,
                  int startLine2,
                  int startLine3,
@@ -415,7 +439,8 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
                  int endLine2,
                  int endLine3,
                  boolean resolved1,
-                 boolean resolved2) {
+                 boolean resolved2,
+                 boolean onesideAppliedConflict) {
       myStartLine1 = startLine1;
       myStartLine2 = startLine2;
       myStartLine3 = startLine3;
@@ -424,6 +449,7 @@ public class TextMergeChange extends ThreesideDiffChangeBase {
       myEndLine3 = endLine3;
       myResolved1 = resolved1;
       myResolved2 = resolved2;
+      myOnesideAppliedConflict = onesideAppliedConflict;
     }
   }
 }
index d561e742530da58ae75eb44a023b698e91b23e40..a84bf23bc8581e2ce64062171c1bb6ddd25f17f5 100644 (file)
@@ -59,7 +59,6 @@ import com.intellij.openapi.util.BooleanGetter;
 import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.registry.Registry;
 import com.intellij.ui.HyperlinkAdapter;
 import com.intellij.ui.awt.RelativePoint;
 import com.intellij.util.Function;
@@ -219,7 +218,8 @@ public class TextMergeTool implements MergeTool {
 
         DiffUtil.registerAction(new ApplySelectedChangesAction(Side.LEFT, true), myPanel);
         DiffUtil.registerAction(new ApplySelectedChangesAction(Side.RIGHT, true), myPanel);
-        DiffUtil.registerAction(new RevertSelectedChangesAction(true), myPanel);
+        DiffUtil.registerAction(new IgnoreSelectedChangesAction(Side.LEFT, true), myPanel);
+        DiffUtil.registerAction(new IgnoreSelectedChangesAction(Side.RIGHT, true), myPanel);
       }
 
       @Override
@@ -262,7 +262,8 @@ public class TextMergeTool implements MergeTool {
 
         group.add(new ApplySelectedChangesAction(Side.LEFT, false));
         group.add(new ApplySelectedChangesAction(Side.RIGHT, false));
-        group.add(new RevertSelectedChangesAction(false));
+        group.add(new IgnoreSelectedChangesAction(Side.LEFT, false));
+        group.add(new IgnoreSelectedChangesAction(Side.RIGHT, false));
         group.add(Separator.getInstance());
 
         group.addAll(super.createEditorPopupActions());
@@ -752,7 +753,7 @@ public class TextMergeTool implements MergeTool {
       }
 
       @CalledInAwt
-      public void markResolved(@NotNull TextMergeChange change) {
+      public void markChangeResolved(@NotNull TextMergeChange change) {
         if (change.isResolved()) return;
         change.setResolved(Side.LEFT, true);
         change.setResolved(Side.RIGHT, true);
@@ -762,7 +763,7 @@ public class TextMergeTool implements MergeTool {
       }
 
       @CalledInAwt
-      public void markResolved(@NotNull TextMergeChange change, @NotNull Side side) {
+      public void markChangeResolved(@NotNull TextMergeChange change, @NotNull Side side) {
         if (change.isResolved(side)) return;
         change.setResolved(side, true);
 
@@ -770,12 +771,21 @@ public class TextMergeTool implements MergeTool {
         reinstallHighlighter(change);
       }
 
+      public void ignoreChange(@NotNull TextMergeChange change, @NotNull Side side, boolean modifier) {
+        if (!change.isConflict() || modifier) {
+          markChangeResolved(change);
+        }
+        else {
+          markChangeResolved(change, side);
+        }
+      }
+
       @CalledWithWriteLock
-      public void replaceChange(@NotNull TextMergeChange change, @NotNull Side side) {
+      public void replaceChange(@NotNull TextMergeChange change, @NotNull Side side, boolean modifier) {
         LOG.assertTrue(myCurrentMergeCommand != null);
         if (change.isResolved(side)) return;
         if (!change.isChange(side)) {
-          markResolved(change);
+          markChangeResolved(change);
           return;
         }
 
@@ -791,7 +801,7 @@ public class TextMergeTool implements MergeTool {
         enterBulkChangeUpdateBlock();
         try {
           if (change.isConflict()) {
-            boolean append = change.isResolved(side.other());
+            boolean append = change.isOnesideAppliedConflict();
             int actualOutputStartLine = append ? outputEndLine : outputStartLine;
 
             DiffUtil.applyModification(getContent(outputSide).getDocument(), actualOutputStartLine, outputEndLine,
@@ -802,10 +812,11 @@ public class TextMergeTool implements MergeTool {
               moveChangesAfterInsertion(change, outputStartLine, newOutputEndLine);
             }
 
-            if (change.getStartLine(oppositeSide) == change.getEndLine(oppositeSide)) {
-              markResolved(change);
+            if (modifier || change.getStartLine(oppositeSide) == change.getEndLine(oppositeSide)) {
+              markChangeResolved(change);
             } else {
-              markResolved(change, side);
+              change.markOnesideAppliedConflict();
+              markChangeResolved(change, side);
             }
           }
           else {
@@ -817,7 +828,7 @@ public class TextMergeTool implements MergeTool {
               moveChangesAfterInsertion(change, outputStartLine, newOutputEndLine);
             }
 
-            markResolved(change);
+            markChangeResolved(change);
           }
         }
         finally {
@@ -1016,26 +1027,30 @@ public class TextMergeTool implements MergeTool {
         protected abstract void apply(@NotNull ThreeSide side, @NotNull List<TextMergeChange> changes);
       }
 
-      private class RevertSelectedChangesAction extends ApplySelectedChangesActionBase {
-        public RevertSelectedChangesAction(boolean shortcut) {
+      private class IgnoreSelectedChangesAction extends ApplySelectedChangesActionBase {
+        @NotNull private final Side mySide;
+
+        public IgnoreSelectedChangesAction(@NotNull Side side, boolean shortcut) {
           super(shortcut);
-          EmptyAction.setupAction(this, "Diff.IgnoreChange", null);
+          mySide = side;
+          EmptyAction.setupAction(this, mySide.select("Diff.IgnoreLeftSide", "Diff.IgnoreRightSide"), null);
         }
 
         @Override
         protected boolean isVisible(@NotNull ThreeSide side) {
-          return true;
+          if (side == ThreeSide.BASE) return true;
+          return side == mySide.select(ThreeSide.LEFT, ThreeSide.RIGHT);
         }
 
         @Override
         protected boolean isEnabled(@NotNull TextMergeChange change) {
-          return !change.isResolved();
+          return !change.isResolved(mySide);
         }
 
         @Override
         protected void apply(@NotNull ThreeSide side, @NotNull List<TextMergeChange> changes) {
           for (TextMergeChange change : changes) {
-            markResolved(change);
+            ignoreChange(change, mySide, false);
           }
         }
       }
@@ -1063,7 +1078,7 @@ public class TextMergeTool implements MergeTool {
         @Override
         protected void apply(@NotNull ThreeSide side, @NotNull List<TextMergeChange> changes) {
           for (int i = changes.size() - 1; i >= 0; i--) {
-            replaceChange(changes.get(i), mySide);
+            replaceChange(changes.get(i), mySide, false);
           }
         }
       }
@@ -1107,7 +1122,7 @@ public class TextMergeTool implements MergeTool {
             if (change.isConflict()) continue;
             if (change.isResolved()) continue;
             Side masterSide = change.isChange(Side.LEFT) ? Side.LEFT : Side.RIGHT;
-            replaceChange(change, masterSide);
+            replaceChange(change, masterSide, false);
           }
         }
 
@@ -1134,7 +1149,7 @@ public class TextMergeTool implements MergeTool {
             if (change.isConflict()) continue;
             if (change.isResolved(mySide)) continue;
             if (!change.isChange(mySide)) continue;
-            replaceChange(change, mySide);
+            replaceChange(change, mySide, false);
           }
         }
 
index 02b920843181362f538efc1a40e36b2d5b25dc88..0f3c17ff8a2737bf68c08f15c3fd9c2cb1546385 100644 (file)
@@ -1237,7 +1237,8 @@ action.Diff.ApplyLeftSide.text=Apply Left Side
 action.Diff.ApplyRightSide.text=Apply Right Side
 action.Diff.AppendLeftSide.text=Append Left Side
 action.Diff.AppendRightSide.text=Append Right Side
-action.Diff.IgnoreChange.text=Ignore
+action.Diff.IgnoreLeftSide.text=Ignore Left Side
+action.Diff.IgnoreRightSide.text=Ignore Right Side
 
 action.FileChooser.ShowHiddens.text=Show Hidden Files and Directories
 action.FileChooser.ShowHiddens.description=Show hidden files and directories
index cabc2f1b89a34ef16da309e8b9f5a761bc11b90e..6b264108ab44322887c8e0adc5b6938d3fee8ab4 100644 (file)
       <action id="Diff.ApplyRightSide" class="com.intellij.openapi.actionSystem.EmptyAction" icon="AllIcons.Diff.Arrow"/>
       <action id="Diff.AppendLeftSide" class="com.intellij.openapi.actionSystem.EmptyAction" icon="AllIcons.Diff.ArrowRightDown"/>
       <action id="Diff.AppendRightSide" class="com.intellij.openapi.actionSystem.EmptyAction" icon="AllIcons.Diff.ArrowLeftDown"/>
-      <action id="Diff.IgnoreChange" class="com.intellij.openapi.actionSystem.EmptyAction" icon="AllIcons.Diff.Remove"/>
+      <action id="Diff.IgnoreLeftSide" class="com.intellij.openapi.actionSystem.EmptyAction" icon="AllIcons.Diff.Remove"/>
+      <action id="Diff.IgnoreRightSide" class="com.intellij.openapi.actionSystem.EmptyAction" icon="AllIcons.Diff.Remove"/>
       <separator/>
 
       <action id="CompareDirs" class="com.intellij.openapi.diff.actions.CompareDirectoriesAction" text="Compare Directories" use-shortcut-of="CompareTwoFiles"/>