diff: use new API to show diff for shelved changes
authorAleksey Pivovarov <AMPivovarov@gmail.com>
Mon, 17 Aug 2015 17:10:59 +0000 (20:10 +0300)
committerAleksey Pivovarov <AMPivovarov@gmail.com>
Wed, 19 Aug 2015 17:36:18 +0000 (20:36 +0300)
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchAction.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/MergedDiffRequestPresentable.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/PatchDiffRequestFactory.java [new file with mode: 0644]
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java

index 71f90779a6322d4a60cf8d29932bc7261f63cfd4..08c553cf8669a49b122b090a32fc36979f548c06 100644 (file)
 package com.intellij.openapi.vcs.changes.patch;
 
 import com.intellij.diff.*;
+import com.intellij.diff.chains.DiffRequestProducerException;
 import com.intellij.diff.contents.DiffContent;
 import com.intellij.diff.contents.DocumentContent;
 import com.intellij.diff.merge.MergeRequest;
 import com.intellij.diff.merge.MergeResult;
 import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.requests.SimpleDiffRequest;
 import com.intellij.diff.util.DiffUtil;
 import com.intellij.openapi.actionSystem.ActionPlaces;
 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.diff.DiffTool;
-import com.intellij.openapi.diff.SimpleContent;
-import com.intellij.openapi.diff.SimpleDiffRequest;
 import com.intellij.openapi.diff.impl.patch.*;
 import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatch;
 import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase;
 import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.impl.DocumentImpl;
 import com.intellij.openapi.extensions.Extensions;
 import com.intellij.openapi.fileChooser.FileChooser;
 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
@@ -61,7 +59,6 @@ import com.intellij.openapi.vcs.VcsBundle;
 import com.intellij.openapi.vcs.changes.ChangeListManager;
 import com.intellij.openapi.vcs.changes.CommitContext;
 import com.intellij.openapi.vfs.LocalFileSystem;
-import com.intellij.openapi.vfs.VfsUtil;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.ui.EditorNotificationPanel;
 import com.intellij.util.Consumer;
