diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryBaseModel.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryBaseModel.java new file mode 100644 index 000000000000..d9d8c56ec492 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryBaseModel.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2017 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.execution.console; + +import com.intellij.openapi.util.ModificationTracker; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public interface ConsoleHistoryBaseModel extends ModificationTracker { + + boolean isEmpty(); + + int getHistorySize(); + + void resetEntries(@NotNull List entries); + + void addToHistory(@Nullable String statement); + + int getMaxHistorySize(); + + void removeFromHistory(String statement); + + @NotNull + List getEntries(); +} 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 1a85e3e77c84..a1be9b31e809 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryController.java @@ -17,6 +17,7 @@ package com.intellij.execution.console; import com.intellij.AppTopics; import com.intellij.codeInsight.lookup.LookupManager; +import com.intellij.execution.console.ConsoleHistoryModel.Entry; import com.intellij.ide.scratch.ScratchFileService; import com.intellij.lang.Language; import com.intellij.openapi.Disposable; @@ -54,9 +55,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.ExceptionUtil; -import com.intellij.util.ObjectUtils; import com.intellij.util.PathUtil; -import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.SafeFileOutputStream; import com.intellij.xml.util.XmlStringUtil; @@ -64,6 +63,7 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.xml.XppReader; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlSerializer; @@ -80,9 +80,7 @@ public class ConsoleHistoryController { private static final Logger LOG = Logger.getInstance("com.intellij.execution.console.ConsoleHistoryController"); - /** @noinspection MismatchedQueryAndUpdateOfCollection*/ - private final static Map ourModels = ConcurrentFactoryMap.createMap(key-> - new ConsoleHistoryModel(null), ContainerUtil::createConcurrentWeakValueMap); + private final static Map ourControllers = ContainerUtil.createConcurrentWeakMap(ContainerUtil.identityStrategy()); @@ -91,7 +89,7 @@ public class ConsoleHistoryController { private final AnAction myHistoryPrev = new MyAction(false, getKeystrokesUpDown(false)); private final AnAction myBrowseHistory = new MyBrowseAction(); private boolean myMultiline; - private final ModelHelper myHelper; + private ModelHelper myHelper; private long myLastSaveStamp; @Deprecated @@ -100,15 +98,21 @@ public class ConsoleHistoryController { } public ConsoleHistoryController(@NotNull ConsoleRootType rootType, @Nullable String persistenceId, @NotNull LanguageConsoleView console) { - this(rootType, persistenceId, console, ourModels.get(getHistoryName(rootType, fixNullPersistenceId(persistenceId, console)))); + this(rootType, fixNullPersistenceId(persistenceId, console), console, + ConsoleHistoryModelProvider.findModelForConsole(fixNullPersistenceId(persistenceId, console), console)); } - private ConsoleHistoryController(@NotNull ConsoleRootType rootType, @Nullable String persistenceId, - @NotNull LanguageConsoleView console, @NotNull ConsoleHistoryModel model) { - myHelper = new ModelHelper(rootType, fixNullPersistenceId(persistenceId, console), model.copy()); + private ConsoleHistoryController(@NotNull ConsoleRootType rootType, @NotNull String persistenceId, + @NotNull LanguageConsoleView console, @NotNull ConsoleHistoryModel model) { + myHelper = new ModelHelper(rootType, persistenceId, model); myConsole = console; } + @TestOnly + public void setModel(@NotNull ConsoleHistoryModel model){ + myHelper = new ModelHelper(myHelper.myRootType, myHelper.myId, model); + } + //@Nullable public static ConsoleHistoryController getController(@NotNull LanguageConsoleView console) { return ourControllers.get(console); @@ -214,7 +218,7 @@ public class ConsoleHistoryController { boolean result = myHelper.loadHistory(id, myConsole.getVirtualFile()); String userValue = myHelper.getContent(); if (prev != userValue && userValue != null) { - setConsoleText(userValue, false, false); + setConsoleText(new Entry(userValue, -1), false, false); } return result; } @@ -238,8 +242,8 @@ public class ConsoleHistoryController { return myBrowseHistory; } - protected void setConsoleText(final String command, final boolean storeUserText, final boolean regularMode) { - if (regularMode && myMultiline && StringUtil.isEmptyOrSpaces(command)) return; + protected void setConsoleText(final Entry command, final boolean storeUserText, final boolean regularMode) { + if (regularMode && myMultiline && StringUtil.isEmptyOrSpaces(command.getText())) return; final Editor editor = myConsole.getCurrentEditor(); final Document document = editor.getDocument(); new WriteCommandAction.Simple(myConsole.getProject()) { @@ -247,10 +251,11 @@ public class ConsoleHistoryController { public void run() { if (storeUserText) { String text = document.getText(); - if (Comparing.equal(command, text) && myHelper.getContent() != null) return; + if (Comparing.equal(command.getText(), text) && myHelper.getContent() != null) return; myHelper.setContent(text); + myHelper.getModel().setContent(text); } - String text = StringUtil.notNullize(command); + String text = StringUtil.notNullize(command.getText()); int offset; if (regularMode) { if (myMultiline) { @@ -258,7 +263,7 @@ public class ConsoleHistoryController { } else { document.setText(text); - offset = document.getTextLength(); + offset = command.getOffset() == -1 ? document.getTextLength() : command.getOffset(); } } else { @@ -302,15 +307,10 @@ public class ConsoleHistoryController { @Override public void actionPerformed(final AnActionEvent e) { - String command; - if (myNext) { - command = getModel().getHistoryNext(); - if (!myMultiline && command == null) return; - } - else { - command = ObjectUtils.chooseNotNull(getModel().getHistoryPrev(), myMultiline ? "" : StringUtil.notNullize(myHelper.getContent())); - } - setConsoleText(command, myNext && !getModel().hasHistory(false), true); + boolean hasHistory = getModel().hasHistory(); // need to check before next line's side effect + Entry command = myNext ? getModel().getHistoryNext() : getModel().getHistoryPrev(); + if (!myMultiline && command == null) return; + setConsoleText(command, myNext && !hasHistory, true); } @Override @@ -344,7 +344,7 @@ public class ConsoleHistoryController { else { final int lineCount = document.getLineCount(); return (lineCount == 0 || document.getLineNumber(caretModel.getOffset()) == lineCount - 1) && - StringUtil.isEmptyOrSpaces(document.getText().substring(caretModel.getOffset())); + (StringUtil.isEmptyOrSpaces(document.getText().substring(caretModel.getOffset())) || myHelper.getModel().prevOnLastLine()); } } @@ -376,6 +376,7 @@ public class ConsoleHistoryController { return content; } + @NotNull @Override protected List getContents() { List entries = getModel().getEntries(); @@ -412,7 +413,7 @@ public class ConsoleHistoryController { chooser.setSplitterOrientation(false); chooser.setSelectedIndex(Math.max(0, getModel().getHistorySize() - getModel().getCurrentIndex() - 1)); if (chooser.showAndGet() && myConsole.getCurrentEditor().getComponent().isShowing()) { - setConsoleText(chooser.getSelectedText(), false, true); + setConsoleText(new Entry(chooser.getSelectedText(), -1), false, true); } } } diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryModel.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryModel.java index ed3e27017377..51a798c7abc5 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryModel.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryModel.java @@ -15,40 +15,49 @@ */ package com.intellij.execution.console; -import com.intellij.openapi.util.ModificationTracker; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; - /** * @author Yuli Fiterman */ -public interface ConsoleHistoryModel extends ModificationTracker { - void resetEntries(@NotNull List entries); - void addToHistory(@Nullable String statement); - - int getMaxHistorySize(); - - void removeFromHistory(String statement); - - List getEntries(); - - boolean isEmpty(); - - int getHistorySize(); +public interface ConsoleHistoryModel extends ConsoleHistoryBaseModel { @Nullable - String getHistoryNext(); + Entry getHistoryNext(); @Nullable - String getHistoryPrev(); + Entry getHistoryPrev(); - boolean hasHistory(boolean next); - - ConsoleHistoryModel copy(); + boolean hasHistory(); int getCurrentIndex(); + void setContent(@NotNull String userContent); + + /* if true then overrides the navigation behavior such that the down key on last line always navigates to prev instead of only when there + are no more characters in from of the caret + */ + default boolean prevOnLastLine() { + return false; + } + + class Entry { + private final String text; + private final int offset; + + public Entry(String text, int offset) { + this.text = text; + this.offset = offset; + } + + public String getText() { + return text; + } + + public int getOffset() { + return offset; + } + } } diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryModelProvider.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryModelProvider.java new file mode 100644 index 000000000000..bf4bf786d242 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleHistoryModelProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2017 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.execution.console; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.extensions.Extensions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + + +public interface ConsoleHistoryModelProvider { + ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.consoleHistoryModelProvider"); + + @Nullable + ConsoleHistoryModel createModel(@NotNull String persistenceId, @NotNull LanguageConsoleView consoleView); + + static ConsoleHistoryModel findModelForConsole(@NotNull String persistenceId, @NotNull LanguageConsoleView consoleView) { + for (ConsoleHistoryModelProvider provider : Extensions.getExtensions(EP_NAME)) { + ConsoleHistoryModel model = provider.createModel(persistenceId, consoleView); + if (model != null) { + return model; + } + } + return DefaultConsoleHistoryModel.createModel(persistenceId); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/console/DefaultConsoleHistoryModel.java b/platform/lang-impl/src/com/intellij/execution/console/DefaultConsoleHistoryModel.java index bd43bc441093..541fca95b542 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/DefaultConsoleHistoryModel.java +++ b/platform/lang-impl/src/com/intellij/execution/console/DefaultConsoleHistoryModel.java @@ -18,23 +18,38 @@ package com.intellij.execution.console; import com.intellij.ide.ui.UISettings; import com.intellij.openapi.util.SimpleModificationTracker; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.ConcurrentFactoryMap; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.LinkedList; import java.util.List; +import java.util.Map; /** * @author Gregory.Shrago */ -class DefaultConsoleHistoryModel extends SimpleModificationTracker implements ConsoleHistoryModel { +public class DefaultConsoleHistoryModel extends SimpleModificationTracker implements ConsoleHistoryModel { /** * @noinspection FieldCanBeLocal */ + + /** + * @noinspection MismatchedQueryAndUpdateOfCollection + */ + private final static Map ourModels = + ConcurrentFactoryMap.createMap(key -> new DefaultConsoleHistoryModel(null), + ContainerUtil::createConcurrentWeakValueMap); + + public static DefaultConsoleHistoryModel createModel(String persistenceId) { + return ourModels.get(persistenceId).copy(); + } + private final Object myLock; private final LinkedList myEntries; private int myIndex; + private String myContent; DefaultConsoleHistoryModel(@Nullable DefaultConsoleHistoryModel masterModel) { myEntries = masterModel == null ? new LinkedList<>() : masterModel.myEntries; @@ -96,6 +111,7 @@ class DefaultConsoleHistoryModel extends SimpleModificationTracker implements Co } } + @NotNull @Override public List getEntries() { synchronized (myLock) { @@ -119,32 +135,33 @@ class DefaultConsoleHistoryModel extends SimpleModificationTracker implements Co @Override @Nullable - public String getHistoryNext() { + public Entry getHistoryNext() { synchronized (myLock) { if (myIndex >= 0) --myIndex; - return getCurrentEntry(); + return new Entry(getCurrentEntry(), -1); } } @Override @Nullable - public String getHistoryPrev() { + public Entry getHistoryPrev() { synchronized (myLock) { if (myIndex <= myEntries.size() - 1) ++myIndex; - return getCurrentEntry(); + return new Entry(getCurrentEntry(), -1); } } @Override - public boolean hasHistory(final boolean next) { + public boolean hasHistory() { synchronized (myLock) { - return next ? myIndex > 0 : myIndex < myEntries.size() - 1; + return myIndex <= myEntries.size() - 1; } } String getCurrentEntry() { synchronized (myLock) { - return myIndex >= 0 && myIndex < myEntries.size() ? myEntries.get(myIndex) : null; + return myIndex >= 0 && myIndex < myEntries.size() ? myEntries.get(myIndex) : + myIndex == myEntries.size() ? myContent : null; } } @@ -153,4 +170,9 @@ class DefaultConsoleHistoryModel extends SimpleModificationTracker implements Co return myIndex; } } + + @Override + public void setContent(@NotNull String userContent) { + myContent = userContent; + } } diff --git a/platform/lang-impl/src/com/intellij/execution/console/PrefixHistoryModel.kt b/platform/lang-impl/src/com/intellij/execution/console/PrefixHistoryModel.kt new file mode 100644 index 000000000000..f84788e821ed --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/console/PrefixHistoryModel.kt @@ -0,0 +1,183 @@ +/* + * Copyright 2000-2017 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.execution.console + +import com.intellij.execution.console.ConsoleHistoryModel.Entry +import com.intellij.ide.ui.UISettings +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.util.ModificationTracker +import com.intellij.openapi.util.SimpleModificationTracker +import com.intellij.openapi.util.TextRange +import com.intellij.util.containers.ConcurrentFactoryMap +import com.intellij.util.containers.ContainerUtil +import gnu.trove.TIntStack + +/** + * @author Yuli Fiterman + */ + +private val MasterModels = ConcurrentFactoryMap.createMap( + { + MasterModel() + }, { + ContainerUtil.createConcurrentWeakValueMap() + }) + + +private fun assertDispatchThread() = ApplicationManager.getApplication().assertIsDispatchThread() + + +fun createModel(persistenceId: String, console: LanguageConsoleView): ConsoleHistoryModel { + val masterModel: MasterModel = MasterModels[persistenceId]!! + fun getPrefixFromConsole(): String { + val caretOffset = console.consoleEditor.caretModel.offset + return console.editorDocument.getText(TextRange.create(0, caretOffset)) + } + return PrefixHistoryModel(masterModel, ::getPrefixFromConsole) + +} + + +private class PrefixHistoryModel constructor(private val masterModel: MasterModel, + private val getPrefixFn: () -> String) : ConsoleHistoryBaseModel by masterModel, + ConsoleHistoryModel { + + var userContent: String = "" + override fun setContent(userContent: String) { + this.userContent = userContent + } + + private var myEntries: List? = null + private var myCurrentIndex: Int = -1 + private var myPrevEntries: TIntStack = TIntStack() + + init { + resetIndex() + } + + override fun resetEntries(entries: MutableList) { + masterModel.resetEntries(entries) + resetIndex() + } + + override fun addToHistory(statement: String?) { + assertDispatchThread() + if (statement.isNullOrEmpty()) { + return + } + masterModel.addToHistory(statement) + resetIndex() + } + + override fun removeFromHistory(statement: String?) { + assertDispatchThread() + if (statement.isNullOrEmpty()) { + return + } + masterModel.removeFromHistory(statement) + resetIndex() + } + + private fun resetIndex() { + myEntries = null + myCurrentIndex = -1 + myPrevEntries.clear(); + } + + override fun getHistoryNext(): Entry? { + val entries = myEntries ?: masterModel.entries + val offset = if (myCurrentIndex == -1) entries.size else myCurrentIndex + if (offset <= 0) { + return null + } + val searchPrefix = getPrefixFn() + val res = entries.withIndex().findLast { it.index < offset && it.value.startsWith(searchPrefix) } ?: return null + + if (myEntries == null) { + myEntries = entries + } + if (myCurrentIndex != -1) { + myPrevEntries.push(myCurrentIndex) + } + + myCurrentIndex = res.index + return Entry(res.value, searchPrefix.length) + } + + override fun getHistoryPrev(): Entry? { + val entries = myEntries ?: return null + if (myPrevEntries.size() > 0) { + myCurrentIndex = myPrevEntries.pop() + return entries[myCurrentIndex].let { Entry(it, -1) } + } + else { + resetIndex() + return Entry(userContent, -1) + } + } + + override fun getCurrentIndex(): Int = + if (myCurrentIndex != -1) { + myCurrentIndex + } + else { + entries.size - 1 + } + + override fun prevOnLastLine(): Boolean = true + + override fun hasHistory(): Boolean = myEntries != null +} + +private class MasterModel(private val modTracker: SimpleModificationTracker = SimpleModificationTracker()) : ConsoleHistoryBaseModel, ModificationTracker by modTracker { + + @Volatile private var myEntries: MutableList = mutableListOf() + + @Suppress("UNCHECKED_CAST") + override fun getEntries(): MutableList = myEntries.toMutableList() + + override fun resetEntries(ent: List) { + myEntries = ent.toMutableList() + } + + override fun addToHistory(statement: String?) { + if (statement == null) { + return + } + val entries = myEntries + entries.remove(statement) + entries.add(statement) + if (entries.size >= maxHistorySize) { + entries.removeAt(0) + } + modTracker.incModificationCount() + } + + override fun removeFromHistory(statement: String?) { + if (statement == null) { + return + } + val entries = myEntries; + entries.remove(statement) + modTracker.incModificationCount() + } + + override fun getMaxHistorySize(): Int = UISettings.instance.consoleCommandHistoryLimit + + override fun isEmpty(): Boolean = entries.isEmpty() + + override fun getHistorySize(): Int = entries.size +} diff --git a/platform/lang-impl/testSources/com/intellij/execution/console/ConsoleHistoryConstrollerTest.java b/platform/lang-impl/testSources/com/intellij/execution/console/ConsoleHistoryConstrollerTest.java new file mode 100644 index 000000000000..3ceeba02a1f9 --- /dev/null +++ b/platform/lang-impl/testSources/com/intellij/execution/console/ConsoleHistoryConstrollerTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2000-2017 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.execution.console; + + +import com.intellij.openapi.fileTypes.PlainTextLanguage; +import com.intellij.openapi.util.Disposer; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.testFramework.EditorTestUtil; +import com.intellij.testFramework.LightPlatformCodeInsightTestCase; +import com.intellij.testFramework.TestActionEvent; + + +/** + * @author Yuli Fiterman + */ +public class ConsoleHistoryConstrollerTest extends LightPlatformCodeInsightTestCase { + private LanguageConsoleImpl myConsole; + private ConsoleHistoryController myHistoryController; + private ConsoleExecuteAction myExecAction; + + @Override + public void setUp() throws Exception { + super.setUp(); + + myConsole = new LanguageConsoleImpl(getProject(), "Test console", PlainTextLanguage.INSTANCE); + myConsole.setConsoleEditorEnabled(true); + myExecAction = new ConsoleExecuteAction(myConsole, new MockExecutionActionHandler()); + myExecAction.registerCustomShortcutSet(myExecAction.getShortcutSet(), myConsole.getConsoleEditor().getComponent()); + myHistoryController = new ConsoleHistoryController(new ConsoleRootType("test console", null) { + }, null, myConsole); + myHistoryController.setModel(PrefixHistoryModelKt.createModel("default", myConsole)); + myHistoryController.install(); + myConsole.setConsoleEditorEnabled(true); + myEditor = myConsole.getConsoleEditor(); + myVFile = myConsole.getVirtualFile(); + myFile = PsiDocumentManager.getInstance(getProject()).getPsiFile(myEditor.getDocument()); + } + + private void setCaretWithText(String markedText) { + myConsole.setInputText(markedText); + EditorTestUtil.CaretAndSelectionState state = EditorTestUtil.extractCaretAndSelectionMarkers(myConsole.getEditorDocument()); + EditorTestUtil.setCaretsAndSelection(myConsole.getConsoleEditor(), state); + PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); + } + + private void executeCommand() { + PsiDocumentManager.getInstance(getProject()).commitAllDocuments(); + myExecAction.actionPerformed(new TestActionEvent()); + } + + private void execStatementList1() { + myConsole.setInputText("Statement 1"); + executeCommand(); + myConsole.setInputText("Statement 2"); + executeCommand(); + + myConsole.setInputText("Statement 3"); + executeCommand(); + + myConsole.setInputText("Different Prefix"); + executeCommand(); + + assertEquals("", myConsole.getEditorDocument().getText()); + } + + private void consoleNext() { + myHistoryController.getHistoryNext().actionPerformed(null); + } + + private void consolePrev() { + myHistoryController.getHistoryPrev().actionPerformed(null); + } + + public void testNavigateUp() { + execStatementList1(); + setCaretWithText("Statement 4"); + consoleNext(); + checkResultByText("Statement 3"); + } + + public void testNavigateDown() { + execStatementList1(); + setCaretWithText("Statement 4"); + consoleNext(); + consolePrev(); + checkResultByText("Statement 4"); + } + + public void testNavigateUpNoPrefix() { + execStatementList1(); + setCaretWithText("Statement 4"); + consoleNext(); + checkResultByText("Different Prefix"); + } + + @Override + public void tearDown() throws Exception { + try { + + Disposer.dispose(myConsole); + myVFile = null; + } + finally { + super.tearDown(); + } + } + + private static class MockExecutionActionHandler extends BaseConsoleExecuteActionHandler { + + public MockExecutionActionHandler() { + super(true); + } + } +} + + + + diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index 83f8f2039151..58925216a26e 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -912,6 +912,7 @@ interface="org.jetbrains.uast.UastLanguagePlugin"/> + diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml index 357a50c76c34..d85175b780e2 100644 --- a/python/src/META-INF/python-core-common.xml +++ b/python/src/META-INF/python-core-common.xml @@ -628,6 +628,7 @@ + diff --git a/python/src/com/jetbrains/python/console/PyConsoleHistoryModelProvider.java b/python/src/com/jetbrains/python/console/PyConsoleHistoryModelProvider.java new file mode 100644 index 000000000000..7c5ee350829e --- /dev/null +++ b/python/src/com/jetbrains/python/console/PyConsoleHistoryModelProvider.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2017 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.jetbrains.python.console; + +import com.intellij.execution.console.ConsoleHistoryModel; +import com.intellij.execution.console.ConsoleHistoryModelProvider; +import com.intellij.execution.console.LanguageConsoleView; +import com.intellij.execution.console.PrefixHistoryModelKt; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Yuli Fiterman + */ +public class PyConsoleHistoryModelProvider implements ConsoleHistoryModelProvider { + @Nullable + @Override + public ConsoleHistoryModel createModel(@NotNull String persistenceId, @NotNull LanguageConsoleView consoleView) { + return consoleView instanceof PythonConsoleView ? PrefixHistoryModelKt.createModel(persistenceId, consoleView) : null; + } +}