Merge remote-tracking branch 'origin/master' into diff-cleanup
authorAleksey Pivovarov <AMPivovarov@gmail.com>
Thu, 18 Jun 2015 14:52:56 +0000 (17:52 +0300)
committerAleksey Pivovarov <AMPivovarov@gmail.com>
Thu, 18 Jun 2015 14:52:56 +0000 (17:52 +0300)
56 files changed:
platform/diff-api/src/com/intellij/diff/util/LineCol.java [moved from platform/diff-impl/src/com/intellij/diff/tools/fragmented/LineCol.java with 94% similarity]
platform/diff-api/src/com/intellij/diff/util/Side.java
platform/diff-impl/src/com/intellij/diff/DiffManagerImpl.java
platform/diff-impl/src/com/intellij/diff/comparison/ComparisonMergeUtil.java [moved from platform/diff-impl/src/com/intellij/diff/comparison/MergeUtil.java with 99% similarity]
platform/diff-impl/src/com/intellij/diff/tools/binary/BinaryDiffTool.java
platform/diff-impl/src/com/intellij/diff/tools/binary/BinaryDiffViewer.java [deleted file]
platform/diff-impl/src/com/intellij/diff/tools/binary/OnesideBinaryDiffViewer.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/binary/ThreesideBinaryDiffViewer.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/binary/TwosideBinaryDiffViewer.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/fragmented/ChangedBlock.java
platform/diff-impl/src/com/intellij/diff/tools/fragmented/HighlightRange.java
platform/diff-impl/src/com/intellij/diff/tools/fragmented/LineNumberConvertor.java
platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedContentPanel.java [moved from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideContentPanel.java with 92% similarity]
platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffChange.java [moved from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffChange.java with 90% similarity]
platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffPanel.java [moved from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffPanel.java with 91% similarity]
platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffTool.java [moved from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffTool.java with 65% similarity]
platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java [moved from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffViewer.java with 73% similarity]
platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedEditorHighlighter.java [moved from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideEditorHighlighter.java with 97% similarity]
platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedEditorRangeHighlighter.java [moved from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideEditorRangeHighlighter.java with 96% similarity]
platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilder.java [moved from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideFragmentBuilder.java with 97% similarity]
platform/diff-impl/src/com/intellij/diff/tools/holders/BinaryEditorHolder.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/holders/EditorHolder.java [moved from platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextContentPanel.java with 55% similarity]
platform/diff-impl/src/com/intellij/diff/tools/holders/EditorHolderFactory.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/holders/TextEditorHolder.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffChange.java
platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffTool.java
platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java
platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleOnesideDiffViewer.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffChange.java
platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java
platform/diff-impl/src/com/intellij/diff/tools/util/FocusTrackerSupport.java
platform/diff-impl/src/com/intellij/diff/tools/util/KeyboardModifierListener.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/util/PrevNextDifferenceIterableBase.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/util/StatusPanel.java
platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java
platform/diff-impl/src/com/intellij/diff/tools/util/base/DiffViewerBase.java
platform/diff-impl/src/com/intellij/diff/tools/util/base/TextDiffViewerUtil.java [moved from platform/diff-impl/src/com/intellij/diff/tools/util/base/TextDiffViewerBase.java with 62% similarity]
platform/diff-impl/src/com/intellij/diff/tools/util/side/HolderPanel.java [moved from platform/diff-impl/src/com/intellij/diff/tools/binary/BinaryContentPanel.java with 52% similarity]
platform/diff-impl/src/com/intellij/diff/tools/util/side/OnesideContentPanel.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/util/side/OnesideDiffViewer.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/util/side/OnesideTextDiffViewer.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/util/side/ThreesideContentPanel.java [moved from platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideContentPanel.java with 58% similarity]
platform/diff-impl/src/com/intellij/diff/tools/util/side/ThreesideDiffViewer.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/util/side/ThreesideTextDiffViewer.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/util/side/TwosideContentPanel.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/util/side/TwosideDiffViewer.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/util/side/TwosideTextDiffViewer.java [new file with mode: 0644]
platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideTextContentPanel.java [deleted file]
platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideTextDiffViewer.java [deleted file]
platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideContentPanel.java [deleted file]
platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextDiffViewer.java [deleted file]
platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java
platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.java [moved from platform/diff-impl/tests/com/intellij/diff/tools/fragmented/OnesideFragmentBuilderAutoTest.java with 98% similarity]
platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderTest.java [moved from platform/diff-impl/tests/com/intellij/diff/tools/fragmented/OnesideFragmentBuilderTest.java with 93% similarity]
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/ChangeDiffRequestProducer.java
plugins/svn4idea/src/org/jetbrains/idea/svn/difftool/properties/SvnPropertiesDiffViewer.java

similarity index 94%
rename from platform/diff-impl/src/com/intellij/diff/tools/fragmented/LineCol.java
rename to platform/diff-api/src/com/intellij/diff/util/LineCol.java
index 965e347a209fe0866660555507f32c3d4fbaad40..c5ce3751e3fabda1bc30687bc9bd9d680bf01c9b 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.intellij.diff.tools.fragmented;
+package com.intellij.diff.util;
 
 import com.intellij.openapi.editor.Document;
 import org.jetbrains.annotations.NotNull;
 
 public class LineCol {
+  // counting from zero
   public final int line;
   public final int column;
 
+  public LineCol(int line) {
+    this(line, 0);
+  }
+
   public LineCol(int line, int column) {
     this.line = line;
     this.column = column;
index 486d68d3d2b18a648ebeedec92c1179a8826ba38..043b4650d24d38532a0391729e9a746c51cb3dfc 100644 (file)
@@ -39,6 +39,11 @@ public enum Side {
     return isLeft ? LEFT : RIGHT;
   }
 
+  @NotNull
+  public static Side fromRight(boolean isRight) {
+    return isRight ? RIGHT : LEFT;
+  }
+
   public int getIndex() {
     return myIndex;
   }
index 816ea9595797bd521e3e3c91767081b10f5f3610..69345c86df04648e8b3ba1dc125acacaa5be7a50 100644 (file)
@@ -23,7 +23,7 @@ import com.intellij.diff.requests.DiffRequest;
 import com.intellij.diff.tools.binary.BinaryDiffTool;
 import com.intellij.diff.tools.dir.DirDiffTool;
 import com.intellij.diff.tools.external.ExternalDiffTool;
-import com.intellij.diff.tools.fragmented.OnesideDiffTool;
+import com.intellij.diff.tools.fragmented.UnifiedDiffTool;
 import com.intellij.diff.tools.simple.SimpleDiffTool;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.project.Project;
@@ -87,7 +87,7 @@ public class DiffManagerImpl extends DiffManagerEx {
   public List<DiffTool> getDiffTools() {
     List<DiffTool> result = new ArrayList<DiffTool>();
     result.add(SimpleDiffTool.INSTANCE);
-    result.add(OnesideDiffTool.INSTANCE);
+    result.add(UnifiedDiffTool.INSTANCE);
     result.add(BinaryDiffTool.INSTANCE);
     result.add(DirDiffTool.INSTANCE);
     Collections.addAll(result, DiffTool.EP_NAME.getExtensions());
similarity index 99%
rename from platform/diff-impl/src/com/intellij/diff/comparison/MergeUtil.java
rename to platform/diff-impl/src/com/intellij/diff/comparison/ComparisonMergeUtil.java
index 99daf1077d037efc5441348fb122d7587492acf4..eb391631c2cd3f82ddf11eb01182046309eba03e 100644 (file)
@@ -27,7 +27,7 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
-public class MergeUtil {
+public class ComparisonMergeUtil {
   @NotNull
   public static List<MergeLineFragment> buildFair(@NotNull FairDiffIterable fragments1,
                                                   @NotNull FairDiffIterable fragments2,
index a3fe98a865a499d6f19954bef397812ccace12fb..8093f89a30f908ce894b18981c8de2fee55a179d 100644 (file)
@@ -17,6 +17,7 @@ package com.intellij.diff.tools.binary;
 
 import com.intellij.diff.DiffContext;
 import com.intellij.diff.FrameDiffTool;
+import com.intellij.diff.requests.ContentDiffRequest;
 import com.intellij.diff.requests.DiffRequest;
 import org.jetbrains.annotations.NotNull;
 
@@ -26,12 +27,17 @@ public class BinaryDiffTool implements FrameDiffTool {
   @NotNull
   @Override
   public DiffViewer createComponent(@NotNull DiffContext context, @NotNull DiffRequest request) {
-    return new BinaryDiffViewer(context, request);
+    if (OnesideBinaryDiffViewer.canShowRequest(context, request)) return new OnesideBinaryDiffViewer(context, request);
+    if (TwosideBinaryDiffViewer.canShowRequest(context, request)) return new TwosideBinaryDiffViewer(context, request);
+    if (ThreesideBinaryDiffViewer.canShowRequest(context, request)) return new ThreesideBinaryDiffViewer(context, request);
+    throw new IllegalArgumentException(request.toString());
   }
 
   @Override
   public boolean canShow(@NotNull DiffContext context, @NotNull DiffRequest request) {
-    return BinaryDiffViewer.canShowRequest(context, request);
+    return OnesideBinaryDiffViewer.canShowRequest(context, request) ||
+           TwosideBinaryDiffViewer.canShowRequest(context, request) ||
+           ThreesideBinaryDiffViewer.canShowRequest(context, request);
   }
 
   @NotNull
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/binary/BinaryDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/binary/BinaryDiffViewer.java
deleted file mode 100644 (file)
index 2260d5c..0000000
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.diff.tools.binary;
-
-import com.intellij.diff.DiffContext;
-import com.intellij.diff.actions.impl.FocusOppositePaneAction;
-import com.intellij.diff.contents.DiffContent;
-import com.intellij.diff.contents.DocumentContent;
-import com.intellij.diff.contents.EmptyContent;
-import com.intellij.diff.contents.FileContent;
-import com.intellij.diff.requests.ContentDiffRequest;
-import com.intellij.diff.requests.DiffRequest;
-import com.intellij.diff.tools.util.DiffNotifications;
-import com.intellij.diff.tools.util.FocusTrackerSupport;
-import com.intellij.diff.tools.util.SimpleDiffPanel;
-import com.intellij.diff.tools.util.StatusPanel;
-import com.intellij.diff.tools.util.base.ListenerDiffViewerBase;
-import com.intellij.diff.util.DiffUtil;
-import com.intellij.diff.util.Side;
-import com.intellij.openapi.Disposable;
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.CommonDataKeys;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.EditorFactory;
-import com.intellij.openapi.fileEditor.FileEditor;
-import com.intellij.openapi.fileEditor.FileEditorProvider;
-import com.intellij.openapi.fileEditor.OpenFileDescriptor;
-import com.intellij.openapi.fileEditor.TextEditor;
-import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager;
-import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
-import com.intellij.openapi.fileTypes.UIBasedFileType;
-import com.intellij.openapi.progress.ProcessCanceledException;
-import com.intellij.openapi.progress.ProgressIndicator;
-import com.intellij.openapi.project.Project;
-import com.intellij.openapi.project.ProjectManager;
-import com.intellij.openapi.util.Computable;
-import com.intellij.openapi.util.Couple;
-import com.intellij.openapi.util.Disposer;
-import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.util.ui.UIUtil;
-import org.jetbrains.annotations.CalledInAwt;
-import org.jetbrains.annotations.NonNls;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-
-public class BinaryDiffViewer extends ListenerDiffViewerBase {
-  public static final Logger LOG = Logger.getInstance(BinaryDiffViewer.class);
-
-  @NotNull private final SimpleDiffPanel myPanel;
-  @NotNull private final BinaryContentPanel myContentPanel;
-  @NotNull private final MyStatusPanel myStatusPanel;
-
-  @Nullable private final FileEditor myEditor1;
-  @Nullable private final FileEditor myEditor2;
-  @Nullable private final FileEditorProvider myEditorProvider1;
-  @Nullable private final FileEditorProvider myEditorProvider2;
-
-  @NotNull private final FocusTrackerSupport.TwosideFocusTrackerSupport myFocusTrackerSupport;
-
-  public BinaryDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
-    super(context, (ContentDiffRequest)request);
-
-    List<JComponent> titlePanel = DiffUtil.createSimpleTitles(myRequest);
-    Couple<Pair<FileEditor, FileEditorProvider>> editors = createEditors();
-
-    myEditor1 = editors.first.first;
-    myEditorProvider1 = editors.first.second;
-    myEditor2 = editors.second.first;
-    myEditorProvider2 = editors.second.second;
-    assert myEditor1 != null || myEditor2 != null;
-
-    myFocusTrackerSupport = new FocusTrackerSupport.TwosideFocusTrackerSupport(getEditor1(), getEditor2());
-    myContentPanel = new BinaryContentPanel(titlePanel, getEditor1(), getEditor2());
-
-    myPanel = new SimpleDiffPanel(myContentPanel, this, context);
-
-    myStatusPanel = new MyStatusPanel();
-
-    new MyFocusOppositePaneAction().setupAction(myPanel);
-  }
-
-  @CalledInAwt
-  public void onDispose() {
-    destroyEditors();
-    super.onDispose();
-  }
-
-  @Override
-  @CalledInAwt
-  protected void processContextHints() {
-    super.processContextHints();
-    myFocusTrackerSupport.processContextHints(myRequest, myContext);
-  }
-
-  @Override
-  @CalledInAwt
-  protected void updateContextHints() {
-    super.updateContextHints();
-    myFocusTrackerSupport.updateContextHints(myRequest, myContext);
-  }
-
-  //
-  // Editors
-  //
-
-  @NotNull
-  protected Couple<Pair<FileEditor, FileEditorProvider>> createEditors() {
-    List<DiffContent> contents = myRequest.getContents();
-
-    Pair<FileEditor, FileEditorProvider> pair1;
-    Pair<FileEditor, FileEditorProvider> pair2;
-
-    try {
-      pair1 = createEditor(contents.get(0));
-      pair2 = createEditor(contents.get(1));
-      return Couple.of(pair1, pair2);
-    }
-    catch (IOException e) {
-      LOG.error(e);
-      Pair<FileEditor, FileEditorProvider> empty = Pair.empty();
-      return Couple.of(empty, empty);
-    }
-  }
-
-  @NotNull
-  private Pair<FileEditor, FileEditorProvider> createEditor(@NotNull final DiffContent content) throws IOException {
-    if (content instanceof EmptyContent) return Pair.empty();
-    if (content instanceof FileContent) {
-      Project project = myProject != null ? myProject : ProjectManager.getInstance().getDefaultProject();
-      VirtualFile file = ((FileContent)content).getFile();
-
-      FileEditorProvider[] providers = FileEditorProviderManager.getInstance().getProviders(project, file);
-      if (providers.length == 0) throw new IOException("Can't find FileEditorProvider");
-
-      FileEditorProvider provider = providers[0];
-      FileEditor editor = provider.createEditor(project, file);
-
-      UIUtil.removeScrollBorder(editor.getComponent());
-
-      return Pair.create(editor, provider);
-    }
-    if (content instanceof DocumentContent) {
-      Document document = ((DocumentContent)content).getDocument();
-      final Editor editor = DiffUtil.createEditor(document, myProject, true);
-
-      TextEditorProvider provider = TextEditorProvider.getInstance();
-      TextEditor fileEditor = provider.getTextEditor(editor);
-
-      Disposer.register(fileEditor, new Disposable() {
-        @Override
-        public void dispose() {
-          EditorFactory.getInstance().releaseEditor(editor);
-        }
-      });
-
-      return Pair.<FileEditor, FileEditorProvider>create(fileEditor, provider);
-    }
-    throw new IllegalArgumentException(content.getClass() + " - " + content.toString());
-  }
-
-
-  private void destroyEditors() {
-    if (getEditor1() != null) {
-      assert myEditorProvider1 != null;
-      myEditorProvider1.disposeEditor(getEditor1());
-    }
-    if (getEditor2() != null) {
-      assert myEditorProvider2 != null;
-      myEditorProvider2.disposeEditor(getEditor2());
-    }
-  }
-
-  //
-  // Diff
-  //
-
-  @Override
-  protected void onSlowRediff() {
-    super.onSlowRediff();
-    myStatusPanel.setBusy(true);
-  }
-
-  @Override
-  @NotNull
-  protected Runnable performRediff(@NotNull final ProgressIndicator indicator) {
-    try {
-      indicator.checkCanceled();
-
-      List<DiffContent> contents = myRequest.getContents();
-
-      if (contents.get(0) instanceof EmptyContent) {
-        return applyNotification(DiffNotifications.INSERTED_CONTENT);
-      }
-
-      if (contents.get(1) instanceof EmptyContent) {
-        return applyNotification(DiffNotifications.REMOVED_CONTENT);
-      }
-
-      if (!(contents.get(0) instanceof FileContent) || !(contents.get(1) instanceof FileContent)) {
-        return applyNotification(null);
-      }
-
-      final VirtualFile file1 = ((FileContent)contents.get(0)).getFile();
-      final VirtualFile file2 = ((FileContent)contents.get(1)).getFile();
-      if (!file1.isValid() || !file2.isValid()) {
-        return applyNotification(DiffNotifications.ERROR);
-      }
-
-      final boolean equal = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
-        @Override
-        public Boolean compute() {
-          try {
-            // we can't use getInputStream() here because we can't restore BOM marker
-            // (getBom() can return null for binary files, while getInputStream() strips BOM for all files).
-            // It can be made for files from VFS that implements FileSystemInterface though.
-            byte[] bytes1 = file1.contentsToByteArray();
-            byte[] bytes2 = file2.contentsToByteArray();
-            return Arrays.equals(bytes1, bytes2);
-          }
-          catch (IOException e) {
-            LOG.warn(e);
-            return false;
-          }
-        }
-      });
-
-      return applyNotification(equal ? DiffNotifications.EQUAL_CONTENTS : null);
-    }
-    catch (ProcessCanceledException ignore) {
-      return applyNotification(DiffNotifications.OPERATION_CANCELED);
-    }
-    catch (Throwable e) {
-      LOG.error(e);
-      return applyNotification(DiffNotifications.ERROR);
-    }
-  }
-
-  @NotNull
-  private Runnable applyNotification(@Nullable final JComponent notification) {
-    return new Runnable() {
-      @Override
-      public void run() {
-        clearDiffPresentation();
-        if (notification != null) myPanel.addNotification(notification);
-      }
-    };
-  }
-
-  private void clearDiffPresentation() {
-    myStatusPanel.setBusy(false);
-    myPanel.resetNotifications();
-  }
-
-  //
-  // Getters
-  //
-
-  @NotNull
-  @Override
-  public JComponent getComponent() {
-    return myPanel;
-  }
-
-  @Nullable
-  @Override
-  public JComponent getPreferredFocusedComponent() {
-    return getCurrentEditor().getPreferredFocusedComponent();
-  }
-
-  @NotNull
-  public Side getCurrentSide() {
-    return myFocusTrackerSupport.getCurrentSide();
-  }
-
-  public void setCurrentSide(@NotNull Side side) {
-    myFocusTrackerSupport.setCurrentSide(side);
-  }
-
-  @Nullable
-  FileEditor getEditor2() {
-    return myEditor2;
-  }
-
-  @Nullable
-  FileEditor getEditor1() {
-    return myEditor1;
-  }
-
-  @NotNull
-  FileEditor getCurrentEditor() {
-    //noinspection ConstantConditions
-    return getCurrentSide().select(getEditor1(), getEditor2());
-  }
-
-  @NotNull
-  @Override
-  protected JComponent getStatusPanel() {
-    return myStatusPanel;
-  }
-
-  //
-  // Misc
-  //
-
-  @Nullable
-  @Override
-  protected OpenFileDescriptor getOpenFileDescriptor() {
-    return getCurrentSide().selectNotNull(getRequest().getContents()).getOpenFileDescriptor();
-  }
-
-  public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
-    if (!(request instanceof ContentDiffRequest)) return false;
-
-    List<DiffContent> contents = ((ContentDiffRequest)request).getContents();
-    if (contents.size() != 2) return false;
-
-    boolean canShow = true;
-    boolean wantShow = false;
-    for (DiffContent content : contents) {
-      canShow &= canShowContent(content, context);
-      wantShow |= wantShowContent(content, context);
-    }
-    return canShow && wantShow;
-  }
-
-  public static boolean canShowContent(@NotNull DiffContent content, @NotNull DiffContext context) {
-    if (content instanceof EmptyContent) return true;
-    if (content instanceof DocumentContent) return true;
-    if (content instanceof FileContent) {
-      Project project = context.getProject();
-      if (project == null) project = ProjectManager.getInstance().getDefaultProject();
-      VirtualFile file = ((FileContent)content).getFile();
-
-      return FileEditorProviderManager.getInstance().getProviders(project, file).length != 0;
-    }
-    return false;
-  }
-
-  public static boolean wantShowContent(@NotNull DiffContent content, @NotNull DiffContext context) {
-    if (content instanceof EmptyContent) return false;
-    if (content instanceof FileContent) {
-      if (content.getContentType() == null) return false;
-      if (content.getContentType().isBinary()) return true;
-      if (content.getContentType() instanceof UIBasedFileType) return true;
-      return false;
-    }
-    return false;
-  }
-
-  //
-  // Actions
-  //
-
-  private class MyFocusOppositePaneAction extends FocusOppositePaneAction {
-    @Override
-    public void actionPerformed(@NotNull AnActionEvent e) {
-      assert getEditor1() != null && getEditor2() != null;
-      setCurrentSide(getCurrentSide().other());
-      myContext.requestFocus();
-    }
-
-    @Override
-    public void update(@NotNull AnActionEvent e) {
-      e.getPresentation().setEnabled(getEditor1() != null && getEditor2() != null);
-    }
-  }
-
-  //
-  // Helpers
-  //
-
-  @Nullable
-  @Override
-  public Object getData(@NonNls String dataId) {
-    if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) {
-      return DiffUtil.getVirtualFile(myRequest, getCurrentSide());
-    }
-    return super.getData(dataId);
-  }
-
-  private static class MyStatusPanel extends StatusPanel {
-    @Override
-    protected int getChangesCount() {
-      return -1;
-    }
-  }
-}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/binary/OnesideBinaryDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/binary/OnesideBinaryDiffViewer.java
new file mode 100644 (file)
index 0000000..685244d
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.binary;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.requests.ContentDiffRequest;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.holders.BinaryEditorHolder;
+import com.intellij.diff.tools.util.DiffNotifications;
+import com.intellij.diff.tools.util.side.OnesideDiffViewer;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.progress.ProgressIndicator;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+public class OnesideBinaryDiffViewer extends OnesideDiffViewer<BinaryEditorHolder> {
+  public static final Logger LOG = Logger.getInstance(OnesideBinaryDiffViewer.class);
+
+  public OnesideBinaryDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    super(context, (ContentDiffRequest)request, BinaryEditorHolder.BinaryEditorHolderFactory.INSTANCE);
+  }
+
+  //
+  // Diff
+  //
+
+  @Override
+  @NotNull
+  protected Runnable performRediff(@NotNull final ProgressIndicator indicator) {
+    JComponent notification = getSide().select(DiffNotifications.REMOVED_CONTENT, DiffNotifications.INSERTED_CONTENT);
+    return applyNotification(notification);
+  }
+
+  @NotNull
+  private Runnable applyNotification(@Nullable final JComponent notification) {
+    return new Runnable() {
+      @Override
+      public void run() {
+        clearDiffPresentation();
+        if (notification != null) myPanel.addNotification(notification);
+      }
+    };
+  }
+
+  private void clearDiffPresentation() {
+    myPanel.resetNotifications();
+  }
+
+  //
+  // Getters
+  //
+
+  @NotNull
+  FileEditor getEditor() {
+    return getEditorHolder().getEditor();
+  }
+
+  //
+  // Misc
+  //
+
+  public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    return OnesideDiffViewer.canShowRequest(context, request, BinaryEditorHolder.BinaryEditorHolderFactory.INSTANCE);
+  }
+}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/binary/ThreesideBinaryDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/binary/ThreesideBinaryDiffViewer.java
new file mode 100644 (file)
index 0000000..c1432bf
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.binary;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.requests.ContentDiffRequest;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.holders.BinaryEditorHolder;
+import com.intellij.diff.tools.util.side.ThreesideDiffViewer;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.util.EmptyRunnable;
+import org.jetbrains.annotations.NotNull;
+
+public class ThreesideBinaryDiffViewer extends ThreesideDiffViewer<BinaryEditorHolder> {
+  public ThreesideBinaryDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    super(context, (ContentDiffRequest)request, BinaryEditorHolder.BinaryEditorHolderFactory.INSTANCE);
+  }
+
+  @Override
+  @NotNull
+  protected Runnable performRediff(@NotNull final ProgressIndicator indicator) {
+    return EmptyRunnable.INSTANCE;
+  }
+
+  public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    return ThreesideDiffViewer.canShowRequest(context, request, BinaryEditorHolder.BinaryEditorHolderFactory.INSTANCE);
+  }
+}
+
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/binary/TwosideBinaryDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/binary/TwosideBinaryDiffViewer.java
new file mode 100644 (file)
index 0000000..a5134ed
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.binary;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.actions.impl.FocusOppositePaneAction;
+import com.intellij.diff.contents.DiffContent;
+import com.intellij.diff.contents.FileContent;
+import com.intellij.diff.requests.ContentDiffRequest;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.holders.BinaryEditorHolder;
+import com.intellij.diff.tools.util.DiffNotifications;
+import com.intellij.diff.tools.util.StatusPanel;
+import com.intellij.diff.tools.util.side.TwosideDiffViewer;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+public class TwosideBinaryDiffViewer extends TwosideDiffViewer<BinaryEditorHolder> {
+  public static final Logger LOG = Logger.getInstance(TwosideBinaryDiffViewer.class);
+
+  @NotNull private final StatusPanel myStatusPanel;
+
+  public TwosideBinaryDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    super(context, (ContentDiffRequest)request, BinaryEditorHolder.BinaryEditorHolderFactory.INSTANCE);
+
+    myStatusPanel = new StatusPanel();
+    new MyFocusOppositePaneAction().setupAction(myPanel);
+  }
+
+  //
+  // Diff
+  //
+
+  @Override
+  protected void onSlowRediff() {
+    super.onSlowRediff();
+    myStatusPanel.setBusy(true);
+  }
+
+  @Override
+  @NotNull
+  protected Runnable performRediff(@NotNull final ProgressIndicator indicator) {
+    try {
+      indicator.checkCanceled();
+
+      List<DiffContent> contents = myRequest.getContents();
+      if (!(contents.get(0) instanceof FileContent) || !(contents.get(1) instanceof FileContent)) {
+        return applyNotification(null);
+      }
+
+      final VirtualFile file1 = ((FileContent)contents.get(0)).getFile();
+      final VirtualFile file2 = ((FileContent)contents.get(1)).getFile();
+      if (!file1.isValid() || !file2.isValid()) {
+        return applyNotification(DiffNotifications.ERROR);
+      }
+
+      final boolean equal = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
+        @Override
+        public Boolean compute() {
+          try {
+            // we can't use getInputStream() here because we can't restore BOM marker
+            // (getBom() can return null for binary files, while getInputStream() strips BOM for all files).
+            // It can be made for files from VFS that implements FileSystemInterface though.
+            byte[] bytes1 = file1.contentsToByteArray();
+            byte[] bytes2 = file2.contentsToByteArray();
+            return Arrays.equals(bytes1, bytes2);
+          }
+          catch (IOException e) {
+            LOG.warn(e);
+            return false;
+          }
+        }
+      });
+
+      return applyNotification(equal ? DiffNotifications.EQUAL_CONTENTS : null);
+    }
+    catch (ProcessCanceledException e) {
+      throw e;
+    }
+    catch (Throwable e) {
+      LOG.error(e);
+      return applyNotification(DiffNotifications.ERROR);
+    }
+  }
+
+  @NotNull
+  private Runnable applyNotification(@Nullable final JComponent notification) {
+    return new Runnable() {
+      @Override
+      public void run() {
+        clearDiffPresentation();
+        if (notification != null) myPanel.addNotification(notification);
+      }
+    };
+  }
+
+  private void clearDiffPresentation() {
+    myStatusPanel.setBusy(false);
+    myPanel.resetNotifications();
+  }
+
+  //
+  // Getters
+  //
+
+  @NotNull
+  FileEditor getCurrentEditor() {
+    return getCurrentEditorHolder().getEditor();
+  }
+
+  @NotNull
+  @Override
+  protected JComponent getStatusPanel() {
+    return myStatusPanel;
+  }
+
+  //
+  // Misc
+  //
+
+  public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    return TwosideDiffViewer.canShowRequest(context, request, BinaryEditorHolder.BinaryEditorHolderFactory.INSTANCE);
+  }
+
+  //
+  // Actions
+  //
+
+  private class MyFocusOppositePaneAction extends FocusOppositePaneAction {
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+      setCurrentSide(getCurrentSide().other());
+      myContext.requestFocus();
+    }
+  }
+}
index b58db4495d08894b2d75daa20f0da714c42804b9..fca102eb031370d197059b346ef582e4b1f7e3db 100644 (file)
@@ -19,7 +19,7 @@ import com.intellij.diff.fragments.LineFragment;
 import com.intellij.diff.fragments.LineFragmentImpl;
 import org.jetbrains.annotations.NotNull;
 
