diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java index 1532fb1f4680..300287efd0f2 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java @@ -86,6 +86,7 @@ public class ConsoleHistoryController implements Disposable { private boolean myMultiline; private ModelHelper myHelper; private long myLastSaveStamp; + private boolean isMoveCaretToTheFirstLine = false; /** * @deprecated use {@link #ConsoleHistoryController(ConsoleRootType, String, LanguageConsoleView)} or {@link #ConsoleHistoryController(ConsoleRootType, String, LanguageConsoleView, ConsoleHistoryModel)} @@ -106,6 +107,12 @@ public class ConsoleHistoryController implements Disposable { myConsole = console; } + public ConsoleHistoryController(@NotNull ConsoleRootType rootType, @Nullable String persistenceId, + @NotNull LanguageConsoleView console, boolean moveCaretToTheFirstLine) { + this(rootType, persistenceId, console); + isMoveCaretToTheFirstLine = moveCaretToTheFirstLine; + } + @TestOnly public void setModel(@NotNull ConsoleHistoryModel model){ myHelper = new ModelHelper(myHelper.myRootType, myHelper.myId, model); @@ -302,6 +309,10 @@ public class ConsoleHistoryController implements Disposable { Entry command = myNext ? getModel().getHistoryNext() : getModel().getHistoryPrev(); if (!myMultiline && command == null || !hasHistory && !myNext) return; setConsoleText(command, !hasHistory, true); + if (isMoveCaretToTheFirstLine && myNext) { + EditorEx consoleEditor = myConsole.getConsoleEditor(); + consoleEditor.getCaretModel().moveToOffset(consoleEditor.getDocument().getLineEndOffset(0)); + } } @Override diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java index 46d97a7ae19c..864ff5da10d0 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java @@ -86,21 +86,26 @@ public final class LanguageConsoleBuilder { } @NotNull - public LanguageConsoleBuilder initActions(@NotNull BaseConsoleExecuteActionHandler executeActionHandler, @NotNull String historyType) { + public LanguageConsoleBuilder initActions(@NotNull BaseConsoleExecuteActionHandler executeActionHandler, @NotNull String historyType, boolean moveCaretToTheFirstLine) { if (consoleView == null) { this.executeActionHandler = executeActionHandler; this.historyType = historyType; } else { - doInitAction(consoleView, executeActionHandler, historyType); + doInitAction(consoleView, executeActionHandler, historyType, moveCaretToTheFirstLine); } return this; } - private void doInitAction(@NotNull LanguageConsoleView console, @NotNull BaseConsoleExecuteActionHandler executeActionHandler, @NotNull String historyType) { + @NotNull + public LanguageConsoleBuilder initActions(@NotNull BaseConsoleExecuteActionHandler executeActionHandler, @NotNull String historyType) { + return initActions(executeActionHandler, historyType, false); + } + + private void doInitAction(@NotNull LanguageConsoleView console, @NotNull BaseConsoleExecuteActionHandler executeActionHandler, @NotNull String historyType, boolean moveCaretToTheFirstLine) { ConsoleExecuteAction action = new ConsoleExecuteAction(console, executeActionHandler, executionEnabled); action.registerCustomShortcutSet(action.getShortcutSet(), console.getConsoleEditor().getComponent()); - new ConsoleHistoryController(new MyConsoleRootType(historyType), null, console).install(); + new ConsoleHistoryController(new MyConsoleRootType(historyType), null, console, moveCaretToTheFirstLine).install(); } /** @@ -161,7 +166,7 @@ public final class LanguageConsoleBuilder { } if (executeActionHandler != null) { assert historyType != null; - doInitAction(consoleView, executeActionHandler, historyType); + doInitAction(consoleView, executeActionHandler, historyType, false); } if (processInputStateKey != null) { diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java index 5a6526777043..602c49035766 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunnerImpl.java @@ -1107,7 +1107,7 @@ public class PydevConsoleRunnerImpl implements PydevConsoleRunner { myConsoleExecuteActionHandler = new PydevConsoleExecuteActionHandler(myConsoleView, myProcessHandler, myPydevConsoleCommunication); myConsoleExecuteActionHandler.setEnabled(false); - new ConsoleHistoryController(PyConsoleRootType.Companion.getInstance(), "", myConsoleView).install(); + new ConsoleHistoryController(PyConsoleRootType.Companion.getInstance(), "", myConsoleView, true).install(); return myConsoleExecuteActionHandler; } diff --git a/python/src/com/jetbrains/python/debugger/PyDebugRunner.java b/python/src/com/jetbrains/python/debugger/PyDebugRunner.java index c34d2d53a4bf..0c93cda9656f 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugRunner.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugRunner.java @@ -7,7 +7,7 @@ import com.intellij.execution.ExecutionManager; import com.intellij.execution.ExecutionResult; import com.intellij.execution.Executor; import com.intellij.execution.configurations.*; -import com.intellij.execution.console.LanguageConsoleBuilder; +import com.intellij.execution.console.*; import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.impl.ConsoleViewImpl; import com.intellij.execution.process.ProcessHandler; @@ -416,7 +416,7 @@ public class PyDebugRunner implements ProgramRunner { pythonConsoleView.setExecutionHandler(consoleExecuteActionHandler); debugProcess.getSession().addSessionListener(consoleExecuteActionHandler); - new LanguageConsoleBuilder(pythonConsoleView).processHandler(processHandler).initActions(consoleExecuteActionHandler, "py"); + new LanguageConsoleBuilder(pythonConsoleView).processHandler(processHandler).initActions(consoleExecuteActionHandler, "py", true); debugConsoleCommunication.addCommunicationListener(new ConsoleCommunicationListener() { diff --git a/python/testSrc/com/jetbrains/env/debug/PythonConsoleTest.java b/python/testSrc/com/jetbrains/env/debug/PythonConsoleTest.java index 6f0a0b2337a2..31e0d47c56eb 100644 --- a/python/testSrc/com/jetbrains/env/debug/PythonConsoleTest.java +++ b/python/testSrc/com/jetbrains/env/debug/PythonConsoleTest.java @@ -3,7 +3,13 @@ package com.jetbrains.env.debug; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +import com.intellij.execution.console.ConsoleHistoryController; +import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.testFramework.EdtTestUtil; +import com.intellij.testFramework.TestActionEvent; import com.intellij.xdebugger.impl.ui.tree.nodes.XDebuggerTreeNode; import com.jetbrains.env.PyEnvTestCase; import com.jetbrains.python.console.PyConsoleOptions; @@ -367,4 +373,62 @@ public class PythonConsoleTest extends PyEnvTestCase { } }); } + + @Test + public void testConsoleHistoryNavigation() { + runPythonTest(new PyConsoleTask("/debug") { + @Override + public void testing() { + addToHistory("a = 1"); + addToHistory(""" + def foo(): + x = 1 + y = 2 + return x + y"""); + + runHistoryAction(true); + checkCaretPosition(true); + + runHistoryAction(true); + checkCaretPosition(true); + + runHistoryAction(false); + checkCaretPosition(false); + } + + private void addToHistory(String command) { + WriteCommandAction.runWriteCommandAction(getProject(), () -> { + ConsoleHistoryController.getController(getConsoleView()).addToHistory(command); + }); + } + + private void runHistoryAction(boolean isNext) { + ConsoleHistoryController controller = ConsoleHistoryController.getController(getConsoleView()); + EdtTestUtil.runInEdtAndWait(() -> { + AnAction action = isNext ? controller.getHistoryNext() : controller.getHistoryPrev(); + action.actionPerformed(TestActionEvent.createTestEvent()); + }); + } + + private void checkCaretPosition(boolean isNext) { + EditorEx consoleEditor = getConsoleView().getConsoleEditor(); + + ApplicationManager.getApplication().runReadAction(() -> { + int offset = consoleEditor.getCaretModel().getOffset(); + int caretLine = consoleEditor.getDocument().getLineNumber(offset); + int linesCount = consoleEditor.getDocument().getLineCount(); + if (linesCount > 1) { + if (isNext) { + assertEquals(0, caretLine); + } + else { + assertEquals(linesCount - 1, caretLine); + } + } else { + assertEquals(0, caretLine); + } + }); + } + }); + } }