@@ -276,7 +273,7 @@ public class ApplyPatchAction extends DumbAwareAction {
 
     final List<DiffContent> contents = ContainerUtil.list(originalContent, mergedContent);
 
-    final DiffRequest request = new com.intellij.diff.requests.SimpleDiffRequest(windowTitle, contents, titles);
+    final DiffRequest request = new SimpleDiffRequest(windowTitle, contents, titles);
     DiffUtil.addNotification(new DiffIsApproximateNotification(), request);
 
     final DiffDialogHints dialogHints = new DiffDialogHints(WindowWrapper.Mode.MODAL);
@@ -309,61 +306,26 @@ public class ApplyPatchAction extends DumbAwareAction {
   }
 
   @NotNull
-  public static SimpleDiffRequest createBadDiffRequest(@Nullable final Project project,
-                                                       @NotNull final VirtualFile file,
-                                                       @NotNull ApplyPatchForBaseRevisionTexts texts,
-                                                       boolean readonly) {
-    final String fullPath = file.getParent() == null ? file.getPath() : file.getParent().getPath();
-    final String title = "Result Of Patch Apply To " + file.getName() + " (" + fullPath + ")";
+  public static DiffRequest createBadDiffRequest(@NotNull final VirtualFile file,
+                                                 @NotNull final ApplyPatchForBaseRevisionTexts texts) throws DiffRequestProducerException {
+    if (texts.getLocal() == null) {
+      throw new DiffRequestProducerException("Can't show diff for '" + file.getPresentableUrl() + "'");
+    }
 
-    final SimpleDiffRequest simpleRequest = new SimpleDiffRequest(project, title);
-    final DocumentImpl patched = new DocumentImpl(texts.getPatched());
-    patched.setReadOnly(false);
+    final String fullPath = file.getParent() == null ? file.getPath() : file.getParent().getPath();
+    final String windowTitle = "Result Of Patch Apply To " + file.getName() + " (" + fullPath + ")";
+    final List<String> titles = ContainerUtil.list(VcsBundle.message("diff.title.local"), "Patched (with problems)");
 
-    final com.intellij.openapi.diff.DocumentContent mergedContent =
-      new com.intellij.openapi.diff.DocumentContent(project, patched, file.getFileType());
-    mergedContent.getDocument().setReadOnly(readonly);
-    final SimpleContent originalContent = new SimpleContent(texts.getLocal().toString(), file.getFileType());
+    final DiffContentFactory contentFactory = DiffContentFactory.getInstance();
+    final DocumentContent originalContent = contentFactory.create(texts.getLocal().toString(), file.getFileType());
+    final DiffContent mergedContent = contentFactory.create(texts.getPatched(), file.getFileType());
 
-    simpleRequest.setContents(originalContent, mergedContent);
-    simpleRequest.setContentTitles(VcsBundle.message("diff.title.local"), "Patched (with problems)");
-    simpleRequest.addHint(DiffTool.HINT_SHOW_MODAL_DIALOG);
-    simpleRequest.addHint(DiffTool.HINT_DIFF_IS_APPROXIMATE);
+    final List<DiffContent> contents = ContainerUtil.list(originalContent, mergedContent);
 
-    if (!readonly) {
-      simpleRequest.setOnOkRunnable(new Runnable() {
-        @Override
-        public void run() {
-          ApplicationManager.getApplication().runWriteAction(new Runnable() {
-            @Override
-            public void run() {
-              final String resultText = mergedContent.getDocument().getText();
-              final Document document = FileDocumentManager.getInstance().getDocument(file);
-              if (document == null) {
-                try {
-                  VfsUtil.saveText(file, resultText);
-                }
-                catch (IOException e) {
-                  // todo bad: we had already returned success by now
-                  showIOException(project, file.getName(), e);
-                }
-              }
-              else {
-                document.setText(resultText);
-                FileDocumentManager.getInstance().saveDocument(document);
-              }
-            }
-          });
-        }
-      });
-    }
-    return simpleRequest;
-  }
+    final DiffRequest request = new SimpleDiffRequest(windowTitle, contents, titles);
+    DiffUtil.addNotification(new DiffIsApproximateNotification(), request);
 
-  private static void showIOException(@Nullable Project project, @NotNull String name, @NotNull IOException e) {
-    Messages.showErrorDialog(project,
-                             VcsBundle.message("patch.apply.error", name, e.getMessage()),
-                             VcsBundle.message("patch.apply.dialog.title"));
+    return request;
   }
 
   private static class DiffIsApproximateNotification extends EditorNotificationPanel {
index dddc8c8f7a97d19dcc54782ffe6177e0938948f7..3bc8615d5f3f7c74a8ffd2d8c8e66a23f4b40194 100644 (file)
 package com.intellij.openapi.vcs.changes.patch;
 
 import com.intellij.openapi.actionSystem.AnAction;
-import com.intellij.openapi.diff.DiffRequestFactory;
-import com.intellij.openapi.diff.MergeRequest;
-import com.intellij.openapi.diff.SimpleDiffRequest;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diff.*;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.impl.DocumentImpl;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.util.Getter;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.vcs.VcsBundle;
 import com.intellij.openapi.vcs.changes.actions.*;
+import com.intellij.openapi.vfs.VfsUtil;
 import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
+import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
 
@@ -35,7 +42,10 @@ public class MergedDiffRequestPresentable implements DiffRequestPresentable {
   private final String myAfterTitle;
   private final Getter<ApplyPatchForBaseRevisionTexts> myTexts;
 
-  public MergedDiffRequestPresentable(final Project project, final Getter<ApplyPatchForBaseRevisionTexts> texts, final VirtualFile file, final String afterTitle) {
+  public MergedDiffRequestPresentable(final Project project,
+                                      final Getter<ApplyPatchForBaseRevisionTexts> texts,
+                                      final VirtualFile file,
+                                      final String afterTitle) {
     myTexts = texts;
     myProject = project;
     myFile = file;
@@ -45,14 +55,14 @@ public class MergedDiffRequestPresentable implements DiffRequestPresentable {
   public MyResult step(DiffChainContext context) {
     if (myFile.getFileType().isBinary()) {
       final boolean nowItIsText = ChangeDiffRequestPresentable.checkAssociate(myProject, myFile.getName(), context);
-      if (! nowItIsText) {
+      if (!nowItIsText) {
         final SimpleDiffRequest request = new SimpleDiffRequest(myProject, null);
         return new MyResult(request, DiffPresentationReturnValue.removeFromList);
       }
     }
     final ApplyPatchForBaseRevisionTexts revisionTexts = myTexts.get();
     if (revisionTexts.getBase() == null) {
-      final SimpleDiffRequest badDiffRequest = ApplyPatchAction.createBadDiffRequest(myProject, myFile, revisionTexts, true);
+      final SimpleDiffRequest badDiffRequest = createBadDiffRequest(myProject, myFile, revisionTexts, true);
       return new MyResult(badDiffRequest, DiffPresentationReturnValue.useRequest);
     }
     final MergeRequest request = DiffRequestFactory.getInstance()
@@ -61,7 +71,7 @@ public class MergedDiffRequestPresentable implements DiffRequestPresentable {
                              revisionTexts.getBase().toString(),
                              myFile.getFileType(), myProject, null, null);
     request.setWindowTitle(VcsBundle.message("patch.apply.conflict.title", FileUtil.toSystemDependentName(myFile.getPresentableUrl())));
-    request.setVersionTitles(new String[] {"Current Version", "Base Version", FileUtil.toSystemDependentName(myAfterTitle)});
+    request.setVersionTitles(new String[]{"Current Version", "Base Version", FileUtil.toSystemDependentName(myAfterTitle)});
     return new MyResult(request, DiffPresentationReturnValue.useRequest);
   }
 
@@ -76,4 +86,62 @@ public class MergedDiffRequestPresentable implements DiffRequestPresentable {
   public List<? extends AnAction> createActions(DiffExtendUIFactory uiFactory) {
     return Collections.emptyList();
   }
+
+  @NotNull
+  public static SimpleDiffRequest createBadDiffRequest(@Nullable final Project project,
+                                                       @NotNull final VirtualFile file,
+                                                       @NotNull ApplyPatchForBaseRevisionTexts texts,
+                                                       boolean readonly) {
+    final String fullPath = file.getParent() == null ? file.getPath() : file.getParent().getPath();
+    final String title = "Result Of Patch Apply To " + file.getName() + " (" + fullPath + ")";
+
+    final SimpleDiffRequest simpleRequest = new SimpleDiffRequest(project, title);
+    final DocumentImpl patched = new DocumentImpl(texts.getPatched());
+    patched.setReadOnly(false);
+
+    final DocumentContent mergedContent =
+      new DocumentContent(project, patched, file.getFileType());
+    mergedContent.getDocument().setReadOnly(readonly);
+    final SimpleContent originalContent = new SimpleContent(texts.getLocal().toString(), file.getFileType());
+
+    simpleRequest.setContents(originalContent, mergedContent);
+    simpleRequest.setContentTitles(VcsBundle.message("diff.title.local"), "Patched (with problems)");
+    simpleRequest.addHint(DiffTool.HINT_SHOW_MODAL_DIALOG);
+    simpleRequest.addHint(DiffTool.HINT_DIFF_IS_APPROXIMATE);
+
+    if (!readonly) {
+      simpleRequest.setOnOkRunnable(new Runnable() {
+        @Override
+        public void run() {
+          ApplicationManager.getApplication().runWriteAction(new Runnable() {
+            @Override
+            public void run() {
+              final String resultText = mergedContent.getDocument().getText();
+              final Document document = FileDocumentManager.getInstance().getDocument(file);
+              if (document == null) {
+                try {
+                  VfsUtil.saveText(file, resultText);
+                }
+                catch (IOException e) {
+                  // todo bad: we had already returned success by now
+                  showIOException(project, file.getName(), e);
+                }
+              }
+              else {
+                document.setText(resultText);
+                FileDocumentManager.getInstance().saveDocument(document);
+              }
+            }
+          });
+        }
+      });
+    }
+    return simpleRequest;
+  }
+
+  private static void showIOException(@Nullable Project project, @NotNull String name, @NotNull IOException e) {
+    Messages.showErrorDialog(project,
+                             VcsBundle.message("patch.apply.error", name, e.getMessage()),
+                             VcsBundle.message("patch.apply.dialog.title"));
+  }
 }
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/PatchDiffRequestFactory.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/PatchDiffRequestFactory.java
new file mode 100644 (file)
index 0000000..27cd16a
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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.openapi.vcs.changes.patch;
+
+import com.intellij.diff.DiffContentFactory;
+import com.intellij.diff.chains.DiffRequestProducerException;
+import com.intellij.diff.contents.DocumentContent;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.diff.requests.SimpleDiffRequest;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Getter;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.UserDataHolder;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vcs.VcsBundle;
+import com.intellij.openapi.vcs.changes.Change;
+import com.intellij.openapi.vcs.changes.actions.diff.ChangeDiffRequestProducer;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class PatchDiffRequestFactory {
+  @NotNull
+  public static DiffRequest createFromChange(@Nullable Project project,
+                                             @NotNull Change change,
+                                             @NotNull String name,
+                                             @NotNull UserDataHolder context,
+                                             @NotNull ProgressIndicator indicator)
+    throws DiffRequestProducerException {
+    ChangeDiffRequestProducer proxyProducer = ChangeDiffRequestProducer.create(project, change);
+    if (proxyProducer == null) throw new DiffRequestProducerException("Can't show diff for '" + name + "'");
+    return proxyProducer.process(context, indicator);
+  }
+
+  public static DiffRequest createConflict(@Nullable Project project,
+                                           @Nullable VirtualFile file,
+                                           @NotNull String afterTitle,
+                                           @NotNull final Getter<ApplyPatchForBaseRevisionTexts> textsGetter,
+                                           @NotNull String name,
+                                           @NotNull UserDataHolder context,
+                                           @NotNull ProgressIndicator indicator)
+    throws DiffRequestProducerException {
+    if (file == null) throw new DiffRequestProducerException("Can't show diff for '" + name + "'");
+    if (file.getFileType().isBinary()) throw new DiffRequestProducerException("Can't show diff for binary file '" + name + "'");
+
+    final Ref<ApplyPatchForBaseRevisionTexts> textsRef = new Ref<ApplyPatchForBaseRevisionTexts>();
+    ApplicationManager.getApplication().invokeAndWait(new Runnable() {
+      @Override
+      public void run() {
+        textsRef.set(textsGetter.get());
+      }
+    }, indicator.getModalityState());
+    ApplyPatchForBaseRevisionTexts texts = textsRef.get();
+
+    if (texts.getBase() == null) {
+      return ApplyPatchAction.createBadDiffRequest(file, texts);
+    }
+    else {
+      DiffContentFactory contentFactory = DiffContentFactory.getInstance();
+      String path = FileUtil.toSystemDependentName(file.getPresentableUrl());
+      FileType type = file.getFileType();
+
+      String windowTitle = VcsBundle.message("patch.apply.conflict.title", path);
+
+      DocumentContent localContent = contentFactory.create(texts.getLocal().toString(), type);
+      DocumentContent baseContent = contentFactory.create(texts.getBase().toString(), type);
+      DocumentContent patchedContent = contentFactory.create(texts.getPatched(), type);
+
+      return new SimpleDiffRequest(windowTitle, localContent, baseContent, patchedContent,
+                                   "Current Version", "Base Version", afterTitle);
+    }
+  }
+}
+
index eb201b2bee231cb3be15932b35d1e0bd2292bf1e..1defa4924cbe76c0e94fdec3ca217d43eebf9fac 100644 (file)
  */
 package com.intellij.openapi.vcs.changes.shelf;
 
-import com.intellij.openapi.actionSystem.*;
+import com.intellij.diff.DiffDialogHints;
+import com.intellij.diff.DiffManager;
+import com.intellij.diff.actions.impl.GoToChangePopupBuilder;
+import com.intellij.diff.chains.DiffRequestChain;
+import com.intellij.diff.chains.DiffRequestProducer;
+import com.intellij.diff.chains.DiffRequestProducerException;
+import com.intellij.diff.requests.DiffRequest;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.diff.impl.patch.*;
-import com.intellij.openapi.diff.impl.patch.apply.ApplyTextFilePatch;
+import com.intellij.openapi.diff.impl.patch.apply.ApplyFilePatchBase;
 import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.progress.ProgressIndicator;
 import com.intellij.openapi.project.DumbAware;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.MessageType;
 import com.intellij.openapi.util.Getter;
+import com.intellij.openapi.util.UserDataHolder;
+import com.intellij.openapi.util.UserDataHolderBase;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vcs.FilePath;
 import com.intellij.openapi.vcs.FileStatus;
 import com.intellij.openapi.vcs.VcsException;
-import com.intellij.openapi.vcs.changes.*;
-import com.intellij.openapi.vcs.changes.actions.*;
+import com.intellij.openapi.vcs.changes.Change;
+import com.intellij.openapi.vcs.changes.ChangeListManager;
+import com.intellij.openapi.vcs.changes.CommitContext;
+import com.intellij.openapi.vcs.changes.FilePathsHelper;
+import com.intellij.openapi.vcs.changes.actions.diff.ChangeGoToChangePopupAction;
 import com.intellij.openapi.vcs.changes.patch.ApplyPatchForBaseRevisionTexts;
-import com.intellij.openapi.vcs.changes.patch.MergedDiffRequestPresentable;
-import com.intellij.openapi.vcs.changes.ui.ChangesComparator;
+import com.intellij.openapi.vcs.changes.patch.PatchDiffRequestFactory;
 import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
 import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.Consumer;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.vcsUtil.VcsUtil;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.*;
 
-/**
- * @author yole
- */
 public class DiffShelvedChangesAction extends AnAction implements DumbAware {
+  public void update(final AnActionEvent e) {
+    e.getPresentation().setEnabled(isEnabled(e.getDataContext()));
+  }
+
   public void actionPerformed(final AnActionEvent e) {
     showShelvedChangesDiff(e.getDataContext());
   }
 
+  public static boolean isEnabled(final DataContext dc) {
+    final Project project = CommonDataKeys.PROJECT.getData(dc);
+    if (project == null) return false;
+
+    ShelvedChangeList[] changeLists = ShelvedChangesViewManager.SHELVED_CHANGELIST_KEY.getData(dc);
+    if (changeLists == null) changeLists = ShelvedChangesViewManager.SHELVED_RECYCLED_CHANGELIST_KEY.getData(dc);
+    if (changeLists == null || changeLists.length != 1) return false;
+
+    return true;
+  }
+
   public static void showShelvedChangesDiff(final DataContext dc) {
     final Project project = CommonDataKeys.PROJECT.getData(dc);
     if (project == null) return;
     if (ChangeListManager.getInstance(project).isFreezedWithNotification(null)) return;
 
     ShelvedChangeList[] changeLists = ShelvedChangesViewManager.SHELVED_CHANGELIST_KEY.getData(dc);
-    if (changeLists == null) {
-      changeLists = ShelvedChangesViewManager.SHELVED_RECYCLED_CHANGELIST_KEY.getData(dc);
-    }
+    if (changeLists == null) changeLists = ShelvedChangesViewManager.SHELVED_RECYCLED_CHANGELIST_KEY.getData(dc);
+    if (changeLists == null || changeLists.length != 1) return;
 
-    if (changeLists == null) return;
-    final List<ShelvedChange> changesFromFirstList = changeLists[0].getChanges(project);
+    final List<ShelvedChange> textChanges = changeLists[0].getChanges(project);
+    final List<ShelvedBinaryFile> binaryChanges = changeLists[0].getBinaryFiles();
 
-    final ArrayList<DiffRequestPresentable> diffRequestPresentables = new ArrayList<DiffRequestPresentable>();
-    final ApplyPatchContext context = new ApplyPatchContext(project.getBaseDir(), 0, false, false);
-    final PatchesPreloader preloader = new PatchesPreloader(project);
+    final List<MyDiffRequestProducer> diffRequestProducers = new ArrayList<MyDiffRequestProducer>();
 
-    final List<String> missing = new LinkedList<String>();
-    processTextChanges(project, changesFromFirstList, diffRequestPresentables, context, preloader, missing);
-    final List<ShelvedBinaryFile> files = changeLists[0].getBinaryFiles();
-    processBinaryFiles(project, files, diffRequestPresentables);
-    if (! missing.isEmpty()) {
-      // 7-8
-      VcsBalloonProblemNotifier.showOverChangesView(project, "Show Diff: Cannot find base for: " + StringUtil.join(missing, ",\n"), MessageType.WARNING);
-    }
+    processTextChanges(project, textChanges, diffRequestProducers);
+    processBinaryFiles(project, binaryChanges, diffRequestProducers);
 
-    Collections.sort(diffRequestPresentables, ChangeDiffRequestComparator.getInstance());
+    Collections.sort(diffRequestProducers, ChangeDiffRequestComparator.getInstance());
 
     // selected changes inside lists
-    final Set<String> selectedPaths = new HashSet<String>();
-    final List<ShelvedChange> shelvedChanges = ShelvedChangesViewManager.SHELVED_CHANGE_KEY.getData(dc);
-    final List<ShelvedBinaryFile> binaryFiles = ShelvedChangesViewManager.SHELVED_BINARY_FILE_KEY.getData(dc);
-    for (ShelvedChange change : shelvedChanges) {
-      selectedPaths.add(FilePathsHelper.convertPath(ChangesUtil.getFilePath(change.getChange(project)).getPath()));
-    }
-    for (ShelvedBinaryFile file : binaryFiles) {
-      selectedPaths.add(FilePathsHelper.convertPath(ChangesUtil.getFilePath(file.createChange(project))));
-    }
-    int idx = 0;
-    for (DiffRequestPresentable presentable : diffRequestPresentables) {
-      final String path = FilePathsHelper.convertPath(presentable.getPathPresentation());
-      if (selectedPaths.contains(path)) {
+    final Set<Object> selectedChanges = new HashSet<Object>();
+    selectedChanges.addAll(ContainerUtil.notNullize(ShelvedChangesViewManager.SHELVED_CHANGE_KEY.getData(dc)));
+    selectedChanges.addAll(ContainerUtil.notNullize(ShelvedChangesViewManager.SHELVED_BINARY_FILE_KEY.getData(dc)));
+
+    int index = 0;
+    for (int i = 0; i < diffRequestProducers.size(); i++) {
+      MyDiffRequestProducer producer = diffRequestProducers.get(i);
+      if (selectedChanges.contains(producer.getBinaryChange()) || selectedChanges.contains(producer.getTextChange())) {
+        index = i;
         break;
       }
-      ++ idx;
     }
-    idx = idx >= diffRequestPresentables.size() ? 0 : idx;
-    ShowDiffAction.showDiffImpl(project, diffRequestPresentables, idx, new ShowDiffUIContext(true));
+
+    MyDiffRequestChain chain = new MyDiffRequestChain(diffRequestProducers, index);
+    DiffManager.getInstance().showDiff(project, chain, DiffDialogHints.FRAME);
   }
 
-  private static class ChangeDiffRequestComparator implements Comparator<DiffRequestPresentable> {
+  private static class ChangeDiffRequestComparator implements Comparator<DiffRequestProducer> {
     private final static ChangeDiffRequestComparator ourInstance = new ChangeDiffRequestComparator();
 
     public static ChangeDiffRequestComparator getInstance() {
@@ -107,96 +127,112 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware {
     }
 
     @Override
-    public int compare(DiffRequestPresentable o1, DiffRequestPresentable o2) {
-      return FilePathsHelper.convertPath(o1.getPathPresentation()).compareTo(FilePathsHelper.convertPath(o2.getPathPresentation()));
+    public int compare(DiffRequestProducer o1, DiffRequestProducer o2) {
+      return FilePathsHelper.convertPath(o1.getName()).compareTo(FilePathsHelper.convertPath(o2.getName()));
     }
   }
 
-  private static void processBinaryFiles(final Project project,
-                                         List<ShelvedBinaryFile> files,
-                                         ArrayList<DiffRequestPresentable> diffRequestPresentables) {
+  private static void processBinaryFiles(@NotNull final Project project,
+                                         @NotNull List<ShelvedBinaryFile> files,
+                                         @NotNull List<MyDiffRequestProducer> diffRequestProducers) {
     final String base = project.getBaseDir().getPath();
-    for (final ShelvedBinaryFile file : files) {
-      diffRequestPresentables.add(new DiffRequestPresentableProxy() {
+    for (final ShelvedBinaryFile shelvedChange : files) {
+      diffRequestProducers.add(new MyDiffRequestProducer(shelvedChange) {
         @NotNull
         @Override
-        public DiffRequestPresentable init() throws VcsException {
-          return new ChangeDiffRequestPresentable(project, file.createChange(project));
+        public DiffRequest process(@NotNull UserDataHolder context, @NotNull ProgressIndicator indicator)
+          throws DiffRequestProducerException, ProcessCanceledException {
+          Change change = shelvedChange.createChange(project);
+          return PatchDiffRequestFactory.createFromChange(project, change, getName(), context, indicator);
         }
 
+        @NotNull
         @Override
-        public String getPathPresentation() {
-          final File file1 = new File(base, file.AFTER_PATH == null ? file.BEFORE_PATH : file.AFTER_PATH);
-          return FileUtil.toSystemDependentName(file1.getPath());
+        public FilePath getFilePath() {
+          final File file = new File(base, shelvedChange.AFTER_PATH == null ? shelvedChange.BEFORE_PATH : shelvedChange.AFTER_PATH);
+          return VcsUtil.getFilePath(file);
         }
       });
     }
   }
 
-  private static void processTextChanges(final Project project,
-                                         List<ShelvedChange> changesFromFirstList,
-                                         ArrayList<DiffRequestPresentable> diffRequestPresentables,
-                                         final ApplyPatchContext context,
-                                         final PatchesPreloader preloader,
-                                         List<String> missing) {
+  private static void processTextChanges(@NotNull final Project project,
+                                         @NotNull List<ShelvedChange> changesFromFirstList,
+                                         @NotNull List<MyDiffRequestProducer> diffRequestProducers) {
     final String base = project.getBasePath();
+    final List<String> missing = new LinkedList<String>();
+
+    final ApplyPatchContext patchContext = new ApplyPatchContext(project.getBaseDir(), 0, false, false);
+    final PatchesPreloader preloader = new PatchesPreloader(project);
+
     for (final ShelvedChange shelvedChange : changesFromFirstList) {
       final String beforePath = shelvedChange.getBeforePath();
+      final String afterPath = shelvedChange.getAfterPath();
+
+      final VirtualFile file;
       try {
-        final VirtualFile f = ApplyTextFilePatch
-          .findPatchTarget(context, beforePath, shelvedChange.getAfterPath(), FileStatus.ADDED.equals(shelvedChange.getFileStatus()));
-        if ((! FileStatus.ADDED.equals(shelvedChange.getFileStatus())) && ((f == null) || (! f.exists()))) {
-          if (beforePath != null) {
-            missing.add(beforePath);
-          }
-          continue;
+        boolean isNewFile = FileStatus.ADDED.equals(shelvedChange.getFileStatus());
+        file = ApplyFilePatchBase.findPatchTarget(patchContext, beforePath, afterPath, isNewFile);
+        if (!isNewFile && (file == null || !file.exists())) throw new FileNotFoundException(beforePath);
+      }
+      catch (IOException e) {
+        if (beforePath != null) missing.add(beforePath);
+        continue;
+      }
+
+      diffRequestProducers.add(new MyDiffRequestProducer(shelvedChange) {
+        @NotNull
+        @Override
+        public FilePath getFilePath() {
+          File file = new File(base, shelvedChange.getAfterPath() == null ? shelvedChange.getBeforePath() : shelvedChange.getAfterPath());
+          return VcsUtil.getFilePath(file);
         }
 
-        diffRequestPresentables.add(new DiffRequestPresentableProxy() {
-          @NotNull
-          @Override
-          public DiffRequestPresentable init() throws VcsException {
-            if (shelvedChange.isConflictingChange(project)) {
+        @NotNull
+        @Override
+        public DiffRequest process(@NotNull UserDataHolder context, @NotNull ProgressIndicator indicator)
+          throws DiffRequestProducerException, ProcessCanceledException {
+          if (shelvedChange.isConflictingChange(project)) {
+            try {
               final CommitContext commitContext = new CommitContext();
               final TextFilePatch patch = preloader.getPatch(shelvedChange, commitContext);
-              final FilePath pathBeforeRename = context.getPathBeforeRename(f);
+              final FilePath pathBeforeRename = patchContext.getPathBeforeRename(file);
               final String relativePath = patch.getAfterName() == null ? patch.getBeforeName() : patch.getAfterName();
-              final Getter<ApplyPatchForBaseRevisionTexts> revisionTextsGetter = new Getter<ApplyPatchForBaseRevisionTexts>() {
+
+              final Getter<CharSequence> baseContentGetter = new Getter<CharSequence>() {
+                @Override
+                public CharSequence get() {
+                  BaseRevisionTextPatchEP baseRevisionTextPatchEP =
+                    Extensions.findExtension(PatchEP.EP_NAME, project, BaseRevisionTextPatchEP.class);
+                  return baseRevisionTextPatchEP.provideContent(relativePath, commitContext);
+                }
+              };
+
+              Getter<ApplyPatchForBaseRevisionTexts> getter = new Getter<ApplyPatchForBaseRevisionTexts>() {
                 @Override
                 public ApplyPatchForBaseRevisionTexts get() {
-                  return ApplyPatchForBaseRevisionTexts.create(project, f, pathBeforeRename, patch,
-                                                          new Getter<CharSequence>() {
-                                                            @Override
-                                                            public CharSequence get() {
-                                                              final BaseRevisionTextPatchEP
-                                                                baseRevisionTextPatchEP = Extensions
-                                                                .findExtension(PatchEP.EP_NAME, project, BaseRevisionTextPatchEP.class);
-                                                              if (baseRevisionTextPatchEP != null && commitContext != null) {
-                                                                return baseRevisionTextPatchEP.provideContent(relativePath, commitContext);
-                                                              }
-                                                              return null;
-                                                            }
-                                                          });
+                  return ApplyPatchForBaseRevisionTexts.create(project, file, pathBeforeRename, patch, baseContentGetter);
                 }
               };
-              return new MergedDiffRequestPresentable(project, revisionTextsGetter, f, "Shelved Version");
+
+              return PatchDiffRequestFactory.createConflict(project, file, "Shelved Version", getter, getName(), context, indicator);
             }
-            else {
-              final Change change = shelvedChange.getChange(project);
-              return new ChangeDiffRequestPresentable(project, change);
+            catch (VcsException e) {
+              throw new DiffRequestProducerException("Can't show diff for '" + getName() + "'", e);
             }
           }
-
-          @Override
-          public String getPathPresentation() {
-            return FileUtil.toSystemDependentName(
-              new File(base, shelvedChange.getAfterPath() == null ? shelvedChange.getBeforePath() : shelvedChange.getAfterPath()).getPath());
+          else {
+            final Change change = shelvedChange.getChange(project);
+            return PatchDiffRequestFactory.createFromChange(project, change, getName(), context, indicator);
           }
-        });
-      }
-      catch (IOException e) {
-        continue;
-      }
+        }
+      });
+    }
+
+    if (!missing.isEmpty()) {
+      // 7-8
+      VcsBalloonProblemNotifier.showOverChangesView(project, "Show Diff: Cannot find base for: " + StringUtil.join(missing, ",\n"),
+                                                    MessageType.WARNING);
     }
   }
 
@@ -233,19 +269,93 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware {
     }
   }
 
-  private final static class MyComparator implements Comparator<ShelvedChange> {
-    private final Project myProject;
+  private static class MyDiffRequestChain extends UserDataHolderBase implements DiffRequestChain, GoToChangePopupBuilder.Chain {
+    @NotNull private final List<MyDiffRequestProducer> myProducers;
+    private int myIndex = 0;
 
-    public MyComparator(Project project) {
-      myProject = project;
+    public MyDiffRequestChain(@NotNull List<MyDiffRequestProducer> producers, int index) {
+      myProducers = producers;
+      myIndex = index;
     }
 
-    public int compare(final ShelvedChange o1, final ShelvedChange o2) {
-      return ChangesComparator.getInstance(true).compare(o1.getChange(myProject), o2.getChange(myProject));
+    @NotNull
+    @Override
+    public List<? extends DiffRequestProducer> getRequests() {
+      return myProducers;
+    }
+
+    @Override
+    public int getIndex() {
+      return myIndex;
+    }
+
+    @Override
+    public void setIndex(int index) {
+      assert index >= 0 && index < myProducers.size();
+      myIndex = index;
+    }
+
+    @NotNull
+    @Override
+    public AnAction createGoToChangeAction(@NotNull Consumer<Integer> onSelected) {
+      return new ChangeGoToChangePopupAction.Fake<MyDiffRequestChain>(this, myIndex, onSelected) {
+        @NotNull
+        @Override
+        protected FilePath getFilePath(int index) {
+          return myProducers.get(index).getFilePath();
+        }
+
+        @NotNull
+        @Override
+        protected FileStatus getFileStatus(int index) {
+          return myProducers.get(index).getFileStatus();
+        }
+      };
     }
   }
 
-  public void update(final AnActionEvent e) {
-    ActionManager.getInstance().getAction("ChangesView.Diff").update(e);
+  private static abstract class MyDiffRequestProducer implements DiffRequestProducer {
+    @Nullable private final ShelvedChange myTextChange;
+    @Nullable private final ShelvedBinaryFile myBinaryChange;
+
+    public MyDiffRequestProducer(@NotNull ShelvedChange textChange) {
+      myBinaryChange = null;
+      myTextChange = textChange;
+    }
+
+    public MyDiffRequestProducer(@NotNull ShelvedBinaryFile binaryChange) {
+      myBinaryChange = binaryChange;
+      myTextChange = null;
+    }
+
+    @Nullable
+    public ShelvedChange getTextChange() {
+      return myTextChange;
+    }
+
+    @Nullable
+    public ShelvedBinaryFile getBinaryChange() {
+      return myBinaryChange;
+    }
+
+    @NotNull
+    @Override
+    public String getName() {
+      return FileUtil.toSystemDependentName(getFilePath().getPath());
+    }
+
+    @NotNull
+    protected FileStatus getFileStatus() {
+      if (myTextChange != null) {
+        return myTextChange.getFileStatus();
+      }
+      else {
+        assert myBinaryChange != null;
+        return myBinaryChange.getFileStatus();
+      }
+    }
+
+    @NotNull
+    protected abstract FilePath getFilePath();
   }
 }