Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement cmap #225

Merged
merged 1 commit into from
Nov 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion src/com/maddyhome/idea/vim/KeyHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -745,8 +763,10 @@ private void startWaitingForArgument(Editor editor,
// the key handler when it's complete.
if (action instanceof InsertCompletedDigraphAction) {
editorState.startDigraphSequence();
setPromptCharacterEx('?');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this code supposed to insert ? character under the caret when the user inserts digraph? IIRC, this functionality already works, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but using KeyHandler changed the logic and I needed to implement it here

} else if (action instanceof InsertCompletedLiteralAction) {
editorState.startLiteralSequence();
setPromptCharacterEx('^');
}
break;
case EX_STRING:
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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;
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

Comment on lines +30 to +32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this value presented to properly support some commands with digraphs? E.g. dt<C-k>o:.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is. Without it , e.g. testDeleteToDigraph will fail.

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.NonNls;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -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();
}
}
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
13 changes: 10 additions & 3 deletions src/com/maddyhome/idea/vim/ui/ExTextField.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
}
}

Expand Down Expand Up @@ -329,15 +336,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 @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,11 @@ public void testReplaceEmptyTagContent() {
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("[("),
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