diff: keep vcs annotations on swithing between viewers/requests
[idea/community.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / actions / AnnotateDiffViewerAction.java
index d348ae3a37e373de5f1796badd730e2873a1f41f..6ad2424ebfbeb8e7853500c7cb38c6f6a2616994 100644 (file)
@@ -15,6 +15,8 @@
  */
 package com.intellij.openapi.vcs.actions;
 
+import com.intellij.diff.DiffContext;
+import com.intellij.diff.DiffExtension;
 import com.intellij.diff.FrameDiffTool.DiffViewer;
 import com.intellij.diff.contents.DiffContent;
 import com.intellij.diff.contents.FileContent;
@@ -23,12 +25,14 @@ import com.intellij.diff.requests.DiffRequest;
 import com.intellij.diff.tools.fragmented.UnifiedDiffViewer;
 import com.intellij.diff.tools.util.DiffDataKeys;
 import com.intellij.diff.tools.util.base.DiffViewerBase;
+import com.intellij.diff.tools.util.base.DiffViewerListener;
 import com.intellij.diff.tools.util.side.OnesideTextDiffViewer;
 import com.intellij.diff.tools.util.side.TwosideTextDiffViewer;
 import com.intellij.diff.util.Side;
 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.editor.Editor;
 import com.intellij.openapi.localVcs.UpToDateLineNumberProvider;
 import com.intellij.openapi.progress.ProgressIndicator;
@@ -36,6 +40,7 @@ import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.progress.Task;
 import com.intellij.openapi.project.DumbAwareAction;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.vcs.*;
 import com.intellij.openapi.vcs.annotate.AnnotationProvider;
@@ -54,6 +59,11 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public class AnnotateDiffViewerAction extends DumbAwareAction {
+  public static final Logger LOG = Logger.getInstance(AnnotateDiffViewerAction.class);
+
+  private static final Key<AnnotationData[]> CACHE_KEY = Key.create("Diff.AnnotateAction.Cache");
+  private static final Key<boolean[]> ANNOTATIONS_SHOWN_KEY = Key.create("Diff.AnnotateAction.AnnotationShown");
+
   private static final ViewerAnnotator[] ANNOTATORS = new ViewerAnnotator[]{
     new TwosideAnnotator(), new OnesideAnnotator(), new UnifiedAnnotator()
   };
@@ -118,12 +128,19 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
     final Project project = viewer.getProject();
     assert project != null;
 
+    AnnotationData data = getDataFromCache(viewer, side);
+    if (data != null) {
+      annotator.showAnnotation(viewer, side, data);
+      return;
+    }
+
     final FileAnnotationLoader loader = createAnnotationsLoader(project, viewer.getRequest(), side);
     assert loader != null;
 
     markRunningProgress(viewer, side, true);
 
     // TODO: show progress in diff viewer
+    // TODO: we can abort loading on DiffViewer.dispose(). But vcs can't stop gracefully anyway.
     ProgressManager.getInstance().run(new Task.Backgroundable(project, VcsBundle.message("retrieving.annotations"), true,
                                                               BackgroundFromStartOption.getInstance()) {
       public void run(@NotNull ProgressIndicator indicator) {
@@ -142,9 +159,13 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
         if (loader.getException() != null) {
           AbstractVcsHelper.getInstance(myProject).showError(loader.getException(), VcsBundle.message("operation.name.annotate"));
         }
-        if (loader.getResult() != null) {
-          if (viewer.isDisposed()) return;
-          annotator.showAnnotation(viewer, side, loader.getResult());
+        if (loader.getResult() == null) return;
+        if (viewer.isDisposed()) return;
+
+        annotator.showAnnotation(viewer, side, loader.getResult());
+
+        if (loader.shouldCache()) {
+          putDataToCache(viewer, side, loader.getResult());
         }
       }
     });
@@ -163,7 +184,7 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
       if (annotationProvider == null) return null;
 
       if (revision instanceof CurrentContentRevision) {
-        return new FileAnnotationLoader(vcs) {
+        return new FileAnnotationLoader(vcs, false) {
           @Override
           public FileAnnotation compute() throws VcsException {
             final VirtualFile file = ((CurrentContentRevision)revision).getVirtualFile();
@@ -174,7 +195,7 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
       }
       else {
         if (!(annotationProvider instanceof AnnotationProviderEx)) return null;
-        return new FileAnnotationLoader(vcs) {
+        return new FileAnnotationLoader(vcs, true) {
           @Override
           public FileAnnotation compute() throws VcsException {
             return ((AnnotationProviderEx)annotationProvider).annotate(revision.getFile(), revision.getRevisionNumber());
@@ -195,7 +216,7 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
         final AnnotationProvider annotationProvider = vcs.getAnnotationProvider();
         if (annotationProvider == null) return null;
 
-        return new FileAnnotationLoader(vcs) {
+        return new FileAnnotationLoader(vcs, false) {
           @Override
           public FileAnnotation compute() throws VcsException {
             return annotationProvider.annotate(file);
@@ -207,6 +228,69 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
     return null;
   }
 
+  private static void putDataToCache(@NotNull DiffViewerBase viewer, @NotNull Side side, @NotNull AnnotationData data) {
+    AnnotationData[] cache = viewer.getRequest().getUserData(CACHE_KEY);
+    if (cache == null || cache.length != 2) {
+      cache = new AnnotationData[2];
+      viewer.getRequest().putUserData(CACHE_KEY, cache);
+    }
+    cache[side.getIndex()] = data;
+  }
+
+  @Nullable
+  private static AnnotationData getDataFromCache(@NotNull DiffViewerBase viewer, @NotNull Side side) {
+    AnnotationData[] cache = viewer.getRequest().getUserData(CACHE_KEY);
+    if (cache != null && cache.length == 2) {
+      return side.select(cache);
+    }
+    return null;
+  }
+
+  public static class MyDiffExtension extends DiffExtension {
+    @Override
+    public void onViewerCreated(@NotNull DiffViewer diffViewer, @NotNull DiffContext context, @NotNull DiffRequest request) {
+      if (diffViewer instanceof DiffViewerBase) {
+        DiffViewerBase viewer = (DiffViewerBase)diffViewer;
+        viewer.addListener(new MyDiffViewerListener(viewer));
+      }
+    }
+  }
+
+  private static class MyDiffViewerListener extends DiffViewerListener {
+    @NotNull private final DiffViewerBase myViewer;
+
+    public MyDiffViewerListener(@NotNull DiffViewerBase viewer) {
+      myViewer = viewer;
+    }
+
+    @Override
+    public void onInit() {
+      if (myViewer.getProject() == null) return;
+
+      boolean[] annotationsShown = myViewer.getRequest().getUserData(ANNOTATIONS_SHOWN_KEY);
+      if (annotationsShown == null || annotationsShown.length != 2) return;
+
+      ViewerAnnotator annotator = getAnnotator(myViewer);
+      if (annotator == null) return;
+
+      if (annotationsShown[0]) doAnnotate(annotator, myViewer, Side.LEFT);
+      if (annotationsShown[1]) doAnnotate(annotator, myViewer, Side.RIGHT);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void onDispose() {
+      ViewerAnnotator annotator = getAnnotator(myViewer);
+      if (annotator == null) return;
+
+      boolean[] annotationsShown = new boolean[2];
+      annotationsShown[0] = annotator.isAnnotationShown(myViewer, Side.LEFT);
+      annotationsShown[1] = annotator.isAnnotationShown(myViewer, Side.RIGHT);
+
+      myViewer.getRequest().putUserData(ANNOTATIONS_SHOWN_KEY, annotationsShown);
+    }
+  }
+
   private static class TwosideAnnotator extends ViewerAnnotator<TwosideTextDiffViewer> {
     @Override
     @NotNull
@@ -230,7 +314,8 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
 
     @Override
     public void showAnnotation(@NotNull TwosideTextDiffViewer viewer, @NotNull Side side, @NotNull AnnotationData data) {
-      AnnotateToggleAction.doAnnotate(viewer.getEditor(side), viewer.getProject(), null, data.annotation, data.vcs, null);
+      Project project = ObjectUtils.assertNotNull(viewer.getProject());
+      AnnotateToggleAction.doAnnotate(viewer.getEditor(side), project, null, data.annotation, data.vcs, null);
     }
   }
 
@@ -257,7 +342,8 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
     @Override
     public void showAnnotation(@NotNull OnesideTextDiffViewer viewer, @NotNull Side side, @NotNull AnnotationData data) {
       if (side != viewer.getSide()) return;
-      AnnotateToggleAction.doAnnotate(viewer.getEditor(), viewer.getProject(), null, data.annotation, data.vcs, null);
+      Project project = ObjectUtils.assertNotNull(viewer.getProject());
+      AnnotateToggleAction.doAnnotate(viewer.getEditor(), project, null, data.annotation, data.vcs, null);
     }
   }
 
@@ -284,8 +370,9 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
     @Override
     public void showAnnotation(@NotNull UnifiedDiffViewer viewer, @NotNull Side side, @NotNull AnnotationData data) {
       if (side != viewer.getMasterSide()) return;
+      Project project = ObjectUtils.assertNotNull(viewer.getProject());
       UnifiedUpToDateLineNumberProvider lineNumberProvider = new UnifiedUpToDateLineNumberProvider(viewer, side);
-      AnnotateToggleAction.doAnnotate(viewer.getEditor(), viewer.getProject(), null, data.annotation, data.vcs, lineNumberProvider);
+      AnnotateToggleAction.doAnnotate(viewer.getEditor(), project, null, data.annotation, data.vcs, lineNumberProvider);
     }
   }
 
@@ -338,12 +425,14 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
 
   private abstract static class FileAnnotationLoader {
     @NotNull private final AbstractVcs myVcs;
+    private final boolean myShouldCache;
 
     private VcsException myException;
     private FileAnnotation myResult;
 
-    public FileAnnotationLoader(@NotNull AbstractVcs vcs) {
+    public FileAnnotationLoader(@NotNull AbstractVcs vcs, boolean cache) {
       myVcs = vcs;
+      myShouldCache = cache;
     }
 
     public VcsException getException() {
@@ -354,6 +443,10 @@ public class AnnotateDiffViewerAction extends DumbAwareAction {
       return new AnnotationData(myVcs, myResult);
     }
 
+    public boolean shouldCache() {
+      return myShouldCache;
+    }
+
     public void run() {
       try {
         myResult = compute();