mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-02-25 02:46:01 +01:00
Implement cmap
This commit is contained in:
parent
c1af738cc5
commit
5c9faba7f4
src/com/maddyhome/idea/vim
test/org/jetbrains/plugins/ideavim
@ -45,6 +45,7 @@ import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
|
||||
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 org.jetbrains.annotations.Nullable;
|
||||
|
||||
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 @@ public class KeyHandler {
|
||||
}
|
||||
|
||||
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 @@ public class KeyHandler {
|
||||
// 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 class KeyHandler {
|
||||
|
||||
}
|
||||
|
||||
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 class KeyHandler {
|
||||
editorState.popModes();
|
||||
}
|
||||
|
||||
KeyHandler.getInstance().reset(editor);
|
||||
if (editorState.getCommandBuilder().isDone()) {
|
||||
KeyHandler.getInstance().reset(editor);
|
||||
}
|
||||
}
|
||||
|
||||
private final Editor editor;
|
||||
|
@ -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) {
|
||||
|
@ -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 class ExEditorKit extends DefaultEditorKit {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 class ExTextField extends JTextField {
|
||||
(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 @@ public class ExTextField extends JTextField {
|
||||
setCurrentActionPromptCharacter(pendingIndicator);
|
||||
}
|
||||
|
||||
void clearCurrentAction() {
|
||||
public void clearCurrentAction() {
|
||||
if (currentAction != null) {
|
||||
currentAction.reset();
|
||||
}
|
||||
@ -337,7 +344,7 @@ public class ExTextField extends JTextField {
|
||||
clearCurrentActionPromptCharacter();
|
||||
}
|
||||
|
||||
void setCurrentActionPromptCharacter(char promptCharacter) {
|
||||
public void setCurrentActionPromptCharacter(char promptCharacter) {
|
||||
actualText = removePromptCharacter();
|
||||
this.currentActionPromptCharacter = promptCharacter;
|
||||
currentActionPromptCharacterOffset = currentActionPromptCharacterOffset == -1 ? getCaretPosition() : currentActionPromptCharacterOffset;
|
||||
|
@ -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)
|
||||
|
@ -448,6 +448,11 @@ public class MotionActionTest extends VimTestCase {
|
||||
myFixture.checkResult("<a><c></c></a>");
|
||||
}
|
||||
|
||||
public void testDeleteToDigraph() {
|
||||
typeTextInFile(parseKeys("d/<C-K>O:<CR>"),"ab<caret>cdÖef");
|
||||
myFixture.checkResult("abÖef");
|
||||
}
|
||||
|
||||
// |[(|
|
||||
public void testUnmatchedOpenParenthesis() {
|
||||
typeTextInFile(parseKeys("[("),
|
||||
|
@ -550,6 +550,34 @@ class ExEntryTest : VimTestCase() {
|
||||
assertExText("hello")
|
||||
}
|
||||
|
||||
fun `test cmap`() {
|
||||
typeExInput(":cmap x z<CR>")
|
||||
typeExInput(":cnoremap w z<CR>")
|
||||
typeExInput(":cmap z y<CR>")
|
||||
typeExInput(":z")
|
||||
assertExText("y")
|
||||
deactivateExEntry()
|
||||
|
||||
typeExInput(":x")
|
||||
assertExText("y")
|
||||
deactivateExEntry()
|
||||
|
||||
typeExInput(":w")
|
||||
assertExText("z")
|
||||
}
|
||||
|
||||
fun `test cmap Ctrl`() {
|
||||
typeExInput(":cmap \\<C-B> b<CR>")
|
||||
typeExInput(":<C-B>")
|
||||
assertExText("b")
|
||||
deactivateExEntry()
|
||||
|
||||
VimPlugin.getRegister().setKeys('e', StringHelper.parseKeys("hello world"))
|
||||
typeExInput(":cmap d \\<C-R><CR>")
|
||||
typeExInput(":de")
|
||||
assertExText("hello world")
|
||||
}
|
||||
|
||||
private fun typeExInput(text: String) {
|
||||
assertTrue("Ex command must start with ':', '/' or '?'",
|
||||
text.startsWith(":") || text.startsWith('/') || text.startsWith('?'))
|
||||
|
Loading…
Reference in New Issue
Block a user