VCS: diff from annotations: jump to the line on which diff was invoked
authorirengrig <Irina.Chernushina@jetbrains.com>
Wed, 11 Aug 2010 10:06:49 +0000 (14:06 +0400)
committerirengrig <Irina.Chernushina@jetbrains.com>
Wed, 11 Aug 2010 10:06:49 +0000 (14:06 +0400)
12 files changed:
platform/lvcs-impl/src/com/intellij/history/integration/ui/views/DirectoryHistoryDialog.java
platform/platform-api/src/com/intellij/openapi/diff/DiffNavigationContext.java [new file with mode: 0644]
platform/platform-api/src/com/intellij/openapi/diff/DiffTool.java
platform/platform-impl/src/com/intellij/openapi/diff/impl/DiffPanelImpl.java
platform/util/src/com/intellij/util/containers/CacheOneStepIterator.java [new file with mode: 0644]
platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffAction.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffUIContext.java [new file with mode: 0644]
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDifferentiatedDialog.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesAction.java
platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesBrowser.java
platform/vcs-impl/src/com/intellij/openapi/vcs/update/ShowUpdatedDiffAction.java

index f8b66fc6ad788a0857ba1bd39cbdee8052fa6110..6c737c2c781a13e9a282ca96312ab51f9295c03b 100644 (file)
@@ -21,14 +21,13 @@ import com.intellij.history.core.revisions.Difference;
 import com.intellij.history.integration.IdeaGateway;
 import com.intellij.history.integration.ui.models.DirectoryHistoryDialogModel;
 import com.intellij.openapi.actionSystem.*;
-import com.intellij.openapi.diff.DiffManager;
-import com.intellij.openapi.diff.DiffRequest;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.IconLoader;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.vcs.changes.Change;
 import com.intellij.openapi.vcs.changes.actions.ShowDiffAction;
+import com.intellij.openapi.vcs.changes.actions.ShowDiffUIContext;
 import com.intellij.openapi.vcs.changes.ui.ChangeNodeDecorator;
 import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNode;
 import com.intellij.openapi.vcs.changes.ui.ChangesTreeList;
@@ -38,7 +37,6 @@ import com.intellij.ui.DocumentAdapter;
 import com.intellij.ui.ExcludingTraversalPolicy;
 import com.intellij.ui.SearchTextField;
 import com.intellij.ui.SearchTextFieldWithStoredHistory;
-import com.intellij.ui.treeStructure.actions.CollapseAllAction;
 import com.intellij.util.Consumer;
 import com.intellij.util.containers.ContainerUtil;
 import gnu.trove.THashSet;
@@ -210,7 +208,7 @@ public class DirectoryHistoryDialog extends HistoryDialog<DirectoryHistoryDialog
         public boolean value(Change change) {
           return selectedSet.contains(change);
         }
