From 80f04188ca6cfa39f900a361e88c0bfe8c84b77d Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Thu, 11 Dec 2014 23:18:05 +0300 Subject: [PATCH] diff: carefully keep editor scroll/caret position --- .../impl/CacheDiffRequestChainProcessor.java | 25 +-- .../diff/tools/oneside/OnesideDiffViewer.java | 181 +++++++++++++----- .../diff/tools/simple/SimpleDiffViewer.java | 5 +- .../diff/tools/util/DiffUserDataKeys.java | 14 +- .../diff/tools/util/ScrollToLineHelper.java | 32 ---- .../util/twoside/TwosideTextDiffViewer.java | 143 ++++++++++---- .../openapi/util/diff/util/DiffUtil.java | 17 +- 7 files changed, 271 insertions(+), 146 deletions(-) delete mode 100644 platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/ScrollToLineHelper.java diff --git a/platform/diff-impl/src/com/intellij/openapi/util/diff/impl/CacheDiffRequestChainProcessor.java b/platform/diff-impl/src/com/intellij/openapi/util/diff/impl/CacheDiffRequestChainProcessor.java index 80230c97ca62..d6e2700762b5 100644 --- a/platform/diff-impl/src/com/intellij/openapi/util/diff/impl/CacheDiffRequestChainProcessor.java +++ b/platform/diff-impl/src/com/intellij/openapi/util/diff/impl/CacheDiffRequestChainProcessor.java @@ -31,6 +31,7 @@ import com.intellij.openapi.util.diff.tools.ErrorDiffTool; import com.intellij.openapi.util.diff.tools.external.ExternalDiffTool; import com.intellij.openapi.util.diff.tools.util.DiffDataKeys; import com.intellij.openapi.util.diff.tools.util.DiffUserDataKeys; +import com.intellij.openapi.util.diff.tools.util.DiffUserDataKeys.ScrollToPolicy; import com.intellij.openapi.util.diff.tools.util.PrevNextDifferenceIterable; import com.intellij.openapi.util.diff.tools.util.SoftHardCacheMap; import com.intellij.openapi.util.diff.util.DiffUtil; @@ -47,9 +48,6 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import static com.intellij.openapi.util.diff.tools.util.DiffUserDataKeys.ScrollToPolicy.FIRST_CHANGE; -import static com.intellij.openapi.util.diff.tools.util.DiffUserDataKeys.ScrollToPolicy.LAST_CHANGE; - public abstract class CacheDiffRequestChainProcessor implements Disposable { private static final Logger LOG = Logger.getInstance(CacheDiffRequestChainProcessor.class); @@ -138,15 +136,17 @@ public abstract class CacheDiffRequestChainProcessor implements Disposable { // public void updateRequest() { - updateRequest(false); + updateRequest(false, null); } - public void updateRequest(boolean force) { + public void updateRequest(boolean force, @Nullable ScrollToPolicy scrollToChangePolicy) { boolean hadFocus = isFocused(); DiffRequest request = loadRequest(); if (!force && request == myActiveRequest) return; + request.putUserData(DiffUserDataKeys.SCROLL_TO_CHANGE, scrollToChangePolicy); + myState.destroy(); myToolbarStatusPanel.setContent(null); myToolbarPanel.setContent(null); @@ -371,15 +371,15 @@ public abstract class CacheDiffRequestChainProcessor implements Disposable { } if (myIterationState != IterationState.NEXT) { - if (iterable != null) iterable.notify("Press again to go to the next file"); // TODO: provide "change" word in chain UserData - for tests/etc + // TODO: provide "change" word in chain UserData - for tests/etc + if (iterable != null) iterable.notify("Press again to go to the next file"); myIterationState = IterationState.NEXT; return; } myIterationState = IterationState.NONE; myRequestChain.setIndex(myRequestChain.getIndex() + 1); - myContext.putUserData(DiffUserDataKeys.SCROLL_TO_CHANGE, FIRST_CHANGE); - updateRequest(); + updateRequest(false, ScrollToPolicy.FIRST_CHANGE); } } @@ -421,8 +421,7 @@ public abstract class CacheDiffRequestChainProcessor implements Disposable { myIterationState = IterationState.NONE; myRequestChain.setIndex(myRequestChain.getIndex() - 1); - myContext.putUserData(DiffUserDataKeys.SCROLL_TO_CHANGE, LAST_CHANGE); - updateRequest(); + updateRequest(false, ScrollToPolicy.LAST_CHANGE); } } @@ -447,7 +446,6 @@ public abstract class CacheDiffRequestChainProcessor implements Disposable { @Override public void actionPerformed(@NotNull AnActionEvent e) { myRequestChain.setIndex(myRequestChain.getIndex() + 1); - myContext.putUserData(DiffUserDataKeys.SCROLL_TO_CHANGE, null); updateRequest(); } } @@ -471,7 +469,6 @@ public abstract class CacheDiffRequestChainProcessor implements Disposable { @Override public void actionPerformed(@NotNull AnActionEvent e) { myRequestChain.setIndex(myRequestChain.getIndex() - 1); - myContext.putUserData(DiffUserDataKeys.SCROLL_TO_CHANGE, null); updateRequest(); } } @@ -483,7 +480,6 @@ public abstract class CacheDiffRequestChainProcessor implements Disposable { public void consume(Integer index) { if (index >= 0 && index != myRequestChain.getIndex()) { myRequestChain.setIndex(index); - myContext.putUserData(DiffUserDataKeys.SCROLL_TO_CHANGE, null); updateRequest(); } } @@ -578,8 +574,7 @@ public abstract class CacheDiffRequestChainProcessor implements Disposable { } myToolOrder.add(index, myDiffTool); - myContext.putUserData(DiffUserDataKeys.SCROLL_TO_CHANGE, null); - updateRequest(true); + updateRequest(true, null); } } } diff --git a/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/oneside/OnesideDiffViewer.java b/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/oneside/OnesideDiffViewer.java index 96ffd4b8587b..bf66d41c3f2e 100644 --- a/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/oneside/OnesideDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/oneside/OnesideDiffViewer.java @@ -10,10 +10,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.DiffBundle; import com.intellij.openapi.diff.DiffNavigationContext; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.LogicalPosition; +import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.fileEditor.OpenFileDescriptor; @@ -25,6 +22,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.diff.actions.BufferedLineIterator; import com.intellij.openapi.util.diff.actions.NavigationContextChecker; @@ -43,7 +41,6 @@ import com.intellij.openapi.util.diff.tools.util.DiffDataKeys; import com.intellij.openapi.util.diff.tools.util.DiffUserDataKeys; import com.intellij.openapi.util.diff.tools.util.DiffUserDataKeys.ScrollToPolicy; import com.intellij.openapi.util.diff.tools.util.PrevNextDifferenceIterable; -import com.intellij.openapi.util.diff.tools.util.ScrollToLineHelper; import com.intellij.openapi.util.diff.tools.util.base.HighlightPolicy; import com.intellij.openapi.util.diff.tools.util.base.TextDiffViewerBase; import com.intellij.openapi.util.diff.util.CalledInAwt; @@ -135,13 +132,11 @@ class OnesideDiffViewer extends TextDiffViewerBase { Side side = myContext.getUserData(DiffUserDataKeys.PREFERRED_FOCUS_SIDE); if (side != null) myMasterSide = side; - myScrollToLineHelper.processContext(myContext, myRequest); + myScrollToLineHelper.processContext(); } private void updateContextHints() { - int onesideLine = DiffUtil.getLineToScroll(myEditor); - Pair pair = transferLineFromOneside(onesideLine); - myRequest.putUserData(DiffUserDataKeys.SCROLL_TO_LINE, pair); + myScrollToLineHelper.updateContext(); } @NotNull @@ -368,7 +363,7 @@ class OnesideDiffViewer extends TextDiffViewerBase { myChangedBlockData = new ChangedBlockData(diffChanges, convertor, invertedConvertor, diffSeparators); // TODO: remember current in current caret position position on rediff rather than positon in combined file - myScrollToLineHelper.scrollOnRediff(); + myScrollToLineHelper.onRediff(); myStatusPanel.update(); myPanel.setGoodContent(); @@ -387,40 +382,46 @@ class OnesideDiffViewer extends TextDiffViewerBase { } @CalledInAwt - private Pair transferLineFromOneside(int line) { - if (myChangedBlockData == null) return Pair.create(myMasterSide, line); + private Pair transferLineFromOneside(int line) { + int[] lines = new int[2]; + + if (myChangedBlockData == null) { + lines[0] = myActualContent1 != null ? line : 0; + lines[1] = myActualContent2 != null ? line : 0; + return Pair.create(lines, myMasterSide); + } LineNumberConvertor lineConvertor = myChangedBlockData.getLineNumberConvertor(); TIntFunction convertor1 = lineConvertor.getConvertor1(); TIntFunction convertor2 = lineConvertor.getConvertor2(); - int line1 = convertor1.execute(line); - int line2 = convertor2.execute(line); + lines[0] = convertor1.execute(line); + lines[1] = convertor2.execute(line); - if (line1 == -1 && line2 == -1) { - line1 = convertor1.execute(line - 1); - line2 = convertor2.execute(line - 1); - if (line1 != -1) line1++; - if (line2 != -1) line2++; + if (lines[0] == -1 && lines[1] == -1) { + lines[0] = convertor1.execute(line - 1); + lines[1] = convertor2.execute(line - 1); + if (lines[0] != -1) lines[0]++; + if (lines[1] != -1) lines[1]++; } - if (line1 == -1 && line2 == -1) { - line1 = convertor1.execute(line + 1); - line2 = convertor2.execute(line + 1); - if (line1 > 0) line1--; - if (line2 > 0) line2--; + if (lines[0] == -1 && lines[1] == -1) { + lines[0] = convertor1.execute(line + 1); + lines[1] = convertor2.execute(line + 1); + if (lines[0] > 0) lines[0]--; + if (lines[1] > 0) lines[1]--; } - if (line1 == -1 && line2 == -1) { - return Pair.create(myMasterSide, line); + if (lines[0] == -1 && lines[1] == -1) { + return Pair.create(lines, myMasterSide); } - if (line1 == -1) { - return Pair.create(Side.RIGHT, line2); + if (lines[0] == -1) { + return Pair.create(lines, Side.RIGHT); } - if (line2 == -1) { - return Pair.create(Side.LEFT, line1); + if (lines[1] == -1) { + return Pair.create(lines, Side.LEFT); } - return myMasterSide.isLeft() ? Pair.create(Side.LEFT, line1) : Pair.create(Side.RIGHT, line2); + return Pair.create(lines, myMasterSide); } @CalledInAwt @@ -529,12 +530,12 @@ class OnesideDiffViewer extends TextDiffViewerBase { if (descriptor != null) return descriptor; } else { - Pair pair = transferLineFromOneside(myEditor.offsetToLogicalPosition(offset).line); + Pair pair = transferLineFromOneside(myEditor.offsetToLogicalPosition(offset).line); OpenFileDescriptor descriptor1 = myActualContent1.getOpenFileDescriptor(offset); OpenFileDescriptor descriptor2 = myActualContent2.getOpenFileDescriptor(offset); if (descriptor1 == null) return descriptor2; if (descriptor2 == null) return descriptor1; - return pair.first.select(descriptor1, descriptor2); + pair.second.select(descriptor1, descriptor2); } return null; @@ -934,28 +935,91 @@ class OnesideDiffViewer extends TextDiffViewerBase { } } - private class MyScrollToLineHelper extends ScrollToLineHelper { - public void scrollOnRediff() { - if (myShouldScroll && myScrollToChange != null) if (doScrollToChange(myScrollToChange)) onSuccessfulScroll(); - if (myShouldScroll && myScrollToLine != null) if (doScrollToLine(myScrollToLine)) onSuccessfulScroll(); - if (myShouldScroll && myNavigationContext != null) if (doScrollToContext(myNavigationContext)) onSuccessfulScroll(); - if (myShouldScroll) doScrollToChange(ScrollToPolicy.FIRST_CHANGE); - onSuccessfulScroll(); + private class MyScrollToLineHelper { + protected boolean myShouldScroll = true; + + @Nullable private ScrollToPolicy myScrollToChange; + @Nullable private EditorPosition myEditorPosition; + @Nullable private LogicalPosition[] myCaretPosition; + @Nullable private DiffNavigationContext myNavigationContext; + + public void processContext() { + myScrollToChange = myRequest.getUserData(DiffUserDataKeys.SCROLL_TO_CHANGE); + myEditorPosition = myRequest.getUserData(EditorPosition.KEY); + myCaretPosition = myRequest.getUserData(DiffUserDataKeys.EDITORS_CARET_POSITION); + myNavigationContext = myRequest.getUserData(DiffUserDataKeys.NAVIGATION_CONTEXT); } - private boolean doScrollToLine(@NotNull Pair scrollToLine) { - int onesideLine = transferLineToOneside(scrollToLine.first, scrollToLine.second); - DiffUtil.scrollToLine(myEditor, onesideLine); + public void updateContext() { + LogicalPosition position = myEditor.getCaretModel().getLogicalPosition(); + Pair pair = transferLineFromOneside(position.line); + LogicalPosition[] carets = new LogicalPosition[2]; + carets[0] = getPosition(pair.first[0], position.column); + carets[1] = getPosition(pair.first[1], position.column); + + EditorPosition editorsPosition = new EditorPosition(carets, getPoint(myEditor)); + + myRequest.putUserData(DiffUserDataKeys.SCROLL_TO_CHANGE, null); + myRequest.putUserData(EditorPosition.KEY, editorsPosition); + myRequest.putUserData(DiffUserDataKeys.EDITORS_CARET_POSITION, carets); + myRequest.putUserData(DiffUserDataKeys.NAVIGATION_CONTEXT, null); + } + + public void onRediff() { + if (myShouldScroll && myScrollToChange != null) { + myShouldScroll = !doScrollToChange(myScrollToChange); + } + if (myShouldScroll && myNavigationContext != null) { + myShouldScroll = !doScrollToContext(myNavigationContext); + } + if (myShouldScroll && myCaretPosition != null && myCaretPosition.length == 2) { + if (myEditorPosition != null && myEditorPosition.isSame(myCaretPosition)) { + scrollToPoint(myEditor, myEditorPosition.myPoint); + } + else { + doScrollToLine(myMasterSide, myMasterSide.selectN(myCaretPosition)); + } + } + if (myShouldScroll) { + doScrollToChange(ScrollToPolicy.FIRST_CHANGE); + } + myShouldScroll = false; + } + + @NotNull + private LogicalPosition getPosition(int line, int column) { + if (line == -1) return new LogicalPosition(0, 0); + return new LogicalPosition(line, column); + } + + @NotNull + private Point getPoint(@Nullable Editor editor) { + if (editor == null) return new Point(0, 0); + ScrollingModel model = editor.getScrollingModel(); + return new Point(model.getHorizontalScrollOffset(), model.getVerticalScrollOffset()); + } + + private void scrollToPoint(@Nullable Editor editor, @NotNull Point point) { + if (editor == null) return; + editor.getScrollingModel().disableAnimation(); + editor.getScrollingModel().scrollHorizontally(point.x); + editor.getScrollingModel().scrollVertically(point.y); + editor.getScrollingModel().enableAnimation(); + } + + private boolean doScrollToLine(@NotNull Side side, @NotNull LogicalPosition position) { + int onesideLine = transferLineToOneside(side, position.line); + DiffUtil.scrollEditor(myEditor, onesideLine, position.column); return true; } - private boolean doScrollToChange(@NotNull ScrollToPolicy scrollToPolicy) { + private boolean doScrollToChange(@NotNull ScrollToPolicy scrollToChangePolicy) { if (myChangedBlockData == null) return false; List changes = myChangedBlockData.getDiffChanges(); if (changes.isEmpty()) return false; OnesideDiffChange targetChange; - switch (scrollToPolicy) { + switch (scrollToChangePolicy) { case FIRST_CHANGE: targetChange = changes.get(0); break; @@ -963,10 +1027,10 @@ class OnesideDiffViewer extends TextDiffViewerBase { targetChange = changes.get(changes.size() - 1); break; default: - throw new IllegalArgumentException(scrollToPolicy.name()); + throw new IllegalArgumentException(scrollToChangePolicy.name()); } - DiffUtil.scrollToLine(myEditor, targetChange.getLine1()); + DiffUtil.scrollEditor(myEditor, targetChange.getLine1()); return true; } @@ -986,7 +1050,28 @@ class OnesideDiffViewer extends TextDiffViewerBase { } if (line == -1) return false; - return doScrollToLine(Pair.create(Side.RIGHT, line)); + return doScrollToLine(Side.RIGHT, new LogicalPosition(line, 0)); + } + } + + private static class EditorPosition { + public static final Key KEY = Key.create("Diff.OnesideEditorPosition"); + + @NotNull public final LogicalPosition[] myCaretPosition; + @NotNull public final Point myPoint; + + public EditorPosition(@NotNull LogicalPosition[] caretPosition, @NotNull Point point) { + myCaretPosition = caretPosition; + myPoint = point; + } + + public boolean isSame(@Nullable LogicalPosition[] caretPosition) { + // TODO: allow small fluctuations ? + if (caretPosition == null) return true; + if (caretPosition.length != 2) return false; + if (!caretPosition[0].equals(myCaretPosition[0])) return false; + if (!caretPosition[1].equals(myCaretPosition[1])) return false; + return true; } } } diff --git a/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/simple/SimpleDiffViewer.java b/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/simple/SimpleDiffViewer.java index 0e184630ed09..467a66573f59 100644 --- a/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/simple/SimpleDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/simple/SimpleDiffViewer.java @@ -345,7 +345,7 @@ class SimpleDiffViewer extends TwosideTextDiffViewer { EditorEx editor = getCurrentEditor(); int line = getCurrentSide().getStartLine(targetChange.getFragment()); - DiffUtil.scrollToLine(editor, line); + DiffUtil.scrollEditor(editor, line); return true; } @@ -366,7 +366,8 @@ class SimpleDiffViewer extends TwosideTextDiffViewer { } if (line == -1) return false; - return doScrollToLine(Pair.create(Side.RIGHT, line)); + scrollToLine(Side.RIGHT, line); + return true; } // diff --git a/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/DiffUserDataKeys.java b/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/DiffUserDataKeys.java index 47e3cf1dc846..d1392ddbafea 100644 --- a/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/DiffUserDataKeys.java +++ b/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/DiffUserDataKeys.java @@ -3,23 +3,22 @@ package com.intellij.openapi.util.diff.tools.util; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.diff.DiffNavigationContext; +import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.diff.util.Side; import javax.swing.*; import java.util.List; public interface DiffUserDataKeys { - /* - * Scroll priority: SCROLL_TO_CHANGE > SCROLL_TO_LINE > NAVIGATION_CONTEXT > DEFAULT(FIRST_CHANGE) - */ - // // DiffRequest // - Key> SCROLL_TO_LINE = Key.create("Diff.ScrollToLine"); + enum ScrollToPolicy {FIRST_CHANGE, LAST_CHANGE} + + Key SCROLL_TO_CHANGE = Key.create("Diff.ScrollToChange"); + Key EDITORS_CARET_POSITION = Key.create("Diff.EditorsCaretPosition"); Key NAVIGATION_CONTEXT = Key.create("Diff.NavigationContext"); Key HELP_ID = Key.create("Diff.HelpId"); @@ -31,9 +30,6 @@ public interface DiffUserDataKeys { Key PREFERRED_FOCUS_SIDE = Key.create("Diff.PreferredFocusSide"); - Key SCROLL_TO_CHANGE = Key.create("Diff.ScrollSoChange"); - enum ScrollToPolicy {FIRST_CHANGE, LAST_CHANGE} - // // DiffChain // diff --git a/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/ScrollToLineHelper.java b/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/ScrollToLineHelper.java deleted file mode 100644 index 4eeeccd32edf..000000000000 --- a/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/ScrollToLineHelper.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.intellij.openapi.util.diff.tools.util; - -import com.intellij.openapi.diff.DiffNavigationContext; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffContext; -import com.intellij.openapi.util.diff.requests.DiffRequest; -import com.intellij.openapi.util.diff.tools.util.DiffUserDataKeys.ScrollToPolicy; -import com.intellij.openapi.util.diff.util.Side; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class ScrollToLineHelper { - protected boolean myShouldScroll = true; - - @Nullable protected ScrollToPolicy myScrollToChange; - @Nullable protected Pair myScrollToLine; - @Nullable protected DiffNavigationContext myNavigationContext; - - public void processContext(@NotNull DiffContext context, @NotNull DiffRequest request) { - myScrollToChange = context.getUserData(DiffUserDataKeys.SCROLL_TO_CHANGE); - myScrollToLine = request.getUserData(DiffUserDataKeys.SCROLL_TO_LINE); - myNavigationContext = request.getUserData(DiffUserDataKeys.NAVIGATION_CONTEXT); - } - - public void onSuccessfulScroll() { - myShouldScroll = false; - - myScrollToChange = null; - myScrollToLine = null; - myNavigationContext = null; - } -} diff --git a/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/twoside/TwosideTextDiffViewer.java b/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/twoside/TwosideTextDiffViewer.java index ca7e29ba01c9..c58f924e1b34 100644 --- a/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/twoside/TwosideTextDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/openapi/util/diff/tools/util/twoside/TwosideTextDiffViewer.java @@ -3,16 +3,13 @@ package com.intellij.openapi.util.diff.tools.util.twoside; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.DiffNavigationContext; -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.*; 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.Pair; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.diff.actions.impl.FocusOppositePaneAction; import com.intellij.openapi.util.diff.actions.impl.OpenInEditorWithMouseAction; import com.intellij.openapi.util.diff.api.FrameDiffTool.DiffContext; @@ -23,7 +20,6 @@ import com.intellij.openapi.util.diff.requests.ContentDiffRequest; import com.intellij.openapi.util.diff.requests.DiffRequest; import com.intellij.openapi.util.diff.tools.util.DiffUserDataKeys; import com.intellij.openapi.util.diff.tools.util.DiffUserDataKeys.ScrollToPolicy; -import com.intellij.openapi.util.diff.tools.util.ScrollToLineHelper; import com.intellij.openapi.util.diff.tools.util.SyncScrollSupport; import com.intellij.openapi.util.diff.tools.util.base.TextDiffViewerBase; import com.intellij.openapi.util.diff.util.CalledInAwt; @@ -122,8 +118,8 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { if (side != null) myCurrentSide = side; } - myScrollToLineHelper.processContext(myContext, myRequest); - myScrollToLineHelper.scrollOnInit(); + myScrollToLineHelper.processContext(); + myScrollToLineHelper.onInit(); } private void updateContextHints() { @@ -131,9 +127,7 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { myContext.putUserData(DiffUserDataKeys.PREFERRED_FOCUS_SIDE, myCurrentSide); } - Side side = getCurrentSide(); - int line = DiffUtil.getLineToScroll(getCurrentEditor()); - myRequest.putUserData(DiffUserDataKeys.SCROLL_TO_LINE, Pair.create(side, line)); + myScrollToLineHelper.updateContext(); } @NotNull @@ -166,7 +160,7 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { @CalledInAwt protected void scrollOnRediff() { - myScrollToLineHelper.scrollOnRediff(); + myScrollToLineHelper.onRediff(); } // @@ -273,21 +267,15 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { // @CalledInAwt - protected boolean doScrollToLine(@NotNull Pair scrollToLine) { - Side side = scrollToLine.first; - Integer line = scrollToLine.second; - + protected void scrollToLine(@NotNull Side side, int line) { Editor editor = side.select(myEditor1, myEditor2); - if (editor != null && line != -1) { - DiffUtil.scrollToLine(editor, line); - myCurrentSide = side; - return true; - } - return false; + if (editor == null) return; + DiffUtil.scrollEditor(editor, line); + myCurrentSide = side; } @CalledInAwt - protected boolean doScrollToChange(@NotNull ScrollToPolicy scrollToPolicy) { + protected boolean doScrollToChange(@NotNull ScrollToPolicy scrollToChangePolicy) { return false; } @@ -409,19 +397,108 @@ public abstract class TwosideTextDiffViewer extends TextDiffViewerBase { } } - private class MyScrollToLineHelper extends ScrollToLineHelper { - public void scrollOnInit() { - if (myShouldScroll && myScrollToLine != null && myScrollToChange == null) if (doScrollToLine(myScrollToLine)) onSuccessfulScroll(); + private class MyScrollToLineHelper { + protected boolean myShouldScroll = true; + + @Nullable private ScrollToPolicy myScrollToChange; + @Nullable private EditorsPosition myEditorsPosition; + @Nullable private LogicalPosition[] myCaretPosition; + @Nullable private DiffNavigationContext myNavigationContext; + + public void processContext() { + myScrollToChange = myRequest.getUserData(DiffUserDataKeys.SCROLL_TO_CHANGE); + myEditorsPosition = myRequest.getUserData(EditorsPosition.KEY); + myCaretPosition = myRequest.getUserData(DiffUserDataKeys.EDITORS_CARET_POSITION); + myNavigationContext = myRequest.getUserData(DiffUserDataKeys.NAVIGATION_CONTEXT); } - public void scrollOnRediff() { - EditorEx editor = getCurrentEditor(); - if (editor.getCaretModel().getOffset() != 0 || editor.getScrollingModel().getVerticalScrollOffset() != 0) return; + public void updateContext() { + LogicalPosition[] carets = new LogicalPosition[2]; + carets[0] = getPosition(myEditor1); + carets[1] = getPosition(myEditor2); - if (myShouldScroll && myScrollToChange != null) if (doScrollToChange(myScrollToChange)) onSuccessfulScroll(); - if (myShouldScroll && myNavigationContext != null) if (doScrollToContext(myNavigationContext)) onSuccessfulScroll(); - if (myShouldScroll) doScrollToChange(ScrollToPolicy.FIRST_CHANGE); - onSuccessfulScroll(); + EditorsPosition editorsPosition = new EditorsPosition(carets, getPoint(myEditor1), getPoint(myEditor2)); + + myRequest.putUserData(DiffUserDataKeys.SCROLL_TO_CHANGE, null); + myRequest.putUserData(EditorsPosition.KEY, editorsPosition); + myRequest.putUserData(DiffUserDataKeys.EDITORS_CARET_POSITION, carets); + myRequest.putUserData(DiffUserDataKeys.NAVIGATION_CONTEXT, null); + } + + public void onInit() { + if (!myShouldScroll) return; + if (myScrollToChange != null) return; + if (myNavigationContext != null) return; + + if (myCaretPosition != null && myCaretPosition.length == 2) { + if (myEditor1 != null) myEditor1.getCaretModel().moveToLogicalPosition(myCaretPosition[0]); + if (myEditor2 != null) myEditor2.getCaretModel().moveToLogicalPosition(myCaretPosition[1]); + + if (myEditorsPosition != null && myEditorsPosition.isSame(myCaretPosition)) { + scrollToPoint(myEditor1, myEditorsPosition.myPoint1); + scrollToPoint(myEditor2, myEditorsPosition.myPoint2); + } + else { + getCurrentEditor().getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + } + myShouldScroll = false; + } + } + + public void onRediff() { + if (myShouldScroll && myScrollToChange != null) { + myShouldScroll = !doScrollToChange(myScrollToChange); + } + if (myShouldScroll && myNavigationContext != null) { + myShouldScroll = !doScrollToContext(myNavigationContext); + } + if (myShouldScroll) { + doScrollToChange(ScrollToPolicy.FIRST_CHANGE); + } + myShouldScroll = false; + } + + @NotNull + private LogicalPosition getPosition(@Nullable Editor editor) { + return editor != null ? editor.getCaretModel().getLogicalPosition() : new LogicalPosition(0, 0); + } + + @NotNull + private Point getPoint(@Nullable Editor editor) { + if (editor == null) return new Point(0, 0); + ScrollingModel model = editor.getScrollingModel(); + return new Point(model.getHorizontalScrollOffset(), model.getVerticalScrollOffset()); + } + + private void scrollToPoint(@Nullable Editor editor, @NotNull Point point) { + if (editor == null) return; + editor.getScrollingModel().disableAnimation(); + editor.getScrollingModel().scrollHorizontally(point.x); + editor.getScrollingModel().scrollVertically(point.y); + editor.getScrollingModel().enableAnimation(); + } + } + + private static class EditorsPosition { + public static final Key KEY = Key.create("Diff.EditorsPosition"); + + @NotNull public final LogicalPosition[] myCaretPosition; + @NotNull public final Point myPoint1; + @NotNull public final Point myPoint2; + + public EditorsPosition(@NotNull LogicalPosition[] caretPosition, @NotNull Point point1, @NotNull Point point2) { + myCaretPosition = caretPosition; + myPoint1 = point1; + myPoint2 = point2; + } + + public boolean isSame(@Nullable LogicalPosition[] caretPosition) { + // TODO: allow small fluctuations ? + if (caretPosition == null) return true; + if (caretPosition.length != 2) return false; + if (!caretPosition[0].equals(myCaretPosition[0])) return false; + if (!caretPosition[1].equals(myCaretPosition[1])) return false; + return true; } } } diff --git a/platform/diff-impl/src/com/intellij/openapi/util/diff/util/DiffUtil.java b/platform/diff-impl/src/com/intellij/openapi/util/diff/util/DiffUtil.java index 4c96b002a92c..196499e9c3a2 100644 --- a/platform/diff-impl/src/com/intellij/openapi/util/diff/util/DiffUtil.java +++ b/platform/diff-impl/src/com/intellij/openapi/util/diff/util/DiffUtil.java @@ -146,10 +146,18 @@ public class DiffUtil { editor.reinitSettings(); } - public static void scrollToLine(@Nullable final Editor editor, int line) { + public static void scrollEditor(@Nullable final Editor editor, int line) { + scrollEditor(editor, line, 0); + } + + public static void scrollEditor(@Nullable final Editor editor, int line, int column) { + scrollEditor(editor, new LogicalPosition(line, column)); + } + + public static void scrollEditor(@Nullable final Editor editor, @NotNull LogicalPosition position) { if (editor == null) return; editor.getCaretModel().removeSecondaryCarets(); - editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(line, 0)); + editor.getCaretModel().moveToLogicalPosition(position); ScrollingModel scrollingModel = editor.getScrollingModel(); scrollingModel.disableAnimation(); scrollingModel.scrollToCaret(ScrollType.CENTER); @@ -164,11 +172,6 @@ public class DiffUtil { scrollingModel.scrollToCaret(ScrollType.CENTER); } - public static int getLineToScroll(@NotNull Editor editor) { - // TODO: scroll to visible area, not caret - return editor.getCaretModel().getLogicalPosition().line; - } - // // UI //