-public class ChangedBlock {
+class ChangedBlock {
   private final int myStartOffset1;
   private final int myEndOffset1;
   private final int myStartOffset2;
index 178a28c789c89148e1294779a1d6fdf51d118fca..1f6b04e06f92517f450cdea864f8ae18ad5c75ed 100644 (file)
@@ -19,7 +19,7 @@ import com.intellij.diff.util.Side;
 import com.intellij.openapi.util.TextRange;
 import org.jetbrains.annotations.NotNull;
 
-public class HighlightRange {
+class HighlightRange {
   @NotNull private final TextRange myBase;
   @NotNull private final TextRange myChanged;
   @NotNull private final Side mySide;
index 6a587c0b1bc95f4260562b210f7c5fd6837c1262..9a0be36da44aaadfe882edee9efce477bd57206d 100644 (file)
@@ -24,7 +24,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
-public class LineNumberConvertor {
+class LineNumberConvertor {
   // Oneside -> Twoside
   @NotNull private final TreeMap<Integer, Integer> myFragments1;
   @NotNull private final TreeMap<Integer, Integer> myFragments2;
@@ -173,20 +173,6 @@ public class LineNumberConvertor {
     public LineNumberConvertor build() {
       return new LineNumberConvertor(myFragments1, myFragments2, myInvertedFragments1, myInvertedFragments2);
     }
-
-    @NotNull
-    public static LineNumberConvertor createLeft(int lines) {
-      Builder builder = new Builder();
-      builder.put1(0, 0, lines);
-      return builder.build();
-    }
-
-    @NotNull
-    public static LineNumberConvertor createRight(int lines) {
-      Builder builder = new Builder();
-      builder.put2(0, 0, lines);
-      return builder.build();
-    }
   }
 
   /*
similarity index 92%
rename from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideContentPanel.java
rename to platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedContentPanel.java
index 0378e3c11f6ef9c7ad1a4361515988b2231d3ebd..4a2e8cba8a1b450dca284950c7fe8add209cc4a1 100644 (file)
@@ -22,8 +22,8 @@ import javax.swing.*;
 import java.awt.*;
 import java.util.List;
 
-public class OnesideContentPanel extends JPanel {
-  public OnesideContentPanel(@NotNull List<JComponent> titles, @NotNull Editor editor) {
+class UnifiedContentPanel extends JPanel {
+  public UnifiedContentPanel(@NotNull List<JComponent> titles, @NotNull Editor editor) {
     super(new BorderLayout());
 
     add(editor.getComponent(), BorderLayout.CENTER);
similarity index 90%
rename from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffChange.java
rename to platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffChange.java
index d0a3d5706dc0cf368577af7690f267f607a28186..662216e88724c6b416e0cb324aacccb57eb82e82 100644 (file)
@@ -19,6 +19,7 @@ import com.intellij.diff.fragments.DiffFragment;
 import com.intellij.diff.fragments.LineFragment;
 import com.intellij.diff.util.DiffDrawUtil;
 import com.intellij.diff.util.DiffUtil;
+import com.intellij.diff.util.DiffUtil.UpdatedLineRange;
 import com.intellij.diff.util.Side;
 import com.intellij.diff.util.TextDiffType;
 import com.intellij.icons.AllIcons;
@@ -37,8 +38,8 @@ import javax.swing.*;
 import java.util.ArrayList;
 import java.util.List;
 
-public class OnesideDiffChange {
-  @NotNull private final OnesideDiffViewer myViewer;
+public class UnifiedDiffChange {
+  @NotNull private final UnifiedDiffViewer myViewer;
   @NotNull private final EditorEx myEditor;
 
   // Boundaries of this change in myEditor. If current state is out-of-date - approximate value.
@@ -50,7 +51,7 @@ public class OnesideDiffChange {
   @NotNull private final List<RangeHighlighter> myHighlighters = new ArrayList<RangeHighlighter>();
   @NotNull private final List<MyGutterOperation> myOperations = new ArrayList<MyGutterOperation>();
 
-  public OnesideDiffChange(@NotNull OnesideDiffViewer viewer, @NotNull ChangedBlock block, boolean innerFragments) {
+  public UnifiedDiffChange(@NotNull UnifiedDiffViewer viewer, @NotNull ChangedBlock block, boolean innerFragments) {
     myViewer = viewer;
     myEditor = viewer.getEditor();
 
@@ -89,8 +90,6 @@ public class OnesideDiffChange {
   }
 
   private void doInstallActionHighlighters() {
-    if (myViewer.getDocument(Side.LEFT) == null || myViewer.getDocument(Side.RIGHT) == null) return;
-
     boolean leftEditable = myViewer.isEditable(Side.LEFT, false);
     boolean rightEditable = myViewer.isEditable(Side.RIGHT, false);
 
@@ -166,30 +165,9 @@ public class OnesideDiffChange {
   }
 
   public void processChange(int oldLine1, int oldLine2, int shift) {
-    if (myLine2 <= oldLine1) return;
-    if (myLine1 >= oldLine2) {
-      myLine1 += shift;
-      myLine2 += shift;
-      return;
-    }
-
-    if (myLine1 <= oldLine1 && myLine2 >= oldLine2) {
-      myLine2 += shift;
-      return;
-    }
-
-    // range is destroyed. We don't know new boundaries.
-    // Anything below is just a guess in attempt to keep changes ordered.
-    int newLine2 = oldLine2 + shift;
-    if (myLine2 < oldLine2) {
-      if (myLine2 > newLine2) myLine2 = newLine2;
-    }
-    else {
-      myLine2 += shift; // end of change is outside of modified range - just shift
-    }
-
-    if (myLine1 < oldLine1) myLine1 = Math.min(myLine1, newLine2);
-    if (myLine1 > myLine2) myLine1 = myLine2;
+    UpdatedLineRange newRange = DiffUtil.updateRangeOnModification(myLine1, myLine2, oldLine1, oldLine2, shift);
+    myLine1 = newRange.startLine;
+    myLine2 = newRange.endLine;
   }
 
   //
@@ -281,12 +259,11 @@ public class OnesideDiffChange {
           public void actionPerformed(AnActionEvent e) {
             final Project project = e.getProject();
             final Document document = myViewer.getDocument(sourceSide.other());
-            assert document != null;
 
             DiffUtil.executeWriteCommand(document, project, "Replace change", new Runnable() {
               @Override
               public void run() {
-                myViewer.applyChange(OnesideDiffChange.this, sourceSide);
+                myViewer.applyChange(UnifiedDiffChange.this, sourceSide);
               }
             });
             // applyChange() will schedule rediff, but we want to try to do it in sync
similarity index 91%
rename from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffPanel.java
rename to platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffPanel.java
index 396a8988786a2981cd81ca3f7fd63290d3a012ef..862bcf8975775b8b84312aaa075f2e687118071b 100644 (file)
@@ -19,7 +19,6 @@ import com.intellij.diff.DiffContext;
 import com.intellij.diff.comparison.DiffTooBigException;
 import com.intellij.diff.tools.util.base.DiffPanelBase;
 import com.intellij.openapi.actionSystem.DataProvider;
-import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.project.Project;
 import com.intellij.util.ui.AsyncProcessIcon;
 import org.jetbrains.annotations.NotNull;
@@ -30,7 +29,7 @@ import java.awt.*;
 
 import static com.intellij.diff.util.DiffUtil.createMessagePanel;
 
-public class OnesideDiffPanel extends DiffPanelBase {
+public class UnifiedDiffPanel extends DiffPanelBase {
   private static final String GOOD_CONTENT = "GoodContent";
   private static final String LOADING_CONTENT = "LoadingContent";
   private static final String TOO_BIG_CONTENT = "TooBigContent";
@@ -39,13 +38,13 @@ public class OnesideDiffPanel extends DiffPanelBase {
 
   @NotNull private final AsyncProcessIcon.Big myBusyIcon;
 
-  public OnesideDiffPanel(@Nullable Project project,
-                          @NotNull OnesideContentPanel content,
+  public UnifiedDiffPanel(@Nullable Project project,
+                          @NotNull UnifiedContentPanel content,
                           @NotNull DataProvider provider,
                           @NotNull DiffContext context) {
     super(project, provider, context);
 
-    myBusyIcon = new AsyncProcessIcon.Big("OnesideDiff");
+    myBusyIcon = new AsyncProcessIcon.Big("UnifiedDiff");
 
     JPanel centerPanel = new JPanel(new BorderLayout());
     centerPanel.add(myNotificationsPanel, BorderLayout.NORTH);
similarity index 65%
rename from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffTool.java
rename to platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffTool.java
index 881099ae5f91f2554b3119331db206b1fb6d914e..ca1b431fa14f9ef9b2c6d91ea37bcb599d709ca8 100644 (file)
@@ -18,20 +18,23 @@ package com.intellij.diff.tools.fragmented;
 import com.intellij.diff.DiffContext;
 import com.intellij.diff.FrameDiffTool;
 import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.simple.SimpleOnesideDiffViewer;
 import org.jetbrains.annotations.NotNull;
 
-public class OnesideDiffTool implements FrameDiffTool {
-  public static final OnesideDiffTool INSTANCE = new OnesideDiffTool();
+public class UnifiedDiffTool implements FrameDiffTool {
+  public static final UnifiedDiffTool INSTANCE = new UnifiedDiffTool();
 
   @NotNull
   @Override
   public DiffViewer createComponent(@NotNull DiffContext context, @NotNull DiffRequest request) {
-    return new OnesideDiffViewer(context, request);
+    if (SimpleOnesideDiffViewer.canShowRequest(context, request)) return new SimpleOnesideDiffViewer(context, request);
+    if (UnifiedDiffViewer.canShowRequest(context, request)) return new UnifiedDiffViewer(context, request);
+    throw new IllegalArgumentException(request.toString());
   }
 
   @Override
   public boolean canShow(@NotNull DiffContext context, @NotNull DiffRequest request) {
-    return OnesideDiffViewer.canShowRequest(context, request);
+    return SimpleOnesideDiffViewer.canShowRequest(context, request) || UnifiedDiffViewer.canShowRequest(context, request);
   }
 
   @NotNull
similarity index 73%
rename from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideDiffViewer.java
rename to platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java
index 1138a748dfa375c2446ba869ffbbb3ef30aa78e6..f0cf5497bf83ed6f551b360b56af215ba5efebdd 100644 (file)
@@ -20,24 +20,18 @@ import com.intellij.diff.actions.BufferedLineIterator;
 import com.intellij.diff.actions.DocumentFragmentContent;
 import com.intellij.diff.actions.NavigationContextChecker;
 import com.intellij.diff.actions.impl.OpenInEditorWithMouseAction;
+import com.intellij.diff.actions.impl.SetEditorSettingsAction;
 import com.intellij.diff.comparison.DiffTooBigException;
-import com.intellij.diff.contents.DiffContent;
 import com.intellij.diff.contents.DocumentContent;
 import com.intellij.diff.fragments.LineFragment;
 import com.intellij.diff.requests.ContentDiffRequest;
 import com.intellij.diff.requests.DiffRequest;
 import com.intellij.diff.tools.util.*;
-import com.intellij.diff.tools.util.base.HighlightPolicy;
-import com.intellij.diff.tools.util.base.IgnorePolicy;
-import com.intellij.diff.tools.util.base.InitialScrollPositionSupport;
-import com.intellij.diff.tools.util.base.TextDiffViewerBase;
-import com.intellij.diff.tools.util.twoside.TwosideTextDiffViewer;
-import com.intellij.diff.util.DiffUserDataKeys;
+import com.intellij.diff.tools.util.base.*;
+import com.intellij.diff.tools.util.side.TwosideTextDiffViewer;
+import com.intellij.diff.util.*;
 import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy;
-import com.intellij.diff.util.DiffUtil;
 import com.intellij.diff.util.DiffUtil.DocumentData;
-import com.intellij.diff.util.LineRange;
-import com.intellij.diff.util.Side;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.actionSystem.AnAction;
 import com.intellij.openapi.actionSystem.CommonDataKeys;
@@ -61,10 +55,8 @@ import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Computable;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.UserDataHolder;
-import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.util.Function;
 import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.text.MergingCharSequence;
 import gnu.trove.TIntFunction;
 import org.jetbrains.annotations.*;
 
@@ -76,17 +68,14 @@ import java.util.List;
 
 import static com.intellij.diff.util.DiffUtil.getLineCount;
 
-public class OnesideDiffViewer extends TextDiffViewerBase {
-  public static final Logger LOG = Logger.getInstance(OnesideDiffViewer.class);
+public class UnifiedDiffViewer extends ListenerDiffViewerBase {
+  public static final Logger LOG = Logger.getInstance(UnifiedDiffViewer.class);
 
   @NotNull protected final EditorEx myEditor;
   @NotNull protected final Document myDocument;
-  @NotNull private final OnesideDiffPanel myPanel;
+  @NotNull private final UnifiedDiffPanel myPanel;
 
-  @Nullable private final DocumentContent myActualContent1;
-  @Nullable private final DocumentContent myActualContent2;
-
-  @NotNull private final MySetEditorSettingsAction myEditorSettingsAction;
+  @NotNull private final SetEditorSettingsAction myEditorSettingsAction;
   @NotNull private final PrevNextDifferenceIterable myPrevNextDifferenceIterable;
   @NotNull private final MyStatusPanel myStatusPanel;
 
@@ -106,22 +95,13 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   private boolean myStateIsOutOfDate; // whether something was changed since last rediff
   private boolean mySuppressEditorTyping; // our state is inconsistent. No typing can be handled correctly
 
-  public OnesideDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
+  public UnifiedDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
     super(context, (ContentDiffRequest)request);
 
     myPrevNextDifferenceIterable = new MyPrevNextDifferenceIterable();
     myStatusPanel = new MyStatusPanel();
 
-    myForceReadOnlyFlags = checkForceReadOnly();
-
-
-    List<DiffContent> contents = myRequest.getContents();
-    myActualContent1 = contents.get(0) instanceof DocumentContent ? ((DocumentContent)contents.get(0)) : null;
-    myActualContent2 = contents.get(1) instanceof DocumentContent ? ((DocumentContent)contents.get(1)) : null;
-    assert myActualContent1 != null || myActualContent2 != null;
-
-    if (myActualContent1 == null) myMasterSide = Side.RIGHT;
-    if (myActualContent2 == null) myMasterSide = Side.LEFT;
+    myForceReadOnlyFlags = TextDiffViewerUtil.checkForceReadOnly(myContext, myRequest);
 
     boolean leftEditable = isEditable(Side.LEFT, false);
     boolean rightEditable = isEditable(Side.RIGHT, false);
@@ -133,22 +113,25 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     myEditor = DiffUtil.createEditor(myDocument, myProject, true, true);
 
     List<JComponent> titles = DiffUtil.createTextTitles(myRequest, ContainerUtil.list(myEditor, myEditor));
-    OnesideContentPanel contentPanel = new OnesideContentPanel(titles, myEditor);
+    UnifiedContentPanel contentPanel = new UnifiedContentPanel(titles, myEditor);
 
-    myPanel = new OnesideDiffPanel(myProject, contentPanel, this, myContext);
+    myPanel = new UnifiedDiffPanel(myProject, contentPanel, this, myContext);
 
     myFoldingModel = new MyFoldingModel(myEditor, this);
 
-    myEditorSettingsAction = new MySetEditorSettingsAction();
+    myEditorSettingsAction = new SetEditorSettingsAction(getTextSettings(), getEditors());
     myEditorSettingsAction.applyDefaults();
 
     new MyOpenInEditorWithMouseAction().register(getEditors());
+
+    TextDiffViewerUtil.checkDifferentDocuments(myRequest);
   }
 
   @Override
   @CalledInAwt
   protected void onInit() {
     super.onInit();
+    installEditorListeners();
     installTypingSupport();
     myPanel.setLoadingContent(); // We need loading panel only for initial rediff()
   }
@@ -165,7 +148,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   protected void processContextHints() {
     super.processContextHints();
     Side side = DiffUtil.getUserData(myRequest, myContext, DiffUserDataKeys.MASTER_SIDE);
-    if (side != null && side.select(myActualContent1, myActualContent2) != null) myMasterSide = side;
+    if (side != null) myMasterSide = side;
 
     myInitialScrollHelper.processContext(myRequest);
   }
@@ -224,6 +207,16 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     return group;
   }
 
+  @NotNull
+  protected List<AnAction> createEditorPopupActions() {
+    return TextDiffViewerUtil.createEditorPopupActions();
+  }
+
+  @CalledInAwt
+  protected void installEditorListeners() {
+    new TextDiffViewerUtil.EditorActionsPopup(createEditorPopupActions()).install(getEditors());
+  }
+
   //
   // Diff
   //
@@ -240,64 +233,9 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   protected Runnable performRediff(@NotNull final ProgressIndicator indicator) {
     try {
       indicator.checkCanceled();
-      assert myActualContent1 != null || myActualContent2 != null;
-
-      if (myActualContent1 == null) {
-        final DocumentContent content = myActualContent2;
-        final Document document = content.getDocument();
-
-        OnesideDocumentData data = ApplicationManager.getApplication().runReadAction(new Computable<OnesideDocumentData>() {
-          @Override
-          public OnesideDocumentData compute() {
-            EditorHighlighter highlighter = DiffUtil.createEditorHighlighter(myProject, content);
-            OnesideEditorRangeHighlighter rangeHighlighter = new OnesideEditorRangeHighlighter(myProject, content.getDocument());
-            return new OnesideDocumentData(document.getImmutableCharSequence(), getLineCount(document), highlighter, rangeHighlighter);
-          }
-        });
-
-        List<ChangedBlock> blocks = new ArrayList<ChangedBlock>();
-        blocks.add(ChangedBlock.createInserted(data.getText().length() + 1, data.getLines()));
-
-        indicator.checkCanceled();
-        LineNumberConvertor convertor = LineNumberConvertor.Builder.createRight(data.getLines());
-
-        CombinedEditorData editorData = new CombinedEditorData(new MergingCharSequence(data.getText(), "\n"), data.getHighlighter(),
-                                                               data.getRangeHighlighter(), content.getContentType(),
-                                                               convertor.createConvertor1(), null);
-
-        return apply(editorData, blocks, convertor, Collections.singletonList(new LineRange(0, data.getLines())), false, false);
-      }
-
-      if (myActualContent2 == null) {
-        final DocumentContent content = myActualContent1;
-        final Document document = content.getDocument();
-
-        OnesideDocumentData data = ApplicationManager.getApplication().runReadAction(new Computable<OnesideDocumentData>() {
-          @Override
-          public OnesideDocumentData compute() {
-            EditorHighlighter highlighter = DiffUtil.createEditorHighlighter(myProject, content);
-            OnesideEditorRangeHighlighter rangeHighlighter = new OnesideEditorRangeHighlighter(myProject, content.getDocument());
-            return new OnesideDocumentData(document.getImmutableCharSequence(), getLineCount(document), highlighter, rangeHighlighter);
-          }
-        });
-
-        List<ChangedBlock> blocks = new ArrayList<ChangedBlock>();
-        blocks.add(ChangedBlock.createDeleted(data.getText().length() + 1, data.getLines()));
-
-        indicator.checkCanceled();
-        LineNumberConvertor convertor = LineNumberConvertor.Builder.createLeft(data.getLines());
-
-        CombinedEditorData editorData = new CombinedEditorData(new MergingCharSequence(data.getText(), "\n"), data.getHighlighter(),
-                                                               data.getRangeHighlighter(), content.getContentType(),
-                                                               convertor.createConvertor2(), null);
-
-        return apply(editorData, blocks, convertor, Collections.singletonList(new LineRange(0, data.getLines())), false, false);
-      }
 
-      final DocumentContent content1 = myActualContent1;
-      final DocumentContent content2 = myActualContent2;
-      final Document document1 = content1.getDocument();
-      final Document document2 = content2.getDocument();
+      final Document document1 = getContent1().getDocument();
+      final Document document2 = getContent2().getDocument();
 
       final DocumentData documentData = ApplicationManager.getApplication().runReadAction(new Computable<DocumentData>() {
         @Override
@@ -310,12 +248,15 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
       final boolean innerFragments = getDiffConfig().innerFragments;
       final List<LineFragment> fragments = DiffUtil.compareWithCache(myRequest, documentData, getDiffConfig(), indicator);
 
+      final DocumentContent content1 = getContent1();
+      final DocumentContent content2 = getContent2();
+
       indicator.checkCanceled();
       TwosideDocumentData data = ApplicationManager.getApplication().runReadAction(new Computable<TwosideDocumentData>() {
         @Override
         public TwosideDocumentData compute() {
           indicator.checkCanceled();
-          OnesideFragmentBuilder builder = new OnesideFragmentBuilder(fragments, document1, document2, myMasterSide);
+          UnifiedFragmentBuilder builder = new UnifiedFragmentBuilder(fragments, document1, document2, myMasterSide);
           builder.exec();
 
           indicator.checkCanceled();
@@ -324,13 +265,13 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
                                                            documentData.getText1(), documentData.getText2(), builder.getRanges(),
                                                            builder.getText().length());
 
-          OnesideEditorRangeHighlighter rangeHighlighter = new OnesideEditorRangeHighlighter(myProject, document1, document2,
+          UnifiedEditorRangeHighlighter rangeHighlighter = new UnifiedEditorRangeHighlighter(myProject, document1, document2,
                                                                                              builder.getRanges());
 
           return new TwosideDocumentData(builder, highlighter, rangeHighlighter);
         }
       });
-      OnesideFragmentBuilder builder = data.getBuilder();
+      UnifiedFragmentBuilder builder = data.getBuilder();
 
       FileType fileType = content2.getContentType() == null ? content1.getContentType() : content2.getContentType();
 
@@ -343,7 +284,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
 
       return apply(editorData, builder.getBlocks(), convertor, changedLines, isEqual, innerFragments);
     }
-    catch (DiffTooBigException ignore) {
+    catch (DiffTooBigException e) {
       return new Runnable() {
         @Override
         public void run() {
@@ -352,14 +293,8 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
         }
       };
     }
-    catch (ProcessCanceledException ignore) {
-      return new Runnable() {
-        @Override
-        public void run() {
-          clearDiffPresentation();
-          myPanel.setOperationCanceledContent();
-        }
-      };
+    catch (ProcessCanceledException e) {
+      throw e;
     }
     catch (Throwable e) {
       LOG.error(e);
@@ -393,7 +328,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   protected void markStateIsOutOfDate() {
     myStateIsOutOfDate = true;
     if (myChangedBlockData != null) {
-      for (OnesideDiffChange diffChange : myChangedBlockData.getDiffChanges()) {
+      for (UnifiedDiffChange diffChange : myChangedBlockData.getDiffChanges()) {
         diffChange.updateGutterActions();
       }
     }
@@ -414,7 +349,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     if (highlighter1 == null) highlighter1 = DiffUtil.initEmptyEditorHighlighter(project, text1);
     if (highlighter2 == null) highlighter2 = DiffUtil.initEmptyEditorHighlighter(project, text2);
 
-    return new OnesideEditorHighlighter(myDocument, highlighter1, highlighter2, ranges, textLength);
+    return new UnifiedEditorHighlighter(myDocument, highlighter1, highlighter2, ranges, textLength);
   }
 
   @NotNull
@@ -454,9 +389,9 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
         if (data.getRangeHighlighter() != null) data.getRangeHighlighter().apply(myProject, myDocument);
 
 
-        ArrayList<OnesideDiffChange> diffChanges = new ArrayList<OnesideDiffChange>(blocks.size());
+        ArrayList<UnifiedDiffChange> diffChanges = new ArrayList<UnifiedDiffChange>(blocks.size());
         for (ChangedBlock block : blocks) {
-          diffChanges.add(new OnesideDiffChange(OnesideDiffViewer.this, block, innerFragments));
+          diffChanges.add(new UnifiedDiffChange(UnifiedDiffViewer.this, block, innerFragments));
         }
 
         List<RangeMarker> guarderRangeBlocks = new ArrayList<RangeMarker>();
@@ -492,8 +427,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   }
 
   @Contract("!null, _ -> !null")
-  private static TIntFunction mergeConverters(@Nullable final TIntFunction convertor, @NotNull final TIntFunction separatorLines) {
-    if (convertor == null) return null;
+  private static TIntFunction mergeConverters(@NotNull final TIntFunction convertor, @NotNull final TIntFunction separatorLines) {
     return new TIntFunction() {
       @Override
       public int execute(int value) {
@@ -544,8 +478,8 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     int[] lines = new int[2];
 
     if (myChangedBlockData == null) {
-      lines[0] = myActualContent1 != null ? line : 0;
-      lines[1] = myActualContent2 != null ? line : 0;
+      lines[0] = line;
+      lines[1] = line;
       return Pair.create(lines, myMasterSide);
     }
 
@@ -575,7 +509,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   private void destroyChangedBlockData() {
     if (myChangedBlockData == null) return;
 
-    for (OnesideDiffChange change : myChangedBlockData.getDiffChanges()) {
+    for (UnifiedDiffChange change : myChangedBlockData.getDiffChanges()) {
       change.destroyHighlighter();
     }
     for (RangeMarker block : myChangedBlockData.getGuardedRangeBlocks()) {
@@ -583,7 +517,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     }
     myChangedBlockData = null;
 
-    OnesideEditorRangeHighlighter.erase(myProject, myDocument);
+    UnifiedEditorRangeHighlighter.erase(myProject, myDocument);
 
     myFoldingModel.destroy();
 
@@ -608,19 +542,13 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
         myDuringTwosideDocumentModification = true;
 
         Document twosideDocument = getDocument(myMasterSide);
-        assert twosideDocument != null;
 
-        int offset1 = e.getOffset();
-        int offset2 = e.getOffset() + e.getOldLength();
+        LineCol onesideStartPosition = LineCol.fromOffset(myDocument, e.getOffset());
+        LineCol onesideEndPosition = LineCol.fromOffset(myDocument, e.getOffset() + e.getOldLength());
 
-        if (StringUtil.endsWithChar(e.getOldFragment(), '\n') &&
-            StringUtil.endsWithChar(e.getNewFragment(), '\n')) {
-          offset2--;
-        }
-
-        LineCol onesideStartPosition = LineCol.fromOffset(myDocument, offset1);
-        LineCol onesideEndPosition = LineCol.fromOffset(myDocument, offset2);
-        int shift = StringUtil.countNewLines(e.getNewFragment()) - StringUtil.countNewLines(e.getOldFragment());
+        int line1 = onesideStartPosition.line;
+        int line2 = onesideEndPosition.line + 1;
+        int shift = DiffUtil.countLinesShift(e);
 
         int twosideStartLine = transferLineFromOnesideStrict(myMasterSide, onesideStartPosition.line);
         int twosideEndLine = transferLineFromOnesideStrict(myMasterSide, onesideEndPosition.line);
@@ -635,12 +563,12 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
         int twosideEndOffset = twosideDocument.getLineStartOffset(twosideEndLine) + onesideEndPosition.column;
         twosideDocument.replaceString(twosideStartOffset, twosideEndOffset, e.getNewFragment());
 
-        for (OnesideDiffChange change : myChangedBlockData.getDiffChanges()) {
-          change.processChange(onesideStartPosition.line, onesideEndPosition.line + 1, shift);
+        for (UnifiedDiffChange change : myChangedBlockData.getDiffChanges()) {
+          change.processChange(line1, line2, shift);
         }
 
         LineNumberConvertor lineNumberConvertor = myChangedBlockData.getLineNumberConvertor();
-        lineNumberConvertor.handleOnesideChange(onesideStartPosition.line, onesideEndPosition.line + 1, shift, myMasterSide);
+        lineNumberConvertor.handleOnesideChange(line1, line2, shift, myMasterSide);
       }
       finally {
         // TODO: we can avoid marking state out-of-date in some simple cases (like in SimpleDiffViewer)
@@ -662,8 +590,8 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
       Document document2 = getDocument(Side.RIGHT);
       info.append("==== OnesideDiffViewer Debug Info ====");
       info.append("myMasterSide - ").append(myMasterSide).append('\n');
-      info.append("myLeftDocument.length() - ").append(document1 != null ? document1.getTextLength() : null).append('\n');
-      info.append("myRightDocument.length() - ").append(document2 != null ? document2.getTextLength() : null).append('\n');
+      info.append("myLeftDocument.length() - ").append(document1.getTextLength()).append('\n');
+      info.append("myRightDocument.length() - ").append(document2.getTextLength()).append('\n');
       info.append("myDocument.length() - ").append(myDocument.getTextLength()).append('\n');
       info.append("e.getOffset() - ").append(e.getOffset()).append('\n');
       info.append("e.getNewLength() - ").append(e.getNewLength()).append('\n');
@@ -696,7 +624,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   }
 
   @CalledWithWriteLock
-  public void applyChange(@NotNull OnesideDiffChange change, @NotNull Side sourceSide) {
+  public void applyChange(@NotNull UnifiedDiffChange change, @NotNull Side sourceSide) {
     if (myStateIsOutOfDate || myChangedBlockData == null) return;
 
     Side affectedSide = sourceSide.other();
@@ -704,7 +632,6 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
 
     Document document1 = getDocument(Side.LEFT);
     Document document2 = getDocument(Side.RIGHT);
-    assert document1 != null && document2 != null;
 
     LineFragment lineFragment = change.getLineFragment();
 
@@ -722,6 +649,17 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   // Impl
   //
 
+
+  @NotNull
+  public TextDiffSettingsHolder.TextDiffSettings getTextSettings() {
+    return TextDiffViewerUtil.getTextSettings(myContext);
+  }
+
+  @NotNull
+  public FoldingModelSupport.Settings getFoldingModelSettings() {
+    return TextDiffViewerUtil.getFoldingModelSettings(myContext);
+  }
+
   @NotNull
   private DiffUtil.DiffConfig getDiffConfig() {
     return new DiffUtil.DiffConfig(getIgnorePolicy(), getHighlightPolicy());
@@ -751,14 +689,34 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   }
 
   @NotNull
-  @Override
   protected List<? extends EditorEx> getEditors() {
     return Collections.singletonList(myEditor);
   }
 
+  @NotNull
+  protected List<? extends DocumentContent> getContents() {
+    //noinspection unchecked
+    return (List<? extends DocumentContent>)(List)myRequest.getContents();
+  }
+
+  @NotNull
+  protected DocumentContent getContent(@NotNull Side side) {
+    return side.select(getContents());
+  }
+
+  @NotNull
+  protected DocumentContent getContent1() {
+    return getContent(Side.LEFT);
+  }
+
+  @NotNull
+  protected DocumentContent getContent2() {
+    return getContent(Side.RIGHT);
+  }
+
   @CalledInAwt
   @Nullable
-  protected List<OnesideDiffChange> getDiffChanges() {
+  protected List<UnifiedDiffChange> getDiffChanges() {
     return myChangedBlockData == null ? null : myChangedBlockData.getDiffChanges();
   }
 
@@ -785,14 +743,12 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   public boolean isEditable(@NotNull Side side, boolean respectReadOnlyLock) {
     if (myReadOnlyLockSet && respectReadOnlyLock) return false;
     if (side.select(myForceReadOnlyFlags)) return false;
-    Document document = getDocument(side);
-    return document != null && DiffUtil.canMakeWritable(document);
+    return DiffUtil.canMakeWritable(getDocument(side));
   }
 
-  @Nullable
+  @NotNull
   public Document getDocument(@NotNull Side side) {
-    DocumentContent content = side.select(myActualContent1, myActualContent2);
-    return content != null ? content.getDocument() : null;
+    return getContent(side).getDocument();
   }
 
   protected boolean isStateIsOutOfDate() {
@@ -811,11 +767,11 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
 
   @CalledInAwt
   @Nullable
-  protected OnesideDiffChange getCurrentChange() {
+  protected UnifiedDiffChange getCurrentChange() {
     if (myChangedBlockData == null) return null;
     int caretLine = myEditor.getCaretModel().getLogicalPosition().line;
 
-    for (OnesideDiffChange change : myChangedBlockData.getDiffChanges()) {
+    for (UnifiedDiffChange change : myChangedBlockData.getDiffChanges()) {
       if (DiffUtil.isSelectedByLine(caretLine, change.getLine1(), change.getLine2())) return change;
     }
     return null;
@@ -824,24 +780,16 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   @CalledInAwt
   @Nullable
   protected OpenFileDescriptor getOpenFileDescriptor(int offset) {
-    assert myActualContent1 != null || myActualContent2 != null;
-    if (myActualContent2 == null) {
-      return myActualContent1.getOpenFileDescriptor(offset);
-    }
-    if (myActualContent1 == null) {
-      return myActualContent2.getOpenFileDescriptor(offset);
-    }
-
     LogicalPosition position = myEditor.offsetToLogicalPosition(offset);
     Pair<int[], Side> pair = transferLineFromOneside(position.line);
-    int offset1 = DiffUtil.getOffset(myActualContent1.getDocument(), pair.first[0], position.column);
-    int offset2 = DiffUtil.getOffset(myActualContent2.getDocument(), pair.first[1], position.column);
+    int offset1 = DiffUtil.getOffset(getContent1().getDocument(), pair.first[0], position.column);
+    int offset2 = DiffUtil.getOffset(getContent2().getDocument(), pair.first[1], position.column);
 
     // TODO: issue: non-optimal GoToSource position with caret on deleted block for "Compare with local"
     //       we should transfer using calculated diff, not jump to "somehow related" position from old content's descriptor
 
-    OpenFileDescriptor descriptor1 = myActualContent1.getOpenFileDescriptor(offset1);
-    OpenFileDescriptor descriptor2 = myActualContent2.getOpenFileDescriptor(offset2);
+    OpenFileDescriptor descriptor1 = getContent1().getOpenFileDescriptor(offset1);
+    OpenFileDescriptor descriptor2 = getContent2().getOpenFileDescriptor(offset2);
     if (descriptor1 == null) return descriptor2;
     if (descriptor2 == null) return descriptor1;
     return pair.second.select(descriptor1, descriptor2);
@@ -855,69 +803,32 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   // Actions
   //
 
-  private class MyPrevNextDifferenceIterable implements PrevNextDifferenceIterable {
+  private class MyPrevNextDifferenceIterable extends PrevNextDifferenceIterableBase<UnifiedDiffChange> {
+    @NotNull
     @Override
-    public boolean canGoNext() {
-      List<OnesideDiffChange> diffChanges = getDiffChanges();
-      if (diffChanges == null || diffChanges.isEmpty()) return false;
-
-      int line = myEditor.getCaretModel().getLogicalPosition().line;
-      OnesideDiffChange lastChange = diffChanges.get(diffChanges.size() - 1);
-      if (lastChange.getLine1() <= line) return false;
-
-      return true;
+    protected List<UnifiedDiffChange> getChanges() {
+      return ContainerUtil.notNullize(getDiffChanges());
     }
 
+    @NotNull
     @Override
-    public void goNext() {
-      List<OnesideDiffChange> diffChanges = getDiffChanges();
-      assert diffChanges != null;
-      int line = myEditor.getCaretModel().getLogicalPosition().line;
-
-      OnesideDiffChange next = null;
-      for (int i = 0; i < diffChanges.size(); i++) {
-        OnesideDiffChange change = diffChanges.get(i);
-        if (change.getLine1() <= line) continue;
-
-        next = change;
-        break;
-      }
-
-      assert next != null;
-
-      DiffUtil.scrollEditor(myEditor, next.getLine1(), true);
+    protected EditorEx getEditor() {
+      return myEditor;
     }
 
     @Override
-    public boolean canGoPrev() {
-      List<OnesideDiffChange> diffChanges = getDiffChanges();
-      if (diffChanges == null || diffChanges.isEmpty()) return false;
-
-      int line = myEditor.getCaretModel().getLogicalPosition().line;
-      OnesideDiffChange firstChange = diffChanges.get(0);
-      if (firstChange.getLine2() > line) return false;
-
-      return true;
+    protected int getStartLine(@NotNull UnifiedDiffChange change) {
+      return change.getLine1();
     }
 
     @Override
-    public void goPrev() {
-      List<OnesideDiffChange> diffChanges = getDiffChanges();
-      assert diffChanges != null;
-      int line = myEditor.getCaretModel().getLogicalPosition().line;
-
-      OnesideDiffChange prev = null;
-      for (int i = 0; i < diffChanges.size(); i++) {
-        OnesideDiffChange change = diffChanges.get(i);
-        if (change.getLine2() <= line) continue;
-
-        prev = diffChanges.get(i - 1);
-        break;
-      }
-
-      if (prev == null) prev = diffChanges.get(diffChanges.size() - 1);
+    protected int getEndLine(@NotNull UnifiedDiffChange change) {
+      return change.getLine2();
+    }
 
-      DiffUtil.scrollEditor(myEditor, prev.getLine1(), true);
+    @Override
+    protected void scrollToChange(@NotNull UnifiedDiffChange change) {
+      DiffUtil.scrollEditor(myEditor, change.getLine1(), true);
     }
   }
 
@@ -930,14 +841,22 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     }
   }
 
-  private class MyToggleExpandByDefaultAction extends ToggleExpandByDefaultAction {
+  private class MyToggleExpandByDefaultAction extends TextDiffViewerUtil.ToggleExpandByDefaultAction {
+    public MyToggleExpandByDefaultAction() {
+      super(getTextSettings());
+    }
+
     @Override
     protected void expandAll(boolean expand) {
       myFoldingModel.expandAll(expand);
     }
   }
 
-  private class MyHighlightPolicySettingAction extends HighlightPolicySettingAction {
+  private class MyHighlightPolicySettingAction extends TextDiffViewerUtil.HighlightPolicySettingAction {
+    public MyHighlightPolicySettingAction() {
+      super(getTextSettings());
+    }
+
     @NotNull
     @Override
     protected HighlightPolicy getCurrentSetting() {
@@ -951,9 +870,18 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
       settings.remove(HighlightPolicy.DO_NOT_HIGHLIGHT);
       return settings;
     }
+
+    @Override
+    protected void onSettingsChanged() {
+      rediff();
+    }
   }
 
-  private class MyIgnorePolicySettingAction extends IgnorePolicySettingAction {
+  private class MyIgnorePolicySettingAction extends TextDiffViewerUtil.IgnorePolicySettingAction {
+    public MyIgnorePolicySettingAction() {
+      super(getTextSettings());
+    }
+
     @NotNull
     @Override
     protected IgnorePolicy getCurrentSetting() {
@@ -967,10 +895,16 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
       settings.remove(IgnorePolicy.IGNORE_WHITESPACES_CHUNKS);
       return settings;
     }
+
+    @Override
+    protected void onSettingsChanged() {
+      rediff();
+    }
   }
 
-  private class MyReadOnlyLockAction extends ReadOnlyLockAction {
+  private class MyReadOnlyLockAction extends TextDiffViewerUtil.ReadOnlyLockAction {
     public MyReadOnlyLockAction() {
+      super(getContext());
       init();
     }
 
@@ -978,8 +912,8 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     protected void doApply(boolean readOnly) {
       myReadOnlyLockSet = readOnly;
       if (myChangedBlockData != null) {
-        for (OnesideDiffChange onesideDiffChange : myChangedBlockData.getDiffChanges()) {
-          onesideDiffChange.updateGutterActions();
+        for (UnifiedDiffChange unifiedDiffChange : myChangedBlockData.getDiffChanges()) {
+          unifiedDiffChange.updateGutterActions();
         }
       }
       updateEditorCanBeTyped();
@@ -987,8 +921,8 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
 
     @Override
     protected boolean canEdit() {
-      return myActualContent1 != null && !myForceReadOnlyFlags[0] && DiffUtil.canMakeWritable(myActualContent1.getDocument()) ||
-             myActualContent2 != null && !myForceReadOnlyFlags[1] && DiffUtil.canMakeWritable(myActualContent2.getDocument());
+      return !myForceReadOnlyFlags[0] && DiffUtil.canMakeWritable(getContent1().getDocument()) ||
+             !myForceReadOnlyFlags[1] && DiffUtil.canMakeWritable(getContent2().getDocument());
     }
   }
 
@@ -1004,9 +938,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     private AllLinesIterator(@NotNull Side side) {
       mySide = side;
 
-      DocumentContent content = mySide.select(myActualContent1, myActualContent2);
-      assert content != null;
-      myDocument = content.getDocument();
+      myDocument = getContent(mySide).getDocument();
     }
 
     @Override
@@ -1035,11 +967,11 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
 
   private class ChangedLinesIterator extends BufferedLineIterator {
     @NotNull private final Side mySide;
-    @NotNull private final List<OnesideDiffChange> myChanges;
+    @NotNull private final List<UnifiedDiffChange> myChanges;
 
     private int myIndex = 0;
 
-    private ChangedLinesIterator(@NotNull Side side, @NotNull List<OnesideDiffChange> changes) {
+    private ChangedLinesIterator(@NotNull Side side, @NotNull List<UnifiedDiffChange> changes) {
       mySide = side;
       myChanges = changes;
       init();
@@ -1052,17 +984,16 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
 
     @Override
     public void loadNextBlock() {
-      if (myActualContent2 == null) return; // because we want only insertions
       LOG.assertTrue(!myStateIsOutOfDate);
 
-      OnesideDiffChange change = myChanges.get(myIndex);
+      UnifiedDiffChange change = myChanges.get(myIndex);
       myIndex++;
 
       LineFragment lineFragment = change.getLineFragment();
 
       int insertedStart = lineFragment.getStartOffset2();
       int insertedEnd = lineFragment.getEndOffset2();
-      CharSequence insertedText = myActualContent2.getDocument().getCharsSequence().subSequence(insertedStart, insertedEnd);
+      CharSequence insertedText = getContent(mySide).getDocument().getCharsSequence().subSequence(insertedStart, insertedEnd);
 
       int lineNumber = lineFragment.getStartLine2();
 
@@ -1091,7 +1022,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
       return myEditor;
     }
     else if (DiffDataKeys.CURRENT_CHANGE_RANGE.is(dataId)) {
-      OnesideDiffChange change = getCurrentChange();
+      UnifiedDiffChange change = getCurrentChange();
       if (change != null) {
         return new LineRange(change.getLine1(), change.getLine2());
       }
@@ -1106,58 +1037,21 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     }
   }
 
-  private static class OnesideDocumentData {
-    @NotNull private final CharSequence myText;
-    private final int myLines;
-
-    @Nullable private final EditorHighlighter myHighlighter;
-    @Nullable private final OnesideEditorRangeHighlighter myRangeHighlighter;
-
-    public OnesideDocumentData(@NotNull CharSequence text,
-                               int lines,
-                               @Nullable EditorHighlighter highlighter,
-                               @Nullable OnesideEditorRangeHighlighter rangeHighlighter) {
-      myText = text;
-      myLines = lines;
-      myHighlighter = highlighter;
-      myRangeHighlighter = rangeHighlighter;
-    }
-
-    @NotNull
-    public CharSequence getText() {
-      return myText;
-    }
-
-    public int getLines() {
-      return myLines;
-    }
-
-    @Nullable
-    public EditorHighlighter getHighlighter() {
-      return myHighlighter;
-    }
-
-    @Nullable
-    public OnesideEditorRangeHighlighter getRangeHighlighter() {
-      return myRangeHighlighter;
-    }
-  }
-
   private static class TwosideDocumentData {
-    @NotNull private final OnesideFragmentBuilder myBuilder;
+    @NotNull private final UnifiedFragmentBuilder myBuilder;
     @Nullable private final EditorHighlighter myHighlighter;
-    @Nullable private final OnesideEditorRangeHighlighter myRangeHighlighter;
+    @Nullable private final UnifiedEditorRangeHighlighter myRangeHighlighter;
 
-    public TwosideDocumentData(@NotNull OnesideFragmentBuilder builder,
+    public TwosideDocumentData(@NotNull UnifiedFragmentBuilder builder,
                                @Nullable EditorHighlighter highlighter,
-                               @Nullable OnesideEditorRangeHighlighter rangeHighlighter) {
+                               @Nullable UnifiedEditorRangeHighlighter rangeHighlighter) {
       myBuilder = builder;
       myHighlighter = highlighter;
       myRangeHighlighter = rangeHighlighter;
     }
 
     @NotNull
-    public OnesideFragmentBuilder getBuilder() {
+    public UnifiedFragmentBuilder getBuilder() {
       return myBuilder;
     }
 
@@ -1167,17 +1061,17 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     }
 
     @Nullable
-    public OnesideEditorRangeHighlighter getRangeHighlighter() {
+    public UnifiedEditorRangeHighlighter getRangeHighlighter() {
       return myRangeHighlighter;
     }
   }
 
   private static class ChangedBlockData {
-    @NotNull private final List<OnesideDiffChange> myDiffChanges;
+    @NotNull private final List<UnifiedDiffChange> myDiffChanges;
     @NotNull private final List<RangeMarker> myGuardedRangeBlocks;
     @NotNull private final LineNumberConvertor myLineNumberConvertor;
 
-    public ChangedBlockData(@NotNull List<OnesideDiffChange> diffChanges,
+    public ChangedBlockData(@NotNull List<UnifiedDiffChange> diffChanges,
                             @NotNull List<RangeMarker> guarderRangeBlocks,
                             @NotNull LineNumberConvertor lineNumberConvertor) {
       myDiffChanges = diffChanges;
@@ -1186,7 +1080,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     }
 
     @NotNull
-    public List<OnesideDiffChange> getDiffChanges() {
+    public List<UnifiedDiffChange> getDiffChanges() {
       return myDiffChanges;
     }
 
@@ -1204,17 +1098,17 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
   private static class CombinedEditorData {
     @NotNull private final CharSequence myText;
     @Nullable private final EditorHighlighter myHighlighter;
-    @Nullable private final OnesideEditorRangeHighlighter myRangeHighlighter;
+    @Nullable private final UnifiedEditorRangeHighlighter myRangeHighlighter;
     @Nullable private final FileType myFileType;
     @NotNull private final TIntFunction myLineConvertor1;
-    @Nullable private final TIntFunction myLineConvertor2;
+    @NotNull private final TIntFunction myLineConvertor2;
 
     public CombinedEditorData(@NotNull CharSequence text,
                               @Nullable EditorHighlighter highlighter,
-                              @Nullable OnesideEditorRangeHighlighter rangeHighlighter,
+                              @Nullable UnifiedEditorRangeHighlighter rangeHighlighter,
                               @Nullable FileType fileType,
                               @NotNull TIntFunction convertor1,
-                              @Nullable TIntFunction convertor2) {
+                              @NotNull TIntFunction convertor2) {
       myText = text;
       myHighlighter = highlighter;
       myRangeHighlighter = rangeHighlighter;
@@ -1234,7 +1128,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     }
 
     @Nullable
-    public OnesideEditorRangeHighlighter getRangeHighlighter() {
+    public UnifiedEditorRangeHighlighter getRangeHighlighter() {
       return myRangeHighlighter;
     }
 
@@ -1248,7 +1142,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
       return myLineConvertor1;
     }
 
-    @Nullable
+    @NotNull
     public TIntFunction getLineConvertor2() {
       return myLineConvertor2;
     }
@@ -1258,7 +1152,7 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     @NotNull
     @Override
     protected List<? extends Editor> getEditors() {
-      return OnesideDiffViewer.this.getEditors();
+      return UnifiedDiffViewer.this.getEditors();
     }
 
     @Override
@@ -1320,9 +1214,9 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
 
     private boolean doScrollToChange(@NotNull ScrollToPolicy scrollToChangePolicy) {
       if (myChangedBlockData == null) return false;
-      List<OnesideDiffChange> changes = myChangedBlockData.getDiffChanges();
+      List<UnifiedDiffChange> changes = myChangedBlockData.getDiffChanges();
 
-      OnesideDiffChange targetChange = scrollToChangePolicy.select(changes);
+      UnifiedDiffChange targetChange = scrollToChangePolicy.select(changes);
       if (targetChange == null) return false;
 
       DiffUtil.scrollEditor(myEditor, targetChange.getLine1(), false);
@@ -1344,7 +1238,6 @@ public class OnesideDiffViewer extends TextDiffViewerBase {
     protected boolean doScrollToContext() {
       if (myNavigationContext == null) return false;
       if (myChangedBlockData == null) return false;
-      if (myActualContent2 == null) return false;
 
       ChangedLinesIterator changedLinesIterator = new ChangedLinesIterator(Side.RIGHT, myChangedBlockData.getDiffChanges());
       NavigationContextChecker checker = new NavigationContextChecker(changedLinesIterator, myNavigationContext);
similarity index 97%
rename from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideEditorHighlighter.java
rename to platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedEditorHighlighter.java
index a7ade89b52939e293fabe1bd9d50d021de68566d..3099af0d0e472af155837fa5f6c590dc66ff6bb1 100644 (file)
@@ -34,13 +34,13 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
-public class OnesideEditorHighlighter implements EditorHighlighter {
-  public static final Logger LOG = OnesideDiffViewer.LOG;
+class UnifiedEditorHighlighter implements EditorHighlighter {
+  public static final Logger LOG = UnifiedDiffViewer.LOG;
 
   @NotNull private final Document myDocument;
   @NotNull private final List<Element> myPieces;
 
-  public OnesideEditorHighlighter(@NotNull Document document,
+  public UnifiedEditorHighlighter(@NotNull Document document,
                                   @NotNull EditorHighlighter highlighter1,
                                   @NotNull EditorHighlighter highlighter2,
                                   @NotNull List<HighlightRange> ranges,
similarity index 96%
rename from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideEditorRangeHighlighter.java
rename to platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedEditorRangeHighlighter.java
index 67c524aef07074511034d77811c1ec69b527c005..740027bb11c209ef760d9c35ec67f55b96f3db42 100644 (file)
@@ -32,12 +32,12 @@ import org.jetbrains.annotations.Nullable;
 import java.util.ArrayList;
 import java.util.List;
 
-public class OnesideEditorRangeHighlighter {
-  public static final Logger LOG = OnesideDiffViewer.LOG;
+class UnifiedEditorRangeHighlighter {
+  public static final Logger LOG = UnifiedDiffViewer.LOG;
 
   @NotNull private final List<Element> myPieces = new ArrayList<Element>();
 
-  public OnesideEditorRangeHighlighter(@Nullable Project project, @NotNull Document document) {
+  public UnifiedEditorRangeHighlighter(@Nullable Project project, @NotNull Document document) {
     ApplicationManager.getApplication().assertReadAccessAllowed();
 
     MarkupModelEx model = (MarkupModelEx)DocumentMarkupModel.forDocument(document, project, false);
@@ -56,7 +56,7 @@ public class OnesideEditorRangeHighlighter {
     });
   }
 
-  public OnesideEditorRangeHighlighter(@Nullable Project project,
+  public UnifiedEditorRangeHighlighter(@Nullable Project project,
                                        @NotNull Document document1,
                                        @NotNull Document document2,
                                        @NotNull List<HighlightRange> ranges) {
similarity index 97%
rename from platform/diff-impl/src/com/intellij/diff/tools/fragmented/OnesideFragmentBuilder.java
rename to platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilder.java
index 3b94882981df144360866163ccd73e5f18a4d416..b994a86409321c43d68165e680e864ab99e97e10 100644 (file)
@@ -15,7 +15,6 @@
  */
 package com.intellij.diff.tools.fragmented;
 
-import com.intellij.diff.fragments.DiffFragment;
 import com.intellij.diff.fragments.LineFragment;
 import com.intellij.diff.util.LineRange;
 import com.intellij.diff.util.Side;
@@ -27,7 +26,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 // This class works incorrectly with non-fair differences (when chunk of matched lines has different length in left/right files)
-public class OnesideFragmentBuilder {
+class UnifiedFragmentBuilder {
   @NotNull private final List<LineFragment> myFragments;
   @NotNull private final Document myDocument1;
   @NotNull private final Document myDocument2;
@@ -39,7 +38,7 @@ public class OnesideFragmentBuilder {
   @NotNull private final LineNumberConvertor.Builder myConvertor = new LineNumberConvertor.Builder();
   @NotNull private final List<LineRange> myChangedLines = new ArrayList<LineRange>();
 
-  public OnesideFragmentBuilder(@NotNull List<LineFragment> fragments,
+  public UnifiedFragmentBuilder(@NotNull List<LineFragment> fragments,
                                 @NotNull Document document1,
                                 @NotNull Document document2,
                                 @NotNull Side masterSide) {
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/holders/BinaryEditorHolder.java b/platform/diff-impl/src/com/intellij/diff/tools/holders/BinaryEditorHolder.java
new file mode 100644 (file)
index 0000000..8844147
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.holders;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.contents.DiffContent;
+import com.intellij.diff.contents.DocumentContent;
+import com.intellij.diff.contents.FileContent;
+import com.intellij.diff.util.DiffUtil;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorProvider;
+import com.intellij.openapi.fileEditor.TextEditor;
+import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager;
+import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
+import com.intellij.openapi.fileTypes.UIBasedFileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.event.FocusListener;
+
+public class BinaryEditorHolder extends EditorHolder {
+  @NotNull protected final FileEditor myEditor;
+  @NotNull protected final FileEditorProvider myEditorProvider;
+
+  public BinaryEditorHolder(@NotNull FileEditor editor, @NotNull FileEditorProvider editorProvider) {
+    myEditor = editor;
+    myEditorProvider = editorProvider;
+  }
+
+  @NotNull
+  public FileEditor getEditor() {
+    return myEditor;
+  }
+
+  @Override
+  public void dispose() {
+    myEditorProvider.disposeEditor(myEditor);
+  }
+
+  @NotNull
+  @Override
+  public JComponent getComponent() {
+    return myEditor.getComponent();
+  }
+
+  @Override
+  public void installFocusListener(@NotNull FocusListener listener) {
+    myEditor.getComponent().addFocusListener(listener);
+  }
+
+  @Nullable
+  @Override
+  public JComponent getPreferredFocusedComponent() {
+    return myEditor.getPreferredFocusedComponent();
+  }
+
+  //
+  // Build
+  //
+
+  public static class BinaryEditorHolderFactory extends EditorHolderFactory<BinaryEditorHolder> {
+    public static BinaryEditorHolderFactory INSTANCE = new BinaryEditorHolderFactory();
+
+    @Override
+    @NotNull
+    public BinaryEditorHolder create(@NotNull DiffContent content, @NotNull DiffContext context) {
+      Project project = context.getProject();
+      if (content instanceof FileContent) {
+        if (project == null) project = ProjectManager.getInstance().getDefaultProject();
+        VirtualFile file = ((FileContent)content).getFile();
+
+        FileEditorProvider[] providers = FileEditorProviderManager.getInstance().getProviders(project, file);
+        if (providers.length == 0) throw new IllegalStateException("Can't find FileEditorProvider: " + file.getFileType());
+
+        FileEditorProvider provider = providers[0];
+        FileEditor editor = provider.createEditor(project, file);
+
+        UIUtil.removeScrollBorder(editor.getComponent());
+
+        return new BinaryEditorHolder(editor, provider);
+      }
+      if (content instanceof DocumentContent) {
+        Document document = ((DocumentContent)content).getDocument();
+        final Editor editor = DiffUtil.createEditor(document, project, true);
+
+        TextEditorProvider provider = TextEditorProvider.getInstance();
+        TextEditor fileEditor = provider.getTextEditor(editor);
+
+        Disposer.register(fileEditor, new Disposable() {
+          @Override
+          public void dispose() {
+            EditorFactory.getInstance().releaseEditor(editor);
+          }
+        });
+
+        return new BinaryEditorHolder(fileEditor, provider);
+      }
+
+      throw new IllegalArgumentException(content.getClass() + " - " + content.toString());
+    }
+
+    @Override
+    public boolean canShowContent(@NotNull DiffContent content, @NotNull DiffContext context) {
+      if (content instanceof DocumentContent) return true;
+      if (content instanceof FileContent) {
+        Project project = context.getProject();
+        if (project == null) project = ProjectManager.getInstance().getDefaultProject();
+        VirtualFile file = ((FileContent)content).getFile();
+
+        return FileEditorProviderManager.getInstance().getProviders(project, file).length != 0;
+      }
+      return false;
+    }
+
+    @Override
+    public boolean wantShowContent(@NotNull DiffContent content, @NotNull DiffContext context) {
+      if (content instanceof FileContent) {
+        if (content.getContentType() == null) return false;
+        if (content.getContentType().isBinary()) return true;
+        if (content.getContentType() instanceof UIBasedFileType) return true;
+        return false;
+      }
+      return false;
+    }
+  }
+}
similarity index 55%
rename from platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextContentPanel.java
rename to platform/diff-impl/src/com/intellij/diff/tools/holders/EditorHolder.java
index 13f029e80a872799b63f5daf60b9e48138f57865..bd4efc221bc85ba3e5da87901680b3d9b4b0c877 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.intellij.diff.tools.util.twoside;
+package com.intellij.diff.tools.holders;
 
-import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.Disposable;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
-import java.util.List;
+import java.awt.event.FocusListener;
 
-public class TwosideTextContentPanel extends TwosideContentPanel {
-  public TwosideTextContentPanel(@NotNull List<JComponent> titleComponents,
-                                 @Nullable Editor editor1,
-                                 @Nullable Editor editor2) {
-    super(titleComponents, getComponent(editor1), getComponent(editor2));
-  }
+public abstract class EditorHolder implements Disposable {
+  @NotNull
+  public abstract JComponent getComponent();
 
   @Nullable
-  private static JComponent getComponent(@Nullable Editor editor) {
-    return editor != null ? editor.getComponent() : null;
+  public abstract JComponent getPreferredFocusedComponent();
+
+  public void installFocusListener(@NotNull FocusListener listener) {
   }
 }
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/holders/EditorHolderFactory.java b/platform/diff-impl/src/com/intellij/diff/tools/holders/EditorHolderFactory.java
new file mode 100644 (file)
index 0000000..d75637e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.holders;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.contents.DiffContent;
+import org.jetbrains.annotations.NotNull;
+
+public abstract class EditorHolderFactory<T extends EditorHolder> {
+  public abstract boolean canShowContent(@NotNull DiffContent content, @NotNull DiffContext context);
+
+  public abstract boolean wantShowContent(@NotNull DiffContent content, @NotNull DiffContext context);
+
+  public abstract T create(@NotNull DiffContent content, @NotNull DiffContext context);
+}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/holders/TextEditorHolder.java b/platform/diff-impl/src/com/intellij/diff/tools/holders/TextEditorHolder.java
new file mode 100644 (file)
index 0000000..50abbb2
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.holders;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.contents.DiffContent;
+import com.intellij.diff.contents.DocumentContent;
+import com.intellij.diff.util.DiffUtil;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.event.FocusListener;
+
+public class TextEditorHolder extends EditorHolder {
+  @NotNull protected final EditorEx myEditor;
+
+  public TextEditorHolder(@NotNull EditorEx editor) {
+    myEditor = editor;
+  }
+
+  @NotNull
+  public EditorEx getEditor() {
+    return myEditor;
+  }
+
+  @Override
+  public void dispose() {
+    EditorFactory.getInstance().releaseEditor(myEditor);
+  }
+
+  @NotNull
+  @Override
+  public JComponent getComponent() {
+    return myEditor.getComponent();
+  }
+
+  @Override
+  public void installFocusListener(@NotNull FocusListener listener) {
+    myEditor.getContentComponent().addFocusListener(listener);
+  }
+
+  @Nullable
+  @Override
+  public JComponent getPreferredFocusedComponent() {
+    return myEditor.getContentComponent();
+  }
+
+  //
+  // Build
+  //
+
+  public static class TextEditorHolderFactory extends EditorHolderFactory<TextEditorHolder> {
+    public static TextEditorHolderFactory INSTANCE = new TextEditorHolderFactory();
+
+    @Override
+    @NotNull
+    public TextEditorHolder create(@NotNull DiffContent content, @NotNull DiffContext context) {
+      if (!(content instanceof DocumentContent)) throw new IllegalArgumentException(content.toString());
+      Project project = context.getProject();
+      DocumentContent documentContent = (DocumentContent)content;
+
+      EditorEx editor = DiffUtil.createEditor(documentContent.getDocument(), project, false, true);
+      DiffUtil.configureEditor(editor, documentContent, project);
+      return new TextEditorHolder(editor);
+    }
+
+    @Override
+    public boolean canShowContent(@NotNull DiffContent content, @NotNull DiffContext context) {
+      if (content instanceof DocumentContent) return true;
+      return false;
+    }
+
+    @Override
+    public boolean wantShowContent(@NotNull DiffContent content, @NotNull DiffContext context) {
+      if (content instanceof DocumentContent) return true;
+      return false;
+    }
+  }
+}
index 6ebca07a36ba2b0254a8aef27896bcc221139038..5f7752fb0eca63229e260e533430c8dc1e0f9aad 100644 (file)
@@ -43,9 +43,6 @@ public class SimpleDiffChange {
   @NotNull private final LineFragment myFragment;
   @Nullable private final List<DiffFragment> myInnerFragments;
 
-  @Nullable private final EditorEx myEditor1;
-  @Nullable private final EditorEx myEditor2;
-
   @NotNull private final List<RangeHighlighter> myHighlighters = new ArrayList<RangeHighlighter>();
   @NotNull private final List<MyGutterOperation> myOperations = new ArrayList<MyGutterOperation>();
 
@@ -56,17 +53,12 @@ public class SimpleDiffChange {
   // TODO: adjust color from inner fragments - configurable
   public SimpleDiffChange(@NotNull SimpleDiffViewer viewer,
                           @NotNull LineFragment fragment,
-                          @Nullable EditorEx editor1,
-                          @Nullable EditorEx editor2,
                           boolean inlineHighlight) {
     myViewer = viewer;
 
     myFragment = fragment;
     myInnerFragments = inlineHighlight ? fragment.getInnerFragments() : null;
 
-    myEditor1 = editor1;
-    myEditor2 = editor2;
-
     installHighlighter();
   }
 
@@ -112,15 +104,12 @@ public class SimpleDiffChange {
   }
 
   private void doInstallActionHighlighters() {
-    if (myEditor1 != null && myEditor2 != null) {
-      myOperations.add(createOperation(Side.LEFT));
-      myOperations.add(createOperation(Side.RIGHT));
-    }
+    myOperations.add(createOperation(Side.LEFT));
+    myOperations.add(createOperation(Side.RIGHT));
   }
 
   private void createHighlighter(@NotNull Side side, boolean ignored) {
-    Editor editor = side.select(myEditor1, myEditor2);
-    if (editor == null) return;
+    Editor editor = myViewer.getEditor(side);
 
     int start = side.getStartOffset(myFragment);
     int end = side.getEndOffset(myFragment);
@@ -141,9 +130,6 @@ public class SimpleDiffChange {
   }
 
   private void createInlineHighlighter(@NotNull DiffFragment fragment, @NotNull Side side) {
-    Editor editor = side.select(myEditor1, myEditor2);
-    if (editor == null) return;
-
     int start = side.getStartOffset(fragment);
     int end = side.getEndOffset(fragment);
     TextDiffType type = DiffUtil.getDiffType(fragment);
@@ -152,6 +138,7 @@ public class SimpleDiffChange {
     start += startOffset;
     end += startOffset;
 
+    Editor editor = myViewer.getEditor(side);
     RangeHighlighter highlighter = DiffDrawUtil.createInlineHighlighter(editor, start, end, type);
     myHighlighters.add(highlighter);
   }
@@ -190,26 +177,22 @@ public class SimpleDiffChange {
   public boolean processChange(int oldLine1, int oldLine2, int shift, @NotNull Side side) {
     int line1 = getStartLine(side);
     int line2 = getEndLine(side);
+    int sideIndex = side.getIndex();
 
-    if (line2 <= oldLine1) return false;
-    if (line1 >= oldLine2) {
-      myLineStartShifts[side.getIndex()] += shift;
-      myLineEndShifts[side.getIndex()] += shift;
-      return false;
-    }
+    DiffUtil.UpdatedLineRange newRange = DiffUtil.updateRangeOnModification(line1, line2, oldLine1, oldLine2, shift);
+    myLineStartShifts[sideIndex] += newRange.startLine - line1;
+    myLineEndShifts[sideIndex] += newRange.endLine - line2;
 
-    if (line1 <= oldLine1 && line2 >= oldLine2) {
-      myLineEndShifts[side.getIndex()] += shift;
-      return false;
-    }
+    if (newRange.damaged) {
+      for (MyGutterOperation operation : myOperations) {
+        operation.dispose();
+      }
+      myOperations.clear();
 
-    for (MyGutterOperation operation : myOperations) {
-      operation.dispose();
+      myIsValid = false;
     }
-    myOperations.clear();
 
-    myIsValid = false;
-    return true;
+    return newRange.damaged;
   }
 
   //
@@ -217,8 +200,6 @@ public class SimpleDiffChange {
   //
 
   public boolean isSelectedByLine(int line, @NotNull Side side) {
-    if (myEditor1 == null || myEditor2 == null) return false;
-
     int line1 = getStartLine(side);
     int line2 = getEndLine(side);
 
@@ -231,9 +212,8 @@ public class SimpleDiffChange {
 
   @NotNull
   private MyGutterOperation createOperation(@NotNull Side side) {
-    assert myEditor1 != null && myEditor2 != null;
     int offset = side.getStartOffset(myFragment);
-    EditorEx editor = side.select(myEditor1, myEditor2);
+    EditorEx editor = myViewer.getEditor(side);
     RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(offset, offset,
                                                                                HighlighterLayer.ADDITIONAL_SYNTAX,
                                                                                null,
@@ -273,13 +253,11 @@ public class SimpleDiffChange {
 
     @Nullable
     public GutterIconRenderer createRenderer() {
-      assert myEditor1 != null && myEditor2 != null;
-
       myCtrlPressed = myViewer.getModifierProvider().isCtrlPressed();
       myShiftPressed = myViewer.getModifierProvider().isShiftPressed();
 
-      boolean isEditable = DiffUtil.isEditable(mySide.select(myEditor1, myEditor2));
-      boolean isOtherEditable = DiffUtil.isEditable(mySide.other().select(myEditor1, myEditor2));
+      boolean isEditable = DiffUtil.isEditable(myViewer.getEditor(mySide));
+      boolean isOtherEditable = DiffUtil.isEditable(myViewer.getEditor(mySide.other()));
       boolean isAppendable = myFragment.getStartLine1() != myFragment.getEndLine1() &&
                              myFragment.getStartLine2() != myFragment.getEndLine2();
 
@@ -328,8 +306,7 @@ public class SimpleDiffChange {
                                                 @NotNull final String tooltipText,
                                                 @NotNull final Icon icon,
                                                 @NotNull final Runnable perform) {
-    assert myEditor1 != null && myEditor2 != null;
-    if (!DiffUtil.isEditable(sourceSide.other().select(myEditor1, myEditor2))) return null;
+    if (!DiffUtil.isEditable(myViewer.getEditor(sourceSide.other()))) return null;
     return new GutterIconRenderer() {
       @NotNull
       @Override
@@ -347,13 +324,10 @@ public class SimpleDiffChange {
         return new DumbAwareAction() {
           @Override
           public void actionPerformed(AnActionEvent e) {
-            final Project project = e.getProject();
-            final Document document1 = myEditor1.getDocument();
-            final Document document2 = myEditor2.getDocument();
-
             if (!myIsValid) return;
-
-            DiffUtil.executeWriteCommand(sourceSide.other().select(document1, document2), project, "Replace change", new Runnable() {
+            final Project project = e.getProject();
+            final Document document = myViewer.getEditor(sourceSide.other()).getDocument();
+            DiffUtil.executeWriteCommand(document, project, "Replace change", new Runnable() {
               @Override
               public void run() {
                 perform.run();
index 00ad0ec846e3677bf5b03310aa73075fc0be4000..bc8d6bfcda7f90dbfcf22cb4c860c4404185d07b 100644 (file)
@@ -26,13 +26,17 @@ public class SimpleDiffTool implements FrameDiffTool {
   @NotNull
   @Override
   public DiffViewer createComponent(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    if (SimpleOnesideDiffViewer.canShowRequest(context, request)) return new SimpleOnesideDiffViewer(context, request);
+    if (SimpleDiffViewer.canShowRequest(context, request)) return new SimpleDiffViewer(context, request);
     if (SimpleThreesideDiffViewer.canShowRequest(context, request)) return new SimpleThreesideDiffViewer(context, request);
-    return new SimpleDiffViewer(context, request);
+    throw new IllegalArgumentException(request.toString());
   }
 
   @Override
   public boolean canShow(@NotNull DiffContext context, @NotNull DiffRequest request) {
-    return SimpleDiffViewer.canShowRequest(context, request) || SimpleThreesideDiffViewer.canShowRequest(context, request);
+    return SimpleOnesideDiffViewer.canShowRequest(context, request) ||
+           SimpleDiffViewer.canShowRequest(context, request) ||
+           SimpleThreesideDiffViewer.canShowRequest(context, request);
   }
 
   @NotNull
index 8a8a65a87f964a68b2d29f47c79dd299f2fe70cd..a0a7762d679e40d0ec5c2e770959a028cf88ff0f 100644 (file)
@@ -19,19 +19,17 @@ import com.intellij.diff.DiffContext;
 import com.intellij.diff.actions.BufferedLineIterator;
 import com.intellij.diff.actions.NavigationContextChecker;
 import com.intellij.diff.comparison.DiffTooBigException;
-import com.intellij.diff.contents.DocumentContent;
 import com.intellij.diff.fragments.LineFragment;
-import com.intellij.diff.fragments.LineFragmentImpl;
 import com.intellij.diff.requests.ContentDiffRequest;
 import com.intellij.diff.requests.DiffRequest;
 import com.intellij.diff.tools.util.*;
 import com.intellij.diff.tools.util.base.HighlightPolicy;
-import com.intellij.diff.tools.util.twoside.TwosideTextDiffViewer;
+import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
+import com.intellij.diff.tools.util.side.TwosideTextDiffViewer;
 import com.intellij.diff.util.*;
 import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy;
 import com.intellij.diff.util.DiffUtil.DocumentData;
 import com.intellij.icons.AllIcons;
-import com.intellij.ide.IdeEventQueue;
 import com.intellij.openapi.Disposable;
 import com.intellij.openapi.actionSystem.AnAction;
 import com.intellij.openapi.actionSystem.AnActionEvent;
@@ -53,15 +51,13 @@ import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.UserDataHolder;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.util.Function;
-import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.*;
 
 import javax.swing.*;
 import java.awt.*;
-import java.awt.event.KeyEvent;
-import java.awt.event.WindowEvent;
-import java.awt.event.WindowFocusListener;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Iterator;
 import java.util.List;
 
 import static com.intellij.diff.util.DiffUtil.getLineCount;
@@ -76,7 +72,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   @NotNull private final List<SimpleDiffChange> myDiffChanges = new ArrayList<SimpleDiffChange>();
   @NotNull private final List<SimpleDiffChange> myInvalidDiffChanges = new ArrayList<SimpleDiffChange>();
 
-  @Nullable private final MyFoldingModel myFoldingModel;
+  @NotNull private final MyFoldingModel myFoldingModel;
   @NotNull private final MyInitialScrollHelper myInitialScrollHelper = new MyInitialScrollHelper();
   @NotNull private final ModifierProvider myModifierProvider;
 
@@ -86,7 +82,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     mySyncScrollable = new MySyncScrollable();
     myPrevNextDifferenceIterable = new MyPrevNextDifferenceIterable();
     myStatusPanel = new MyStatusPanel();
-    myFoldingModel = createFoldingModel(getEditor1(), getEditor2());
+    myFoldingModel = new MyFoldingModel(getEditors(), this);
 
     myModifierProvider = new ModifierProvider();
   }
@@ -102,7 +98,6 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   @Override
   @CalledInAwt
   protected void onDispose() {
-    myModifierProvider.destroy();
     destroyChangedBlocks();
     super.onDispose();
   }
@@ -112,10 +107,10 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   protected List<AnAction> createToolbarActions() {
     List<AnAction> group = new ArrayList<AnAction>();
 
-    group.add(new IgnorePolicySettingAction());
-    group.add(new HighlightPolicySettingAction());
+    group.add(new MyIgnorePolicySettingAction());
+    group.add(new MyHighlightPolicySettingAction());
     group.add(new MyToggleExpandByDefaultAction());
-    group.add(new ToggleAutoScrollAction());
+    group.add(new MyToggleAutoScrollAction());
     group.add(new MyReadOnlyLockAction());
     group.add(myEditorSettingsAction);
 
@@ -128,11 +123,11 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     List<AnAction> group = new ArrayList<AnAction>();
 
     group.add(Separator.getInstance());
-    group.add(new IgnorePolicySettingAction().getPopupGroup());
+    group.add(new MyIgnorePolicySettingAction().getPopupGroup());
     group.add(Separator.getInstance());
-    group.add(new HighlightPolicySettingAction().getPopupGroup());
+    group.add(new MyHighlightPolicySettingAction().getPopupGroup());
     group.add(Separator.getInstance());
-    group.add(new ToggleAutoScrollAction());
+    group.add(new MyToggleAutoScrollAction());
     group.add(new MyToggleExpandByDefaultAction());
 
     return group;
@@ -153,13 +148,6 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     return group;
   }
 
-  @Nullable
-  private MyFoldingModel createFoldingModel(@Nullable EditorEx editor1, @Nullable EditorEx editor2) {
-    if (editor1 == null || editor2 == null) return null;
-
-    return new MyFoldingModel(editor1, editor2, this);
-  }
-
   @Override
   @CalledInAwt
   protected void processContextHints() {
@@ -171,7 +159,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   @CalledInAwt
   protected void updateContextHints() {
     super.updateContextHints();
-    if (myFoldingModel != null) myFoldingModel.updateContext(myRequest, getFoldingModelSettings());
+    myFoldingModel.updateContext(myRequest, getFoldingModelSettings());
     myInitialScrollHelper.updateContext(myRequest);
   }
 
@@ -179,6 +167,11 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   // Diff
   //
 
+  @NotNull
+  public FoldingModelSupport.Settings getFoldingModelSettings() {
+    return TextDiffViewerUtil.getFoldingModelSettings(myContext);
+  }
+
   @Override
   protected void onSlowRediff() {
     super.onSlowRediff();
@@ -192,44 +185,8 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     try {
       indicator.checkCanceled();
 
-      assert getActualContent1() != null || getActualContent2() != null;
-
-      if (getActualContent1() == null) {
-        final DocumentContent content = getActualContent2();
-        final Document document = content.getDocument();
-
-        CompareData data = ApplicationManager.getApplication().runReadAction(new Computable<CompareData>() {
-          @Override
-          public CompareData compute() {
-            List<LineFragment> fragments = Collections.<LineFragment>singletonList(new LineFragmentImpl(0, 0, 0, getLineCount(document),
-                                                                                                        0, 0, 0, document.getTextLength()));
-            return new CompareData(fragments, false);
-          }
-        });
-
-        return apply(data);
-      }
-
-      if (getActualContent2() == null) {
-        final DocumentContent content = getActualContent1();
-        final Document document = content.getDocument();
-
-        CompareData data = ApplicationManager.getApplication().runReadAction(new Computable<CompareData>() {
-          @Override
-          public CompareData compute() {
-            List<LineFragment> fragments = Collections.<LineFragment>singletonList(new LineFragmentImpl(0, getLineCount(document), 0, 0,
-                                                                                                        0, document.getTextLength(), 0, 0));
-            return new CompareData(fragments, false);
-          }
-        });
-
-        return apply(data);
-      }
-
-      final DocumentContent content1 = getActualContent1();
-      final DocumentContent content2 = getActualContent2();
-      final Document document1 = content1.getDocument();
-      final Document document2 = content2.getDocument();
+      final Document document1 = getContent1().getDocument();
+      final Document document2 = getContent2().getDocument();
 
       DocumentData data = ApplicationManager.getApplication().runReadAction(new Computable<DocumentData>() {
         @Override
@@ -249,11 +206,11 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
 
       return apply(new CompareData(lineFragments, isEqualContents));
     }
-    catch (DiffTooBigException ignore) {
+    catch (DiffTooBigException e) {
       return applyNotification(DiffNotifications.DIFF_TOO_BIG);
     }
-    catch (ProcessCanceledException ignore) {
-      return applyNotification(DiffNotifications.OPERATION_CANCELED);
+    catch (ProcessCanceledException e) {
+      throw e;
     }
     catch (Throwable e) {
       LOG.error(e);
@@ -266,21 +223,18 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     return new Runnable() {
       @Override
       public void run() {
-        if (myFoldingModel != null) myFoldingModel.updateContext(myRequest, getFoldingModelSettings());
+        myFoldingModel.updateContext(myRequest, getFoldingModelSettings());
         clearDiffPresentation();
 
         if (data.isEqualContent()) myPanel.addNotification(DiffNotifications.EQUAL_CONTENTS);
 
         if (data.getFragments() != null) {
           for (LineFragment fragment : data.getFragments()) {
-            myDiffChanges.add(new SimpleDiffChange(SimpleDiffViewer.this, fragment, getEditor1(), getEditor2(),
-                                                   getHighlightPolicy().isFineFragments()));
+            myDiffChanges.add(new SimpleDiffChange(SimpleDiffViewer.this, fragment, getHighlightPolicy().isFineFragments()));
           }
         }
 
-        if (myFoldingModel != null) {
-          myFoldingModel.install(data.getFragments(), myRequest, getFoldingModelSettings());
-        }
+        myFoldingModel.install(data.getFragments(), myRequest, getFoldingModelSettings());
 
         myInitialScrollHelper.onRediff();
 
@@ -332,7 +286,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     }
     myInvalidDiffChanges.clear();
 
-    if (myFoldingModel != null) myFoldingModel.destroy();
+    myFoldingModel.destroy();
 
     myContentPanel.repaintDivider();
     myStatusPanel.update();
@@ -343,7 +297,6 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   protected void onBeforeDocumentChange(@NotNull DocumentEvent e) {
     super.onBeforeDocumentChange(e);
     if (myDiffChanges.isEmpty()) return;
-    if (getEditor1() == null || getEditor2() == null) return;
 
     Side side = null;
     if (e.getDocument() == getEditor(Side.LEFT).getDocument()) side = Side.LEFT;
@@ -353,17 +306,9 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
       return;
     }
 
-    int offset1 = e.getOffset();
-    int offset2 = e.getOffset() + e.getOldLength();
-
-    if (StringUtil.endsWithChar(e.getOldFragment(), '\n') &&
-        StringUtil.endsWithChar(e.getNewFragment(), '\n')) {
-      offset2--;
-    }
-
-    int line1 = e.getDocument().getLineNumber(offset1);
-    int line2 = e.getDocument().getLineNumber(offset2) + 1;
-    int shift = StringUtil.countNewLines(e.getNewFragment()) - StringUtil.countNewLines(e.getOldFragment());
+    int line1 = e.getDocument().getLineNumber(e.getOffset());
+    int line2 = e.getDocument().getLineNumber(e.getOffset() + e.getOldLength()) + 1;
+    int shift = DiffUtil.countLinesShift(e);
 
     List<SimpleDiffChange> invalid = new ArrayList<SimpleDiffChange>();
     for (SimpleDiffChange change : myDiffChanges) {
@@ -381,13 +326,11 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   @Override
   protected void onDocumentChange(@NotNull DocumentEvent e) {
     super.onDocumentChange(e);
-    if (myFoldingModel != null) myFoldingModel.onDocumentChanged(e);
+    myFoldingModel.onDocumentChanged(e);
   }
 
   @CalledInAwt
   protected boolean doScrollToChange(@NotNull ScrollToPolicy scrollToPolicy) {
-    if (getEditor1() == null || getEditor2() == null) return true;
-
     SimpleDiffChange targetChange = scrollToPolicy.select(myDiffChanges);
     if (targetChange == null) return false;
 
@@ -396,9 +339,6 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   }
 
   private void doScrollToChange(@NotNull SimpleDiffChange change, final boolean animated) {
-    if (getEditor1() == null || getEditor2() == null) return;
-    assert mySyncScrollSupport != null;
-
     final int line1 = change.getStartLine(Side.LEFT);
     final int line2 = change.getStartLine(Side.RIGHT);
     final int endLine1 = change.getEndLine(Side.LEFT);
@@ -407,12 +347,10 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     DiffUtil.moveCaret(getEditor1(), line1);
     DiffUtil.moveCaret(getEditor2(), line2);
 
-    mySyncScrollSupport.makeVisible(getCurrentSide(), line1, endLine1, line2, endLine2, animated);
+    getSyncScrollSupport().makeVisible(getCurrentSide(), line1, endLine1, line2, endLine2, animated);
   }
 
   protected boolean doScrollToContext(@NotNull DiffNavigationContext context) {
-    if (getEditor2() == null) return false;
-
     ChangedLinesIterator changedLinesIterator = new ChangedLinesIterator(Side.RIGHT);
     NavigationContextChecker checker = new NavigationContextChecker(changedLinesIterator, context);
     int line = checker.contextMatchCheck();
@@ -455,6 +393,13 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     return myModifierProvider;
   }
 
+  @NotNull
+  @Override
+  public SyncScrollSupport.TwosideSyncScrollSupport getSyncScrollSupport() {
+    //noinspection ConstantConditions
+    return super.getSyncScrollSupport();
+  }
+
   //
   // Misc
   //
@@ -467,10 +412,8 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   @NotNull
   @CalledInAwt
   private List<SimpleDiffChange> getSelectedChanges(@NotNull Side side) {
-    EditorEx editor = getEditor(side);
-    if (editor == null) return Collections.emptyList();
+    final BitSet lines = DiffUtil.getSelectedLines(getEditor(side));
 
-    final BitSet lines = DiffUtil.getSelectedLines(editor);
     List<SimpleDiffChange> affectedChanges = new ArrayList<SimpleDiffChange>();
     for (int i = myDiffChanges.size() - 1; i >= 0; i--) {
       SimpleDiffChange change = myDiffChanges.get(i);
@@ -487,10 +430,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   @Nullable
   @CalledInAwt
   private SimpleDiffChange getSelectedChange(@NotNull Side side) {
-    EditorEx editor = getEditor(side);
-    if (editor == null) return null;
-
-    int caretLine = editor.getCaretModel().getLogicalPosition().line;
+    int caretLine = getEditor(side).getCaretModel().getLogicalPosition().line;
 
     for (SimpleDiffChange change : myDiffChanges) {
       int line1 = change.getStartLine(side);
@@ -505,78 +445,40 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   // Actions
   //
 
-  private class MyPrevNextDifferenceIterable implements PrevNextDifferenceIterable {
+  private class MyPrevNextDifferenceIterable extends PrevNextDifferenceIterableBase<SimpleDiffChange> {
+    @NotNull
     @Override
-    public boolean canGoNext() {
-      if (myDiffChanges.isEmpty()) return false;
-
-      EditorEx editor = getCurrentEditor();
-      int line = editor.getCaretModel().getLogicalPosition().line;
-      if (line == editor.getDocument().getLineCount() - 1) return false;
-
-      SimpleDiffChange lastChange = myDiffChanges.get(myDiffChanges.size() - 1);
-      if (lastChange.getStartLine(getCurrentSide()) <= line) return false;
-
-      return true;
+    protected List<SimpleDiffChange> getChanges() {
+      return myDiffChanges;
     }
 
+    @NotNull
     @Override
-    public void goNext() {
-      EditorEx editor = getCurrentEditor();
-
-      int line = editor.getCaretModel().getLogicalPosition().line;
-
-      SimpleDiffChange next = null;
-      for (int i = 0; i < myDiffChanges.size(); i++) {
-        SimpleDiffChange change = myDiffChanges.get(i);
-        if (change.getStartLine(getCurrentSide()) <= line) continue;
-
-        next = change;
-        break;
-      }
-
-      assert next != null;
-      doScrollToChange(next, true);
+    protected EditorEx getEditor() {
+      return getCurrentEditor();
     }
 
     @Override
-    public boolean canGoPrev() {
-      if (myDiffChanges.isEmpty()) return false;
-
-      EditorEx editor = getCurrentEditor();
-      int line = editor.getCaretModel().getLogicalPosition().line;
-      if (line == 0) return false;
-
-      SimpleDiffChange firstChange = myDiffChanges.get(0);
-      if (firstChange.getEndLine(getCurrentSide()) > line) return false;
-      if (firstChange.getStartLine(getCurrentSide()) >= line) return false;
-
-      return true;
+    protected int getStartLine(@NotNull SimpleDiffChange change) {
+      return change.getStartLine(getCurrentSide());
     }
 
     @Override
-    public void goPrev() {
-      EditorEx editor = getCurrentEditor();
-
-      int line = editor.getCaretModel().getLogicalPosition().line;
-
-      SimpleDiffChange prev = null;
-      for (int i = 0; i < myDiffChanges.size(); i++) {
-        SimpleDiffChange change = myDiffChanges.get(i);
-
-        SimpleDiffChange next = i < myDiffChanges.size() - 1 ? myDiffChanges.get(i + 1) : null;
-        if (next == null || next.getEndLine(getCurrentSide()) > line || next.getStartLine(getCurrentSide()) >= line) {
-          prev = change;
-          break;
-        }
-      }
+    protected int getEndLine(@NotNull SimpleDiffChange change) {
+      return change.getEndLine(getCurrentSide());
+    }
 
-      assert prev != null;
-      doScrollToChange(prev, true);
+    @Override
+    protected void scrollToChange(@NotNull SimpleDiffChange change) {
+      doScrollToChange(change, true);
     }
   }
 
-  private class MyReadOnlyLockAction extends EditorReadOnlyLockAction {
+  private class MyReadOnlyLockAction extends TextDiffViewerUtil.EditorReadOnlyLockAction {
+    public MyReadOnlyLockAction() {
+      super(getContext(), getEditableEditors());
+    }
+
     @Override
     protected void doApply(boolean readOnly) {
       super.doApply(readOnly);
@@ -604,37 +506,31 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     @Override
     public void update(@NotNull AnActionEvent e) {
       Editor editor = e.getData(CommonDataKeys.EDITOR);
-      Side side = Side.fromLeft(editor == getEditor1());
-
-      if (getEditor1() == null || getEditor2() == null) {
-        e.getPresentation().setEnabledAndVisible(false);
-        return;
-      }
-
       if (editor != getEditor1() && editor != getEditor2()) {
         e.getPresentation().setEnabledAndVisible(false);
         return;
       }
 
-      Editor modifiedEditor = side.other(myModifyOpposite).select(getEditor1(), getEditor2());
+      Side side = Side.fromLeft(editor == getEditor(Side.LEFT));
+      Editor modifiedEditor = getEditor(side.other(myModifyOpposite));
+
       if (!DiffUtil.isEditable(modifiedEditor)) {
         e.getPresentation().setEnabledAndVisible(false);
         return;
       }
 
       e.getPresentation().setIcon(getIcon(side));
+      e.getPresentation().setVisible(true);
       e.getPresentation().setEnabled(isSomeChangeSelected(side));
     }
 
     @Override
     public void actionPerformed(@NotNull final AnActionEvent e) {
-      assert getEditor1() != null && getEditor2() != null;
-
       Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
-      final Side side = Side.fromLeft(editor == getEditor1());
+      final Side side = Side.fromLeft(editor == getEditor(Side.LEFT));
       final List<SimpleDiffChange> selectedChanges = getSelectedChanges(side);
 
-      Editor modifiedEditor = side.other(myModifyOpposite).select(getEditor1(), getEditor2());
+      Editor modifiedEditor = getEditor(side.other(myModifyOpposite));
       String title = e.getPresentation().getText() + " selected changes";
       DiffUtil.executeWriteCommand(modifiedEditor.getDocument(), e.getProject(), title, new Runnable() {
         @Override
@@ -647,10 +543,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     protected boolean isSomeChangeSelected(@NotNull Side side) {
       if (myDiffChanges.isEmpty()) return false;
 
-      Editor editor = getEditor(side);
-      if (editor == null) return false;
-
-      List<Caret> carets = editor.getCaretModel().getAllCarets();
+      List<Caret> carets = getEditor(side).getCaretModel().getAllCarets();
       if (carets.size() != 1) return true;
       Caret caret = carets.get(0);
       if (caret.hasSelection()) return true;
@@ -728,17 +621,11 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
 
   @CalledWithWriteLock
   public void replaceChange(@NotNull SimpleDiffChange change, @NotNull final Side sourceSide) {
-    assert getEditor1() != null && getEditor2() != null;
-
     if (!change.isValid()) return;
+    Side outputSide = sourceSide.other();
 
-    final Document document1 = getEditor1().getDocument();
-    final Document document2 = getEditor2().getDocument();
-
-    DiffUtil.applyModification(sourceSide.other().select(document1, document2),
-                               change.getStartLine(sourceSide.other()), change.getEndLine(sourceSide.other()),
-                               sourceSide.select(document1, document2),
-                               change.getStartLine(sourceSide), change.getEndLine(sourceSide));
+    DiffUtil.applyModification(getEditor(outputSide).getDocument(), change.getStartLine(outputSide), change.getEndLine(outputSide),
+                               getEditor(sourceSide).getDocument(), change.getStartLine(sourceSide), change.getEndLine(sourceSide));
 
     change.destroyHighlighter();
     myDiffChanges.remove(change);
@@ -746,27 +633,47 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
 
   @CalledWithWriteLock
   public void appendChange(@NotNull SimpleDiffChange change, @NotNull final Side sourceSide) {
-    assert getEditor1() != null && getEditor2() != null;
-
     if (!change.isValid()) return;
     if (change.getStartLine(sourceSide) == change.getEndLine(sourceSide)) return;
+    Side outputSide = sourceSide.other();
 
-    final Document document1 = getEditor1().getDocument();
-    final Document document2 = getEditor2().getDocument();
-
-    DiffUtil.applyModification(sourceSide.other().select(document1, document2),
-                               change.getEndLine(sourceSide.other()), change.getEndLine(sourceSide.other()),
-                               sourceSide.select(document1, document2),
-                               change.getStartLine(sourceSide), change.getEndLine(sourceSide));
+    DiffUtil.applyModification(getEditor(outputSide).getDocument(), change.getEndLine(outputSide), change.getEndLine(outputSide),
+                               getEditor(sourceSide).getDocument(), change.getStartLine(sourceSide), change.getEndLine(sourceSide));
 
     change.destroyHighlighter();
     myDiffChanges.remove(change);
   }
 
-  private class MyToggleExpandByDefaultAction extends ToggleExpandByDefaultAction {
+  private class MyHighlightPolicySettingAction extends TextDiffViewerUtil.HighlightPolicySettingAction {
+    public MyHighlightPolicySettingAction() {
+      super(getTextSettings());
+    }
+
+    @Override
+    protected void onSettingsChanged() {
+      rediff();
+    }
+  }
+
+  private class MyIgnorePolicySettingAction extends TextDiffViewerUtil.IgnorePolicySettingAction {
+    public MyIgnorePolicySettingAction() {
+      super(getTextSettings());
+    }
+
+    @Override
+    protected void onSettingsChanged() {
+      rediff();
+    }
+  }
+
+  private class MyToggleExpandByDefaultAction extends TextDiffViewerUtil.ToggleExpandByDefaultAction {
+    public MyToggleExpandByDefaultAction() {
+      super(getTextSettings());
+    }
+
     @Override
     protected void expandAll(boolean expand) {
-      if (myFoldingModel != null) myFoldingModel.expandAll(expand);
+      myFoldingModel.expandAll(expand);
     }
   }
 
@@ -782,9 +689,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     private AllLinesIterator(@NotNull Side side) {
       mySide = side;
 
-      Editor editor = getEditor(mySide);
-      assert editor != null;
-      myDocument = editor.getDocument();
+      myDocument = getEditor(mySide).getDocument();
     }
 
     @Override
@@ -833,9 +738,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
       int line1 = change.getStartLine(mySide);
       int line2 = change.getEndLine(mySide);
 
-      Editor editor = getEditor(mySide);
-      assert editor != null;
-      Document document = editor.getDocument();
+      Document document = getEditor(mySide).getDocument();
 
       for (int i = line1; i < line2; i++) {
         int offset1 = document.getLineStartOffset(i);
@@ -882,8 +785,6 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
 
     @Override
     protected void processHelper(@NotNull ScrollHelper helper) {
-      assert getEditor1() != null && getEditor2() != null;
-
       if (!helper.process(0, 0)) return;
       for (SimpleDiffChange diffChange : myDiffChanges) {
         if (!helper.process(diffChange.getStartLine(Side.LEFT), diffChange.getStartLine(Side.RIGHT))) return;
@@ -896,7 +797,6 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   private class MyDividerPainter implements DiffSplitter.Painter, DiffDividerDrawUtil.DividerPaintable {
     @Override
     public void paint(@NotNull Graphics g, @NotNull JComponent divider) {
-      if (getEditor1() == null || getEditor2() == null) return;
       Graphics2D gg = DiffDividerDrawUtil.getDividerGraphics(g, divider, getEditor1().getComponent());
 
       gg.setColor(DiffDrawUtil.getDividerColor(getEditor1()));
@@ -905,7 +805,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
       //DividerPolygonUtil.paintSimplePolygons(gg, divider.getWidth(), getEditor1(), getEditor2(), this);
       DiffDividerDrawUtil.paintPolygons(gg, divider.getWidth(), getEditor1(), getEditor2(), this);
 
-      if (myFoldingModel != null) myFoldingModel.paintOnDivider(gg, divider);
+      myFoldingModel.paintOnDivider(gg, divider);
 
       gg.dispose();
     }
@@ -948,86 +848,13 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
     }
   }
 
-  public class ModifierProvider {
-    private boolean myShiftPressed;
-    private boolean myCtrlPressed;
-    private boolean myAltPressed;
-
-    private Window myWindow;
-
-    private final WindowFocusListener myWindowFocusListener = new WindowFocusListener() {
-      @Override
-      public void windowGainedFocus(WindowEvent e) {
-        resetState();
-      }
-
-      @Override
-      public void windowLostFocus(WindowEvent e) {
-        resetState();
-      }
-    };
-
+  public class ModifierProvider extends KeyboardModifierListener {
     public void init() {
-      // we can use KeyListener on Editors, but Ctrl+Click will not work with focus in other place.
-      // ex: commit dialog with focus in commit message
-      IdeEventQueue.getInstance().addPostprocessor(new IdeEventQueue.EventDispatcher() {
-        @Override
-        public boolean dispatch(AWTEvent e) {
-          if (e instanceof KeyEvent) {
-            onKeyEvent((KeyEvent)e);
-          }
-          return false;
-        }
-      }, SimpleDiffViewer.this);
-
-      myWindow = UIUtil.getWindow(myPanel);
-      if (myWindow != null) {
-        myWindow.addWindowFocusListener(myWindowFocusListener);
-      }
-    }
-
-    public void destroy() {
-      if (myWindow != null) {
-        myWindow.removeWindowFocusListener(myWindowFocusListener);
-      }
+      init(myPanel, SimpleDiffViewer.this);
     }
 
-    private void onKeyEvent(KeyEvent e) {
-      final int keyCode = e.getKeyCode();
-      if (keyCode == KeyEvent.VK_SHIFT) {
-        myShiftPressed = e.getID() == KeyEvent.KEY_PRESSED;
-        updateActions();
-      }
-      if (keyCode == KeyEvent.VK_CONTROL) {
-        myCtrlPressed = e.getID() == KeyEvent.KEY_PRESSED;
-        updateActions();
-      }
-      if (keyCode == KeyEvent.VK_ALT) {
-        myAltPressed = e.getID() == KeyEvent.KEY_PRESSED;
-        updateActions();
-      }
-    }
-
-    private void resetState() {
-      myShiftPressed = false;
-      myAltPressed = false;
-      myCtrlPressed = false;
-      updateActions();
-    }
-
-    public boolean isShiftPressed() {
-      return myShiftPressed;
-    }
-
-    public boolean isCtrlPressed() {
-      return myCtrlPressed;
-    }
-
-    public boolean isAltPressed() {
-      return myAltPressed;
-    }
-
-    public void updateActions() {
+    @Override
+    public void onModifiersChanged() {
       for (SimpleDiffChange change : myDiffChanges) {
         change.updateGutterActions(false);
       }
@@ -1037,8 +864,8 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer {
   private static class MyFoldingModel extends FoldingModelSupport {
     private final MyPaintable myPaintable = new MyPaintable(0, 1);
 
-    public MyFoldingModel(@NotNull EditorEx editor1, @NotNull EditorEx editor2, @NotNull Disposable disposable) {
-      super(new EditorEx[]{editor1, editor2}, disposable);
+    public MyFoldingModel(@NotNull List<? extends EditorEx> editors, @NotNull Disposable disposable) {
+      super(editors.toArray(new EditorEx[2]), disposable);
     }
 
     public void install(@Nullable final List<LineFragment> fragments,
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleOnesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleOnesideDiffViewer.java
new file mode 100644 (file)
index 0000000..8f7fae0
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.simple;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.actions.NavigationContextChecker;
+import com.intellij.diff.contents.DocumentContent;
+import com.intellij.diff.requests.ContentDiffRequest;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.util.DiffDataKeys;
+import com.intellij.diff.tools.util.base.HighlightPolicy;
+import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
+import com.intellij.diff.tools.util.side.OnesideTextDiffViewer;
+import com.intellij.diff.util.DiffDrawUtil;
+import com.intellij.diff.util.DiffUtil;
+import com.intellij.diff.util.LineRange;
+import com.intellij.diff.util.TextDiffType;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.Separator;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.diff.DiffNavigationContext;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.openapi.editor.markup.RangeHighlighter;
+import com.intellij.openapi.editor.markup.SeparatorPlacement;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.util.Pair;
+import org.jetbrains.annotations.CalledInAwt;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import static com.intellij.diff.util.DiffUtil.getLineCount;
+
+public class SimpleOnesideDiffViewer extends OnesideTextDiffViewer {
+  public static final Logger LOG = Logger.getInstance(SimpleOnesideDiffViewer.class);
+
+  @NotNull private final MyInitialScrollHelper myInitialScrollHelper = new MyInitialScrollHelper();
+
+  @NotNull private final List<RangeHighlighter> myHighlighters = new ArrayList<RangeHighlighter>();
+
+  public SimpleOnesideDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    super(context, (ContentDiffRequest)request);
+  }
+
+  @Override
+  @CalledInAwt
+  protected void onDispose() {
+    for (RangeHighlighter highlighter : myHighlighters) {
+      highlighter.dispose();
+    }
+    myHighlighters.clear();
+    super.onDispose();
+  }
+
+  @NotNull
+  @Override
+  protected List<AnAction> createToolbarActions() {
+    List<AnAction> group = new ArrayList<AnAction>();
+
+    group.add(new MyIgnorePolicySettingAction());
+    group.add(new MyHighlightPolicySettingAction());
+    group.add(new MyReadOnlyLockAction());
+    group.add(myEditorSettingsAction);
+
+    return group;
+  }
+
+  @Nullable
+  @Override
+  protected List<AnAction> createPopupActions() {
+    List<AnAction> group = new ArrayList<AnAction>();
+
+    group.add(Separator.getInstance());
+    group.add(new MyIgnorePolicySettingAction().getPopupGroup());
+    group.add(Separator.getInstance());
+    group.add(new MyHighlightPolicySettingAction().getPopupGroup());
+
+    return group;
+  }
+
+  @Override
+  @CalledInAwt
+  protected void processContextHints() {
+    super.processContextHints();
+    myInitialScrollHelper.processContext(myRequest);
+  }
+
+  @Override
+  @CalledInAwt
+  protected void updateContextHints() {
+    super.updateContextHints();
+    myInitialScrollHelper.updateContext(myRequest);
+  }
+
+  //
+  // Diff
+  //
+
+  @Override
+  @NotNull
+  protected Runnable performRediff(@NotNull final ProgressIndicator indicator) {
+    indicator.checkCanceled();
+
+    return new Runnable() {
+      @Override
+      public void run() {
+        clearDiffPresentation();
+
+        boolean shouldHighlight = getTextSettings().getHighlightPolicy() != HighlightPolicy.DO_NOT_HIGHLIGHT;
+        if (shouldHighlight) {
+          final DocumentContent content = getContent();
+          final Document document = content.getDocument();
+
+          int start = 0;
+          int end = document.getTextLength();
+          TextDiffType type = getSide().select(TextDiffType.DELETED, TextDiffType.INSERTED);
+
+          myHighlighters.add(DiffDrawUtil.createHighlighter(getEditor(), start, end, type, false));
+
+          int startLine = 0;
+          int endLine = getLineCount(document);
+
+          if (startLine != endLine) {
+            myHighlighters.add(DiffDrawUtil.createLineMarker(getEditor(), startLine, type, SeparatorPlacement.TOP));
+            myHighlighters.add(DiffDrawUtil.createLineMarker(getEditor(), endLine - 1, type, SeparatorPlacement.BOTTOM));
+          }
+        }
+
+        myInitialScrollHelper.onRediff();
+      }
+    };
+  }
+
+
+  private void clearDiffPresentation() {
+    myPanel.resetNotifications();
+
+    for (RangeHighlighter highlighter : myHighlighters) {
+      highlighter.dispose();
+    }
+    myHighlighters.clear();
+  }
+
+  //
+  // Impl
+  //
+
+  private void doScrollToChange(final boolean animated) {
+    DiffUtil.moveCaret(getEditor(), 0);
+    DiffUtil.scrollEditor(getEditor(), 0, animated);
+  }
+
+  protected boolean doScrollToContext(@NotNull DiffNavigationContext context) {
+    if (getSide().isLeft()) return false;
+
+    AllLinesIterator allLinesIterator = new AllLinesIterator();
+    NavigationContextChecker checker2 = new NavigationContextChecker(allLinesIterator, context);
+    int line = checker2.contextMatchCheck();
+    if (line == -1) return false;
+
+    scrollToLine(line);
+    return true;
+  }
+
+  //
+  // Misc
+  //
+
+  @SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
+  public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    return OnesideTextDiffViewer.canShowRequest(context, request);
+  }
+
+  //
+  // Actions
+  //
+
+  private class MyReadOnlyLockAction extends TextDiffViewerUtil.EditorReadOnlyLockAction {
+    public MyReadOnlyLockAction() {
+      super(getContext(), getEditableEditors());
+    }
+  }
+
+  //
+  // Modification operations
+  //
+
+  private class MyHighlightPolicySettingAction extends TextDiffViewerUtil.HighlightPolicySettingAction {
+    public MyHighlightPolicySettingAction() {
+      super(getTextSettings());
+    }
+
+    @Override
+    protected void onSettingsChanged() {
+      rediff();
+    }
+  }
+
+  private class MyIgnorePolicySettingAction extends TextDiffViewerUtil.IgnorePolicySettingAction {
+    public MyIgnorePolicySettingAction() {
+      super(getTextSettings());
+    }
+
+    @Override
+    protected void onSettingsChanged() {
+      rediff();
+    }
+  }
+
+  //
+  // Scroll from annotate
+  //
+
+  private class AllLinesIterator implements Iterator<Pair<Integer, CharSequence>> {
+    @NotNull private final Document myDocument;
+    private int myLine = 0;
+
+    private AllLinesIterator() {
+      myDocument = getEditor().getDocument();
+    }
+
+    @Override
+    public boolean hasNext() {
+      return myLine < getLineCount(myDocument);
+    }
+
+    @Override
+    public Pair<Integer, CharSequence> next() {
+      int offset1 = myDocument.getLineStartOffset(myLine);
+      int offset2 = myDocument.getLineEndOffset(myLine);
+
+      CharSequence text = myDocument.getImmutableCharSequence().subSequence(offset1, offset2);
+
+      Pair<Integer, CharSequence> pair = new Pair<Integer, CharSequence>(myLine, text);
+      myLine++;
+
+      return pair;
+    }
+
+    @Override
+    public void remove() {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  //
+  // Helpers
+  //
+
+  @Nullable
+  @Override
+  public Object getData(@NonNls String dataId) {
+    if (DiffDataKeys.CURRENT_CHANGE_RANGE.is(dataId)) {
+      int lineCount = getLineCount(getEditor().getDocument());
+      return new LineRange(0, lineCount);
+    }
+    return super.getData(dataId);
+  }
+
+  private class MyInitialScrollHelper extends MyInitialScrollPositionHelper {
+    @Override
+    protected boolean doScrollToChange() {
+      if (myScrollToChange == null) return false;
+      SimpleOnesideDiffViewer.this.doScrollToChange(false);
+      return true;
+    }
+
+    @Override
+    protected boolean doScrollToFirstChange() {
+      SimpleOnesideDiffViewer.this.doScrollToChange(false);
+      return true;
+    }
+
+    @Override
+    protected boolean doScrollToContext() {
+      if (myNavigationContext == null) return false;
+      return SimpleOnesideDiffViewer.this.doScrollToContext(myNavigationContext);
+    }
+
+    @Override
+    protected boolean doScrollToPosition() {
+      if (myCaretPosition == null) return false;
+
+      LogicalPosition position = getSide().select(myCaretPosition);
+      getEditor().getCaretModel().moveToLogicalPosition(position);
+
+      if (myEditorsPosition != null && myEditorsPosition.isSame(position)) {
+        DiffUtil.scrollToPoint(getEditor(), myEditorsPosition.myPoints[0], false);
+      }
+      else {
+        DiffUtil.scrollToCaret(getEditor(), false);
+      }
+      return true;
+    }
+
+    @Nullable
+    @Override
+    protected LogicalPosition[] getCaretPositions() {
+      int index = getSide().getIndex();
+      int otherIndex = getSide().other().getIndex();
+
+      LogicalPosition[] carets = new LogicalPosition[2];
+      carets[index] = getEditor().getCaretModel().getLogicalPosition();
+      carets[otherIndex] = new LogicalPosition(0, 0);
+      return carets;
+    }
+  }
+}
index c98f6bce7ebb0cb2dd5be1f10019e31ac7afa77d..6dc7b9856c9430056098a1fc146c8d6f014325f6 100644 (file)
@@ -132,20 +132,13 @@ public class SimpleThreesideDiffChange {
   public boolean processChange(int oldLine1, int oldLine2, int shift, @NotNull ThreeSide side) {
     int line1 = getStartLine(side);
     int line2 = getEndLine(side);
+    int sideIndex = side.getIndex();
 
-    if (line2 <= oldLine1) return false;
-    if (line1 >= oldLine2) {
-      myLineStartShifts[side.getIndex()] += shift;
-      myLineEndShifts[side.getIndex()] += shift;
-      return false;
-    }
-
-    if (line1 <= oldLine1 && line2 >= oldLine2) {
-      myLineEndShifts[side.getIndex()] += shift;
-      return false;
-    }
+    DiffUtil.UpdatedLineRange newRange = DiffUtil.updateRangeOnModification(line1, line2, oldLine1, oldLine2, shift);
+    myLineStartShifts[sideIndex] += newRange.startLine - line1;
+    myLineEndShifts[sideIndex] += newRange.endLine - line2;
 
-    return true;
+    return newRange.damaged;
   }
 
   //
index 298c213dd3a621399df0a55f286ca2a10e995802..044c6cb42434c41e7df2f58360179552c1126e7d 100644 (file)
@@ -17,9 +17,9 @@ package com.intellij.diff.tools.simple;
 
 import com.intellij.diff.DiffContext;
 import com.intellij.diff.comparison.ByLine;
+import com.intellij.diff.comparison.ComparisonMergeUtil;
 import com.intellij.diff.comparison.ComparisonPolicy;
 import com.intellij.diff.comparison.DiffTooBigException;
-import com.intellij.diff.comparison.MergeUtil;
 import com.intellij.diff.comparison.iterables.FairDiffIterable;
 import com.intellij.diff.contents.DiffContent;
 import com.intellij.diff.contents.DocumentContent;
@@ -28,7 +28,12 @@ import com.intellij.diff.requests.ContentDiffRequest;
 import com.intellij.diff.requests.DiffRequest;
 import com.intellij.diff.tools.util.*;
 import com.intellij.diff.tools.util.base.IgnorePolicy;
-import com.intellij.diff.tools.util.threeside.ThreesideTextDiffViewer;
+import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
+import com.intellij.diff.tools.util.side.ThreesideTextDiffViewer;
+import com.intellij.diff.util.DiffDividerDrawUtil;
+import com.intellij.diff.util.DiffUtil;
+import com.intellij.diff.util.Side;
+import com.intellij.diff.util.ThreeSide;
 import com.intellij.diff.util.*;
 import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy;
 import com.intellij.openapi.Disposable;
@@ -36,6 +41,7 @@ import com.intellij.openapi.actionSystem.AnAction;
 import com.intellij.openapi.actionSystem.Separator;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.diff.DiffBundle;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.event.DocumentEvent;
@@ -71,6 +77,8 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
 
   @NotNull private final List<SimpleThreesideDiffChange> myDiffChanges = new ArrayList<SimpleThreesideDiffChange>();
   @NotNull private final List<SimpleThreesideDiffChange> myInvalidDiffChanges = new ArrayList<SimpleThreesideDiffChange>();
+  private int myChangesCount = -1;
+  private int myConflictsCount = -1;
 
   @NotNull private final MyFoldingModel myFoldingModel;
   @NotNull private final MyInitialScrollHelper myInitialScrollHelper = new MyInitialScrollHelper();
@@ -109,8 +117,8 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
     group.add(new MyIgnorePolicySettingAction());
     //group.add(new MyHighlightPolicySettingAction()); // TODO
     group.add(new MyToggleExpandByDefaultAction());
-    group.add(new ToggleAutoScrollAction());
-    group.add(new EditorReadOnlyLockAction());
+    group.add(new MyToggleAutoScrollAction());
+    group.add(new MyEditorReadOnlyLockAction());
     group.add(myEditorSettingsAction);
 
     group.add(Separator.getInstance());
@@ -131,7 +139,7 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
     //group.add(Separator.getInstance());
     //group.add(new MyHighlightPolicySettingAction().getPopupGroup());
     group.add(Separator.getInstance());
-    group.add(new ToggleAutoScrollAction());
+    group.add(new MyToggleAutoScrollAction());
     group.add(new MyToggleExpandByDefaultAction());
 
     return group;
@@ -156,6 +164,11 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
   // Diff
   //
 
+  @NotNull
+  public FoldingModelSupport.Settings getFoldingModelSettings() {
+    return TextDiffViewerUtil.getFoldingModelSettings(myContext);
+  }
+
   @Override
   protected void onSlowRediff() {
     super.onSlowRediff();
@@ -189,15 +202,15 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
       ComparisonPolicy comparisonPolicy = getIgnorePolicy().getComparisonPolicy();
       FairDiffIterable fragments1 = ByLine.compareTwoStepFair(sequences[1], sequences[0], comparisonPolicy, indicator);
       FairDiffIterable fragments2 = ByLine.compareTwoStepFair(sequences[1], sequences[2], comparisonPolicy, indicator);
-      List<MergeLineFragment> mergeFragments = MergeUtil.buildFair(fragments1, fragments2, indicator);
+      List<MergeLineFragment> mergeFragments = ComparisonMergeUtil.buildFair(fragments1, fragments2, indicator);
 
       return apply(mergeFragments, comparisonPolicy);
     }
-    catch (DiffTooBigException ignore) {
+    catch (DiffTooBigException e) {
       return applyNotification(DiffNotifications.DIFF_TOO_BIG);
     }
-    catch (ProcessCanceledException ignore) {
-      return applyNotification(DiffNotifications.OPERATION_CANCELED);
+    catch (ProcessCanceledException e) {
+      throw e;
     }
     catch (Throwable e) {
       LOG.error(e);
@@ -214,8 +227,13 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
         myFoldingModel.updateContext(myRequest, getFoldingModelSettings());
         clearDiffPresentation();
 
+        myChangesCount = 0;
+        myConflictsCount = 0;
         for (MergeLineFragment fragment : fragments) {
-          myDiffChanges.add(new SimpleThreesideDiffChange(fragment, getEditors(), comparisonPolicy));
+          SimpleThreesideDiffChange change = new SimpleThreesideDiffChange(fragment, getEditors(), comparisonPolicy);
+          myDiffChanges.add(change);
+          if (change.getDiffType() != TextDiffType.CONFLICT) myChangesCount++;
+          if (change.getDiffType() == TextDiffType.CONFLICT) myConflictsCount++;
         }
 
         myFoldingModel.install(fragments, myRequest, getFoldingModelSettings());
@@ -256,6 +274,9 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
     }
     myInvalidDiffChanges.clear();
 
+    myChangesCount = -1;
+    myConflictsCount = -1;
+
     myFoldingModel.destroy();
 
     myContentPanel.repaintDividers();
@@ -281,17 +302,9 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
       return;
     }
 
-    int offset1 = e.getOffset();
-    int offset2 = e.getOffset() + e.getOldLength();
-
-    if (StringUtil.endsWithChar(e.getOldFragment(), '\n') &&
-        StringUtil.endsWithChar(e.getNewFragment(), '\n')) {
-      offset2--;
-    }
-
-    int line1 = e.getDocument().getLineNumber(offset1);
-    int line2 = e.getDocument().getLineNumber(offset2) + 1;
-    int shift = StringUtil.countNewLines(e.getNewFragment()) - StringUtil.countNewLines(e.getOldFragment());
+    int line1 = e.getDocument().getLineNumber(e.getOffset());
+    int line2 = e.getDocument().getLineNumber(e.getOffset() + e.getOldLength()) + 1;
+    int shift = DiffUtil.countLinesShift(e);
 
     List<SimpleThreesideDiffChange> invalid = new ArrayList<SimpleThreesideDiffChange>();
     for (SimpleThreesideDiffChange change : myDiffChanges) {
@@ -339,14 +352,6 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
   // Getters
   //
 
-  private int getCurrentStartLine(@NotNull SimpleThreesideDiffChange change) {
-    return change.getStartLine(getCurrentSide());
-  }
-
-  private int getCurrentEndLine(@NotNull SimpleThreesideDiffChange change) {
-    return change.getEndLine(getCurrentSide());
-  }
-
   @NotNull
   protected List<SimpleThreesideDiffChange> getDiffChanges() {
     return myDiffChanges;
@@ -392,85 +397,51 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
   // Actions
   //
 
-  private class MyPrevNextDifferenceIterable implements PrevNextDifferenceIterable {
+  private class MyPrevNextDifferenceIterable extends PrevNextDifferenceIterableBase<SimpleThreesideDiffChange> {
+    @NotNull
     @Override
-    public boolean canGoNext() {
-      if (myDiffChanges.isEmpty()) return false;
-
-      EditorEx editor = getCurrentEditor();
-      int line = editor.getCaretModel().getLogicalPosition().line;
-      if (line == editor.getDocument().getLineCount() - 1) return false;
-
-      SimpleThreesideDiffChange lastChange = myDiffChanges.get(myDiffChanges.size() - 1);
-      if (getCurrentStartLine(lastChange) <= line) return false;
-
-      return true;
+    protected List<SimpleThreesideDiffChange> getChanges() {
+      return myDiffChanges;
     }
 
+    @NotNull
     @Override
-    public void goNext() {
-      EditorEx editor = getCurrentEditor();
-
-      int line = editor.getCaretModel().getLogicalPosition().line;
-
-      SimpleThreesideDiffChange next = null;
-      for (int i = 0; i < myDiffChanges.size(); i++) {
-        SimpleThreesideDiffChange change = myDiffChanges.get(i);
-        if (getCurrentStartLine(change) <= line) continue;
-
-        next = change;
-        break;
-      }
-
-      assert next != null;
-      doScrollToChange(next, true);
+    protected EditorEx getEditor() {
+      return getCurrentEditor();
     }
 
     @Override
-    public boolean canGoPrev() {
-      if (myDiffChanges.isEmpty()) return false;
-
-      EditorEx editor = getCurrentEditor();
-      int line = editor.getCaretModel().getLogicalPosition().line;
-      if (line == 0) return false;
-
-      SimpleThreesideDiffChange firstChange = myDiffChanges.get(0);
-      if (getCurrentEndLine(firstChange) > line) return false;
-      if (getCurrentStartLine(firstChange) >= line) return false;
-
-      return true;
+    protected int getStartLine(@NotNull SimpleThreesideDiffChange change) {
+      return change.getStartLine(getCurrentSide());
     }
 
     @Override
-    public void goPrev() {
-      EditorEx editor = getCurrentEditor();
-
-      int line = editor.getCaretModel().getLogicalPosition().line;
-
-      SimpleThreesideDiffChange prev = null;
-      for (int i = 0; i < myDiffChanges.size(); i++) {
-        SimpleThreesideDiffChange change = myDiffChanges.get(i);
-
-        SimpleThreesideDiffChange next = i < myDiffChanges.size() - 1 ? myDiffChanges.get(i + 1) : null;
-        if (next == null || getCurrentEndLine(next) > line || getCurrentStartLine(next) >= line) {
-          prev = change;
-          break;
-        }
-      }
+    protected int getEndLine(@NotNull SimpleThreesideDiffChange change) {
+      return change.getEndLine(getCurrentSide());
+    }
 
-      assert prev != null;
-      doScrollToChange(prev, true);
+    @Override
+    protected void scrollToChange(@NotNull SimpleThreesideDiffChange change) {
+      doScrollToChange(change, true);
     }
   }
 
-  private class MyToggleExpandByDefaultAction extends ToggleExpandByDefaultAction {
+  private class MyToggleExpandByDefaultAction extends TextDiffViewerUtil.ToggleExpandByDefaultAction {
+    public MyToggleExpandByDefaultAction() {
+      super(getTextSettings());
+    }
+
     @Override
     protected void expandAll(boolean expand) {
       myFoldingModel.expandAll(expand);
     }
   }
 
-  private class MyIgnorePolicySettingAction extends IgnorePolicySettingAction {
+  private class MyIgnorePolicySettingAction extends TextDiffViewerUtil.IgnorePolicySettingAction {
+    public MyIgnorePolicySettingAction() {
+      super(getTextSettings());
+    }
+
     @NotNull
     @Override
     protected IgnorePolicy getCurrentSetting() {
@@ -484,6 +455,17 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
       settings.remove(IgnorePolicy.IGNORE_WHITESPACES_CHUNKS);
       return settings;
     }
+
+    @Override
+    protected void onSettingsChanged() {
+      rediff();
+    }
+  }
+
+  protected class MyEditorReadOnlyLockAction extends TextDiffViewerUtil.EditorReadOnlyLockAction {
+    public MyEditorReadOnlyLockAction() {
+      super(getContext(), getEditableEditors());
+    }
   }
 
   //
@@ -599,9 +581,22 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewer {
   }
 
   private class MyStatusPanel extends StatusPanel {
+    @Nullable
     @Override
-    protected int getChangesCount() {
-      return myDiffChanges.size() + myInvalidDiffChanges.size();
+    protected String getMessage() {
+      if (myChangesCount < 0 || myConflictsCount < 0) return null;
+      if (myChangesCount == 0 && myConflictsCount == 0) {
+        return DiffBundle.message("merge.dialog.all.conflicts.resolved.message.text");
+      }
+      return makeCounterWord(myChangesCount, "change") + ". " + makeCounterWord(myConflictsCount, "conflict");
+    }
+
+    @NotNull
+    private String makeCounterWord(int number, @NotNull String word) {
+      if (number == 0) {
+        return "No " + StringUtil.pluralize(word);
+      }
+      return number + " " + StringUtil.pluralize(word, number);
     }
   }
 
index 49030bed6851155a758329cce882288edbecff86..f4e2ebcad48dcf97110d79ef2ec9f29fc876286f 100644 (file)
@@ -17,13 +17,11 @@ package com.intellij.diff.tools.util;
 
 import com.intellij.diff.DiffContext;
 import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.holders.EditorHolder;
 import com.intellij.diff.util.DiffUserDataKeys;
 import com.intellij.diff.util.Side;
 import com.intellij.diff.util.ThreeSide;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.fileEditor.FileEditor;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.awt.event.FocusAdapter;
@@ -34,34 +32,13 @@ public class FocusTrackerSupport<T> {
   public static class TwosideFocusTrackerSupport {
     @NotNull private Side myCurrentSide;
 
-    private final boolean myDumbMode;
-    @Nullable private final MyFocusListener myListener1;
-    @Nullable private final MyFocusListener myListener2;
+    public TwosideFocusTrackerSupport(@NotNull List<? extends EditorHolder> holders) {
+      assert holders.size() == 2;
 
-    public TwosideFocusTrackerSupport(@Nullable Editor editor1, @Nullable Editor editor2) {
-      this(getComponent(editor1), getComponent(editor2));
-    }
-
-    public TwosideFocusTrackerSupport(@Nullable FileEditor editor1, @Nullable FileEditor editor2) {
-      this(getComponent(editor1), getComponent(editor2));
-    }
-
-    public TwosideFocusTrackerSupport(@Nullable JComponent component1, @Nullable JComponent component2) {
-      assert component1 != null || component2 != null;
-      myCurrentSide = component2 != null ? Side.RIGHT : Side.LEFT;
+      myCurrentSide = Side.RIGHT;
 
-      myDumbMode = component1 == null || component2 == null;
-      if (!myDumbMode) {
-        myListener1 = new MyFocusListener(Side.LEFT);
-        component1.addFocusListener(myListener1);
-
-        myListener2 = new MyFocusListener(Side.RIGHT);
-        component2.addFocusListener(myListener2);
-      }
-      else {
-        myListener1 = null;
-        myListener2 = null;
-      }
+      addListener(holders, Side.LEFT);
+      addListener(holders, Side.RIGHT);
     }
 
     @NotNull
@@ -70,7 +47,6 @@ public class FocusTrackerSupport<T> {
     }
 
     public void setCurrentSide(@NotNull Side side) {
-      if (myDumbMode) return;
       myCurrentSide = side;
     }
 
@@ -80,10 +56,13 @@ public class FocusTrackerSupport<T> {
     }
 
     public void updateContextHints(@NotNull DiffRequest request, @NotNull DiffContext context) {
-      if (myDumbMode) return;
       context.putUserData(DiffUserDataKeys.PREFERRED_FOCUS_SIDE, myCurrentSide);
     }
 
+    private void addListener(@NotNull List<? extends EditorHolder> holders, @NotNull Side side) {
+      side.select(holders).installFocusListener(new MyFocusListener(side));
+    }
+
     private class MyFocusListener extends FocusAdapter {
       @NotNull private final Side mySide;
 
@@ -100,47 +79,12 @@ public class FocusTrackerSupport<T> {
   public static class ThreesideFocusTrackerSupport {
     @NotNull private ThreeSide myCurrentSide;
 
-    private final boolean myDumbMode;
-    @Nullable private final MyFocusListener myListener1;
-    @Nullable private final MyFocusListener myListener2;
-    @Nullable private final MyFocusListener myListener3;
-
-    public ThreesideFocusTrackerSupport(@NotNull List<? extends Editor> editors) {
-      this(editors.get(0), editors.get(1), editors.get(2));
-    }
+    public ThreesideFocusTrackerSupport(@NotNull List<? extends EditorHolder> holders) {
+      myCurrentSide = ThreeSide.BASE;
 
-    public ThreesideFocusTrackerSupport(@Nullable Editor editor1, @Nullable Editor editor2, @Nullable Editor editor3) {
-      this(getComponent(editor1), getComponent(editor2), getComponent(editor3));
-    }
-
-    public ThreesideFocusTrackerSupport(@Nullable FileEditor editor1, @Nullable FileEditor editor2, @Nullable FileEditor editor3) {
-      this(getComponent(editor1), getComponent(editor2), getComponent(editor3));
-    }
-
-    public ThreesideFocusTrackerSupport(@Nullable JComponent component1, @Nullable JComponent component2, @Nullable JComponent component3) {
-      assert component1 != null || component2 != null || component3 != null;
-      myCurrentSide = component2 != null ? ThreeSide.BASE : component1 != null ? ThreeSide.LEFT : ThreeSide.RIGHT;
-
-      boolean c1 = component1 != null;
-      boolean c2 = component2 != null;
-      boolean c3 = component3 != null;
-      myDumbMode = (!c1 && !c2) || (!c1 && !c3) || (!c2 && !c3); // only one not-null element
-
-      if (!myDumbMode) {
-        myListener1 = component1 != null ? new MyFocusListener(ThreeSide.LEFT) : null;
-        if (component1 != null) component1.addFocusListener(myListener1);
-
-        myListener2 = component2 != null ? new MyFocusListener(ThreeSide.BASE) : null;
-        if (component2 != null) component2.addFocusListener(myListener2);
-
-        myListener3 = component3 != null ? new MyFocusListener(ThreeSide.RIGHT) : null;
-        if (component3 != null) component3.addFocusListener(myListener3);
-      }
-      else {
-        myListener1 = null;
-        myListener2 = null;
-        myListener3 = null;
-      }
+      addListener(holders, ThreeSide.LEFT);
+      addListener(holders, ThreeSide.BASE);
+      addListener(holders, ThreeSide.RIGHT);
     }
 
     @NotNull
@@ -149,7 +93,6 @@ public class FocusTrackerSupport<T> {
     }
 
     public void setCurrentSide(@NotNull ThreeSide side) {
-      if (myDumbMode || side.select(myListener1, myListener2, myListener3) == null) return;
       myCurrentSide = side;
     }
 
@@ -159,10 +102,13 @@ public class FocusTrackerSupport<T> {
     }
 
     public void updateContextHints(@NotNull DiffRequest request, @NotNull DiffContext context) {
-      if (myDumbMode) return;
       context.putUserData(DiffUserDataKeys.PREFERRED_FOCUS_THREESIDE, myCurrentSide);
     }
 
+    private void addListener(@NotNull List<? extends EditorHolder> holders, @NotNull ThreeSide side) {
+      side.select(holders).installFocusListener(new MyFocusListener(side));
+    }
+
     private class MyFocusListener extends FocusAdapter {
       @NotNull private final ThreeSide mySide;
 
@@ -175,14 +121,4 @@ public class FocusTrackerSupport<T> {
       }
     }
   }
-
-  @Nullable
-  private static JComponent getComponent(@Nullable Editor editor) {
-    return editor != null ? editor.getContentComponent() : null;
-  }
-
-  @Nullable
-  private static JComponent getComponent(@Nullable FileEditor editor) {
-    return editor != null ? editor.getComponent() : null;
-  }
 }
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/KeyboardModifierListener.java b/platform/diff-impl/src/com/intellij/diff/tools/util/KeyboardModifierListener.java
new file mode 100644 (file)
index 0000000..2b3959a
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.util;
+
+import com.intellij.ide.IdeEventQueue;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowFocusListener;
+
+public class KeyboardModifierListener {
+  private boolean myShiftPressed;
+  private boolean myCtrlPressed;
+  private boolean myAltPressed;
+
+  @Nullable private Window myWindow;
+
+  private final WindowFocusListener myWindowFocusListener = new WindowFocusListener() {
+    @Override
+    public void windowGainedFocus(WindowEvent e) {
+      resetState();
+    }
+
+    @Override
+    public void windowLostFocus(WindowEvent e) {
+      resetState();
+    }
+  };
+
+  public void init(@NotNull JComponent component, @NotNull Disposable disposable) {
+    assert myWindow == null;
+
+    Disposer.register(disposable, new Disposable() {
+      @Override
+      public void dispose() {
+        destroy();
+      }
+    });
+
+    // we can use KeyListener on Editors, but Ctrl+Click will not work with focus in other place.
+    // ex: commit dialog with focus in commit message
+    IdeEventQueue.getInstance().addPostprocessor(new IdeEventQueue.EventDispatcher() {
+      @Override
+      public boolean dispatch(AWTEvent e) {
+        if (e instanceof KeyEvent) {
+          onKeyEvent((KeyEvent)e);
+        }
+        return false;
+      }
+    }, disposable);
+
+    myWindow = UIUtil.getWindow(component);
+    if (myWindow != null) {
+      myWindow.addWindowFocusListener(myWindowFocusListener);
+    }
+  }
+
+  public void destroy() {
+    if (myWindow != null) {
+      myWindow.removeWindowFocusListener(myWindowFocusListener);
+      myWindow = null;
+    }
+  }
+
+  private void onKeyEvent(KeyEvent e) {
+    final int keyCode = e.getKeyCode();
+    if (keyCode == KeyEvent.VK_SHIFT) {
+      myShiftPressed = e.getID() == KeyEvent.KEY_PRESSED;
+      onModifiersChanged();
+    }
+    if (keyCode == KeyEvent.VK_CONTROL) {
+      myCtrlPressed = e.getID() == KeyEvent.KEY_PRESSED;
+      onModifiersChanged();
+    }
+    if (keyCode == KeyEvent.VK_ALT) {
+      myAltPressed = e.getID() == KeyEvent.KEY_PRESSED;
+      onModifiersChanged();
+    }
+  }
+
+  private void resetState() {
+    myShiftPressed = false;
+    myAltPressed = false;
+    myCtrlPressed = false;
+    onModifiersChanged();
+  }
+
+  public boolean isShiftPressed() {
+    return myShiftPressed;
+  }
+
+  public boolean isCtrlPressed() {
+    return myCtrlPressed;
+  }
+
+  public boolean isAltPressed() {
+    return myAltPressed;
+  }
+
+  public void onModifiersChanged() {
+  }
+}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/PrevNextDifferenceIterableBase.java b/platform/diff-impl/src/com/intellij/diff/tools/util/PrevNextDifferenceIterableBase.java
new file mode 100644 (file)
index 0000000..9ffacde
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.util;
+
+import com.intellij.openapi.editor.ex.EditorEx;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public abstract class PrevNextDifferenceIterableBase<T> implements PrevNextDifferenceIterable {
+  @NotNull
+  protected abstract List<T> getChanges();
+
+  @NotNull
+  protected abstract EditorEx getEditor();
+
+  protected abstract int getStartLine(@NotNull T change);
+
+  protected abstract int getEndLine(@NotNull T change);
+
+  protected abstract void scrollToChange(@NotNull T change);
+
+  @Override
+  public boolean canGoNext() {
+    List<T> changes = getChanges();
+    if (changes.isEmpty()) return false;
+
+    EditorEx editor = getEditor();
+    int line = editor.getCaretModel().getLogicalPosition().line;
+    if (line == editor.getDocument().getLineCount() - 1) return false;
+
+    T lastChange = changes.get(changes.size() - 1);
+    if (getStartLine(lastChange) <= line) return false;
+
+    return true;
+  }
+
+  @Override
+  public void goNext() {
+    List<T> changes = getChanges();
+    int line = getEditor().getCaretModel().getLogicalPosition().line;
+
+    T next = null;
+    for (int i = 0; i < changes.size(); i++) {
+      T change = changes.get(i);
+      if (getStartLine(change) <= line) continue;
+
+      next = change;
+      break;
+    }
+
+    assert next != null;
+    scrollToChange(next);
+  }
+
+  @Override
+  public boolean canGoPrev() {
+    List<T> changes = getChanges();
+    if (changes.isEmpty()) return false;
+
+    int line = getEditor().getCaretModel().getLogicalPosition().line;
+    if (line == 0) return false;
+
+    T firstChange = changes.get(0);
+    if (getEndLine(firstChange) > line) return false;
+    if (getStartLine(firstChange) >= line) return false;
+
+    return true;
+  }
+
+  @Override
+  public void goPrev() {
+    List<T> changes = getChanges();
+    int line = getEditor().getCaretModel().getLogicalPosition().line;
+
+    T prev = null;
+    for (int i = 0; i < changes.size(); i++) {
+      T change = changes.get(i);
+
+      T next = i < changes.size() - 1 ? changes.get(i + 1) : null;
+      if (next == null || getEndLine(next) > line || getStartLine(next) >= line) {
+        prev = change;
+        break;
+      }
+    }
+
+    assert prev != null;
+    scrollToChange(prev);
+  }
+}
\ No newline at end of file
index 1f55eb4d609487ffceca70459adddc4bc4ff3ee0..fa775b7b6ab0396b3022ab8e32adf1bc4563f40e 100644 (file)
 package com.intellij.diff.tools.util;
 
 import com.intellij.openapi.diff.DiffBundle;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.ui.IdeBorderFactory;
 import com.intellij.util.ui.AnimatedIcon;
 import com.intellij.util.ui.AsyncProcessIcon;
+import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.awt.*;
 
-public abstract class StatusPanel extends JPanel {
+public class StatusPanel extends JPanel {
   private final JLabel myTextLabel;
   private final AnimatedIcon myBusySpinner;
 
@@ -40,9 +42,9 @@ public abstract class StatusPanel extends JPanel {
   }
 
   public void update() {
-    int count = getChangesCount();
-    myTextLabel.setVisible(count != -1);
-    myTextLabel.setText(DiffBundle.message("diff.count.differences.status.text", count));
+    String message = getMessage();
+    myTextLabel.setVisible(message != null);
+    myTextLabel.setText(StringUtil.notNullize(message));
   }
 
   public void setBusy(boolean busy) {
@@ -56,5 +58,14 @@ public abstract class StatusPanel extends JPanel {
     }
   }
 
-  protected abstract int getChangesCount();
+  @Nullable
+  protected String getMessage() {
+    int count = getChangesCount();
+    if (count == -1) return null;
+    return DiffBundle.message("diff.count.differences.status.text", count);
+  }
+
+  protected int getChangesCount() {
+    return -1;
+  }
 }
index 6930cb2f2f1c777f5ecac1d33319620db3d94e85..cd9ae43117106ef46d211416bb77ca72ecaf43cc 100644 (file)
@@ -55,6 +55,10 @@ public class SyncScrollSupport {
     private boolean myDisabled = false;
     private boolean myDuringSyncScroll = false;
 
+    public TwosideSyncScrollSupport(@NotNull List<? extends Editor> editors, @NotNull SyncScrollable scrollable) {
+      this(Side.LEFT.select(editors), Side.RIGHT.select(editors), scrollable);
+    }
+
     public TwosideSyncScrollSupport(@NotNull Editor editor1, @NotNull Editor editor2, @NotNull SyncScrollable scrollable) {
       myEditor1 = editor1;
       myEditor2 = editor2;
index c897e28e9a0668a2fdd28adf529c9bd8e28798fd..38117e7fa4d0060839f0ab0dbbf3d82e1b9ac2a8 100644 (file)
@@ -168,6 +168,11 @@ public abstract class DiffViewerBase implements DiffViewer, DataProvider {
     return myRequest;
   }
 
+  @NotNull
+  public DiffContext getContext() {
+    return myContext;
+  }
+
   public boolean isDisposed() {
     return myDisposed;
   }
similarity index 62%
rename from platform/diff-impl/src/com/intellij/diff/tools/util/base/TextDiffViewerBase.java
rename to platform/diff-impl/src/com/intellij/diff/tools/util/base/TextDiffViewerUtil.java
index 2adcdd60b156fbecd0fecb9ebd0dbcc2dae4ed76..02a907e793b633f43b0328a459dfcbf78735967f 100644 (file)
@@ -16,7 +16,8 @@
 package com.intellij.diff.tools.util.base;
 
 import com.intellij.diff.DiffContext;
-import com.intellij.diff.actions.impl.SetEditorSettingsAction;
+import com.intellij.diff.contents.DiffContent;
+import com.intellij.diff.contents.DocumentContent;
 import com.intellij.diff.requests.ContentDiffRequest;
 import com.intellij.diff.tools.util.FoldingModelSupport;
 import com.intellij.diff.tools.util.base.TextDiffSettingsHolder.TextDiffSettings;
@@ -24,8 +25,11 @@ import com.intellij.diff.util.DiffUserDataKeys;
 import com.intellij.diff.util.DiffUserDataKeysEx;
 import com.intellij.diff.util.DiffUtil;
 import com.intellij.icons.AllIcons;
+import com.intellij.openapi.Disposable;
 import com.intellij.openapi.actionSystem.*;
 import com.intellij.openapi.actionSystem.ex.ComboBoxAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.actions.EditorActionUtil;
 import com.intellij.openapi.editor.event.EditorMouseEvent;
 import com.intellij.openapi.editor.ex.EditorEx;
@@ -35,7 +39,6 @@ import com.intellij.openapi.util.Key;
 import com.intellij.ui.ToggleActionButton;
 import com.intellij.util.EditorPopupHandler;
 import com.intellij.util.containers.ContainerUtil;
-import org.jetbrains.annotations.CalledInAwt;
 import org.jetbrains.annotations.NotNull;
 
 import javax.swing.*;
@@ -45,74 +48,12 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
+public class TextDiffViewerUtil {
+  public static final Logger LOG = Logger.getInstance(TextDiffViewerUtil.class);
   public static final Key<Boolean> READ_ONLY_LOCK_KEY = Key.create("ReadOnlyLockAction");
 
-  @NotNull private final TextDiffSettings myTextSettings;
-
-  @NotNull private final MyFontSizeListener myFontSizeListener = new MyFontSizeListener();
-
-  @NotNull private final MyEditorMouseListener myEditorPopupListener = new MyEditorMouseListener();
-  @NotNull private List<AnAction> myEditorPopupActions;
-
-  public TextDiffViewerBase(@NotNull DiffContext context, @NotNull ContentDiffRequest request) {
-    super(context, request);
-
-    myTextSettings = initTextSettings(context);
-  }
-
-  @Override
-  protected void onInit() {
-    super.onInit();
-    myEditorPopupActions = createEditorPopupActions();
-    installEditorListeners();
-  }
-
-  @Override
-  protected void onDispose() {
-    destroyEditorListeners();
-    super.onDispose();
-  }
-
-  @CalledInAwt
-  protected void installEditorListeners() {
-    List<? extends EditorEx> editors = getEditors();
-
-    for (EditorEx editor : editors) {
-      if (editor != null) editor.addEditorMouseListener(myEditorPopupListener);
-    }
-
-    if (editors.size() > 1) {
-      for (EditorEx editor : editors) {
-        if (editor != null) editor.addPropertyChangeListener(myFontSizeListener);
-      }
-    }
-  }
-
-  @CalledInAwt
-  protected void destroyEditorListeners() {
-    List<? extends EditorEx> editors = getEditors();
-
-    for (EditorEx editor : editors) {
-      if (editor != null) editor.removeEditorMouseListener(myEditorPopupListener);
-    }
-
-    if (editors.size() > 1) {
-      for (EditorEx editor : editors) {
-        if (editor != null) editor.removePropertyChangeListener(myFontSizeListener);
-      }
-    }
-  }
-
-  //
-  // Abstract
-  //
-
   @NotNull
-  protected abstract List<? extends EditorEx> getEditors();
-
-  @NotNull
-  protected List<AnAction> createEditorPopupActions() {
+  public static List<AnAction> createEditorPopupActions() {
     List<AnAction> result = new ArrayList<AnAction>();
     result.add(ActionManager.getInstance().getAction("CompareClipboardWithSelection"));
 
@@ -122,28 +63,14 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
     return result;
   }
 
-  @CalledInAwt
-  protected void onSettingsChanged() {
-    rediff();
-  }
-
-  //
-  // Impl
-  //
-
   @NotNull
-  protected TextDiffSettings getTextSettings() {
-    return myTextSettings;
-  }
-
-  @NotNull
-  protected FoldingModelSupport.Settings getFoldingModelSettings() {
-    TextDiffSettings settings = getTextSettings();
+  public static FoldingModelSupport.Settings getFoldingModelSettings(@NotNull DiffContext context) {
+    TextDiffSettings settings = getTextSettings(context);
     return new FoldingModelSupport.Settings(settings.getContextRange(), settings.isExpandByDefault());
   }
 
   @NotNull
-  private static TextDiffSettings initTextSettings(@NotNull DiffContext context) {
+  public static TextDiffSettings getTextSettings(@NotNull DiffContext context) {
     TextDiffSettings settings = context.getUserData(TextDiffSettingsHolder.KEY);
     if (settings == null) {
       settings = TextDiffSettings.getSettings(context.getUserData(DiffUserDataKeysEx.PLACE));
@@ -155,21 +82,17 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
     return settings;
   }
 
-  //
-  // Helpers
-  //
-
   @NotNull
-  protected boolean[] checkForceReadOnly() {
-    int contentCount = myRequest.getContents().size();
+  public static boolean[] checkForceReadOnly(@NotNull DiffContext context, @NotNull ContentDiffRequest request) {
+    int contentCount = request.getContents().size();
     boolean[] result = new boolean[contentCount];
 
-    if (DiffUtil.isUserDataFlagSet(DiffUserDataKeys.FORCE_READ_ONLY, myRequest, myContext)) {
+    if (DiffUtil.isUserDataFlagSet(DiffUserDataKeys.FORCE_READ_ONLY, request, context)) {
       Arrays.fill(result, true);
       return result;
     }
 
-    boolean[] data = myRequest.getUserData(DiffUserDataKeys.FORCE_READ_ONLY_CONTENTS);
+    boolean[] data = request.getUserData(DiffUserDataKeys.FORCE_READ_ONLY_CONTENTS);
     if (data != null && data.length == contentCount) {
       return data;
     }
@@ -177,35 +100,30 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
     return result;
   }
 
-  private class MyFontSizeListener implements PropertyChangeListener {
-    private boolean myDuringUpdate = false;
-
-    public void propertyChange(PropertyChangeEvent evt) {
-      if (myDuringUpdate) return;
-
-      if (!EditorEx.PROP_FONT_SIZE.equals(evt.getPropertyName())) return;
-      if (evt.getOldValue().equals(evt.getNewValue())) return;
-      int fontSize = ((Integer)evt.getNewValue()).intValue();
-
-      for (EditorEx editor : getEditors()) {
-        if (editor != null && evt.getSource() != editor) updateEditor(editor, fontSize);
+  public static void checkDifferentDocuments(@NotNull ContentDiffRequest request) {
+    // Actually, this should be a valid case. But it has little practical sense and will require explicit checks everywhere.
+    // Some listeners will be processed once instead of 2 times, some listeners will cause illegal document modifications.
+    List<DiffContent> contents = request.getContents();
+
+    boolean sameDocuments = false;
+    for (int i = 0; i < contents.size(); i++) {
+      for (int j = i + 1; j < contents.size(); j++) {
+        DiffContent content1 = contents.get(i);
+        DiffContent content2 = contents.get(j);
+        if (!(content1 instanceof DocumentContent)) continue;
+        if (!(content2 instanceof DocumentContent)) continue;
+        sameDocuments |= ((DocumentContent)content1).getDocument() == ((DocumentContent)content2).getDocument();
       }
     }
 
-    public void updateEditor(@NotNull EditorEx editor, int fontSize) {
-      try {
-        myDuringUpdate = true;
-        editor.setFontSize(fontSize);
-      }
-      finally {
-        myDuringUpdate = false;
+    if (sameDocuments) {
+      StringBuilder message = new StringBuilder();
+      message.append("DiffRequest with same documents detected\n");
+      message.append(request.toString()).append("\n");
+      for (DiffContent content : contents) {
+        message.append(content.toString()).append("\n");
       }
-    }
-  }
-
-  protected class MySetEditorSettingsAction extends SetEditorSettingsAction {
-    public MySetEditorSettingsAction() {
-      super(myTextSettings, getEditors());
+      LOG.error(new Throwable(message.toString()));
     }
   }
 
@@ -214,7 +132,7 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
   //
 
   // TODO: pretty icons ?
-  protected static abstract class ComboBoxSettingAction<T> extends ComboBoxAction implements DumbAware {
+  public static abstract class ComboBoxSettingAction<T> extends ComboBoxAction implements DumbAware {
     private DefaultActionGroup myChildren;
 
     public ComboBoxSettingAction() {
@@ -276,14 +194,17 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
     }
   }
 
-  protected class HighlightPolicySettingAction extends ComboBoxSettingAction<HighlightPolicy> {
-    public HighlightPolicySettingAction() {
+  public static abstract class HighlightPolicySettingAction extends ComboBoxSettingAction<HighlightPolicy> {
+    @NotNull protected final TextDiffSettings mySettings;
+
+    public HighlightPolicySettingAction(@NotNull TextDiffSettings settings) {
+      mySettings = settings;
     }
 
     @Override
     protected void applySetting(@NotNull HighlightPolicy setting, @NotNull AnActionEvent e) {
       if (getCurrentSetting() == setting) return;
-      getTextSettings().setHighlightPolicy(setting);
+      mySettings.setHighlightPolicy(setting);
       update(e);
       onSettingsChanged();
     }
@@ -291,7 +212,7 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
     @NotNull
     @Override
     protected HighlightPolicy getCurrentSetting() {
-      return getTextSettings().getHighlightPolicy();
+      return mySettings.getHighlightPolicy();
     }
 
     @NotNull
@@ -305,16 +226,21 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
     protected List<HighlightPolicy> getAvailableSettings() {
       return Arrays.asList(HighlightPolicy.values());
     }
+
+    protected abstract void onSettingsChanged();
   }
 
-  protected class IgnorePolicySettingAction extends ComboBoxSettingAction<IgnorePolicy> {
-    public IgnorePolicySettingAction() {
+  public static abstract class IgnorePolicySettingAction extends ComboBoxSettingAction<IgnorePolicy> {
+    @NotNull protected final TextDiffSettings mySettings;
+
+    public IgnorePolicySettingAction(@NotNull TextDiffSettings settings) {
+      mySettings = settings;
     }
 
     @Override
     protected void applySetting(@NotNull IgnorePolicy setting, @NotNull AnActionEvent e) {
       if (getCurrentSetting() == setting) return;
-      getTextSettings().setIgnorePolicy(setting);
+      mySettings.setIgnorePolicy(setting);
       update(e);
       onSettingsChanged();
     }
@@ -322,7 +248,7 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
     @NotNull
     @Override
     protected IgnorePolicy getCurrentSetting() {
-      return getTextSettings().getIgnorePolicy();
+      return mySettings.getIgnorePolicy();
     }
 
     @NotNull
@@ -336,55 +262,66 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
     protected List<IgnorePolicy> getAvailableSettings() {
       return Arrays.asList(IgnorePolicy.values());
     }
+
+    protected abstract void onSettingsChanged();
   }
 
-  protected class ToggleAutoScrollAction extends ToggleActionButton implements DumbAware {
-    public ToggleAutoScrollAction() {
+  public static class ToggleAutoScrollAction extends ToggleActionButton implements DumbAware {
+    @NotNull protected final TextDiffSettings mySettings;
+
+    public ToggleAutoScrollAction(@NotNull TextDiffSettings settings) {
       super("Synchronize Scrolling", AllIcons.Actions.SynchronizeScrolling);
+      mySettings = settings;
       setEnabledInModalContext(true);
     }
 
     @Override
     public boolean isSelected(AnActionEvent e) {
-      return getTextSettings().isEnableSyncScroll();
+      return mySettings.isEnableSyncScroll();
     }
 
     @Override
     public void setSelected(AnActionEvent e, boolean state) {
-      getTextSettings().setEnableSyncScroll(state);
+      mySettings.setEnableSyncScroll(state);
     }
   }
 
-  protected abstract class ToggleExpandByDefaultAction extends ToggleActionButton implements DumbAware {
-    public ToggleExpandByDefaultAction() {
+  public static abstract class ToggleExpandByDefaultAction extends ToggleActionButton implements DumbAware {
+    @NotNull protected final TextDiffSettings mySettings;
+
+    public ToggleExpandByDefaultAction(@NotNull TextDiffSettings settings) {
       super("Collapse unchanged fragments", AllIcons.Actions.Collapseall);
+      mySettings = settings;
       setEnabledInModalContext(true);
     }
 
     @Override
     public boolean isVisible() {
-      return getTextSettings().getContextRange() != -1;
+      return mySettings.getContextRange() != -1;
     }
 
     @Override
     public boolean isSelected(AnActionEvent e) {
-      return !getTextSettings().isExpandByDefault();
+      return !mySettings.isExpandByDefault();
     }
 
     @Override
     public void setSelected(AnActionEvent e, boolean state) {
       boolean expand = !state;
-      if (getTextSettings().isExpandByDefault() == expand) return;
-      getTextSettings().setExpandByDefault(expand);
+      if (mySettings.isExpandByDefault() == expand) return;
+      mySettings.setExpandByDefault(expand);
       expandAll(expand);
     }
 
     protected abstract void expandAll(boolean expand);
   }
 
-  protected abstract class ReadOnlyLockAction extends ToggleAction implements DumbAware {
-    public ReadOnlyLockAction() {
+  public static abstract class ReadOnlyLockAction extends ToggleAction implements DumbAware {
+    @NotNull protected final DiffContext myContext;
+
+    public ReadOnlyLockAction(@NotNull DiffContext context) {
       super("Disable editing", null, AllIcons.Nodes.Padlock);
+      myContext = context;
       setEnabledInModalContext(true);
     }
 
@@ -424,14 +361,11 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
     protected abstract boolean canEdit();
   }
 
-  protected class EditorReadOnlyLockAction extends ReadOnlyLockAction {
+  public static class EditorReadOnlyLockAction extends ReadOnlyLockAction {
     private final List<? extends EditorEx> myEditableEditors;
 
-    public EditorReadOnlyLockAction() {
-      this(getEditableEditors(getEditors()));
-    }
-
-    public EditorReadOnlyLockAction(@NotNull List<? extends EditorEx> editableEditors) {
+    public EditorReadOnlyLockAction(@NotNull DiffContext context, @NotNull List<? extends EditorEx> editableEditors) {
+      super(context);
       myEditableEditors = editableEditors;
       init();
     }
@@ -450,7 +384,7 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
   }
 
   @NotNull
-  protected static List<? extends EditorEx> getEditableEditors(@NotNull List<? extends EditorEx> editors) {
+  public static List<? extends EditorEx> getEditableEditors(@NotNull List<? extends EditorEx> editors) {
     return ContainerUtil.filter(editors, new Condition<EditorEx>() {
       @Override
       public boolean value(EditorEx editor) {
@@ -459,7 +393,61 @@ public abstract class TextDiffViewerBase extends ListenerDiffViewerBase {
     });
   }
 
-  private final class MyEditorMouseListener extends EditorPopupHandler {
+  public static class EditorFontSizeSynchronizer implements PropertyChangeListener {
+    @NotNull private final List<? extends EditorEx> myEditors;
+
+    private boolean myDuringUpdate = false;
+
+    public EditorFontSizeSynchronizer(@NotNull List<? extends EditorEx> editors) {
+      myEditors = editors;
+    }
+
+    public void install(@NotNull Disposable disposable) {
+      if (ContainerUtil.skipNulls(myEditors).size() < 2) return;
+
+      for (EditorEx editor : myEditors) {
+        if (editor == null) continue;
+        editor.addPropertyChangeListener(this, disposable);
+      }
+    }
+
+    public void propertyChange(PropertyChangeEvent evt) {
+      if (myDuringUpdate) return;
+
+      if (!EditorEx.PROP_FONT_SIZE.equals(evt.getPropertyName())) return;
+      if (evt.getOldValue().equals(evt.getNewValue())) return;
+      int fontSize = ((Integer)evt.getNewValue()).intValue();
+
+      for (EditorEx editor : myEditors) {
+        if (editor != null && evt.getSource() != editor) updateEditor(editor, fontSize);
+      }
+    }
+
+    public void updateEditor(@NotNull EditorEx editor, int fontSize) {
+      try {
+        myDuringUpdate = true;
+        editor.setFontSize(fontSize);
+      }
+      finally {
+        myDuringUpdate = false;
+      }
+    }
+  }
+
+  public static class EditorActionsPopup extends EditorPopupHandler {
+    @NotNull private final List<? extends AnAction> myEditorPopupActions;
+
+    public EditorActionsPopup(@NotNull List<? extends AnAction> editorPopupActions) {
+      myEditorPopupActions = editorPopupActions;
+    }
+
+    public void install(@NotNull List<? extends Editor> editors) {
+      for (Editor editor : editors) {
+        if (editor == null) continue;
+        editor.addEditorMouseListener(this);
+      }
+    }
+
     @Override
     public void invokePopup(final EditorMouseEvent event) {
       if (myEditorPopupActions.isEmpty()) return;
similarity index 52%
rename from platform/diff-impl/src/com/intellij/diff/tools/binary/BinaryContentPanel.java
rename to platform/diff-impl/src/com/intellij/diff/tools/util/side/HolderPanel.java
index 6e706eac2c46db5e6b94ea65f8a346bcad43392b..7c9d00a4718649d48fb98746525508088866cf7a 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.intellij.diff.tools.binary;
+package com.intellij.diff.tools.util.side;
 
-import com.intellij.diff.tools.util.twoside.TwosideContentPanel;
-import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.diff.tools.holders.EditorHolder;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
-import java.util.List;
+import java.awt.*;
 
-public class BinaryContentPanel extends TwosideContentPanel {
-  public BinaryContentPanel(@NotNull List<JComponent> titleComponents,
-                            @Nullable FileEditor editor1,
-                            @Nullable FileEditor editor2) {
-    super(titleComponents, getComponent(editor1), getComponent(editor2));
-  }
-
-  @Nullable
-  private static JComponent getComponent(@Nullable FileEditor editor) {
-    return editor != null ? editor.getComponent() : null;
+public class HolderPanel extends JPanel {
+  public HolderPanel(@NotNull EditorHolder holder, @Nullable JComponent title) {
+    super(new BorderLayout());
+    add(holder.getComponent(), BorderLayout.CENTER);
+    if (title != null) add(title, BorderLayout.NORTH);
   }
 }
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/side/OnesideContentPanel.java b/platform/diff-impl/src/com/intellij/diff/tools/util/side/OnesideContentPanel.java
new file mode 100644 (file)
index 0000000..401a95a
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.util.side;
+
+import com.intellij.diff.tools.holders.EditorHolder;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class OnesideContentPanel extends JPanel {
+  public OnesideContentPanel(@NotNull EditorHolder holder, @NotNull JComponent titleComponent) {
+    super(new BorderLayout());
+
+    add(new HolderPanel(holder, titleComponent), BorderLayout.CENTER);
+  }
+}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/side/OnesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/util/side/OnesideDiffViewer.java
new file mode 100644 (file)
index 0000000..8197454
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.util.side;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.contents.DiffContent;
+import com.intellij.diff.contents.EmptyContent;
+import com.intellij.diff.requests.ContentDiffRequest;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.holders.EditorHolder;
+import com.intellij.diff.tools.holders.EditorHolderFactory;
+import com.intellij.diff.tools.util.DiffDataKeys;
+import com.intellij.diff.tools.util.SimpleDiffPanel;
+import com.intellij.diff.tools.util.base.ListenerDiffViewerBase;
+import com.intellij.diff.util.DiffUtil;
+import com.intellij.diff.util.Side;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.util.Disposer;
+import org.jetbrains.annotations.CalledInAwt;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+public abstract class OnesideDiffViewer<T extends EditorHolder> extends ListenerDiffViewerBase {
+  @NotNull protected final SimpleDiffPanel myPanel;
+  @NotNull protected final OnesideContentPanel myContentPanel;
+
+  @NotNull private final Side mySide;
+  @NotNull private final T myHolder;
+
+  public OnesideDiffViewer(@NotNull DiffContext context, @NotNull ContentDiffRequest request, @NotNull EditorHolderFactory<T> factory) {
+    super(context, request);
+
+    mySide = Side.fromRight(myRequest.getContents().get(0) instanceof EmptyContent);
+    myHolder = createEditorHolder(factory);
+
+    JComponent titlePanels = createTitle();
+    myContentPanel = new OnesideContentPanel(myHolder, titlePanels);
+
+    myPanel = new SimpleDiffPanel(myContentPanel, this, context);
+  }
+
+  @Override
+  @CalledInAwt
+  protected void onDispose() {
+    destroyEditorHolder();
+    super.onDispose();
+  }
+
+  //
+  // Editors
+  //
+
+  @NotNull
+  protected T createEditorHolder(@NotNull EditorHolderFactory<T> factory) {
+    DiffContent content = mySide.select(myRequest.getContents());
+    return factory.create(content, myContext);
+  }
+
+  private void destroyEditorHolder() {
+    Disposer.dispose(myHolder);
+  }
+
+  @NotNull
+  protected JComponent createTitle() {
+    List<JComponent> simpleTitles = DiffUtil.createSimpleTitles(myRequest);
+    return mySide.select(simpleTitles);
+  }
+
+  //
+  // Getters
+  //
+
+  @NotNull
+  @Override
+  public JComponent getComponent() {
+    return myPanel;
+  }
+
+  @Nullable
+  @Override
+  public JComponent getPreferredFocusedComponent() {
+    if (!myPanel.isGoodContent()) return null;
+    return getEditorHolder().getPreferredFocusedComponent();
+  }
+
+  @NotNull
+  public Side getSide() {
+    return mySide;
+  }
+
+  @NotNull
+  protected DiffContent getContent() {
+    return mySide.select(myRequest.getContents());
+  }
+
+  @NotNull
+  protected T getEditorHolder() {
+    return myHolder;
+  }
+
+  @Nullable
+  @Override
+  public Object getData(@NonNls String dataId) {
+    if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) {
+      return DiffUtil.getVirtualFile(myRequest, mySide);
+    }
+    else if (DiffDataKeys.CURRENT_CONTENT.is(dataId)) {
+      return getContent();
+    }
+    return super.getData(dataId);
+  }
+
+  //
+  // Misc
+  //
+
+  @Nullable
+  @Override
+  protected OpenFileDescriptor getOpenFileDescriptor() {
+    return getContent().getOpenFileDescriptor();
+  }
+
+  public static <T extends EditorHolder> boolean canShowRequest(@NotNull DiffContext context,
+                                                                @NotNull DiffRequest request,
+                                                                @NotNull EditorHolderFactory<T> factory) {
+    if (!(request instanceof ContentDiffRequest)) return false;
+
+    List<DiffContent> contents = ((ContentDiffRequest)request).getContents();
+    if (contents.size() != 2) return false;
+
+    DiffContent content1 = contents.get(0);
+    DiffContent content2 = contents.get(1);
+
+    if (content1 instanceof EmptyContent && factory.wantShowContent(content2, context)) return true;
+    if (content2 instanceof EmptyContent && factory.wantShowContent(content1, context)) return true;
+    return false;
+  }
+}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/side/OnesideTextDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/util/side/OnesideTextDiffViewer.java
new file mode 100644 (file)
index 0000000..6f001d0
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.util.side;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.actions.impl.OpenInEditorWithMouseAction;
+import com.intellij.diff.actions.impl.SetEditorSettingsAction;
+import com.intellij.diff.contents.DocumentContent;
+import com.intellij.diff.requests.ContentDiffRequest;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.holders.EditorHolderFactory;
+import com.intellij.diff.tools.holders.TextEditorHolder;
+import com.intellij.diff.tools.util.DiffDataKeys;
+import com.intellij.diff.tools.util.base.InitialScrollPositionSupport;
+import com.intellij.diff.tools.util.base.TextDiffSettingsHolder;
+import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
+import com.intellij.diff.util.DiffUtil;
+import com.intellij.diff.util.Side;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.CalledInAwt;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class OnesideTextDiffViewer extends OnesideDiffViewer<TextEditorHolder> {
+  public static final Logger LOG = Logger.getInstance(OnesideTextDiffViewer.class);
+
+  @NotNull private final List<? extends EditorEx> myEditableEditors;
+
+  @NotNull protected final SetEditorSettingsAction myEditorSettingsAction;
+
+  public OnesideTextDiffViewer(@NotNull DiffContext context, @NotNull ContentDiffRequest request) {
+    super(context, request, TextEditorHolder.TextEditorHolderFactory.INSTANCE);
+
+    myEditableEditors = TextDiffViewerUtil.getEditableEditors(getEditors());
+
+    myEditorSettingsAction = new SetEditorSettingsAction(getTextSettings(), getEditors());
+    myEditorSettingsAction.applyDefaults();
+
+    new MyOpenInEditorWithMouseAction().register(getEditors());
+  }
+
+  @Override
+  @CalledInAwt
+  protected void onInit() {
+    super.onInit();
+    installEditorListeners();
+  }
+
+  @Override
+  @CalledInAwt
+  protected void onDispose() {
+    destroyEditorListeners();
+    super.onDispose();
+  }
+
+  @NotNull
+  @Override
+  protected TextEditorHolder createEditorHolder(@NotNull EditorHolderFactory<TextEditorHolder> factory) {
+    TextEditorHolder holder = super.createEditorHolder(factory);
+
+    boolean[] forceReadOnly = TextDiffViewerUtil.checkForceReadOnly(myContext, myRequest);
+    if (forceReadOnly[0]) holder.getEditor().setViewer(true);
+
+    return holder;
+  }
+
+  @NotNull
+  @Override
+  protected JComponent createTitle() {
+    List<JComponent> textTitles = DiffUtil.createTextTitles(myRequest, ContainerUtil.list(getEditor(), getEditor()));
+    return getSide().select(textTitles);
+  }
+
+  //
+  // Diff
+  //
+
+  @NotNull
+  public TextDiffSettingsHolder.TextDiffSettings getTextSettings() {
+    return TextDiffViewerUtil.getTextSettings(myContext);
+  }
+
+  @NotNull
+  protected List<AnAction> createEditorPopupActions() {
+    return TextDiffViewerUtil.createEditorPopupActions();
+  }
+
+  //
+  // Listeners
+  //
+
+  @CalledInAwt
+  protected void installEditorListeners() {
+    new TextDiffViewerUtil.EditorActionsPopup(createEditorPopupActions()).install(getEditors());
+  }
+
+  @CalledInAwt
+  protected void destroyEditorListeners() {
+  }
+
+  //
+  // Getters
+  //
+
+  @NotNull
+  public List<? extends EditorEx> getEditors() {
+    return Collections.singletonList(getEditor());
+  }
+
+  @NotNull
+  protected List<? extends EditorEx> getEditableEditors() {
+    return myEditableEditors;
+  }
+
+  @NotNull
+  public EditorEx getEditor() {
+    return getEditorHolder().getEditor();
+  }
+
+  @NotNull
+  @Override
+  public DocumentContent getContent() {
+    //noinspection unchecked
+    return (DocumentContent)super.getContent();
+  }
+
+  //
+  // Abstract
+  //
+
+  @CalledInAwt
+  protected void scrollToLine(int line) {
+    DiffUtil.scrollEditor(getEditor(), line, false);
+  }
+
+  //
+  // Misc
+  //
+
+  @Nullable
+  @Override
+  protected OpenFileDescriptor getOpenFileDescriptor() {
+    int offset = getEditor().getCaretModel().getOffset();
+    return getContent().getOpenFileDescriptor(offset);
+  }
+
+  public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    return OnesideDiffViewer.canShowRequest(context, request, TextEditorHolder.TextEditorHolderFactory.INSTANCE);
+  }
+
+  //
+  // Actions
+  //
+
+  private class MyOpenInEditorWithMouseAction extends OpenInEditorWithMouseAction {
+    @Override
+    protected OpenFileDescriptor getDescriptor(@NotNull Editor editor, int line) {
+      if (editor != getEditor()) return null;
+
+      int offset = editor.logicalPositionToOffset(new LogicalPosition(line, 0));
+      return getContent().getOpenFileDescriptor(offset);
+    }
+  }
+
+  //
+  // Helpers
+  //
+
+  @Nullable
+  @Override
+  public Object getData(@NonNls String dataId) {
+    if (DiffDataKeys.CURRENT_EDITOR.is(dataId)) {
+      return getEditor();
+    }
+    return super.getData(dataId);
+  }
+
+  protected abstract class MyInitialScrollPositionHelper extends InitialScrollPositionSupport.TwosideInitialScrollHelper {
+    @NotNull
+    @Override
+    protected List<? extends Editor> getEditors() {
+      return OnesideTextDiffViewer.this.getEditors();
+    }
+
+    @Override
+    protected void disableSyncScroll(boolean value) {
+    }
+
+    @Override
+    protected boolean doScrollToLine() {
+      if (myScrollToLine == null) return false;
+      Side side = myScrollToLine.first;
+      if (side != getSide()) return false;
+
+      scrollToLine(myScrollToLine.second);
+      return true;
+    }
+  }
+}
similarity index 58%
rename from platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideContentPanel.java
rename to platform/diff-impl/src/com/intellij/diff/tools/util/side/ThreesideContentPanel.java
index a38fd235cfe1ded8bd60951e94a665958b739012..11e4059576a31d5c9e86727c12f9131fc04429a3 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.intellij.diff.tools.util.threeside;
+package com.intellij.diff.tools.util.side;
 
+import com.intellij.diff.tools.holders.EditorHolder;
+import com.intellij.diff.tools.holders.TextEditorHolder;
 import com.intellij.diff.tools.util.DiffSplitter;
 import com.intellij.diff.tools.util.ThreeDiffSplitter;
 import com.intellij.diff.util.Side;
+import com.intellij.diff.util.ThreeSide;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.util.ui.ButtonlessScrollBarUI;
 import org.jetbrains.annotations.CalledInAwt;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -29,14 +34,19 @@ import java.util.List;
 
 public class ThreesideContentPanel extends JPanel {
   @NotNull private final ThreeDiffSplitter mySplitter;
+  @Nullable private final EditorEx myBaseEditor;
 
-  public ThreesideContentPanel(@NotNull List<JComponent> editors, @NotNull List<JComponent> titleComponents) {
+  public ThreesideContentPanel(@NotNull List<? extends EditorHolder> holders, @NotNull List<JComponent> titleComponents) {
     super(new BorderLayout());
-    assert editors.size() == 3;
+    assert holders.size() == 3;
+    assert titleComponents.size() == 3;
+
+    EditorHolder baseHolder = ThreeSide.BASE.select(holders);
+    myBaseEditor = baseHolder instanceof TextEditorHolder ? ((TextEditorHolder)baseHolder).getEditor() : null;
 
     ArrayList<JComponent> components = new ArrayList<JComponent>(3);
     for (int i = 0; i < 3; i++) {
-      components.add(new MyPanel(editors.get(i), titleComponents.get(i)));
+      components.add(new HolderPanel(holders.get(i), titleComponents.get(i)));
     }
 
     mySplitter = new ThreeDiffSplitter(components);
@@ -49,18 +59,16 @@ public class ThreesideContentPanel extends JPanel {
   }
 
   public void repaintDividers() {
+    if (myBaseEditor != null) myBaseEditor.getScrollPane().getVerticalScrollBar().repaint();
     mySplitter.repaintDividers();
   }
 
   public void repaintDivider(@NotNull Side side) {
+    if (side == Side.RIGHT && myBaseEditor != null) myBaseEditor.getScrollPane().getVerticalScrollBar().repaint();
     mySplitter.repaintDivider(side);
   }
 
-  private static class MyPanel extends JPanel {
-    public MyPanel(@NotNull JComponent editor, @Nullable JComponent title) {
-      super(new BorderLayout());
-      add(editor, BorderLayout.CENTER);
-      if (title != null) add(title, BorderLayout.NORTH);
-    }
+  public void setScrollbarPainter(@NotNull ButtonlessScrollBarUI.ScrollbarRepaintCallback painter) {
+    if (myBaseEditor != null) myBaseEditor.registerScrollBarRepaintCallback(painter);
   }
 }
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/side/ThreesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/util/side/ThreesideDiffViewer.java
new file mode 100644 (file)
index 0000000..2d5dee2
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.util.side;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.DiffDialogHints;
+import com.intellij.diff.DiffManager;
+import com.intellij.diff.contents.DiffContent;
+import com.intellij.diff.requests.ContentDiffRequest;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.requests.SimpleDiffRequest;
+import com.intellij.diff.tools.holders.EditorHolder;
+import com.intellij.diff.tools.holders.EditorHolderFactory;
+import com.intellij.diff.tools.util.DiffDataKeys;
+import com.intellij.diff.tools.util.FocusTrackerSupport.ThreesideFocusTrackerSupport;
+import com.intellij.diff.tools.util.SimpleDiffPanel;
+import com.intellij.diff.tools.util.base.ListenerDiffViewerBase;
+import com.intellij.diff.util.DiffUtil;
+import com.intellij.diff.util.ThreeSide;
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.diff.DiffBundle;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.util.Disposer;
+import org.jetbrains.annotations.CalledInAwt;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class ThreesideDiffViewer<T extends EditorHolder> extends ListenerDiffViewerBase {
+  @NotNull protected final SimpleDiffPanel myPanel;
+  @NotNull protected final ThreesideContentPanel myContentPanel;
+
+  @NotNull private final List<T> myHolders;
+
+  @NotNull private final ThreesideFocusTrackerSupport myFocusTrackerSupport;
+
+  public ThreesideDiffViewer(@NotNull DiffContext context, @NotNull ContentDiffRequest request, @NotNull EditorHolderFactory<T> factory) {
+    super(context, request);
+
+    myHolders = createEditorHolders(factory);
+
+    List<JComponent> titlePanel = createTitles();
+    myFocusTrackerSupport = new ThreesideFocusTrackerSupport(myHolders);
+    myContentPanel = new ThreesideContentPanel(myHolders, titlePanel);
+
+    myPanel = new SimpleDiffPanel(myContentPanel, this, context);
+  }
+
+  @Override
+  @CalledInAwt
+  protected void onDispose() {
+    destroyEditorHolders();
+    super.onDispose();
+  }
+
+  @Override
+  @CalledInAwt
+  protected void processContextHints() {
+    super.processContextHints();
+    myFocusTrackerSupport.processContextHints(myRequest, myContext);
+  }
+
+  @Override
+  @CalledInAwt
+  protected void updateContextHints() {
+    super.updateContextHints();
+    myFocusTrackerSupport.updateContextHints(myRequest, myContext);
+  }
+
+  @NotNull
+  protected List<T> createEditorHolders(@NotNull EditorHolderFactory<T> factory) {
+    List<DiffContent> contents = myRequest.getContents();
+
+    List<T> holders = new ArrayList<T>(3);
+    for (int i = 0; i < 3; i++) {
+      DiffContent content = contents.get(i);
+      holders.add(factory.create(content, myContext));
+    }
+    return holders;
+  }
+
+  private void destroyEditorHolders() {
+    for (T holder : myHolders) {
+      Disposer.dispose(holder);
+    }
+  }
+
+  @NotNull
+  protected List<JComponent> createTitles() {
+    return DiffUtil.createSimpleTitles(myRequest);
+  }
+
+  //
+  // Getters
+  //
+
+  @NotNull
+  @Override
+  public JComponent getComponent() {
+    return myPanel;
+  }
+
+  @Nullable
+  @Override
+  public JComponent getPreferredFocusedComponent() {
+    if (!myPanel.isGoodContent()) return null;
+    return getCurrentEditorHolder().getPreferredFocusedComponent();
+  }
+
+  @NotNull
+  public ThreeSide getCurrentSide() {
+    return myFocusTrackerSupport.getCurrentSide();
+  }
+
+  protected void setCurrentSide(@NotNull ThreeSide side) {
+    myFocusTrackerSupport.setCurrentSide(side);
+  }
+
+  @NotNull
+  protected List<T> getEditorHolders() {
+    return myHolders;
+  }
+
+  @NotNull
+  protected T getCurrentEditorHolder() {
+    return getCurrentSide().select(getEditorHolders());
+  }
+
+  @Nullable
+  @Override
+  public Object getData(@NonNls String dataId) {
+    if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) {
+      return DiffUtil.getVirtualFile(myRequest, getCurrentSide());
+    }
+    else if (DiffDataKeys.CURRENT_CONTENT.is(dataId)) {
+      return getCurrentSide().select(myRequest.getContents());
+    }
+    return super.getData(dataId);
+  }
+
+  //
+  // Misc
+  //
+
+  @Nullable
+  @Override
+  protected OpenFileDescriptor getOpenFileDescriptor() {
+    return getCurrentSide().select(getRequest().getContents()).getOpenFileDescriptor();
+  }
+
+  public static <T extends EditorHolder> boolean canShowRequest(@NotNull DiffContext context,
+                                                                @NotNull DiffRequest request,
+                                                                @NotNull EditorHolderFactory<T> factory) {
+    if (!(request instanceof ContentDiffRequest)) return false;
+
+    List<DiffContent> contents = ((ContentDiffRequest)request).getContents();
+    if (contents.size() != 3) return false;
+
+    boolean canShow = true;
+    boolean wantShow = false;
+    for (DiffContent content : contents) {
+      canShow &= factory.canShowContent(content, context);
+      wantShow |= factory.wantShowContent(content, context);
+    }
+    return canShow && wantShow;
+  }
+
+  //
+  // Actions
+  //
+
+  protected class ShowLeftBasePartialDiffAction extends ShowPartialDiffAction {
+    public ShowLeftBasePartialDiffAction() {
+      super(ThreeSide.LEFT, ThreeSide.BASE, DiffBundle.message("merge.partial.diff.action.name.0.1"), null, AllIcons.Diff.LeftDiff);
+    }
+  }
+
+  protected class ShowBaseRightPartialDiffAction extends ShowPartialDiffAction {
+    public ShowBaseRightPartialDiffAction() {
+      super(ThreeSide.BASE, ThreeSide.RIGHT, DiffBundle.message("merge.partial.diff.action.name.1.2"), null, AllIcons.Diff.RightDiff);
+    }
+  }
+
+  protected class ShowLeftRightPartialDiffAction extends ShowPartialDiffAction {
+    public ShowLeftRightPartialDiffAction() {
+      super(ThreeSide.LEFT, ThreeSide.RIGHT, DiffBundle.message("merge.partial.diff.action.name"), null, AllIcons.Diff.BranchDiff);
+    }
+  }
+
+  protected class ShowPartialDiffAction extends DumbAwareAction {
+    @NotNull private final ThreeSide mySide1;
+    @NotNull private final ThreeSide mySide2;
+
+    public ShowPartialDiffAction(@NotNull ThreeSide side1,
+                                 @NotNull ThreeSide side2,
+                                 @NotNull String text,
+                                 @Nullable String description,
+                                 @NotNull Icon icon) {
+      super(text, description, icon);
+      mySide1 = side1;
+      mySide2 = side2;
+    }
+
+    @Override
+    public void actionPerformed(AnActionEvent e) {
+      List<DiffContent> contents = myRequest.getContents();
+      List<String> titles = myRequest.getContentTitles();
+
+      DiffRequest request = new SimpleDiffRequest(myRequest.getTitle(),
+                                                  mySide1.select(contents), mySide2.select(contents),
+                                                  mySide1.select(titles), mySide2.select(titles));
+      DiffManager.getInstance().showDiff(myProject, request, new DiffDialogHints(null, myPanel));
+    }
+  }
+}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/side/ThreesideTextDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/util/side/ThreesideTextDiffViewer.java
new file mode 100644 (file)
index 0000000..963cecc
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.util.side;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.actions.impl.OpenInEditorWithMouseAction;
+import com.intellij.diff.actions.impl.SetEditorSettingsAction;
+import com.intellij.diff.contents.DocumentContent;
+import com.intellij.diff.requests.ContentDiffRequest;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.holders.EditorHolderFactory;
+import com.intellij.diff.tools.holders.TextEditorHolder;
+import com.intellij.diff.tools.util.DiffDataKeys;
+import com.intellij.diff.tools.util.SyncScrollSupport;
+import com.intellij.diff.tools.util.SyncScrollSupport.ThreesideSyncScrollSupport;
+import com.intellij.diff.tools.util.base.InitialScrollPositionSupport;
+import com.intellij.diff.tools.util.base.TextDiffSettingsHolder;
+import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
+import com.intellij.diff.util.DiffUtil;
+import com.intellij.diff.util.Side;
+import com.intellij.diff.util.ThreeSide;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.openapi.editor.event.DocumentEvent;
+import com.intellij.openapi.editor.event.VisibleAreaEvent;
+import com.intellij.openapi.editor.event.VisibleAreaListener;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.editor.ex.EditorMarkupModel;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.CalledInAwt;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+public abstract class ThreesideTextDiffViewer extends ThreesideDiffViewer<TextEditorHolder> {
+  public static final Logger LOG = Logger.getInstance(ThreesideTextDiffViewer.class);
+
+  @Nullable private List<? extends EditorEx> myEditors;
+  @NotNull private final List<? extends EditorEx> myEditableEditors;
+
+  @NotNull private final MyVisibleAreaListener myVisibleAreaListener1 = new MyVisibleAreaListener(Side.LEFT);
+  @NotNull private final MyVisibleAreaListener myVisibleAreaListener2 = new MyVisibleAreaListener(Side.RIGHT);
+  @Nullable protected ThreesideSyncScrollSupport mySyncScrollSupport;
+
+  @NotNull protected final SetEditorSettingsAction myEditorSettingsAction;
+
+  public ThreesideTextDiffViewer(@NotNull DiffContext context, @NotNull ContentDiffRequest request) {
+    super(context, request, TextEditorHolder.TextEditorHolderFactory.INSTANCE);
+
+    //new MyFocusOppositePaneAction().setupAction(myPanel, this); // TODO
+
+    myEditorSettingsAction = new SetEditorSettingsAction(getTextSettings(), getEditors());
+    myEditorSettingsAction.applyDefaults();
+
+    new MyOpenInEditorWithMouseAction().register(getEditors());
+
+    myEditableEditors = TextDiffViewerUtil.getEditableEditors(getEditors());
+
+    TextDiffViewerUtil.checkDifferentDocuments(myRequest);
+  }
+
+  @Override
+  @CalledInAwt
+  protected void onInit() {
+    super.onInit();
+    installEditorListeners();
+  }
+
+  @Override
+  @CalledInAwt
+  protected void onDispose() {
+    destroyEditorListeners();
+    super.onDispose();
+  }
+
+  @NotNull
+  @Override
+  protected List<TextEditorHolder> createEditorHolders(@NotNull EditorHolderFactory<TextEditorHolder> factory) {
+    List<TextEditorHolder> holders = super.createEditorHolders(factory);
+
+    boolean[] forceReadOnly = TextDiffViewerUtil.checkForceReadOnly(myContext, myRequest);
+    for (int i = 0; i < 3; i++) {
+      if (forceReadOnly[i]) holders.get(i).getEditor().setViewer(true);
+    }
+
+    ThreeSide.LEFT.select(holders).getEditor().setVerticalScrollbarOrientation(EditorEx.VERTICAL_SCROLLBAR_LEFT);
+    ((EditorMarkupModel)ThreeSide.BASE.select(holders).getEditor().getMarkupModel()).setErrorStripeVisible(false);
+
+    if (Registry.is("diff.divider.repainting.disable.blitting")) {
+      for (TextEditorHolder holder : holders) {
+        holder.getEditor().getScrollPane().getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
+      }
+    }
+
+    return holders;
+  }
+
+  @NotNull
+  @Override
+  protected List<JComponent> createTitles() {
+    return DiffUtil.createTextTitles(myRequest, getEditors());
+  }
+
+  //
+  // Listeners
+  //
+
+  @CalledInAwt
+  protected void installEditorListeners() {
+    new TextDiffViewerUtil.EditorActionsPopup(createEditorPopupActions()).install(getEditors());
+
+    new TextDiffViewerUtil.EditorFontSizeSynchronizer(getEditors()).install(this);
+
+    getEditor(ThreeSide.LEFT).getScrollingModel().addVisibleAreaListener(myVisibleAreaListener1);
+    getEditor(ThreeSide.BASE).getScrollingModel().addVisibleAreaListener(myVisibleAreaListener1);
+
+    getEditor(ThreeSide.BASE).getScrollingModel().addVisibleAreaListener(myVisibleAreaListener2);
+    getEditor(ThreeSide.RIGHT).getScrollingModel().addVisibleAreaListener(myVisibleAreaListener2);
+
+    SyncScrollSupport.SyncScrollable scrollable1 = getSyncScrollable(Side.LEFT);
+    SyncScrollSupport.SyncScrollable scrollable2 = getSyncScrollable(Side.RIGHT);
+    if (scrollable1 != null && scrollable2 != null) {
+      mySyncScrollSupport = new ThreesideSyncScrollSupport(getEditors(), scrollable1, scrollable2);
+    }
+  }
+
+  @CalledInAwt
+  public void destroyEditorListeners() {
+    getEditor(ThreeSide.LEFT).getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener1);
+    getEditor(ThreeSide.BASE).getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener1);
+
+    getEditor(ThreeSide.BASE).getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener2);
+    getEditor(ThreeSide.RIGHT).getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener2);
+
+    mySyncScrollSupport = null;
+  }
+
+  protected void disableSyncScrollSupport(boolean disable) {
+    if (mySyncScrollSupport != null) {
+      mySyncScrollSupport.setDisabled(disable);
+    }
+  }
+
+  //
+  // Diff
+  //
+
+  @NotNull
+  public TextDiffSettingsHolder.TextDiffSettings getTextSettings() {
+    return TextDiffViewerUtil.getTextSettings(myContext);
+  }
+
+  @NotNull
+  protected List<AnAction> createEditorPopupActions() {
+    return TextDiffViewerUtil.createEditorPopupActions();
+  }
+
+  @Override
+  protected void onDocumentChange(@NotNull DocumentEvent event) {
+    super.onDocumentChange(event);
+    myContentPanel.repaintDividers();
+  }
+
+  //
+  // Getters
+  //
+
+  @NotNull
+  public EditorEx getCurrentEditor() {
+    return getEditor(getCurrentSide());
+  }
+
+  @NotNull
+  public DocumentContent getCurrentContent() {
+    return getContent(getCurrentSide());
+  }
+
+  @NotNull
+  protected List<? extends DocumentContent> getContents() {
+    //noinspection unchecked
+    return (List)myRequest.getContents();
+  }
+
+  @NotNull
+  public List<? extends EditorEx> getEditors() {
+    if (myEditors == null) {
+      myEditors = ContainerUtil.map(getEditorHolders(), new Function<TextEditorHolder, EditorEx>() {
+        @Override
+        public EditorEx fun(TextEditorHolder holder) {
+          return holder.getEditor();
+        }
+      });
+    }
+    return myEditors;
+  }
+
+  @NotNull
+  protected List<? extends EditorEx> getEditableEditors() {
+    return myEditableEditors;
+  }
+
+  @NotNull
+  public EditorEx getEditor(@NotNull ThreeSide side) {
+    return side.select(getEditors());
+  }
+
+  @NotNull
+  public DocumentContent getContent(@NotNull ThreeSide side) {
+    return side.select(getContents());
+  }
+
+  //
+  // Abstract
+  //
+
+  @CalledInAwt
+  protected void scrollToLine(@NotNull ThreeSide side, int line) {
+    DiffUtil.scrollEditor(getEditor(side), line, false);
+    setCurrentSide(side);
+  }
+
+  @Nullable
+  protected abstract SyncScrollSupport.SyncScrollable getSyncScrollable(@NotNull Side side);
+
+  //
+  // Misc
+  //
+
+  @Nullable
+  @Override
+  protected OpenFileDescriptor getOpenFileDescriptor() {
+    int offset = getCurrentEditor().getCaretModel().getOffset();
+    return getCurrentContent().getOpenFileDescriptor(offset);
+  }
+
+  public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    return ThreesideDiffViewer.canShowRequest(context, request, TextEditorHolder.TextEditorHolderFactory.INSTANCE);
+  }
+
+  //
+  // Actions
+  //
+
+  private class MyOpenInEditorWithMouseAction extends OpenInEditorWithMouseAction {
+    @Override
+    protected OpenFileDescriptor getDescriptor(@NotNull Editor editor, int line) {
+      ThreeSide side = null;
+      if (editor == getEditor(ThreeSide.LEFT)) side = ThreeSide.LEFT;
+      if (editor == getEditor(ThreeSide.RIGHT)) side = ThreeSide.RIGHT;
+      if (editor == getEditor(ThreeSide.BASE)) side = ThreeSide.BASE;
+      if (side == null) return null;
+
+      int offset = editor.logicalPositionToOffset(new LogicalPosition(line, 0));
+      return getContent(side).getOpenFileDescriptor(offset);
+    }
+  }
+
+  protected class MyToggleAutoScrollAction extends TextDiffViewerUtil.ToggleAutoScrollAction {
+    public MyToggleAutoScrollAction() {
+      super(getTextSettings());
+    }
+  }
+
+  //
+  // Helpers
+  //
+
+  @Nullable
+  @Override
+  public Object getData(@NonNls String dataId) {
+    if (DiffDataKeys.CURRENT_EDITOR.is(dataId)) {
+      return getCurrentEditor();
+    }
+    return super.getData(dataId);
+  }
+  
+  private class MyVisibleAreaListener implements VisibleAreaListener {
+    @NotNull Side mySide;
+
+    public MyVisibleAreaListener(@NotNull Side side) {
+      mySide = side;
+    }
+
+    @Override
+    public void visibleAreaChanged(VisibleAreaEvent e) {
+      if (mySyncScrollSupport != null) mySyncScrollSupport.visibleAreaChanged(e);
+      if (Registry.is("diff.divider.repainting.fix")) {
+        myContentPanel.repaint();
+      }
+      else {
+        myContentPanel.repaintDivider(mySide);
+      }
+    }
+  }
+
+  protected abstract class MyInitialScrollPositionHelper extends InitialScrollPositionSupport.ThreesideInitialScrollHelper {
+    @NotNull
+    @Override
+    protected List<? extends Editor> getEditors() {
+      return ThreesideTextDiffViewer.this.getEditors();
+    }
+
+    @Override
+    protected void disableSyncScroll(boolean value) {
+      disableSyncScrollSupport(value);
+    }
+
+    @Override
+    protected boolean doScrollToLine() {
+      if (myScrollToLine == null) return false;
+
+      scrollToLine(myScrollToLine.first, myScrollToLine.second);
+      return true;
+    }
+  }
+}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/side/TwosideContentPanel.java b/platform/diff-impl/src/com/intellij/diff/tools/util/side/TwosideContentPanel.java
new file mode 100644 (file)
index 0000000..7b2a7ac
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.util.side;
+
+import com.intellij.diff.tools.holders.EditorHolder;
+import com.intellij.diff.tools.util.DiffSplitter;
+import com.intellij.diff.util.Side;
+import org.jetbrains.annotations.CalledInAwt;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.List;
+
+public class TwosideContentPanel extends JPanel {
+  @NotNull private final DiffSplitter mySplitter;
+
+  public TwosideContentPanel(@NotNull List<? extends EditorHolder> holders, @NotNull List<JComponent> titleComponents) {
+    super(new BorderLayout());
+    assert holders.size() == 2;
+    assert titleComponents.size() == 2;
+
+    mySplitter = new DiffSplitter();
+    mySplitter.setFirstComponent(new HolderPanel(Side.LEFT.select(holders), Side.LEFT.select(titleComponents)));
+    mySplitter.setSecondComponent(new HolderPanel(Side.RIGHT.select(holders), Side.RIGHT.select(titleComponents)));
+    mySplitter.setHonorComponentsMinimumSize(false);
+    add(mySplitter, BorderLayout.CENTER);
+  }
+
+  @CalledInAwt
+  public void setPainter(@Nullable DiffSplitter.Painter painter) {
+    mySplitter.setPainter(painter);
+  }
+
+  public void repaintDivider() {
+    mySplitter.repaintDivider();
+  }
+
+  @NotNull
+  public DiffSplitter getSplitter() {
+    return mySplitter;
+  }
+}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/side/TwosideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/util/side/TwosideDiffViewer.java
new file mode 100644 (file)
index 0000000..67c98f7
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.util.side;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.contents.DiffContent;
+import com.intellij.diff.requests.ContentDiffRequest;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.holders.EditorHolder;
+import com.intellij.diff.tools.holders.EditorHolderFactory;
+import com.intellij.diff.tools.util.DiffDataKeys;
+import com.intellij.diff.tools.util.FocusTrackerSupport.TwosideFocusTrackerSupport;
+import com.intellij.diff.tools.util.SimpleDiffPanel;
+import com.intellij.diff.tools.util.base.ListenerDiffViewerBase;
+import com.intellij.diff.util.DiffUtil;
+import com.intellij.diff.util.Side;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.util.Disposer;
+import org.jetbrains.annotations.CalledInAwt;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class TwosideDiffViewer<T extends EditorHolder> extends ListenerDiffViewerBase {
+  @NotNull protected final SimpleDiffPanel myPanel;
+  @NotNull protected final TwosideContentPanel myContentPanel;
+
+  @NotNull private final List<T> myHolders;
+
+  @NotNull private final TwosideFocusTrackerSupport myFocusTrackerSupport;
+
+  public TwosideDiffViewer(@NotNull DiffContext context, @NotNull ContentDiffRequest request, @NotNull EditorHolderFactory<T> factory) {
+    super(context, request);
+
+    myHolders = createEditorHolders(factory);
+
+    List<JComponent> titlePanels = createTitles();
+    myFocusTrackerSupport = new TwosideFocusTrackerSupport(myHolders);
+    myContentPanel = new TwosideContentPanel(myHolders, titlePanels);
+
+    myPanel = new SimpleDiffPanel(myContentPanel, this, context);
+  }
+
+  @Override
+  @CalledInAwt
+  protected void onDispose() {
+    destroyEditorHolders();
+    super.onDispose();
+  }
+
+  @Override
+  @CalledInAwt
+  protected void processContextHints() {
+    super.processContextHints();
+    myFocusTrackerSupport.processContextHints(myRequest, myContext);
+  }
+
+  @Override
+  @CalledInAwt
+  protected void updateContextHints() {
+    super.updateContextHints();
+    myFocusTrackerSupport.updateContextHints(myRequest, myContext);
+  }
+
+  //
+  // Editors
+  //
+
+  @NotNull
+  protected List<T> createEditorHolders(@NotNull EditorHolderFactory<T> factory) {
+    List<DiffContent> contents = myRequest.getContents();
+
+    List<T> holders = new ArrayList<T>(2);
+    for (int i = 0; i < 2; i++) {
+      DiffContent content = contents.get(i);
+      holders.add(factory.create(content, myContext));
+    }
+    return holders;
+  }
+
+  private void destroyEditorHolders() {
+    for (T holder : myHolders) {
+      Disposer.dispose(holder);
+    }
+  }
+
+  @NotNull
+  protected List<JComponent> createTitles() {
+    return DiffUtil.createSimpleTitles(myRequest);
+  }
+
+  //
+  // Getters
+  //
+
+  @NotNull
+  @Override
+  public JComponent getComponent() {
+    return myPanel;
+  }
+
+  @Nullable
+  @Override
+  public JComponent getPreferredFocusedComponent() {
+    if (!myPanel.isGoodContent()) return null;
+    return getCurrentEditorHolder().getPreferredFocusedComponent();
+  }
+
+  @NotNull
+  public Side getCurrentSide() {
+    return myFocusTrackerSupport.getCurrentSide();
+  }
+
+  protected void setCurrentSide(@NotNull Side side) {
+    myFocusTrackerSupport.setCurrentSide(side);
+  }
+
+  @NotNull
+  protected List<T> getEditorHolders() {
+    return myHolders;
+  }
+
+  @NotNull
+  protected T getCurrentEditorHolder() {
+    return getCurrentSide().select(getEditorHolders());
+  }
+
+  @Nullable
+  @Override
+  public Object getData(@NonNls String dataId) {
+    if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) {
+      return DiffUtil.getVirtualFile(myRequest, getCurrentSide());
+    }
+    else if (DiffDataKeys.CURRENT_CONTENT.is(dataId)) {
+      return getCurrentSide().select(myRequest.getContents());
+    }
+    return super.getData(dataId);
+  }
+
+  //
+  // Misc
+  //
+
+  @Nullable
+  @Override
+  protected OpenFileDescriptor getOpenFileDescriptor() {
+    return getCurrentSide().select(getRequest().getContents()).getOpenFileDescriptor();
+  }
+
+  public static <T extends EditorHolder> boolean canShowRequest(@NotNull DiffContext context,
+                                                                @NotNull DiffRequest request,
+                                                                @NotNull EditorHolderFactory<T> factory) {
+    if (!(request instanceof ContentDiffRequest)) return false;
+
+    List<DiffContent> contents = ((ContentDiffRequest)request).getContents();
+    if (contents.size() != 2) return false;
+
+    boolean canShow = true;
+    boolean wantShow = false;
+    for (DiffContent content : contents) {
+      canShow &= factory.canShowContent(content, context);
+      wantShow |= factory.wantShowContent(content, context);
+    }
+    return canShow && wantShow;
+  }
+}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/side/TwosideTextDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/util/side/TwosideTextDiffViewer.java
new file mode 100644 (file)
index 0000000..3bb946c
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.diff.tools.util.side;
+
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.actions.impl.FocusOppositePaneAction;
+import com.intellij.diff.actions.impl.OpenInEditorWithMouseAction;
+import com.intellij.diff.actions.impl.SetEditorSettingsAction;
+import com.intellij.diff.contents.DocumentContent;
+import com.intellij.diff.requests.ContentDiffRequest;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.tools.holders.EditorHolderFactory;
+import com.intellij.diff.tools.holders.TextEditorHolder;
+import com.intellij.diff.tools.util.DiffDataKeys;
+import com.intellij.diff.tools.util.SyncScrollSupport;
+import com.intellij.diff.tools.util.SyncScrollSupport.TwosideSyncScrollSupport;
+import com.intellij.diff.tools.util.base.InitialScrollPositionSupport;
+import com.intellij.diff.tools.util.base.TextDiffSettingsHolder;
+import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
+import com.intellij.diff.util.DiffUtil;
+import com.intellij.diff.util.Side;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.openapi.editor.ScrollType;
+import com.intellij.openapi.editor.event.DocumentEvent;
+import com.intellij.openapi.editor.event.VisibleAreaEvent;
+import com.intellij.openapi.editor.event.VisibleAreaListener;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.CalledInAwt;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+
+public abstract class TwosideTextDiffViewer extends TwosideDiffViewer<TextEditorHolder> {
+  public static final Logger LOG = Logger.getInstance(TwosideTextDiffViewer.class);
+
+  @NotNull private final List<? extends EditorEx> myEditableEditors;
+  @Nullable private List<? extends EditorEx> myEditors;
+
+  @NotNull protected final SetEditorSettingsAction myEditorSettingsAction;
+
+  @NotNull private final MyVisibleAreaListener myVisibleAreaListener = new MyVisibleAreaListener();
+
+  @Nullable private TwosideSyncScrollSupport mySyncScrollSupport;
+
+  public TwosideTextDiffViewer(@NotNull DiffContext context, @NotNull ContentDiffRequest request) {
+    super(context, request, TextEditorHolder.TextEditorHolderFactory.INSTANCE);
+
+    new MyFocusOppositePaneAction(true).setupAction(myPanel);
+    new MyFocusOppositePaneAction(false).setupAction(myPanel);
+
+    myEditorSettingsAction = new SetEditorSettingsAction(getTextSettings(), getEditors());
+    myEditorSettingsAction.applyDefaults();
+
+    new MyOpenInEditorWithMouseAction().register(getEditors());
+
+    myEditableEditors = TextDiffViewerUtil.getEditableEditors(getEditors());
+
+    TextDiffViewerUtil.checkDifferentDocuments(myRequest);
+  }
+
+  @Override
+  @CalledInAwt
+  protected void onInit() {
+    super.onInit();
+    installEditorListeners();
+  }
+
+  @Override
+  @CalledInAwt
+  protected void onDispose() {
+    destroyEditorListeners();
+    super.onDispose();
+  }
+
+  @NotNull
+  @Override
+  protected List<TextEditorHolder> createEditorHolders(@NotNull EditorHolderFactory<TextEditorHolder> factory) {
+    List<TextEditorHolder> holders = super.createEditorHolders(factory);
+
+    boolean[] forceReadOnly = TextDiffViewerUtil.checkForceReadOnly(myContext, myRequest);
+    for (int i = 0; i < 2; i++) {
+      if (forceReadOnly[i]) holders.get(i).getEditor().setViewer(true);
+    }
+
+    Side.LEFT.select(holders).getEditor().setVerticalScrollbarOrientation(EditorEx.VERTICAL_SCROLLBAR_LEFT);
+
+    if (Registry.is("diff.divider.repainting.disable.blitting")) {
+      for (TextEditorHolder holder : holders) {
+        holder.getEditor().getScrollPane().getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
+      }
+    }
+
+    return holders;
+  }
+
+  @NotNull
+  @Override
+  protected List<JComponent> createTitles() {
+    return DiffUtil.createTextTitles(myRequest, getEditors());
+  }
+
+  //
+  // Diff
+  //
+
+  @NotNull
+  public TextDiffSettingsHolder.TextDiffSettings getTextSettings() {
+    return TextDiffViewerUtil.getTextSettings(myContext);
+  }
+
+  @NotNull
+  protected List<AnAction> createEditorPopupActions() {
+    return TextDiffViewerUtil.createEditorPopupActions();
+  }
+
+  @Override
+  protected void onDocumentChange(@NotNull DocumentEvent event) {
+    super.onDocumentChange(event);
+    myContentPanel.repaintDivider();
+  }
+
+  //
+  // Listeners
+  //
+
+  @CalledInAwt
+  protected void installEditorListeners() {
+    new TextDiffViewerUtil.EditorActionsPopup(createEditorPopupActions()).install(getEditors());
+
+    new TextDiffViewerUtil.EditorFontSizeSynchronizer(getEditors()).install(this);
+
+
+    getEditor(Side.LEFT).getScrollingModel().addVisibleAreaListener(myVisibleAreaListener);
+    getEditor(Side.RIGHT).getScrollingModel().addVisibleAreaListener(myVisibleAreaListener);
+
+    SyncScrollSupport.SyncScrollable scrollable = getSyncScrollable();
+    if (scrollable != null) {
+      mySyncScrollSupport = new TwosideSyncScrollSupport(getEditors(), scrollable);
+    }
+  }
+
+  @CalledInAwt
+  protected void destroyEditorListeners() {
+    getEditor(Side.LEFT).getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener);
+    getEditor(Side.RIGHT).getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener);
+
+    mySyncScrollSupport = null;
+  }
+
+  protected void disableSyncScrollSupport(boolean disable) {
+    if (mySyncScrollSupport != null) {
+      mySyncScrollSupport.setDisabled(disable);
+    }
+  }
+
+  //
+  // Getters
+  //
+
+
+  @NotNull
+  protected List<? extends DocumentContent> getContents() {
+    //noinspection unchecked
+    return (List)myRequest.getContents();
+  }
+
+  @NotNull
+  public List<? extends EditorEx> getEditors() {
+    if (myEditors == null) {
+      myEditors = ContainerUtil.map(getEditorHolders(), new Function<TextEditorHolder, EditorEx>() {
+        @Override
+        public EditorEx fun(TextEditorHolder holder) {
+          return holder.getEditor();
+        }
+      });
+    }
+    return myEditors;
+  }
+
+  @NotNull
+  protected List<? extends EditorEx> getEditableEditors() {
+    return myEditableEditors;
+  }
+
+  @NotNull
+  public EditorEx getCurrentEditor() {
+    return getEditor(getCurrentSide());
+  }
+
+  @NotNull
+  public DocumentContent getCurrentContent() {
+    return getContent(getCurrentSide());
+  }
+
+  @NotNull
+  public EditorEx getEditor1() {
+    return getEditor(Side.LEFT);
+  }
+
+  @NotNull
+  public EditorEx getEditor2() {
+    return getEditor(Side.RIGHT);
+  }
+
+
+  @NotNull
+  public EditorEx getEditor(@NotNull Side side) {
+    return side.select(getEditors());
+  }
+
+  @NotNull
+  public DocumentContent getContent(@NotNull Side side) {
+    return side.select(getContents());
+  }
+
+  @NotNull
+  public DocumentContent getContent1() {
+    return getContent(Side.LEFT);
+  }
+
+  @NotNull
+  public DocumentContent getContent2() {
+    return getContent(Side.RIGHT);
+  }
+
+  @Nullable
+  public TwosideSyncScrollSupport getSyncScrollSupport() {
+    return mySyncScrollSupport;
+  }
+
+  //
+  // Abstract
+  //
+
+  @CalledInAwt
+  @NotNull
+  protected LogicalPosition transferPosition(@NotNull Side baseSide, @NotNull LogicalPosition position) {
+    if (mySyncScrollSupport == null) return position;
+    int line = mySyncScrollSupport.getScrollable().transfer(baseSide, position.line);
+    return new LogicalPosition(line, position.column);
+  }
+
+  @CalledInAwt
+  protected void scrollToLine(@NotNull Side side, int line) {
+    DiffUtil.scrollEditor(getEditor(side), line, false);
+    setCurrentSide(side);
+  }
+
+  @Nullable
+  protected abstract SyncScrollSupport.SyncScrollable getSyncScrollable();
+
+  //
+  // Misc
+  //
+
+  @Nullable
+  @Override
+  protected OpenFileDescriptor getOpenFileDescriptor() {
+    int offset = getCurrentEditor().getCaretModel().getOffset();
+    return getCurrentContent().getOpenFileDescriptor(offset);
+  }
+
+  public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
+    return TwosideDiffViewer.canShowRequest(context, request, TextEditorHolder.TextEditorHolderFactory.INSTANCE);
+  }
+
+  //
+  // Actions
+  //
+
+  private class MyFocusOppositePaneAction extends FocusOppositePaneAction {
+    public MyFocusOppositePaneAction(boolean scrollToPosition) {
+      super(scrollToPosition);
+    }
+
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+      Side currentSide = getCurrentSide();
+      Side targetSide = currentSide.other();
+
+      EditorEx currentEditor = getEditor(currentSide);
+      EditorEx targetEditor = getEditor(targetSide);
+
+      if (myScrollToPosition) {
+        LogicalPosition position = transferPosition(currentSide, currentEditor.getCaretModel().getLogicalPosition());
+        targetEditor.getCaretModel().moveToLogicalPosition(position);
+      }
+
+      setCurrentSide(targetSide);
+      myContext.requestFocus();
+      currentEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
+    }
+  }
+
+  private class MyOpenInEditorWithMouseAction extends OpenInEditorWithMouseAction {
+    @Override
+    protected OpenFileDescriptor getDescriptor(@NotNull Editor editor, int line) {
+      Side side = null;
+      if (editor == getEditor(Side.LEFT)) side = Side.LEFT;
+      if (editor == getEditor(Side.RIGHT)) side = Side.RIGHT;
+      if (side == null) return null;
+
+      int offset = editor.logicalPositionToOffset(new LogicalPosition(line, 0));
+      return getContent(side).getOpenFileDescriptor(offset);
+    }
+  }
+
+  protected class MyToggleAutoScrollAction extends TextDiffViewerUtil.ToggleAutoScrollAction {
+    public MyToggleAutoScrollAction() {
+      super(getTextSettings());
+    }
+  }
+
+  //
+  // Helpers
+  //
+
+  @Nullable
+  @Override
+  public Object getData(@NonNls String dataId) {
+    if (DiffDataKeys.CURRENT_EDITOR.is(dataId)) {
+      return getCurrentEditor();
+    }
+    return super.getData(dataId);
+  }
+
+  private class MyVisibleAreaListener implements VisibleAreaListener {
+    @Override
+    public void visibleAreaChanged(VisibleAreaEvent e) {
+      if (mySyncScrollSupport != null) mySyncScrollSupport.visibleAreaChanged(e);
+      if (Registry.is("diff.divider.repainting.fix")) {
+        myContentPanel.repaint();
+      }
+      else {
+        myContentPanel.repaintDivider();
+      }
+    }
+  }
+
+  protected abstract class MyInitialScrollPositionHelper extends InitialScrollPositionSupport.TwosideInitialScrollHelper {
+    @NotNull
+    @Override
+    protected List<? extends Editor> getEditors() {
+      return TwosideTextDiffViewer.this.getEditors();
+    }
+
+    @Override
+    protected void disableSyncScroll(boolean value) {
+      disableSyncScrollSupport(value);
+    }
+
+    @Override
+    protected boolean doScrollToLine() {
+      if (myScrollToLine == null) return false;
+
+      scrollToLine(myScrollToLine.first, myScrollToLine.second);
+      return true;
+    }
+  }
+}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideTextContentPanel.java b/platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideTextContentPanel.java
deleted file mode 100644 (file)
index 73e25f7..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.diff.tools.util.threeside;
-
-import com.intellij.diff.util.Side;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.ex.EditorEx;
-import com.intellij.util.ui.ButtonlessScrollBarUI;
-import org.jetbrains.annotations.NotNull;
-
-import javax.swing.*;
-import java.util.ArrayList;
-import java.util.List;
-
-public class ThreesideTextContentPanel extends ThreesideContentPanel {
-  @NotNull private final List<? extends Editor> myEditors;
-
-  public ThreesideTextContentPanel(@NotNull List<? extends Editor> editors,
-                                   @NotNull List<JComponent> titleComponents) {
-    super(getComponents(editors), titleComponents);
-    myEditors = editors;
-  }
-
-  @NotNull
-  private static List<JComponent> getComponents(@NotNull List<? extends Editor> editors) {
-    List<JComponent> result = new ArrayList<JComponent>();
-
-    for (Editor editor : editors) {
-      result.add(editor != null ? editor.getComponent() : null);
-    }
-    return result;
-  }
-
-  public void setScrollbarPainter(@NotNull ButtonlessScrollBarUI.ScrollbarRepaintCallback painter) {
-    ((EditorEx)myEditors.get(1)).registerScrollBarRepaintCallback(painter);
-  }
-
-  @Override
-  public void repaintDivider(@NotNull Side side) {
-    super.repaintDivider(side);
-    if (side == Side.RIGHT) ((EditorEx)myEditors.get(1)).getScrollPane().getVerticalScrollBar().repaint();
-  }
-}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideTextDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/util/threeside/ThreesideTextDiffViewer.java
deleted file mode 100644 (file)
index 80372f4..0000000
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.diff.tools.util.threeside;
-
-import com.intellij.diff.DiffContext;
-import com.intellij.diff.DiffDialogHints;
-import com.intellij.diff.DiffManager;
-import com.intellij.diff.contents.DiffContent;
-import com.intellij.diff.contents.DocumentContent;
-import com.intellij.diff.requests.ContentDiffRequest;
-import com.intellij.diff.requests.DiffRequest;
-import com.intellij.diff.requests.SimpleDiffRequest;
-import com.intellij.diff.tools.util.DiffDataKeys;
-import com.intellij.diff.tools.util.FocusTrackerSupport.ThreesideFocusTrackerSupport;
-import com.intellij.diff.tools.util.SimpleDiffPanel;
-import com.intellij.diff.tools.util.SyncScrollSupport;
-import com.intellij.diff.tools.util.SyncScrollSupport.ThreesideSyncScrollSupport;
-import com.intellij.diff.tools.util.base.InitialScrollPositionSupport;
-import com.intellij.diff.tools.util.base.TextDiffViewerBase;
-import com.intellij.diff.util.DiffUtil;
-import com.intellij.diff.util.Side;
-import com.intellij.diff.util.ThreeSide;
-import com.intellij.icons.AllIcons;
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.CommonDataKeys;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.diff.DiffBundle;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.EditorFactory;
-import com.intellij.openapi.editor.event.DocumentEvent;
-import com.intellij.openapi.editor.event.VisibleAreaEvent;
-import com.intellij.openapi.editor.event.VisibleAreaListener;
-import com.intellij.openapi.editor.ex.EditorEx;
-import com.intellij.openapi.editor.ex.EditorMarkupModel;
-import com.intellij.openapi.fileEditor.OpenFileDescriptor;
-import com.intellij.openapi.project.DumbAwareAction;
-import com.intellij.openapi.util.registry.Registry;
-import com.intellij.util.containers.ContainerUtil;
-import org.jetbrains.annotations.CalledInAwt;
-import org.jetbrains.annotations.NonNls;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.util.ArrayList;
-import java.util.List;
-
-public abstract class ThreesideTextDiffViewer extends TextDiffViewerBase {
-  public static final Logger LOG = Logger.getInstance(ThreesideTextDiffViewer.class);
-
-  @NotNull private final EditorFactory myEditorFactory = EditorFactory.getInstance();
-
-  @NotNull protected final SimpleDiffPanel myPanel;
-  @NotNull protected final ThreesideTextContentPanel myContentPanel;
-
-  @NotNull private final List<EditorEx> myEditors;
-  @NotNull private final List<DocumentContent> myActualContents;
-
-  @NotNull private final MyVisibleAreaListener myVisibleAreaListener1 = new MyVisibleAreaListener(Side.LEFT);
-  @NotNull private final MyVisibleAreaListener myVisibleAreaListener2 = new MyVisibleAreaListener(Side.RIGHT);
-
-  @NotNull protected final MySetEditorSettingsAction myEditorSettingsAction;
-
-  @NotNull private final ThreesideFocusTrackerSupport myFocusTrackerSupport;
-
-  @Nullable private ThreesideSyncScrollSupport mySyncScrollSupport;
-
-  public ThreesideTextDiffViewer(@NotNull DiffContext context, @NotNull ContentDiffRequest request) {
-    super(context, request);
-
-    List<DiffContent> contents = myRequest.getContents();
-    myActualContents = ContainerUtil.newArrayList((DocumentContent)contents.get(0),
-                                                  (DocumentContent)contents.get(1),
-                                                  (DocumentContent)contents.get(2));
-
-
-    myEditors = createEditors();
-    List<JComponent> titlePanel = DiffUtil.createTextTitles(myRequest, getEditors());
-
-    myFocusTrackerSupport = new ThreesideFocusTrackerSupport(getEditors());
-    myContentPanel = new ThreesideTextContentPanel(getEditors(), titlePanel);
-
-    myPanel = new SimpleDiffPanel(myContentPanel, this, context);
-
-
-    //new MyFocusOppositePaneAction().setupAction(myPanel, this); // TODO
-
-    myEditorSettingsAction = new MySetEditorSettingsAction();
-    myEditorSettingsAction.applyDefaults();
-  }
-
-  @Override
-  @CalledInAwt
-  protected void onDispose() {
-    super.onDispose();
-    destroyEditors();
-  }
-
-  @Override
-  @CalledInAwt
-  protected void processContextHints() {
-    super.processContextHints();
-    myFocusTrackerSupport.processContextHints(myRequest, myContext);
-  }
-
-  @Override
-  @CalledInAwt
-  protected void updateContextHints() {
-    super.updateContextHints();
-    myFocusTrackerSupport.updateContextHints(myRequest, myContext);
-  }
-
-  @NotNull
-  protected List<EditorEx> createEditors() {
-    boolean[] forceReadOnly = checkForceReadOnly();
-    List<EditorEx> editors = new ArrayList<EditorEx>(3);
-
-    for (int i = 0; i < getActualContents().size(); i++) {
-      DocumentContent content = getActualContents().get(i);
-      EditorEx editor = DiffUtil.createEditor(content.getDocument(), myProject, forceReadOnly[i], true);
-      DiffUtil.configureEditor(editor, content, myProject);
-      editors.add(editor);
-
-      if (Registry.is("diff.divider.repainting.disable.blitting")) {
-        editor.getScrollPane().getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
-      }
-    }
-
-    editors.get(0).setVerticalScrollbarOrientation(EditorEx.VERTICAL_SCROLLBAR_LEFT);
-    ((EditorMarkupModel)editors.get(1).getMarkupModel()).setErrorStripeVisible(false);
-
-    return editors;
-  }
-
-  private void destroyEditors() {
-    for (EditorEx editor : getEditors()) {
-      myEditorFactory.releaseEditor(editor);
-    }
-  }
-
-  //
-  // Listeners
-  //
-
-  @CalledInAwt
-  @Override
-  protected void installEditorListeners() {
-    super.installEditorListeners();
-
-    getEditor(ThreeSide.LEFT).getScrollingModel().addVisibleAreaListener(myVisibleAreaListener1);
-    getEditor(ThreeSide.BASE).getScrollingModel().addVisibleAreaListener(myVisibleAreaListener1);
-
-    getEditor(ThreeSide.BASE).getScrollingModel().addVisibleAreaListener(myVisibleAreaListener2);
-    getEditor(ThreeSide.RIGHT).getScrollingModel().addVisibleAreaListener(myVisibleAreaListener2);
-
-    SyncScrollSupport.SyncScrollable scrollable1 = getSyncScrollable(Side.LEFT);
-    SyncScrollSupport.SyncScrollable scrollable2 = getSyncScrollable(Side.RIGHT);
-    if (scrollable1 != null && scrollable2 != null) {
-      mySyncScrollSupport = new ThreesideSyncScrollSupport(getEditors(), scrollable1, scrollable2);
-    }
-  }
-
-  @CalledInAwt
-  @Override
-  public void destroyEditorListeners() {
-    super.destroyEditorListeners();
-
-    getEditor(ThreeSide.LEFT).getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener1);
-    getEditor(ThreeSide.BASE).getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener1);
-
-    getEditor(ThreeSide.BASE).getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener2);
-    getEditor(ThreeSide.RIGHT).getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener2);
-
-    mySyncScrollSupport = null;
-  }
-
-  protected void disableSyncScrollSupport(boolean disable) {
-    if (mySyncScrollSupport != null) {
-      mySyncScrollSupport.setDisabled(disable);
-    }
-  }
-
-  //
-  // Diff
-  //
-
-  @Override
-  protected void onDocumentChange(@NotNull DocumentEvent event) {
-    super.onDocumentChange(event);
-
-    myContentPanel.repaintDividers();
-  }
-
-  //
-  // Getters
-  //
-
-  @NotNull
-  @Override
-  public JComponent getComponent() {
-    return myPanel;
-  }
-
-  @Nullable
-  @Override
-  public JComponent getPreferredFocusedComponent() {
-    return getCurrentEditor().getContentComponent();
-  }
-
-  @NotNull
-  public EditorEx getCurrentEditor() {
-    return getEditor(getCurrentSide());
-  }
-
-  @NotNull
-  public DocumentContent getCurrentContent() {
-    return getCurrentSide().select(getActualContents());
-  }
-
-  @NotNull
-  @Override
-  protected List<? extends EditorEx> getEditors() {
-    return myEditors;
-  }
-
-  @NotNull
-  protected EditorEx getEditor(@NotNull ThreeSide side) {
-    return side.select(myEditors);
-  }
-
-  @NotNull
-  public List<DocumentContent> getActualContents() {
-    return myActualContents;
-  }
-
-  @NotNull
-  public ThreeSide getCurrentSide() {
-    return myFocusTrackerSupport.getCurrentSide();
-  }
-
-  public void setCurrentSide(@NotNull ThreeSide side) {
-    myFocusTrackerSupport.setCurrentSide(side);
-  }
-
-  //
-  // Abstract
-  //
-
-  @CalledInAwt
-  protected void scrollToLine(@NotNull ThreeSide side, int line) {
-    Editor editor = getEditor(side);
-    DiffUtil.scrollEditor(editor, line, false);
-    setCurrentSide(side);
-  }
-
-  @Nullable
-  protected abstract SyncScrollSupport.SyncScrollable getSyncScrollable(@NotNull Side side);
-
-  //
-  // Misc
-  //
-
-  @Nullable
-  @Override
-  protected OpenFileDescriptor getOpenFileDescriptor() {
-    EditorEx editor = getCurrentEditor();
-
-    int offset = editor.getCaretModel().getOffset();
-    return getCurrentContent().getOpenFileDescriptor(offset);
-  }
-
-  public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
-    if (!(request instanceof ContentDiffRequest)) return false;
-
-    List<DiffContent> contents = ((ContentDiffRequest)request).getContents();
-    if (contents.size() != 3) return false;
-
-    boolean canShow = true;
-    boolean wantShow = false;
-    for (DiffContent content : contents) {
-      canShow &= canShowContent(content);
-      wantShow |= wantShowContent(content);
-    }
-    return canShow && wantShow;
-  }
-
-  public static boolean canShowContent(@NotNull DiffContent content) {
-    if (content instanceof DocumentContent) return true;
-    return false;
-  }
-
-  public static boolean wantShowContent(@NotNull DiffContent content) {
-    if (content instanceof DocumentContent) return true;
-    return false;
-  }
-
-  //
-  // Actions
-  //
-
-  protected class ShowLeftBasePartialDiffAction extends ShowPartialDiffAction {
-    public ShowLeftBasePartialDiffAction() {
-      super(ThreeSide.LEFT, ThreeSide.BASE, DiffBundle.message("merge.partial.diff.action.name.0.1"), null, AllIcons.Diff.LeftDiff);
-    }
-  }
-
-  protected class ShowBaseRightPartialDiffAction extends ShowPartialDiffAction {
-    public ShowBaseRightPartialDiffAction() {
-      super(ThreeSide.BASE, ThreeSide.RIGHT, DiffBundle.message("merge.partial.diff.action.name.1.2"), null, AllIcons.Diff.RightDiff);
-    }
-  }
-
-  protected class ShowLeftRightPartialDiffAction extends ShowPartialDiffAction {
-    public ShowLeftRightPartialDiffAction() {
-      super(ThreeSide.LEFT, ThreeSide.RIGHT, DiffBundle.message("merge.partial.diff.action.name"), null, AllIcons.Diff.BranchDiff);
-    }
-  }
-
-  protected class ShowPartialDiffAction extends DumbAwareAction {
-    @NotNull private final ThreeSide mySide1;
-    @NotNull private final ThreeSide mySide2;
-
-    public ShowPartialDiffAction(@NotNull ThreeSide side1, @NotNull ThreeSide side2,
-                                 @NotNull String text, @Nullable String description, @NotNull Icon icon) {
-      super(text, description, icon);
-      mySide1 = side1;
-      mySide2 = side2;
-    }
-
-    @Override
-    public void actionPerformed(AnActionEvent e) {
-      List<DiffContent> contents = myRequest.getContents();
-      List<String> titles = myRequest.getContentTitles();
-
-      DiffRequest request = new SimpleDiffRequest(myRequest.getTitle(),
-                                                  mySide1.select(contents), mySide2.select(contents),
-                                                  mySide1.select(titles), mySide2.select(titles));
-      DiffManager.getInstance().showDiff(myProject, request, new DiffDialogHints(null, myPanel));
-    }
-  }
-
-  //
-  // Helpers
-  //
-
-  @Nullable
-  @Override
-  public Object getData(@NonNls String dataId) {
-    if (DiffDataKeys.CURRENT_EDITOR.is(dataId)) {
-      return getCurrentEditor();
-    }
-    else if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) {
-      return DiffUtil.getVirtualFile(myRequest, getCurrentSide());
-    }
-    else if (DiffDataKeys.CURRENT_CONTENT.is(dataId)) {
-      return getCurrentContent();
-    }
-    return super.getData(dataId);
-  }
-  
-  private class MyVisibleAreaListener implements VisibleAreaListener {
-    @NotNull Side mySide;
-
-    public MyVisibleAreaListener(@NotNull Side side) {
-      mySide = side;
-    }
-
-    @Override
-    public void visibleAreaChanged(VisibleAreaEvent e) {
-      if (mySyncScrollSupport != null) mySyncScrollSupport.visibleAreaChanged(e);
-      if (Registry.is("diff.divider.repainting.fix")) {
-        myContentPanel.repaint();
-      }
-      else {
-        myContentPanel.repaintDivider(mySide);
-      }
-    }
-  }
-
-  protected abstract class MyInitialScrollPositionHelper extends InitialScrollPositionSupport.ThreesideInitialScrollHelper {
-    @NotNull
-    @Override
-    protected List<? extends Editor> getEditors() {
-      return ThreesideTextDiffViewer.this.getEditors();
-    }
-
-    @Override
-    protected void disableSyncScroll(boolean value) {
-      disableSyncScrollSupport(value);
-    }
-
-    @Override
-    protected boolean doScrollToLine() {
-      if (myScrollToLine == null) return false;
-      ThreeSide side = myScrollToLine.first;
-      Integer line = myScrollToLine.second;
-      if (getEditor(side) == null) return false;
-
-      scrollToLine(side, line);
-      return true;
-    }
-  }
-}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideContentPanel.java b/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideContentPanel.java
deleted file mode 100644 (file)
index c1752b4..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.diff.tools.util.twoside;
-
-import com.intellij.diff.tools.util.DiffSplitter;
-import org.jetbrains.annotations.CalledInAwt;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.awt.*;
-import java.util.List;
-
-public class TwosideContentPanel extends JPanel {
-  @Nullable private final DiffSplitter mySplitter;
-
-  public TwosideContentPanel(@NotNull List<JComponent> titleComponents,
-                             @Nullable JComponent editor1,
-                             @Nullable JComponent editor2) {
-    super(new BorderLayout());
-    assert titleComponents.size() == 2;
-
-    if (editor1 != null && editor2 != null) {
-      mySplitter = new DiffSplitter();
-      mySplitter.setFirstComponent(new MyPanel(editor1, titleComponents.get(0)));
-      mySplitter.setSecondComponent(new MyPanel(editor2, titleComponents.get(1)));
-      mySplitter.setHonorComponentsMinimumSize(false);
-      add(mySplitter, BorderLayout.CENTER);
-    }
-    else {
-      mySplitter = null;
-      if (editor1 != null) {
-        add(new MyPanel(editor1, titleComponents.get(0)), BorderLayout.CENTER);
-      }
-      else if (editor2 != null) {
-        add(new MyPanel(editor2, titleComponents.get(1)), BorderLayout.CENTER);
-      }
-    }
-  }
-
-  @CalledInAwt
-  public void setPainter(@Nullable DiffSplitter.Painter painter) {
-    if (mySplitter != null) mySplitter.setPainter(painter);
-  }
-
-  public void repaintDivider() {
-    if (mySplitter != null) mySplitter.repaintDivider();
-  }
-
-  @Nullable
-  public DiffSplitter getSplitter() {
-    return mySplitter;
-  }
-
-  private static class MyPanel extends JPanel {
-    public MyPanel(@NotNull JComponent editor, @Nullable JComponent title) {
-      super(new BorderLayout());
-      add(editor, BorderLayout.CENTER);
-      if (title != null) add(title, BorderLayout.NORTH);
-    }
-  }
-}
diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/util/twoside/TwosideTextDiffViewer.java
deleted file mode 100644 (file)
index 61a692e..0000000
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.diff.tools.util.twoside;
-
-import com.intellij.diff.DiffContext;
-import com.intellij.diff.actions.impl.FocusOppositePaneAction;
-import com.intellij.diff.actions.impl.OpenInEditorWithMouseAction;
-import com.intellij.diff.contents.DiffContent;
-import com.intellij.diff.contents.DocumentContent;
-import com.intellij.diff.contents.EmptyContent;
-import com.intellij.diff.requests.ContentDiffRequest;
-import com.intellij.diff.requests.DiffRequest;
-import com.intellij.diff.tools.util.DiffDataKeys;
-import com.intellij.diff.tools.util.FocusTrackerSupport.TwosideFocusTrackerSupport;
-import com.intellij.diff.tools.util.SimpleDiffPanel;
-import com.intellij.diff.tools.util.SyncScrollSupport;
-import com.intellij.diff.tools.util.SyncScrollSupport.TwosideSyncScrollSupport;
-import com.intellij.diff.tools.util.base.InitialScrollPositionSupport;
-import com.intellij.diff.tools.util.base.TextDiffViewerBase;
-import com.intellij.diff.util.DiffUtil;
-import com.intellij.diff.util.Side;
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.CommonDataKeys;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.editor.EditorFactory;
-import com.intellij.openapi.editor.LogicalPosition;
-import com.intellij.openapi.editor.ScrollType;
-import com.intellij.openapi.editor.event.DocumentEvent;
-import com.intellij.openapi.editor.event.VisibleAreaEvent;
-import com.intellij.openapi.editor.event.VisibleAreaListener;
-import com.intellij.openapi.editor.ex.EditorEx;
-import com.intellij.openapi.fileEditor.OpenFileDescriptor;
-import com.intellij.openapi.util.registry.Registry;
-import com.intellij.util.containers.ContainerUtil;
-import org.jetbrains.annotations.CalledInAwt;
-import org.jetbrains.annotations.NonNls;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.util.List;
-
-public abstract class TwosideTextDiffViewer extends TextDiffViewerBase {
-  public static final Logger LOG = Logger.getInstance(TwosideTextDiffViewer.class);
-
-  @NotNull private final EditorFactory myEditorFactory = EditorFactory.getInstance();
-
-  @NotNull protected final SimpleDiffPanel myPanel;
-  @NotNull protected final TwosideTextContentPanel myContentPanel;
-
-  @Nullable private final EditorEx myEditor1;
-  @Nullable private final EditorEx myEditor2;
-
-  @Nullable private final DocumentContent myActualContent1;
-  @Nullable private final DocumentContent myActualContent2;
-
-  @NotNull protected final MySetEditorSettingsAction myEditorSettingsAction;
-
-  @NotNull private final MyVisibleAreaListener myVisibleAreaListener = new MyVisibleAreaListener();
-
-  @NotNull private final TwosideFocusTrackerSupport myFocusTrackerSupport;
-
-  @Nullable protected TwosideSyncScrollSupport mySyncScrollSupport;
-
-  public TwosideTextDiffViewer(@NotNull DiffContext context, @NotNull ContentDiffRequest request) {
-    super(context, request);
-
-    List<DiffContent> contents = myRequest.getContents();
-    myActualContent1 = contents.get(0) instanceof DocumentContent ? ((DocumentContent)contents.get(0)) : null;
-    myActualContent2 = contents.get(1) instanceof DocumentContent ? ((DocumentContent)contents.get(1)) : null;
-    assert myActualContent1 != null || myActualContent2 != null;
-
-
-    List<EditorEx> editors = createEditors();
-    List<JComponent> titlePanel = DiffUtil.createTextTitles(myRequest, editors);
-
-    myEditor1 = editors.get(0);
-    myEditor2 = editors.get(1);
-    assert myEditor1 != null || myEditor2 != null;
-
-    myFocusTrackerSupport = new TwosideFocusTrackerSupport(getEditor1(), getEditor2());
-    myContentPanel = new TwosideTextContentPanel(titlePanel, getEditor1(), getEditor2());
-
-    myPanel = new SimpleDiffPanel(myContentPanel, this, context);
-
-
-    new MyFocusOppositePaneAction(true).setupAction(myPanel);
-    new MyFocusOppositePaneAction(false).setupAction(myPanel);
-
-    myEditorSettingsAction = new MySetEditorSettingsAction();
-    myEditorSettingsAction.applyDefaults();
-
-    new MyOpenInEditorWithMouseAction().register(getEditors());
-  }
-
-  @Override
-  @CalledInAwt
-  protected void onDispose() {
-    super.onDispose();
-    destroyEditors();
-  }
-
-  @Override
-  @CalledInAwt
-  protected void processContextHints() {
-    super.processContextHints();
-    myFocusTrackerSupport.processContextHints(myRequest, myContext);
-  }
-
-  @Override
-  @CalledInAwt
-  protected void updateContextHints() {
-    super.updateContextHints();
-    myFocusTrackerSupport.updateContextHints(myRequest, myContext);
-  }
-
-  @NotNull
-  protected List<EditorEx> createEditors() {
-    boolean[] forceReadOnly = checkForceReadOnly();
-
-    // TODO: we may want to set editor highlighter in init() to speedup editor initialization
-    EditorEx editor1 = null;
-    EditorEx editor2 = null;
-    if (getActualContent1() != null) {
-      editor1 = DiffUtil.createEditor(getActualContent1().getDocument(), myProject, forceReadOnly[0], true);
-      DiffUtil.configureEditor(editor1, getActualContent1(), myProject);
-    }
-    if (getActualContent2() != null) {
-      editor2 = DiffUtil.createEditor(getActualContent2().getDocument(), myProject, forceReadOnly[1], true);
-      DiffUtil.configureEditor(editor2, getActualContent2(), myProject);
-    }
-    if (editor1 != null && editor2 != null) {
-      editor1.setVerticalScrollbarOrientation(EditorEx.VERTICAL_SCROLLBAR_LEFT);
-    }
-    if (Registry.is("diff.divider.repainting.disable.blitting")) {
-      if (editor1 != null) editor1.getScrollPane().getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
-      if (editor2 != null) editor2.getScrollPane().getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
-    }
-
-    return ContainerUtil.newArrayList(editor1, editor2);
-  }
-
-  //
-  // Diff
-  //
-
-  @Override
-  protected void onDocumentChange(@NotNull DocumentEvent event) {
-    super.onDocumentChange(event);
-    myContentPanel.repaintDivider();
-  }
-
-  //
-  // Listeners
-  //
-
-  private void destroyEditors() {
-    if (getEditor1() != null) myEditorFactory.releaseEditor(getEditor1());
-    if (getEditor2() != null) myEditorFactory.releaseEditor(getEditor2());
-  }
-
-  @CalledInAwt
-  @Override
-  protected void installEditorListeners() {
-    super.installEditorListeners();
-    if (getEditor1() != null) {
-      getEditor1().getScrollingModel().addVisibleAreaListener(myVisibleAreaListener);
-    }
-    if (getEditor2() != null) {
-      getEditor2().getScrollingModel().addVisibleAreaListener(myVisibleAreaListener);
-    }
-    if (getEditor1() != null && getEditor2() != null) {
-      SyncScrollSupport.SyncScrollable scrollable = getSyncScrollable();
-      if (scrollable != null) {
-        mySyncScrollSupport = new TwosideSyncScrollSupport(getEditor1(), getEditor2(), scrollable);
-      }
-    }
-  }
-
-  @CalledInAwt
-  @Override
-  protected void destroyEditorListeners() {
-    super.destroyEditorListeners();
-    if (getEditor1() != null) {
-      getEditor1().getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener);
-    }
-    if (getEditor2() != null) {
-      getEditor2().getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener);
-    }
-    mySyncScrollSupport = null;
-  }
-
-  protected void disableSyncScrollSupport(boolean disable) {
-    if (mySyncScrollSupport != null) {
-      mySyncScrollSupport.setDisabled(disable);
-    }
-  }
-
-  //
-  // Getters
-  //
-
-  @NotNull
-  @Override
-  protected List<? extends EditorEx> getEditors() {
-    return ContainerUtil.list(getEditor1(), getEditor2());
-  }
-
-  @NotNull
-  @Override
-  public JComponent getComponent() {
-    return myPanel;
-  }
-
-  @Nullable
-  @Override
-  public JComponent getPreferredFocusedComponent() {
-    return getCurrentEditor().getContentComponent();
-  }
-
-  @NotNull
-  public Side getCurrentSide() {
-    return myFocusTrackerSupport.getCurrentSide();
-  }
-
-  public void setCurrentSide(@NotNull Side side) {
-    myFocusTrackerSupport.setCurrentSide(side);
-  }
-
-  @NotNull
-  public EditorEx getCurrentEditor() {
-    //noinspection ConstantConditions
-    return getEditor(getCurrentSide());
-  }
-
-  @NotNull
-  public DocumentContent getCurrentContent() {
-    //noinspection ConstantConditions
-    return getActualContent(getCurrentSide());
-  }
-
-  @Nullable
-  protected EditorEx getEditor1() {
-    return myEditor1;
-  }
-
-  @Nullable
-  protected EditorEx getEditor2() {
-    return myEditor2;
-  }
-
-  @Nullable
-  protected EditorEx getEditor(@NotNull Side side) {
-    return side.select(myEditor1, myEditor2);
-  }
-
-  @Nullable
-  public DocumentContent getActualContent1() {
-    return myActualContent1;
-  }
-
-  @Nullable
-  public DocumentContent getActualContent2() {
-    return myActualContent2;
-  }
-
-  @Nullable
-  protected DocumentContent getActualContent(@NotNull Side side) {
-    return side.select(myActualContent1, myActualContent2);
-  }
-
-  //
-  // Abstract
-  //
-
-  @CalledInAwt
-  @NotNull
-  protected LogicalPosition transferPosition(@NotNull Side baseSide, @NotNull LogicalPosition position) {
-    if (mySyncScrollSupport == null) return position;
-    int line = mySyncScrollSupport.getScrollable().transfer(baseSide, position.line);
-    return new LogicalPosition(line, position.column);
-  }
-
-  @CalledInAwt
-  protected void scrollToLine(@NotNull Side side, int line) {
-    Editor editor = getEditor(side);
-    if (editor == null) return;
-    DiffUtil.scrollEditor(editor, line, false);
-    setCurrentSide(side);
-  }
-
-  @Nullable
-  protected abstract SyncScrollSupport.SyncScrollable getSyncScrollable();
-
-  //
-  // Misc
-  //
-
-  @Nullable
-  @Override
-  protected OpenFileDescriptor getOpenFileDescriptor() {
-    EditorEx editor = getCurrentEditor();
-
-    int offset = editor.getCaretModel().getOffset();
-    return getCurrentContent().getOpenFileDescriptor(offset);
-  }
-
-  public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
-    if (!(request instanceof ContentDiffRequest)) return false;
-
-    List<DiffContent> contents = ((ContentDiffRequest)request).getContents();
-    if (contents.size() != 2) return false;
-
-    boolean canShow = true;
-    boolean wantShow = false;
-    for (DiffContent content : contents) {
-      canShow &= canShowContent(content);
-      wantShow |= wantShowContent(content);
-    }
-    return canShow && wantShow;
-  }
-
-  public static boolean canShowContent(@NotNull DiffContent content) {
-    if (content instanceof EmptyContent) return true;
-    if (content instanceof DocumentContent) return true;
-    return false;
-  }
-
-  public static boolean wantShowContent(@NotNull DiffContent content) {
-    if (content instanceof DocumentContent) return true;
-    return false;
-  }
-
-  //
-  // Actions
-  //
-
-  private class MyFocusOppositePaneAction extends FocusOppositePaneAction {
-    public MyFocusOppositePaneAction(boolean scrollToPosition) {
-      super(scrollToPosition);
-    }
-
-    @Override
-    public void actionPerformed(@NotNull AnActionEvent e) {
-      if (getEditor1() == null || getEditor2() == null) return;
-
-      if (myScrollToPosition) {
-        EditorEx currentEditor = getCurrentSide().select(getEditor1(), getEditor2());
-        EditorEx targetEditor = getCurrentSide().other().select(getEditor1(), getEditor2());
-        LogicalPosition position = transferPosition(getCurrentSide(), currentEditor.getCaretModel().getLogicalPosition());
-        targetEditor.getCaretModel().moveToLogicalPosition(position);
-      }
-
-      setCurrentSide(getCurrentSide().other());
-      myContext.requestFocus();
-      getCurrentEditor().getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
-    }
-  }
-
-  private class MyOpenInEditorWithMouseAction extends OpenInEditorWithMouseAction {
-    @Override
-    protected OpenFileDescriptor getDescriptor(@NotNull Editor editor, int line) {
-      if (editor != getEditor1() && editor != getEditor2()) return null;
-      Side side = Side.fromLeft(editor == getEditor1());
-
-      DocumentContent content = getActualContent(side);
-      if (content == null) return null;
-
-      int offset = editor.logicalPositionToOffset(new LogicalPosition(line, 0));
-
-      return content.getOpenFileDescriptor(offset);
-    }
-  }
-
-  //
-  // Helpers
-  //
-
-  @Nullable
-  @Override
-  public Object getData(@NonNls String dataId) {
-    if (DiffDataKeys.CURRENT_EDITOR.is(dataId)) {
-      return getCurrentEditor();
-    }
-    else if (DiffDataKeys.CURRENT_CONTENT.is(dataId)) {
-      return getCurrentContent();
-    }
-    else if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) {
-      return DiffUtil.getVirtualFile(myRequest, getCurrentSide());
-    }
-
-    return super.getData(dataId);
-  }
-
-  private class MyVisibleAreaListener implements VisibleAreaListener {
-    @Override
-    public void visibleAreaChanged(VisibleAreaEvent e) {
-      if (mySyncScrollSupport != null) mySyncScrollSupport.visibleAreaChanged(e);
-      if (Registry.is("diff.divider.repainting.fix")) {
-        myContentPanel.repaint();
-      }
-      else {
-        myContentPanel.repaintDivider();
-      }
-    }
-  }
-
-  protected abstract class MyInitialScrollPositionHelper extends InitialScrollPositionSupport.TwosideInitialScrollHelper {
-    @NotNull
-    @Override
-    protected List<? extends Editor> getEditors() {
-      return TwosideTextDiffViewer.this.getEditors();
-    }
-
-    @Override
-    protected void disableSyncScroll(boolean value) {
-      disableSyncScrollSupport(value);
-    }
-
-    @Override
-    protected boolean doScrollToLine() {
-      if (myScrollToLine == null) return false;
-      Side side = myScrollToLine.first;
-      Integer line = myScrollToLine.second;
-      if (getEditor(side) == null) return false;
-
-      scrollToLine(side, line);
-      return true;
-    }
-  }
-}
index 709db25a0160b5f9607495e33571abb74bdee5ef..0980b40cbe405a5670305a527900d2e013c617d7 100644 (file)
@@ -47,6 +47,7 @@ import com.intellij.openapi.diff.impl.external.DiffManagerImpl;
 import com.intellij.openapi.editor.*;
 import com.intellij.openapi.editor.colors.EditorColors;
 import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.event.DocumentEvent;
 import com.intellij.openapi.editor.ex.EditorEx;
 import com.intellij.openapi.editor.ex.EditorMarkupModel;
 import com.intellij.openapi.editor.ex.util.EmptyEditorHighlighter;
@@ -686,6 +687,62 @@ public class DiffUtil {
     return Math.max(document.getLineCount(), 1);
   }
 
+  //
+  // Updating ranges on change
+  //
+
+  public static int countLinesShift(@NotNull DocumentEvent e) {
+    return StringUtil.countNewLines(e.getNewFragment()) - StringUtil.countNewLines(e.getOldFragment());
+  }
+
+  @NotNull
+  public static UpdatedLineRange updateRangeOnModification(int start, int end, int changeStart, int changeEnd, int shift) {
+    return updateRangeOnModification(start, end, changeStart, changeEnd, shift, false);
+  }
+
+  @NotNull
+  public static UpdatedLineRange updateRangeOnModification(int start, int end, int changeStart, int changeEnd, int shift, boolean greedy) {
+    if (end <= changeStart) { // change before
+      return new UpdatedLineRange(start, end, false);
+    }
+    if (start >= changeEnd) { // change after
+      return new UpdatedLineRange(start + shift, end + shift, false);
+    }
+
+    if (start <= changeStart && end >= changeEnd) { // change inside
+      return new UpdatedLineRange(start, end + shift, false);
+    }
+
+    // range is damaged. We don't know new boundaries.
+    // But we can try to return approximate new position
+    int newChangeEnd = changeEnd + shift;
+
+    if (start >= changeStart && end <= changeEnd) { // fully inside change
+      return greedy ? new UpdatedLineRange(changeStart, newChangeEnd, true) :
+                      new UpdatedLineRange(newChangeEnd, newChangeEnd, true);
+    }
+
+    if (start < changeStart) { // bottom boundary damaged
+      return greedy ? new UpdatedLineRange(start, newChangeEnd, true) :
+                      new UpdatedLineRange(start, changeStart, true);
+    } else { // top boundary damaged
+      return greedy ? new UpdatedLineRange(changeStart, end + shift, true) :
+                      new UpdatedLineRange(newChangeEnd, end + shift, true);
+    }
+  }
+
+  public static class UpdatedLineRange {
+    public final int startLine;
+    public final int endLine;
+    public final boolean damaged;
+
+    public UpdatedLineRange(int startLine, int endLine, boolean damaged) {
+      this.startLine = startLine;
+      this.endLine = endLine;
+      this.damaged = damaged;
+    }
+  }
+
   //
   // Types
   //
similarity index 98%
rename from platform/diff-impl/tests/com/intellij/diff/tools/fragmented/OnesideFragmentBuilderAutoTest.java
rename to platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderAutoTest.java
index 666b5214b4ab47afc900f94489514d2f055c452b..6d3b4c18232104f1cd5438b8164a1c1f6db442b5 100644 (file)
@@ -33,7 +33,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
-public class OnesideFragmentBuilderAutoTest extends AutoTestCase {
+public class UnifiedFragmentBuilderAutoTest extends AutoTestCase {
   private static ComparisonManager myComparisonManager = new ComparisonManagerImpl();
 
   private static final int CHAR_COUNT = 12;
@@ -91,7 +91,7 @@ public class OnesideFragmentBuilderAutoTest extends AutoTestCase {
     List<LineFragment> fragments = myComparisonManager.compareLinesInner(sequence1, sequence2,
                                                                          policy, DumbProgressIndicator.INSTANCE);
 
-    OnesideFragmentBuilder builder = new OnesideFragmentBuilder(fragments, document1, document2, masterSide);
+    UnifiedFragmentBuilder builder = new UnifiedFragmentBuilder(fragments, document1, document2, masterSide);
     builder.exec();
 
     boolean ignoreWhitespaces = policy != ComparisonPolicy.DEFAULT;
similarity index 93%
rename from platform/diff-impl/tests/com/intellij/diff/tools/fragmented/OnesideFragmentBuilderTest.java
rename to platform/diff-impl/tests/com/intellij/diff/tools/fragmented/UnifiedFragmentBuilderTest.java
index 359e1a52028fc1991f7dfa6901404e6a99d18444..dadb6a9e6e30be94f84bc3cea55199395ec0572f 100644 (file)
@@ -29,7 +29,7 @@ import com.intellij.testFramework.UsefulTestCase;
 import java.util.Collections;
 import java.util.List;
 
-public class OnesideFragmentBuilderTest extends UsefulTestCase {
+public class UnifiedFragmentBuilderTest extends UsefulTestCase {
   private static ComparisonManager myComparisonManager = new ComparisonManagerImpl();
 
   public void testEquals() {
@@ -39,7 +39,7 @@ public class OnesideFragmentBuilderTest extends UsefulTestCase {
     List<LineFragment> fragments = myComparisonManager.compareLinesInner(document1.getCharsSequence(), document2.getCharsSequence(),
                                                                          ComparisonPolicy.DEFAULT, DumbProgressIndicator.INSTANCE);
 
-    OnesideFragmentBuilder builder = new OnesideFragmentBuilder(fragments, document1, document2, Side.LEFT);
+    UnifiedFragmentBuilder builder = new UnifiedFragmentBuilder(fragments, document1, document2, Side.LEFT);
     builder.exec();
 
     assertTrue(builder.isEqual());
@@ -55,7 +55,7 @@ public class OnesideFragmentBuilderTest extends UsefulTestCase {
     List<LineFragment> fragments = myComparisonManager.compareLinesInner(document1.getCharsSequence(), document2.getCharsSequence(),
                                                                          ComparisonPolicy.DEFAULT, DumbProgressIndicator.INSTANCE);
 
-    OnesideFragmentBuilder builder = new OnesideFragmentBuilder(fragments, document1, document2, Side.RIGHT);
+    UnifiedFragmentBuilder builder = new UnifiedFragmentBuilder(fragments, document1, document2, Side.RIGHT);
     builder.exec();
 
     assertFalse(builder.isEqual());
index 7f3275ee0e2b28341c950b4f370bea1423ac9521..23f8c063697b082bf87041171a3e49ff62fe0f63 100644 (file)
@@ -284,11 +284,11 @@ public class ChangeDiffRequestProducer implements DiffRequestProducer {
         String title = DiffRequestFactory.getInstance().getTitle(file);
         List<String> titles = ContainerUtil.list(beforeRevisionTitle, "Base Version", afterRevisionTitle);
 
-        // Yep, we hope that it's a text file. And that charset wasn't changed.
+        DiffContentFactory contentFactory = DiffContentFactory.getInstance();
         List<DiffContent> contents = ContainerUtil.list(
-          createTextContent(mergeData.CURRENT, file),
-          createTextContent(mergeData.ORIGINAL, file),
-          createTextContent(mergeData.LAST, file)
+          contentFactory.createFromBytes(project, file, mergeData.CURRENT),
+          contentFactory.createFromBytes(project, file, mergeData.ORIGINAL),
+          contentFactory.createFromBytes(project, file, mergeData.LAST)
         );
 
         return new SimpleDiffRequest(title, contents, titles);
@@ -297,6 +297,10 @@ public class ChangeDiffRequestProducer implements DiffRequestProducer {
         LOG.info(e);
         throw new DiffRequestProducerException(e);
       }
+      catch (IOException e) {
+        LOG.info(e);
+        throw new DiffRequestProducerException(e);
+      }
     }
     else {
       ContentRevision bRev = change.getBeforeRevision();
index b651f102fc60fc0af922428535510369ca9d0a56..6b5e93a541de79a515800609aa5a921fadb5ccf9 100644 (file)
@@ -13,7 +13,7 @@ import com.intellij.diff.fragments.LineFragmentImpl;
 import com.intellij.diff.requests.ContentDiffRequest;
 import com.intellij.diff.tools.util.DiffSplitter;
 import com.intellij.diff.tools.util.SyncScrollSupport;
-import com.intellij.diff.tools.util.twoside.TwosideTextDiffViewer;
+import com.intellij.diff.tools.util.side.TwosideTextDiffViewer;
 import com.intellij.diff.util.*;
 import com.intellij.openapi.actionSystem.PlatformDataKeys;
 import com.intellij.openapi.editor.Document;
@@ -69,8 +69,6 @@ public class SvnPropertiesDiffViewer extends TwosideTextDiffViewer {
     myWrapperRequest = request;
     myDiffChanges = diffChanges;
 
-    assert getEditor1() != null && getEditor2() != null;
-
     for (EditorEx editor : getEditors()) {
       if (editor == null) continue;
       EditorSettings settings = editor.getSettings();
@@ -91,12 +89,11 @@ public class SvnPropertiesDiffViewer extends TwosideTextDiffViewer {
     }
 
     for (DiffChange change : myDiffChanges) {
-      DiffDrawUtil.createBorderLineMarker(getEditor1(), change.myEndLine1, SeparatorPlacement.TOP);
-      DiffDrawUtil.createBorderLineMarker(getEditor2(), change.myEndLine2, SeparatorPlacement.TOP);
+      DiffDrawUtil.createBorderLineMarker(getEditor(Side.LEFT), change.myEndLine1, SeparatorPlacement.TOP);
+      DiffDrawUtil.createBorderLineMarker(getEditor(Side.RIGHT), change.myEndLine2, SeparatorPlacement.TOP);
     }
 
     DiffSplitter splitter = myContentPanel.getSplitter();
-    assert splitter != null;
     splitter.setDividerWidth(120);
     splitter.setShowDividerIcon(false);
   }
@@ -107,12 +104,6 @@ public class SvnPropertiesDiffViewer extends TwosideTextDiffViewer {
     myContentPanel.setPainter(new MyDividerPainter());
   }
 
-  @NotNull
-  @Override
-  protected boolean[] checkForceReadOnly() {
-    return new boolean[]{true, true};
-  }
-
   @NotNull
   @Override
   protected Runnable performRediff(@NotNull ProgressIndicator indicator) {
@@ -147,20 +138,20 @@ public class SvnPropertiesDiffViewer extends TwosideTextDiffViewer {
     return new Runnable() {
       @Override
       public void run() {
-        assert getEditor1() != null && getEditor2() != null;
         for (DiffChange change : myDiffChanges) {
-          setupHighlighting(getEditor1(), change, Side.LEFT);
-          setupHighlighting(getEditor2(), change, Side.RIGHT);
+          setupHighlighting(change, Side.LEFT);
+          setupHighlighting(change, Side.RIGHT);
         }
       }
     };
   }
 
-  private static void setupHighlighting(@NotNull EditorEx editor, @NotNull DiffChange change, @NotNull Side side) {
+  private void setupHighlighting(@NotNull DiffChange change, @NotNull Side side) {
     PropertyRecord record = change.getRecord();
     List<? extends LineFragment> fragments = change.getFragments();
     assert fragments != null;
 
+    EditorEx editor = getEditor(side);
     DocumentEx document = editor.getDocument();
     int shift = document.getLineStartOffset(change.getStartLine(side));
 
@@ -206,7 +197,6 @@ public class SvnPropertiesDiffViewer extends TwosideTextDiffViewer {
 
     @Override
     public void paint(@NotNull Graphics g, @NotNull JComponent divider) {
-      assert getEditor1() != null && getEditor2() != null;
       Graphics2D gg = DiffDividerDrawUtil.getDividerGraphics(g, divider, getEditor1().getComponent());
       Rectangle clip = gg.getClipBounds();
       if (clip == null) return;
@@ -214,23 +204,26 @@ public class SvnPropertiesDiffViewer extends TwosideTextDiffViewer {
       gg.setColor(DiffDrawUtil.getDividerColor());
       gg.fill(clip);
 
-      JComponent header1 = getEditor1().getHeaderComponent();
-      JComponent header2 = getEditor2().getHeaderComponent();
+      EditorEx editor1 = getEditor1();
+      EditorEx editor2 = getEditor2();
+
+      JComponent header1 = editor1.getHeaderComponent();
+      JComponent header2 = editor2.getHeaderComponent();
       int headerOffset1 = header1 == null ? 0 : header1.getHeight();
       int headerOffset2 = header2 == null ? 0 : header2.getHeight();
 
       // TODO: painting is ugly if shift1 != shift2 (ex: search field is opened for one of editors)
-      int shift1 = getEditor1().getScrollingModel().getVerticalScrollOffset() - headerOffset1;
-      int shift2 = getEditor2().getScrollingModel().getVerticalScrollOffset() - headerOffset2;
+      int shift1 = editor1.getScrollingModel().getVerticalScrollOffset() - headerOffset1;
+      int shift2 = editor2.getScrollingModel().getVerticalScrollOffset() - headerOffset2;
       double rotate = shift1 == shift2 ? 0 : Math.atan2(shift2 - shift1, clip.width);
 
-      DiffDividerDrawUtil.paintPolygons(gg, divider.getWidth(), false, rotate == 0, getEditor1(), getEditor2(), this);
+      DiffDividerDrawUtil.paintPolygons(gg, divider.getWidth(), false, rotate == 0, editor1, editor2, this);
 
       for (DiffChange change : myDiffChanges) {
-        int y1 = getEditor1().logicalPositionToXY(new LogicalPosition(change.getStartLine(Side.LEFT), 0)).y - shift1;
-        int y2 = getEditor2().logicalPositionToXY(new LogicalPosition(change.getStartLine(Side.RIGHT), 0)).y - shift2;
-        int endY1 = getEditor1().logicalPositionToXY(new LogicalPosition(change.getEndLine(Side.LEFT), 0)).y - shift1;
-        int endY2 = getEditor2().logicalPositionToXY(new LogicalPosition(change.getEndLine(Side.RIGHT), 0)).y - shift2;
+        int y1 = editor1.logicalPositionToXY(new LogicalPosition(change.getStartLine(Side.LEFT), 0)).y - shift1;
+        int y2 = editor2.logicalPositionToXY(new LogicalPosition(change.getStartLine(Side.RIGHT), 0)).y - shift2;
+        int endY1 = editor1.logicalPositionToXY(new LogicalPosition(change.getEndLine(Side.LEFT), 0)).y - shift1;
+        int endY2 = editor2.logicalPositionToXY(new LogicalPosition(change.getEndLine(Side.RIGHT), 0)).y - shift2;
 
         AffineTransform oldTransform = gg.getTransform();
         gg.translate(0, y1);
@@ -439,6 +432,8 @@ public class SvnPropertiesDiffViewer extends TwosideTextDiffViewer {
       myContent1 = DiffContentFactory.getInstance().create(null, document1);
       myContent2 = DiffContentFactory.getInstance().create(null, document2);
       myEmbedded = embedded;
+
+      putUserData(DiffUserDataKeys.FORCE_READ_ONLY, true);
     }
 
     @NotNull