-      }, myProject, ShowDiffAction.DiffExtendUIFactory.NONE, true);
+      }, myProject, new ShowDiffUIContext(true));
     }
 
     private Iterable<DirectoryChange> iterFileChanges() {
diff --git a/platform/platform-api/src/com/intellij/openapi/diff/DiffNavigationContext.java b/platform/platform-api/src/com/intellij/openapi/diff/DiffNavigationContext.java
new file mode 100644 (file)
index 0000000..3d306aa
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2010 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.diff;
+
+public class DiffNavigationContext {
+  private final String myTargetString;
+  private final Iterable<String> myPreviousLinesIterable;
+
+  public DiffNavigationContext(Iterable<String> previousLinesIterable, String targetString) {
+    myPreviousLinesIterable = previousLinesIterable;
+    myTargetString = targetString;
+  }
+
+  public Iterable<String> getPreviousLinesIterable() {
+    return myPreviousLinesIterable;
+  }
+
+  public String getTargetString() {
+    return myTargetString;
+  }
+}
index 00458e7b73cc00aaea5fd68a23331264834fc3b0..3a39af9d98f07e2c455c57173e29b88317a68567 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.intellij.openapi.diff;
 
+import com.intellij.openapi.actionSystem.DataKey;
 import org.jetbrains.annotations.NonNls;
 
 
@@ -41,6 +42,8 @@ public interface DiffTool {
 
   @NonNls Object HINT_ALLOW_NO_DIFFERENCES = "allowNoDifferences";
 
+  @NonNls DataKey SCROLL_TO_LINE = DataKey.create("scrollToLine");
+
   /**
    * Opens window to compare contents. Clients should call {@link #canShow(com.intellij.openapi.diff.DiffRequest)} first.
    */
index c291de734e3c0bf0358026b081a49a8640922131..097af6a2c19c085bd1cdce9b7cd29bf46c3e78c2 100644 (file)
@@ -19,18 +19,21 @@ import com.intellij.openapi.Disposable;
 import com.intellij.openapi.actionSystem.ActionManager;
 import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.actionSystem.PlatformDataKeys;
+import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.diff.*;
 import com.intellij.openapi.diff.actions.MergeActionGroup;
 import com.intellij.openapi.diff.ex.DiffPanelEx;
 import com.intellij.openapi.diff.ex.DiffPanelOptions;
 import com.intellij.openapi.diff.impl.external.DiffManagerImpl;
+import com.intellij.openapi.diff.impl.fragments.Fragment;
 import com.intellij.openapi.diff.impl.fragments.FragmentList;
 import com.intellij.openapi.diff.impl.highlighting.DiffPanelState;
 import com.intellij.openapi.diff.impl.highlighting.FragmentSide;
 import com.intellij.openapi.diff.impl.splitter.DiffDividerPaint;
 import com.intellij.openapi.diff.impl.splitter.LineBlocks;
 import com.intellij.openapi.diff.impl.util.*;
+import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.ScrollingModel;
 import com.intellij.openapi.editor.event.VisibleAreaListener;
@@ -41,15 +44,20 @@ import com.intellij.openapi.fileEditor.OpenFileDescriptor;
 import com.intellij.openapi.fileTypes.FileType;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.ui.PopupHandler;
+import com.intellij.util.containers.CacheOneStepIterator;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.awt.*;
 import java.security.InvalidParameterException;
+import java.util.Iterator;
+import java.util.LinkedList;
 
 public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSidesContainer {
   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.diff.impl.DiffPanelImpl");
@@ -328,6 +336,7 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
     }
     final JComponent newBottomComponent = data.getBottomComponent();
     myPanel.setBottomComponent(newBottomComponent);
+    myPanel.requestScrollEditors();
   }
 
   private static void setWindowTitle(Window window, String title) {
@@ -360,11 +369,137 @@ public class DiffPanelImpl implements DiffPanelEx, ContentChangeListener, TwoSid
 
     public void scrollEditors() {
       getOptions().onNewContent(myCurrentSide);
+      final DiffNavigationContext scrollContext = (DiffNavigationContext) myDiffRequest.getGenericData().get(DiffTool.SCROLL_TO_LINE.getName());
+      if (scrollContext == null) {
+        scrollCurrentToFirstDiff();
+      } else {
+        final Document document = myRightSide.getEditor().getDocument();
+
+        final FragmentList fragmentList = getFragments();
+
+        final ChangedLinesIterator changedLinesIterator = new ChangedLinesIterator(fragmentList.iterator(), document);
+        final CacheOneStepIterator<Pair<Integer, String>> cacheOneStepIterator =
+          new CacheOneStepIterator<Pair<Integer, String>>(changedLinesIterator);
+        final NavigationContextChecker checker = new NavigationContextChecker(cacheOneStepIterator, scrollContext);
+        int line = checker.contextMatchCheck();
+        if (line < 0) {
+          /*final ChangedLinesIterator changedLinesIterator2 = new ChangedLinesIterator(fragmentList.iterator(), document);
+          final CacheOneStepIterator<Pair<Integer, String>> cacheOneStepIterator2 =
+            new CacheOneStepIterator<Pair<Integer, String>>(changedLinesIterator2);
+          final NavigationContextChecker checker2 = new NavigationContextChecker(cacheOneStepIterator2,
+            new DiffNavigationContext(Collections.<String>emptyList(), scrollContext.getTargetString()));
+          line = checker2.contextMatchCheck();
+          if (line >= 0) {
+            myRightSide.scrollToFirstDiff(line);
+          } else {*/
+            scrollCurrentToFirstDiff();
+          //}
+        } else {
+          myRightSide.scrollToFirstDiff(line);
+        }
+      }
+    }
+
+    private void scrollCurrentToFirstDiff() {
       int[] fragments = getFragmentBeginnings();
       if (fragments.length > 0) myCurrentSide.scrollToFirstDiff(fragments[0]);
     }
   }
 
+  private static class ChangedLinesIterator implements Iterator<Pair<Integer, String>> {
+    private final Document myDocument;
+    private final Iterator<Fragment> myFragmentsIterator;
+    private java.util.List<Pair<Integer, String>> myBuffer;
+
+    private ChangedLinesIterator(Iterator<Fragment> fragmentsIterator, Document document) {
+      myFragmentsIterator = fragmentsIterator;
+      myDocument = document;
+      myBuffer = new LinkedList<Pair<Integer, String>>();
+    }
+
+    @Override
+    public boolean hasNext() {
+      return (! myBuffer.isEmpty()) || myFragmentsIterator.hasNext();
+    }
+
+    @Override
+    public Pair<Integer, String> next() {
+      if (! myBuffer.isEmpty()) {
+        return myBuffer.remove(0);
+      }
+
+      Fragment fragment = null;
+      while (myFragmentsIterator.hasNext()) {
+        fragment = myFragmentsIterator.next();
+        final TextDiffTypeEnum type = fragment.getType();
+        if ((type == null) || TextDiffTypeEnum.DELETED.equals(type) || TextDiffTypeEnum.NONE.equals(type)) continue;
+        break;
+      }
+      if (fragment == null) return null;
+      
+      final TextRange textRange = fragment.getRange(FragmentSide.SIDE2);
+      ApplicationManager.getApplication().runReadAction(new Runnable() {
+        @Override
+        public void run() {
+          final int startLine = myDocument.getLineNumber(textRange.getStartOffset());
+          final int endLine = myDocument.getLineNumber(textRange.getEndOffset());
+          for (int i = startLine; i <= endLine; i++) {
+            String text = myDocument.getText().substring(myDocument.getLineStartOffset(i), myDocument.getLineEndOffset(i));
+            //text = text.endsWith("\r\n") ? text.substring(0, text.length() - 2) : text.substring(0, text.length() - 1);
+            myBuffer.add(new Pair<Integer, String>(i, text));
+          }
+        }
+      });
+      if (myBuffer.isEmpty()) return null;
+      return myBuffer.remove(0);
+    }
+
+    @Override
+    public void remove() {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  private static class NavigationContextChecker {
+    private final Iterator<Pair<Integer, String>> myChangedLinesIterator;
+    private final DiffNavigationContext myContext;
+
+    private NavigationContextChecker(Iterator<Pair<Integer, String>> changedLinesIterator, DiffNavigationContext context) {
+      myChangedLinesIterator = changedLinesIterator;
+      myContext = context;
+    }
+
+    public int contextMatchCheck() {
+      final Iterable<String> contextLines = myContext.getPreviousLinesIterable();
+
+      final Iterator<String> iterator = contextLines.iterator();
+      if (iterator.hasNext()) {
+        String contextLine = iterator.next();
+
+        while (myChangedLinesIterator.hasNext()) {
+          final Pair<Integer, String> pair = myChangedLinesIterator.next();
+          if (pair.getSecond().equals(contextLine)) {
+            if (! iterator.hasNext()) break;
+            contextLine = iterator.next();
+          }
+        }
+        if (iterator.hasNext()) {
+          return -1;
+        }
+      }
+      if (! myChangedLinesIterator.hasNext()) return -1;
+
+      final String targetLine = myContext.getTargetString();
+      while (myChangedLinesIterator.hasNext()) {
+        final Pair<Integer, String> pair = myChangedLinesIterator.next();
+        if (pair.getSecond().equals(targetLine)) {
+          return pair.getFirst();
+        }
+      }
+      return -1;
+    }
+  }
+
   private class MyGenericDataProvider extends GenericDataProvider {
     private final DiffPanelImpl myDiffPanel;
 
diff --git a/platform/util/src/com/intellij/util/containers/CacheOneStepIterator.java b/platform/util/src/com/intellij/util/containers/CacheOneStepIterator.java
new file mode 100644 (file)
index 0000000..e6b51aa
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2000-2010 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.util.containers;
+
+import java.util.Iterator;
+
+// only for those who cannot return null
+public class CacheOneStepIterator<T> implements Iterator<T> {
+  private final Iterator<T> myProbableIterator;
+  private T myPreCalculated;
+
+  public CacheOneStepIterator(final Iterator<T> probableIterator) {
+    myProbableIterator = probableIterator;
+    step();
+  }
+
+  private void step() {
+    if (! myProbableIterator.hasNext()) {
+      myPreCalculated = null;
+    } else {
+      myPreCalculated = myProbableIterator.next();
+    }
+  }
+
+  @Override
+  public boolean hasNext() {
+    return myPreCalculated != null;
+  }
+
+  @Override
+  public T next() {
+    final T result = myPreCalculated;
+    step();
+    return result;
+  }
+
+  @Override
+  public void remove() {
+    throw new UnsupportedOperationException();
+  }
+}
index b7b6cff18f3a53887293f6b2e8aa8cbfe0605ab6..dc0fbfa4c3601a0e75bab9c4740378152b13d35a 100644 (file)
@@ -17,6 +17,7 @@ package com.intellij.openapi.vcs.actions;
 
 import com.intellij.openapi.actionSystem.*;
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.diff.DiffNavigationContext;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.editor.colors.ColorKey;
 import com.intellij.openapi.editor.colors.EditorFontType;
@@ -39,11 +40,13 @@ import com.intellij.openapi.util.IconLoader;
 import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vcs.*;
 import com.intellij.openapi.vcs.annotate.*;
 import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
 import com.intellij.openapi.vcs.changes.Change;
 import com.intellij.openapi.vcs.changes.actions.ShowDiffAction;
+import com.intellij.openapi.vcs.changes.actions.ShowDiffUIContext;
 import com.intellij.openapi.vcs.changes.ui.ChangesComparator;
 import com.intellij.openapi.vcs.changes.ui.ChangesViewBalloonProblemNotifier;
 import com.intellij.openapi.vcs.history.VcsFileRevision;
@@ -55,6 +58,7 @@ import com.intellij.openapi.vcs.impl.VcsBackgroundableActions;
 import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.util.Consumer;
+import com.intellij.util.containers.CacheOneStepIterator;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -550,7 +554,9 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware {
               ChangesViewBalloonProblemNotifier.showMe(myVcs.getProject(), "Can not show diff: " + exc[0].getMessage(), MessageType.ERROR);
             } else if (! changes.isEmpty()) {
               int idx = findSelfInList(changes);
-              ShowDiffAction.showDiffForChange(changes.toArray(new Change[changes.size()]), idx, myVcs.getProject());
+              final ShowDiffUIContext context = new ShowDiffUIContext(true);
+              context.setDiffNavigationContext(createDiffNavigationContext(actualNumber));
+              ShowDiffAction.showDiffForChange(changes.toArray(new Change[changes.size()]), idx, myVcs.getProject(), context);
             }
           }
         });
@@ -581,5 +587,157 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware {
 
       return idx;
     }
+
+    // for current line number
+    private DiffNavigationContext createDiffNavigationContext(final int actualLine) {
+      final MyContentsLines contentsLines = new MyContentsLines(myFileAnnotation.getAnnotatedContent());
+
+      return new DiffNavigationContext(new Iterable<String>() {
+        @Override
+        public Iterator<String> iterator() {
+          return new CacheOneStepIterator<String>(new ContextLineIterator(contentsLines, myFileAnnotation, actualLine));
+        }
+      }, contentsLines.getLineContents(actualLine));
+    }
+
+    private static class MySplittingIterator implements Iterator<Integer> {
+      private final String myContents;
+      // always at the beginning of the _next_ line
+      private int myOffset;
+
+      private MySplittingIterator(final String contents) {
+        myContents = contents;
+        myOffset = 0;
+      }
+
+      @Override
+      public boolean hasNext() {
+        return myOffset < myContents.length();
+      }
+
+      @Override
+      public Integer next() {
+        final int start = myOffset;
+        while (myOffset < myContents.length()) {
+          // \r, \n, or \r\n
+          final char c = myContents.charAt(myOffset);
+          if ('\n' == c) {
+            ++ myOffset;
+            break;
+          } else if ('\r' == c) {
+            if (myOffset + 1 == myContents.length()) {
+              // at the end
+              ++ myOffset;
+              break;
+            } else {
+              myOffset += (('\n' == myContents.charAt(myOffset + 1)) ? 2 : 1);
+              break;
+            }
+          }
+          ++ myOffset;
+        }
+
+        return start;
+      }
+
+      @Override
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+    }
+
+    private static class MyContentsLines {
+      private final MySplittingIterator mySplittingIterator;
+      private final List<Integer> myLinesStartOffsets;
+      private final String myContents;
+      private boolean myLineEndsFinished;
+
+      private MyContentsLines(final String contents) {
+        myContents = contents;
+        mySplittingIterator = new MySplittingIterator(contents);
+        myLinesStartOffsets = new LinkedList<Integer>();
+      }
+
+      public String getLineContents(final int number) {
+        assert (! myLineEndsFinished) || (myLineEndsFinished && (myLinesStartOffsets.size() > number));
+
+        // we need to know end
+        if (myLineEndsFinished || (myLinesStartOffsets.size() > (number + 1))) {
+          return extractCalculated(number);
+        }
+        while (((myLinesStartOffsets.size() - 1) < (number + 1)) && (! myLineEndsFinished) && mySplittingIterator.hasNext()) {
+          final Integer nextStart = mySplittingIterator.next();
+          myLinesStartOffsets.add(nextStart);
+        }
+        myLineEndsFinished = myLinesStartOffsets.size() < (number + 1);
+        return extractCalculated(number);
+      }
+
+      private String extractCalculated(int number) {
+        String text = myContents.substring(myLinesStartOffsets.get(number),
+                                                 (number > (myLinesStartOffsets.size() - 1))
+                                                 ? myContents.length()
+                                                 : myLinesStartOffsets.get(number + 1));
+        text = text.endsWith("\r\n") ? text.substring(0, text.length() - 2) : text.substring(0, text.length() - 1);
+        return text;
+      }
+
+      public int getKnownLinesNumber() {
+        return myLineEndsFinished ? myLinesStartOffsets.size() : -1;
+      }
+    }
+
+    /**
+     * Slightly break the contract: can return null from next() while had claimed hasNext()
+     */
+    private static class ContextLineIterator implements Iterator<String> {
+      private final MyContentsLines myContentsLines;
+
+      private final VcsRevisionNumber myRevisionNumber;
+      private final FileAnnotation myAnnotation;
+      private final int myStopAtLine;
+      // we assume file has at least one line ;)
+      private int myCurrentLine;  // to start looking for next line with revision from
+
+      private ContextLineIterator(final MyContentsLines contentLines, final FileAnnotation annotation, final int stopAtLine) {
+        myAnnotation = annotation;
+        myRevisionNumber = myAnnotation.originalRevision(stopAtLine);
+        myStopAtLine = stopAtLine;
+        myContentsLines = contentLines;
+      }
+
+      @Override
+      public boolean hasNext() {
+        return lineNumberInBounds();
+      }
+
+      private boolean lineNumberInBounds() {
+        final int knownLinesNumber = myContentsLines.getKnownLinesNumber();
+        return ((knownLinesNumber == -1) || (myCurrentLine < knownLinesNumber)) && (myCurrentLine < myStopAtLine);
+      }
+
+      @Override
+      public String next() {
+        int nextLine = -1;
+        while (lineNumberInBounds()) {
+          final VcsRevisionNumber vcsRevisionNumber = myAnnotation.originalRevision(myCurrentLine);
+          if (myRevisionNumber.equals(vcsRevisionNumber)) {
+            nextLine = myCurrentLine;
+            final String text = myContentsLines.getLineContents(nextLine);
+            if (! StringUtil.isEmptyOrSpaces(text)) {
+              ++ myCurrentLine;
+              return text;
+            }
+          }
+          ++ myCurrentLine;
+        }
+        return null;
+      }
+
+      @Override
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+    }
   }
 }
