Skip to content

Commit

Permalink
Implement cmap
Browse files Browse the repository at this point in the history
  • Loading branch information
sumoooru2 committed Oct 24, 2020
1 parent a1f8e7b commit 53c12c8
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 27 deletions.
39 changes: 30 additions & 9 deletions src/com/maddyhome/idea/vim/KeyHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.ActionPlan;
import com.intellij.openapi.editor.actionSystem.CaretSpecificDataContext;
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId;
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
import com.intellij.openapi.project.Project;
Expand All @@ -40,17 +38,14 @@
import com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction;
import com.maddyhome.idea.vim.action.macro.ToggleRecordingAction;
import com.maddyhome.idea.vim.command.*;
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
import com.maddyhome.idea.vim.group.ChangeGroup;
import com.maddyhome.idea.vim.group.RegisterGroup;
import com.maddyhome.idea.vim.group.visual.VimSelection;
import com.maddyhome.idea.vim.group.visual.VisualGroupKt;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import com.maddyhome.idea.vim.helper.*;
import com.maddyhome.idea.vim.key.*;
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
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;
Expand All @@ -59,13 +54,12 @@

import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.intellij.openapi.actionSystem.CommonDataKeys.*;
import static com.intellij.openapi.actionSystem.PlatformDataKeys.PROJECT_FILE_DIRECTORY;
Expand Down Expand Up @@ -599,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);
Expand Down Expand Up @@ -753,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:
Expand Down Expand Up @@ -849,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")
Expand Down Expand Up @@ -917,7 +936,9 @@ public void run() {
editorState.popModes();
}

KeyHandler.getInstance().reset(editor);
if (editorState.getCommandBuilder().isDone()) {
KeyHandler.getInstance().reset(editor);
}
}

private final Editor editor;
Expand Down
21 changes: 21 additions & 0 deletions src/com/maddyhome/idea/vim/command/CommandBuilder.kt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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()
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -147,6 +167,7 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
commandParts.clear()
keyList.clear()
expectedArgumentType = null
prevExpectedArgumentType = null
}

fun resetInProgressCommandPart(commandPartNode: CommandPartNode) {
Expand Down
12 changes: 10 additions & 2 deletions src/com/maddyhome/idea/vim/ui/ExEditorKit.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -142,8 +144,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(), "" + 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(), "" + c, e.getWhen(), e.getModifiers());
super.actionPerformed(event);
}
target.saveLastEntry();
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/com/maddyhome/idea/vim/ui/ExShortcutKeyAction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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))
}
}

Expand Down
18 changes: 10 additions & 8 deletions src/com/maddyhome/idea/vim/ui/ExTextField.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,15 @@

package com.maddyhome.idea.vim.ui;

import com.google.common.collect.ImmutableList;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.util.ui.JBUI;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.VimProjectService;
import com.maddyhome.idea.vim.group.HistoryGroup;
import com.maddyhome.idea.vim.key.KeyMapping;
import com.maddyhome.idea.vim.key.MappingInfo;
import com.maddyhome.idea.vim.key.ToKeysMappingInfo;
import com.maddyhome.idea.vim.helper.UiHelper;
import kotlin.text.StringsKt;
import org.jetbrains.annotations.NotNull;
Expand All @@ -56,6 +51,8 @@ public class ExTextField extends JTextField {

public final static 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
Expand Down Expand Up @@ -286,7 +283,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;
}
}
}

Expand Down Expand Up @@ -333,15 +335,15 @@ void setCurrentAction(@NotNull ExEditorKit.MultiStepAction action, char pendingI
setCurrentActionPromptCharacter(pendingIndicator);
}

void clearCurrentAction() {
public void clearCurrentAction() {
if (currentAction != null) {
currentAction.reset();
}
currentAction = null;
clearCurrentActionPromptCharacter();
}

void setCurrentActionPromptCharacter(char promptCharacter) {
public void setCurrentActionPromptCharacter(char promptCharacter) {
actualText = removePromptCharacter();
this.currentActionPromptCharacter = promptCharacter;
currentActionPromptCharacterOffset = currentActionPromptCharacterOffset == -1 ? getCaretPosition() : currentActionPromptCharacterOffset;
Expand Down
7 changes: 1 addition & 6 deletions test/org/jetbrains/plugins/ideavim/VimTestCase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -407,12 +407,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,11 @@ public void testDeleteOuterTagWithCount() {
myFixture.checkResult("<a></a>");
}

public void testDeleteToDigraph() {
typeTextInFile(parseKeys("d/<C-K>O:<CR>"),"ab<caret>cdÖef");
myFixture.checkResult("abÖef");
}

// |[(|
public void testUnmatchedOpenParenthesis() {
typeTextInFile(parseKeys("[("),
Expand Down
28 changes: 28 additions & 0 deletions test/org/jetbrains/plugins/ideavim/ex/ExEntryTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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('?'))
Expand Down

0 comments on commit 53c12c8

Please sign in to comment.