mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-03-06 18:32:51 +01:00
Convert MapHandler to kotlin
This commit is contained in:
parent
b2c462fbaa
commit
746dbc2533
src/com/maddyhome/idea/vim/ex/handler
@ -1,295 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2019 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ex.handler;
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.command.MappingMode;
|
||||
import com.maddyhome.idea.vim.ex.*;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class MapHandler extends CommandHandler implements VimScriptCommandHandler {
|
||||
public static final char CTRL_V = '\u0016';
|
||||
public static final CommandInfo[] COMMAND_INFOS = new CommandInfo[] {
|
||||
// TODO: Support xmap, smap, map!, lmap
|
||||
new CommandInfo("map", "", MappingMode.NVO, true),
|
||||
new CommandInfo("nm", "ap", MappingMode.N, true),
|
||||
new CommandInfo("vm", "ap", MappingMode.V, true),
|
||||
new CommandInfo("om", "ap", MappingMode.O, true),
|
||||
new CommandInfo("im", "ap", MappingMode.I, true),
|
||||
new CommandInfo("cm", "ap", MappingMode.C, true),
|
||||
// TODO: Support xnoremap, snoremap, noremap!, lnoremap
|
||||
new CommandInfo("no", "remap", MappingMode.NVO, false),
|
||||
new CommandInfo("nn", "oremap", MappingMode.N, false),
|
||||
new CommandInfo("vn", "oremap", MappingMode.V, false),
|
||||
new CommandInfo("ono", "remap", MappingMode.O, false),
|
||||
new CommandInfo("ino", "remap", MappingMode.I, false),
|
||||
new CommandInfo("cno", "remap", MappingMode.C, false),
|
||||
};
|
||||
public static final CommandName[] COMMAND_NAMES = createCommandNames();
|
||||
public static final EnumSet<SpecialArgument> UNSUPPORTED_SPECIAL_ARGUMENTS = EnumSet.of(
|
||||
SpecialArgument.EXPR,
|
||||
SpecialArgument.SCRIPT);
|
||||
|
||||
public MapHandler() {
|
||||
super(COMMAND_NAMES, RANGE_FORBIDDEN | ARGUMENT_OPTIONAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull Editor editor, @NotNull DataContext context,
|
||||
@NotNull ExCommand cmd) throws ExException {
|
||||
return executeCommand(cmd, editor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull ExCommand cmd) throws ExException {
|
||||
executeCommand(cmd, null);
|
||||
}
|
||||
|
||||
private boolean executeCommand(@NotNull ExCommand cmd, @Nullable Editor editor) throws ExException {
|
||||
final CommandInfo commandInfo = getCommandInfo(cmd.getCommand());
|
||||
if (commandInfo != null) {
|
||||
final String argument = cmd.getArgument();
|
||||
final Set<MappingMode> modes = commandInfo.getMappingModes();
|
||||
if (argument.isEmpty()) {
|
||||
return editor != null && VimPlugin.getKey().showKeyMappings(modes, editor);
|
||||
}
|
||||
else {
|
||||
final CommandArguments arguments;
|
||||
try {
|
||||
arguments = parseCommandArguments(argument);
|
||||
}
|
||||
catch (IllegalArgumentException ignored) {
|
||||
return false;
|
||||
}
|
||||
if (arguments != null) {
|
||||
for (SpecialArgument unsupportedArgument : UNSUPPORTED_SPECIAL_ARGUMENTS) {
|
||||
if (arguments.getSpecialArguments().contains(unsupportedArgument)) {
|
||||
throw new ExException("Unsupported map argument: " + unsupportedArgument);
|
||||
}
|
||||
}
|
||||
VimPlugin.getKey().putKeyMapping(modes, arguments.getFromKeys(), arguments.getToKeys(), null,
|
||||
commandInfo.isRecursive());
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static CommandArguments parseCommandArguments(@NotNull String input) {
|
||||
input = getFirstBarSeparatedCommand(input);
|
||||
|
||||
final Set<SpecialArgument> specialArguments = new HashSet<SpecialArgument>();
|
||||
final StringBuilder toKeysBuilder = new StringBuilder();
|
||||
List<KeyStroke> fromKeys = null;
|
||||
for (String part : input.split(" ")) {
|
||||
if (fromKeys != null) {
|
||||
toKeysBuilder.append(" ");
|
||||
toKeysBuilder.append(part);
|
||||
}
|
||||
else {
|
||||
final SpecialArgument specialArgument = SpecialArgument.fromString(part);
|
||||
if (specialArgument != null) {
|
||||
specialArguments.add(specialArgument);
|
||||
}
|
||||
else {
|
||||
fromKeys = parseKeys(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = input.length() - 1; i >= 0; i--) {
|
||||
final char c = input.charAt(i);
|
||||
if (c == ' ') {
|
||||
toKeysBuilder.append(c);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fromKeys != null) {
|
||||
final List<KeyStroke> toKeys = parseKeys(StringUtil.trimLeading(toKeysBuilder.toString()));
|
||||
return new CommandArguments(specialArguments, fromKeys, toKeys);
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getFirstBarSeparatedCommand(@NotNull String input) {
|
||||
final StringBuilder inputBuilder = new StringBuilder();
|
||||
boolean escape = false;
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
final char c = input.charAt(i);
|
||||
if (escape) {
|
||||
escape = false;
|
||||
if (c != '|') {
|
||||
inputBuilder.append('\\');
|
||||
}
|
||||
inputBuilder.append(c);
|
||||
}
|
||||
else if (c == '\\' || c == CTRL_V) {
|
||||
escape = true;
|
||||
}
|
||||
else if (c == '|') {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
inputBuilder.append(c);
|
||||
}
|
||||
}
|
||||
if (input.endsWith("\\")) {
|
||||
inputBuilder.append("\\");
|
||||
}
|
||||
return inputBuilder.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static CommandInfo getCommandInfo(@NotNull String command) {
|
||||
for (CommandInfo commandInfo : COMMAND_INFOS) {
|
||||
if (command.startsWith(commandInfo.getPrefix())) {
|
||||
return commandInfo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static CommandName[] createCommandNames() {
|
||||
final List<CommandName> commandNames = new ArrayList<CommandName>();
|
||||
for (CommandInfo commandInfo : COMMAND_INFOS) {
|
||||
commandNames.add(new CommandName(commandInfo.getPrefix(), commandInfo.getSuffix()));
|
||||
}
|
||||
return commandNames.toArray(new CommandName[commandNames.size()]);
|
||||
}
|
||||
|
||||
private enum SpecialArgument {
|
||||
BUFFER("<buffer>"),
|
||||
NOWAIT("<nowait>"),
|
||||
SILENT("<silent>"),
|
||||
SPECIAL("<special>"),
|
||||
SCRIPT("<script>"),
|
||||
EXPR("<expr>"),
|
||||
UNIQUE("<unique>");
|
||||
|
||||
@NotNull private final String myName;
|
||||
|
||||
SpecialArgument(@NotNull String name) {
|
||||
myName = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static SpecialArgument fromString(@NotNull String s) {
|
||||
for (SpecialArgument argument : SpecialArgument.values()) {
|
||||
if (s.equals(argument.myName)) {
|
||||
return argument;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return myName;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CommandArguments {
|
||||
@NotNull private final Set<SpecialArgument> mySpecialArguments;
|
||||
@NotNull private final List<KeyStroke> myFromKeys;
|
||||
@NotNull private final List<KeyStroke> myToKeys;
|
||||
|
||||
public CommandArguments(@NotNull Set<SpecialArgument> specialArguments, @NotNull List<KeyStroke> fromKeys,
|
||||
@NotNull List<KeyStroke> toKeys) {
|
||||
|
||||
mySpecialArguments = specialArguments;
|
||||
myFromKeys = fromKeys;
|
||||
myToKeys = toKeys;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<SpecialArgument> getSpecialArguments() {
|
||||
return mySpecialArguments;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<KeyStroke> getFromKeys() {
|
||||
return myFromKeys;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<KeyStroke> getToKeys() {
|
||||
return myToKeys;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CommandInfo {
|
||||
@NotNull private final String myPrefix;
|
||||
@NotNull private final String mySuffix;
|
||||
@NotNull private final Set<MappingMode> myMappingModes;
|
||||
private final boolean myRecursive;
|
||||
|
||||
public CommandInfo(@NotNull String prefix, @NotNull String suffix, @NotNull Set<MappingMode> mappingModes,
|
||||
boolean recursive) {
|
||||
myPrefix = prefix;
|
||||
mySuffix = suffix;
|
||||
myMappingModes = mappingModes;
|
||||
myRecursive = recursive;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getPrefix() {
|
||||
return myPrefix;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getSuffix() {
|
||||
return mySuffix;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<MappingMode> getMappingModes() {
|
||||
return myMappingModes;
|
||||
}
|
||||
|
||||
public boolean isRecursive() {
|
||||
return myRecursive;
|
||||
}
|
||||
}
|
||||
}
|
199
src/com/maddyhome/idea/vim/ex/handler/MapHandler.kt
Normal file
199
src/com/maddyhome/idea/vim/ex/handler/MapHandler.kt
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2019 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ex.handler
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler.ARGUMENT_OPTIONAL
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler.RANGE_FORBIDDEN
|
||||
import com.maddyhome.idea.vim.ex.ExCommand
|
||||
import com.maddyhome.idea.vim.ex.ExException
|
||||
import com.maddyhome.idea.vim.ex.commands
|
||||
import com.maddyhome.idea.vim.ex.flags
|
||||
import com.maddyhome.idea.vim.ex.handler.MapHandler.SpecialArgument.EXPR
|
||||
import com.maddyhome.idea.vim.ex.handler.MapHandler.SpecialArgument.SCRIPT
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import java.util.*
|
||||
import javax.swing.KeyStroke
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
class MapHandler : CommandHandler(COMMAND_NAMES, flags(RANGE_FORBIDDEN, ARGUMENT_OPTIONAL)), VimScriptCommandHandler {
|
||||
|
||||
@Throws(ExException::class)
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||
return executeCommand(cmd, editor)
|
||||
}
|
||||
|
||||
@Throws(ExException::class)
|
||||
override fun execute(cmd: ExCommand) {
|
||||
executeCommand(cmd, null)
|
||||
}
|
||||
|
||||
@Throws(ExException::class)
|
||||
private fun executeCommand(cmd: ExCommand, editor: Editor?): Boolean {
|
||||
val commandInfo = COMMAND_INFOS.find { cmd.command.startsWith(it.prefix) }
|
||||
if (commandInfo != null) {
|
||||
val argument = cmd.argument
|
||||
val modes = commandInfo.mappingModes
|
||||
if (argument.isEmpty()) {
|
||||
return editor != null && VimPlugin.getKey().showKeyMappings(modes, editor)
|
||||
} else {
|
||||
val arguments = try {
|
||||
parseCommandArguments(argument)
|
||||
} catch (ignored: IllegalArgumentException) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (arguments != null) {
|
||||
for (unsupportedArgument in UNSUPPORTED_SPECIAL_ARGUMENTS) {
|
||||
if (unsupportedArgument in arguments.specialArguments) {
|
||||
throw ExException("Unsupported map argument: $unsupportedArgument")
|
||||
}
|
||||
}
|
||||
VimPlugin.getKey().putKeyMapping(modes, arguments.fromKeys, arguments.toKeys, null,
|
||||
commandInfo.isRecursive)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private enum class SpecialArgument(val myName: String) {
|
||||
BUFFER("<buffer>"),
|
||||
NOWAIT("<nowait>"),
|
||||
SILENT("<silent>"),
|
||||
SPECIAL("<special>"),
|
||||
SCRIPT("<script>"),
|
||||
EXPR("<expr>"),
|
||||
UNIQUE("<unique>");
|
||||
|
||||
override fun toString(): String {
|
||||
return this.myName
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromString(s: String): SpecialArgument? {
|
||||
for (argument in values()) {
|
||||
if (s == argument.myName) {
|
||||
return argument
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CommandArguments(val specialArguments: Set<SpecialArgument>, val fromKeys: List<KeyStroke>,
|
||||
val toKeys: List<KeyStroke>)
|
||||
|
||||
private class CommandInfo(val prefix: String, suffix: String, val mappingModes: Set<MappingMode>, val isRecursive: Boolean) {
|
||||
val command =
|
||||
if (suffix.isBlank()) {
|
||||
prefix
|
||||
} else "$prefix[$suffix]"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CTRL_V = '\u0016'
|
||||
private val COMMAND_INFOS = arrayOf(
|
||||
// TODO: Support xmap, smap, map!, lmap
|
||||
CommandInfo("map", "", MappingMode.NVO, true),
|
||||
CommandInfo("nm", "ap", MappingMode.N, true),
|
||||
CommandInfo("vm", "ap", MappingMode.V, true),
|
||||
CommandInfo("om", "ap", MappingMode.O, true),
|
||||
CommandInfo("im", "ap", MappingMode.I, true),
|
||||
CommandInfo("cm", "ap", MappingMode.C, true),
|
||||
|
||||
// TODO: Support xnoremap, snoremap, noremap!, lnoremap
|
||||
CommandInfo("no", "remap", MappingMode.NVO, false),
|
||||
CommandInfo("nn", "oremap", MappingMode.N, false),
|
||||
CommandInfo("vn", "oremap", MappingMode.V, false),
|
||||
CommandInfo("ono", "remap", MappingMode.O, false),
|
||||
CommandInfo("ino", "remap", MappingMode.I, false),
|
||||
CommandInfo("cno", "remap", MappingMode.C, false)
|
||||
)
|
||||
val COMMAND_NAMES = commands(*COMMAND_INFOS.map { it.command }.toTypedArray())
|
||||
private val UNSUPPORTED_SPECIAL_ARGUMENTS = EnumSet.of(EXPR, SCRIPT)
|
||||
}
|
||||
|
||||
private fun parseCommandArguments(input: String): CommandArguments? {
|
||||
val firstBarSeparatedCommand = getFirstBarSeparatedCommand(input)
|
||||
|
||||
val specialArguments = HashSet<SpecialArgument>()
|
||||
val toKeysBuilder = StringBuilder()
|
||||
var fromKeys: List<KeyStroke>? = null
|
||||
firstBarSeparatedCommand.split(" ").dropLastWhile { it.isEmpty() }.forEach { part ->
|
||||
if (fromKeys != null) {
|
||||
toKeysBuilder.append(" ")
|
||||
toKeysBuilder.append(part)
|
||||
} else {
|
||||
val specialArgument = SpecialArgument.fromString(part)
|
||||
if (specialArgument != null) {
|
||||
specialArguments.add(specialArgument)
|
||||
} else {
|
||||
fromKeys = parseKeys(part)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i in firstBarSeparatedCommand.length - 1 downTo 0) {
|
||||
val c = firstBarSeparatedCommand[i]
|
||||
if (c == ' ') {
|
||||
toKeysBuilder.append(c)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return fromKeys?.let {
|
||||
val toKeys = parseKeys(toKeysBuilder.toString().trimStart())
|
||||
CommandArguments(specialArguments, it, toKeys)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFirstBarSeparatedCommand(input: String): String {
|
||||
val inputBuilder = StringBuilder()
|
||||
var escape = false
|
||||
for (i in 0 until input.length) {
|
||||
val c = input[i]
|
||||
if (escape) {
|
||||
escape = false
|
||||
if (c != '|') {
|
||||
inputBuilder.append('\\')
|
||||
}
|
||||
inputBuilder.append(c)
|
||||
} else if (c == '\\' || c == CTRL_V) {
|
||||
escape = true
|
||||
} else if (c == '|') {
|
||||
break
|
||||
} else {
|
||||
inputBuilder.append(c)
|
||||
}
|
||||
}
|
||||
if (input.endsWith("\\")) {
|
||||
inputBuilder.append("\\")
|
||||
}
|
||||
return inputBuilder.toString()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user