index 0d51f67fa26e566a202bc999289cf5fe777c993a..522bb795966f16566a104aa88d7d1dcfd99a7684 100644 (file)
@@ -21,6 +21,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.actionSystem.PlatformDataKeys;
 import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.diff.DiffManager;
+import com.intellij.openapi.diff.DiffNavigationContext;
 import com.intellij.openapi.diff.DiffRequest;
 import com.intellij.openapi.diff.DiffTool;
 import com.intellij.openapi.fileTypes.FileType;
@@ -32,7 +33,6 @@ import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.IconLoader;
 import com.intellij.openapi.vcs.*;
 import com.intellij.openapi.vcs.changes.*;
-import com.intellij.util.NotNullFunction;
 import com.intellij.util.containers.Convertor;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -123,7 +123,7 @@ public class ShowDiffAction extends AnAction implements DumbAware {
   }
 
   public static void showDiffForChange(final Change[] changes, final int index, final Project project) {
-    showDiffForChange(changes, index, project, DiffExtendUIFactory.NONE, true);
+    showDiffForChange(changes, index, project, new ShowDiffUIContext(true));
   }
 
   private boolean checkIfThereAreFakeRevisions(final Project project, final Change[] changes) {
@@ -170,7 +170,7 @@ public class ShowDiffAction extends AnAction implements DumbAware {
   }
 
   public static void showDiffForChange(final Iterable<Change> changes, final Condition<Change> selectionChecker,
-                                       final Project project, @NotNull DiffExtendUIFactory actionsFactory, final boolean showFrame) {
+                                       final Project project, @NotNull ShowDiffUIContext context) {
     int cnt = 0;
     int newIndex = -1;
     final List<Change> changeList = new ArrayList<Change>();
@@ -195,38 +195,30 @@ public class ShowDiffAction extends AnAction implements DumbAware {
               public ChangeDiffRequestPresentable convert(Change o) {
                 return new ChangeDiffRequestPresentable(project, o);
               }
-            }), newIndex, actionsFactory, showFrame);
+            }), newIndex, context);
   }
 
