diff --git a/src/com/maddyhome/idea/vim/KeyHandler.java b/src/com/maddyhome/idea/vim/KeyHandler.java
index 87190b910d..968578ed81 100644
--- a/src/com/maddyhome/idea/vim/KeyHandler.java
+++ b/src/com/maddyhome/idea/vim/KeyHandler.java
@@ -45,6 +45,7 @@
import com.maddyhome.idea.vim.helper.*;
import com.maddyhome.idea.vim.key.*;
import com.maddyhome.idea.vim.option.OptionsManager;
+import com.maddyhome.idea.vim.ui.ExEntryPanel;
import com.maddyhome.idea.vim.ui.ShowCmd;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
@@ -53,6 +54,7 @@
import javax.swing.*;
import java.awt.*;
+import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashMap;
@@ -591,6 +593,22 @@ private boolean handleDigraph(@NotNull Editor editor,
}
DigraphResult res = editorState.processDigraphKey(key, editor);
+ if (ExEntryPanel.getInstance().isActive()) {
+ switch (res.getResult()) {
+ case DigraphResult.RES_HANDLED:
+ setPromptCharacterEx(commandBuilder.isPuttingLiteral() ? '^' : key.getKeyChar());
+ break;
+ case DigraphResult.RES_DONE:
+ case DigraphResult.RES_BAD:
+ if (key.getKeyCode() == KeyEvent.VK_C && (key.getModifiers() & InputEvent.CTRL_DOWN_MASK) != 0) {
+ return false;
+ } else {
+ ExEntryPanel.getInstance().getEntry().clearCurrentAction();
+ }
+ break;
+ }
+ }
+
switch (res.getResult()) {
case DigraphResult.RES_HANDLED:
editorState.getCommandBuilder().addKey(key);
@@ -745,8 +763,10 @@ private void startWaitingForArgument(Editor editor,
// the key handler when it's complete.
if (action instanceof InsertCompletedDigraphAction) {
editorState.startDigraphSequence();
+ setPromptCharacterEx('?');
} else if (action instanceof InsertCompletedLiteralAction) {
editorState.startLiteralSequence();
+ setPromptCharacterEx('^');
}
break;
case EX_STRING:
@@ -841,6 +861,13 @@ public void fullReset(@Nullable Editor editor) {
}
+ private void setPromptCharacterEx(final char promptCharacter) {
+ final ExEntryPanel exEntryPanel = ExEntryPanel.getInstance();
+ if (exEntryPanel.isActive()) {
+ exEntryPanel.getEntry().setCurrentActionPromptCharacter(promptCharacter);
+ }
+ }
+
// This class is copied from com.intellij.openapi.editor.actionSystem.DialogAwareDataContext.DialogAwareDataContext
private static final class DialogAwareDataContext implements DataContext {
@SuppressWarnings("rawtypes")
@@ -909,7 +936,9 @@ public void run() {
editorState.popModes();
}
- KeyHandler.getInstance().reset(editor);
+ if (editorState.getCommandBuilder().isDone()) {
+ KeyHandler.getInstance().reset(editor);
+ }
}
private final Editor editor;
diff --git a/src/com/maddyhome/idea/vim/command/CommandBuilder.kt b/src/com/maddyhome/idea/vim/command/CommandBuilder.kt
index ee95fe3bc6..d672880067 100644
--- a/src/com/maddyhome/idea/vim/command/CommandBuilder.kt
+++ b/src/com/maddyhome/idea/vim/command/CommandBuilder.kt
@@ -1,6 +1,9 @@
package com.maddyhome.idea.vim.command
import com.maddyhome.idea.vim.action.DuplicableOperatorAction
+import com.maddyhome.idea.vim.action.ResetModeAction
+import com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction
+import com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.key.CommandPartNode
import com.maddyhome.idea.vim.key.Node
@@ -24,6 +27,9 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
var expectedArgumentType: Argument.Type? = null
private set
+ var prevExpectedArgumentType: Argument.Type? = null
+ private set
+
val isReady get() = commandState == CurrentCommandState.READY
val isBad get() = commandState == CurrentCommandState.BAD_COMMAND
val isEmpty get() = commandParts.isEmpty()
@@ -38,6 +44,7 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
fun pushCommandPart(action: EditorActionHandlerBase) {
commandParts.add(Command(count, action, action.type, action.flags))
+ prevExpectedArgumentType = expectedArgumentType
expectedArgumentType = action.argumentType
count = 0
}
@@ -106,6 +113,14 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
return currentCommandPartNode !is RootNode
}
+ fun isPuttingLiteral(): Boolean {
+ return !commandParts.isEmpty() && commandParts.last.action is InsertCompletedLiteralAction
+ }
+
+ fun isDone(): Boolean {
+ return commandParts.isEmpty()
+ }
+
fun completeCommandPart(argument: Argument) {
commandParts.peekLast().argument = argument
commandState = CurrentCommandState.READY
@@ -121,6 +136,11 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
}
fun buildCommand(): Command {
+ if (commandParts.last.action is InsertCompletedDigraphAction || commandParts.last.action is ResetModeAction) {
+ expectedArgumentType = prevExpectedArgumentType
+ prevExpectedArgumentType = null
+ return commandParts.removeLast()
+ }
var command: Command = commandParts.removeFirst()
while (commandParts.size > 0) {
@@ -147,6 +167,7 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
commandParts.clear()
keyList.clear()
expectedArgumentType = null
+ prevExpectedArgumentType = null
}
fun resetInProgressCommandPart(commandPartNode: CommandPartNode) {
diff --git a/src/com/maddyhome/idea/vim/ui/ExEditorKit.java b/src/com/maddyhome/idea/vim/ui/ExEditorKit.java
index ae0870ca87..8d0ebe482b 100644
--- a/src/com/maddyhome/idea/vim/ui/ExEditorKit.java
+++ b/src/com/maddyhome/idea/vim/ui/ExEditorKit.java
@@ -19,11 +19,13 @@
package com.maddyhome.idea.vim.ui;
import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.KeyHandler;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.common.Register;
import com.maddyhome.idea.vim.helper.DigraphResult;
import com.maddyhome.idea.vim.helper.DigraphSequence;
+import com.maddyhome.idea.vim.helper.EditorDataContext;
import com.maddyhome.idea.vim.helper.SearchHelper;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -143,8 +145,14 @@ public void actionPerformed(@NotNull ActionEvent e) {
if (key != null) {
final char c = key.getKeyChar();
if (c > 0) {
- ActionEvent event = new ActionEvent(e.getSource(), e.getID(), String.valueOf(c), e.getWhen(), e.getModifiers());
- super.actionPerformed(event);
+ if (target.useHandleKeyFromEx) {
+ final ExTextField entry = ExEntryPanel.getInstance().getEntry();
+ final Editor editor = entry.getEditor();
+ KeyHandler.getInstance().handleKey(editor, key, new EditorDataContext(editor, entry.getContext()));
+ } else {
+ ActionEvent event = new ActionEvent(e.getSource(), e.getID(), String.valueOf(c), e.getWhen(), e.getModifiers());
+ super.actionPerformed(event);
+ }
target.saveLastEntry();
}
}
diff --git a/src/com/maddyhome/idea/vim/ui/ExShortcutKeyAction.kt b/src/com/maddyhome/idea/vim/ui/ExShortcutKeyAction.kt
index a55b7d4975..dceeffc0f1 100644
--- a/src/com/maddyhome/idea/vim/ui/ExShortcutKeyAction.kt
+++ b/src/com/maddyhome/idea/vim/ui/ExShortcutKeyAction.kt
@@ -21,7 +21,8 @@ package com.maddyhome.idea.vim.ui
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.KeyboardShortcut
-import com.maddyhome.idea.vim.VimPlugin
+import com.maddyhome.idea.vim.KeyHandler
+import com.maddyhome.idea.vim.helper.EditorDataContext
import java.awt.event.KeyEvent
import javax.swing.KeyStroke
@@ -42,7 +43,8 @@ class ExShortcutKeyAction(private val exEntryPanel: ExEntryPanel) : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
val keyStroke = getKeyStroke(e)
if (keyStroke != null) {
- VimPlugin.getProcess().processExKey(exEntryPanel.entry.editor, keyStroke)
+ val editor = exEntryPanel.entry.editor
+ KeyHandler.getInstance().handleKey(editor, keyStroke, EditorDataContext(editor, e.dataContext))
}
}
diff --git a/src/com/maddyhome/idea/vim/ui/ExTextField.java b/src/com/maddyhome/idea/vim/ui/ExTextField.java
index e44ac08212..215d34956e 100644
--- a/src/com/maddyhome/idea/vim/ui/ExTextField.java
+++ b/src/com/maddyhome/idea/vim/ui/ExTextField.java
@@ -52,6 +52,8 @@ public class ExTextField extends JTextField {
public static final @NonNls String KEYMAP_NAME = "ex";
+ public boolean useHandleKeyFromEx = true;
+
ExTextField() {
// We need to store this in a field, because we can't trust getCaret(), as it will return an instance of
// ComposedTextCaret when working with dead keys or input methods
@@ -282,7 +284,12 @@ public void handleKey(@NotNull KeyStroke stroke) {
(stroke.isOnKeyRelease() ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED),
(new Date()).getTime(), modifiers, keyCode, c);
- super.processKeyEvent(event);
+ useHandleKeyFromEx = false;
+ try {
+ super.processKeyEvent(event);
+ }finally {
+ useHandleKeyFromEx = true;
+ }
}
}
@@ -329,7 +336,7 @@ void setCurrentAction(@NotNull ExEditorKit.MultiStepAction action, char pendingI
setCurrentActionPromptCharacter(pendingIndicator);
}
- void clearCurrentAction() {
+ public void clearCurrentAction() {
if (currentAction != null) {
currentAction.reset();
}
@@ -337,7 +344,7 @@ void clearCurrentAction() {
clearCurrentActionPromptCharacter();
}
- void setCurrentActionPromptCharacter(char promptCharacter) {
+ public void setCurrentActionPromptCharacter(char promptCharacter) {
actualText = removePromptCharacter();
this.currentActionPromptCharacter = promptCharacter;
currentActionPromptCharacterOffset = currentActionPromptCharacterOffset == -1 ? getCaretPosition() : currentActionPromptCharacterOffset;
diff --git a/test/org/jetbrains/plugins/ideavim/VimTestCase.kt b/test/org/jetbrains/plugins/ideavim/VimTestCase.kt
index 906c7a8520..c737753cbc 100644
--- a/test/org/jetbrains/plugins/ideavim/VimTestCase.kt
+++ b/test/org/jetbrains/plugins/ideavim/VimTestCase.kt
@@ -453,12 +453,7 @@ abstract class VimTestCase : UsefulTestCase() {
val inputModel = TestInputModel.getInstance(editor)
var key = inputModel.nextKeyStroke()
while (key != null) {
- val exEntryPanel = ExEntryPanel.getInstance()
- if (exEntryPanel.isActive) {
- exEntryPanel.handleKey(key)
- } else {
- keyHandler.handleKey(editor, key, dataContext)
- }
+ keyHandler.handleKey(editor, key, dataContext)
key = inputModel.nextKeyStroke()
}
}, null, null)
diff --git a/test/org/jetbrains/plugins/ideavim/action/MotionActionTest.java b/test/org/jetbrains/plugins/ideavim/action/MotionActionTest.java
index f968f23141..28da5dc621 100644
--- a/test/org/jetbrains/plugins/ideavim/action/MotionActionTest.java
+++ b/test/org/jetbrains/plugins/ideavim/action/MotionActionTest.java
@@ -448,6 +448,11 @@ public void testReplaceEmptyTagContent() {
myFixture.checkResult("");
}
+ public void testDeleteToDigraph() {
+ typeTextInFile(parseKeys("d/O:"),"abcdÖef");
+ myFixture.checkResult("abÖef");
+ }
+
// |[(|
public void testUnmatchedOpenParenthesis() {
typeTextInFile(parseKeys("[("),
diff --git a/test/org/jetbrains/plugins/ideavim/ex/ExEntryTest.kt b/test/org/jetbrains/plugins/ideavim/ex/ExEntryTest.kt
index a4ecdfe9c4..dfe310df9a 100644
--- a/test/org/jetbrains/plugins/ideavim/ex/ExEntryTest.kt
+++ b/test/org/jetbrains/plugins/ideavim/ex/ExEntryTest.kt
@@ -550,6 +550,34 @@ class ExEntryTest : VimTestCase() {
assertExText("hello")
}
+ fun `test cmap`() {
+ typeExInput(":cmap x z")
+ typeExInput(":cnoremap w z")
+ typeExInput(":cmap z y")
+ typeExInput(":z")
+ assertExText("y")
+ deactivateExEntry()
+
+ typeExInput(":x")
+ assertExText("y")
+ deactivateExEntry()
+
+ typeExInput(":w")
+ assertExText("z")
+ }
+
+ fun `test cmap Ctrl`() {
+ typeExInput(":cmap \\ b")
+ typeExInput(":")
+ assertExText("b")
+ deactivateExEntry()
+
+ VimPlugin.getRegister().setKeys('e', StringHelper.parseKeys("hello world"))
+ typeExInput(":cmap d \\")
+ typeExInput(":de")
+ assertExText("hello world")
+ }
+
private fun typeExInput(text: String) {
assertTrue("Ex command must start with ':', '/' or '?'",
text.startsWith(":") || text.startsWith('/') || text.startsWith('?'))