-  public static void showDiffForChange(Change[] changes, int index, final Project project, @NotNull DiffExtendUIFactory actionsFactory,
-                                       final boolean showFrame) {
-    Change selectedChange = changes [index];
-    final List<Change> changeList = filterDirectoryAndBinaryChanges(changes);
-    if (changeList.isEmpty()) {
-      return;
-    }
-    index = 0;
-    for (int i = 0; i < changeList.size(); i++) {
-      if (changeList.get(i) == selectedChange) {
-        index = i;
-        break;
+  public static void showDiffForChange(final Change[] changes, int index, final Project project, @NotNull ShowDiffUIContext context) {
+    final Change selected = index >= 0 ? changes[index] : null;
+    showDiffForChange(Arrays.asList(changes), new Condition<Change>() {
+      @Override
+      public boolean value(final Change change) {
+        return selected == null ? false : selected.equals(change);
       }
-    }
-    showDiffImpl(project, ObjectsConvertor.convert(changeList,
-            new Convertor<Change, DiffRequestPresentable>() {
-              public ChangeDiffRequestPresentable convert(Change o) {
-                return new ChangeDiffRequestPresentable(project, o);
-              }
-            }), index, actionsFactory, showFrame);
+    }, project, context);
   }
 
-  public static void showDiffImpl(final Project project, List<DiffRequestPresentable> changeList, int index, DiffExtendUIFactory actionsFactory, boolean showFrame) {
-    final ChangeDiffRequest request = new ChangeDiffRequest(project, changeList, actionsFactory, showFrame);
-    
+  public static void showDiffImpl(final Project project, List<DiffRequestPresentable> changeList, int index, @NotNull final ShowDiffUIContext context) {
+    final ChangeDiffRequest request = new ChangeDiffRequest(project, changeList, context.getActionsFactory(), context.isShowFrame());
     final DiffTool tool = DiffManager.getInstance().getDiffTool();
     if (! request.quickCheckHaveStuff()) return;
     final DiffRequest simpleRequest = request.init(index);
+
     if (simpleRequest != null) {
+      final DiffNavigationContext navigationContext = context.getDiffNavigationContext();
+      if (navigationContext != null) {
+        simpleRequest.passForDataContext(DiffTool.SCROLL_TO_LINE, navigationContext);
+      }
       tool.show(simpleRequest);
     }
   }
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffUIContext.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffUIContext.java
new file mode 100644 (file)
index 0000000..1809c65
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2000-2010 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.actions;
+
+import com.intellij.openapi.diff.DiffNavigationContext;
+import org.jetbrains.annotations.NotNull;
+
+public class ShowDiffUIContext {
+  private ShowDiffAction.DiffExtendUIFactory myActionsFactory;
+  private boolean myShowFrame;
+  private DiffNavigationContext myDiffNavigationContext;
+
+  public ShowDiffUIContext() {
+    myActionsFactory = ShowDiffAction.DiffExtendUIFactory.NONE;
+  }
+
+  public ShowDiffUIContext(boolean showFrame) {
+    myShowFrame = showFrame;
+    myActionsFactory = ShowDiffAction.DiffExtendUIFactory.NONE;
+  }
+
+  public ShowDiffAction.DiffExtendUIFactory getActionsFactory() {
+    return myActionsFactory;
+  }
+
+  public void setActionsFactory(@NotNull ShowDiffAction.DiffExtendUIFactory actionsFactory) {
+    this.myActionsFactory = actionsFactory;
+  }
+
+  public DiffNavigationContext getDiffNavigationContext() {
+    return myDiffNavigationContext;
+  }
+
+  public void setDiffNavigationContext(DiffNavigationContext diffNavigationContext) {
+    myDiffNavigationContext = diffNavigationContext;
+  }
+
+  public boolean isShowFrame() {
+    return myShowFrame;
+  }
+
+  public void setShowFrame(boolean showFrame) {
+    this.myShowFrame = showFrame;
+  }
+}
index a4b70f63732511ee33dbd0c8b643d2dc6ce07a14..7e351534066e7d8dcd224b19098269c56e12ae7a 100644 (file)
@@ -41,6 +41,7 @@ import com.intellij.openapi.vcs.changes.ChangeListManager;
 import com.intellij.openapi.vcs.changes.LocalChangeList;
 import com.intellij.openapi.vcs.changes.actions.DiffRequestPresentable;
 import com.intellij.openapi.vcs.changes.actions.ShowDiffAction;
+import com.intellij.openapi.vcs.changes.actions.ShowDiffUIContext;
 import com.intellij.openapi.vcs.changes.ui.*;
 import com.intellij.openapi.vfs.LocalFileSystem;
 import com.intellij.openapi.vfs.VirtualFile;
@@ -851,7 +852,7 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper {
         }
       }
       if (diffRequestPresentables.isEmpty()) return;
-      ShowDiffAction.showDiffImpl(myProject, diffRequestPresentables, selectedIdx, ShowDiffAction.DiffExtendUIFactory.NONE, false);
+      ShowDiffAction.showDiffImpl(myProject, diffRequestPresentables, selectedIdx, new ShowDiffUIContext(false));
     }
   }
 
index 33fb014f80fca878f693872f32aab29aa976d6a9..cd198408e961a2ad2f0e1e8f8377e602c1e72faa 100644 (file)
@@ -29,10 +29,7 @@ import com.intellij.openapi.vcs.FileStatus;
 import com.intellij.openapi.vcs.VcsException;
 import com.intellij.openapi.vcs.changes.Change;
 import com.intellij.openapi.vcs.changes.ContentRevision;
-import com.intellij.openapi.vcs.changes.actions.ChangeDiffRequestPresentable;
-import com.intellij.openapi.vcs.changes.actions.DiffRequestPresentable;
-import com.intellij.openapi.vcs.changes.actions.DiffRequestPresentableProxy;
-import com.intellij.openapi.vcs.changes.actions.ShowDiffAction;
+import com.intellij.openapi.vcs.changes.actions.*;
 import com.intellij.openapi.vcs.changes.patch.ApplyPatchForBaseRevisionTexts;
 import com.intellij.openapi.vcs.changes.patch.MergedDiffRequestPresentable;
 import com.intellij.openapi.vcs.changes.patch.PatchMergeRequestFactory;
@@ -122,7 +119,7 @@ public class DiffShelvedChangesAction extends AnAction implements DumbAware {
       ChangesViewBalloonProblemNotifier.showMe(project, "Show Diff: Cannot find base for: " + StringUtil.join(missing, ",\n"), MessageType.WARNING);
     }
 
-    ShowDiffAction.showDiffImpl(project, diffRequestPresentables, toSelectIdx, ShowDiffAction.DiffExtendUIFactory.NONE, false);
+    ShowDiffAction.showDiffImpl(project, diffRequestPresentables, toSelectIdx, new ShowDiffUIContext(false));
   }
 
   private static class PatchesPreloader {
index 75195cc7ae414de599c03cecf694c92b9ef14ade..e13c6d895e3720f25a8f217659958e48469ea90b 100644 (file)
@@ -24,6 +24,7 @@ import com.intellij.openapi.vcs.VcsBundle;
 import com.intellij.openapi.vcs.VcsDataKeys;
 import com.intellij.openapi.vcs.changes.*;
 import com.intellij.openapi.vcs.changes.actions.ShowDiffAction;
+import com.intellij.openapi.vcs.changes.actions.ShowDiffUIContext;
 import com.intellij.openapi.vfs.VirtualFile;
 import org.jetbrains.annotations.Nullable;
 
@@ -197,7 +198,9 @@ public class ChangesBrowser extends JPanel implements TypeSafeDataProvider {
   }
 
   protected void showDiffForChanges(Change[] changesArray, final int indexInSelection) {
-    ShowDiffAction.showDiffForChange(changesArray, indexInSelection, myProject, myDiffExtendUIFactory, isInFrame());
+    final ShowDiffUIContext context = new ShowDiffUIContext(isInFrame());
+    context.setActionsFactory(myDiffExtendUIFactory);
+    ShowDiffAction.showDiffForChange(changesArray, indexInSelection, myProject, context);
   }
 
   private void showDiff() {
index db31cfb2b5f15ab1d8d76a7811533f70b499b2d0..e176fe8008478b8ae8938ac65bcec36481574983 100644 (file)
@@ -19,8 +19,8 @@ import com.intellij.history.ByteContent;
 import com.intellij.history.Label;
 import com.intellij.openapi.actionSystem.*;
 import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
-import com.intellij.openapi.project.Project;
 import com.intellij.openapi.project.DumbAware;
+import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Condition;
 import com.intellij.openapi.util.IconLoader;
 import com.intellij.openapi.util.io.FileUtil;
@@ -28,11 +28,11 @@ import com.intellij.openapi.vcs.*;
 import com.intellij.openapi.vcs.changes.Change;
 import com.intellij.openapi.vcs.changes.ContentRevision;
 import com.intellij.openapi.vcs.changes.actions.ShowDiffAction;
+import com.intellij.openapi.vcs.changes.actions.ShowDiffUIContext;
 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.openapi.vfs.encoding.EncodingManager;
 import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
-import com.intellij.util.NotNullFunction;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -91,7 +91,7 @@ public class ShowUpdatedDiffAction extends AnAction implements DumbAware {
     final String selectedUrl = VcsDataKeys.UPDATE_VIEW_SELECTED_PATH.getData(dc);
 
     ShowDiffAction.showDiffForChange(new MyIterableWrapper(iterable.iterator(), before, after), new MySelectionMarker(selectedUrl),
-                                     project, ShowDiffAction.DiffExtendUIFactory.NONE, true);
+                                     project, new ShowDiffUIContext(true));
   }
 
   private static class MySelectionMarker implements Condition<Change> {