1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-03-05 15:32:51 +01:00

Move execution of mapping to MappingInfo

This commit is contained in:
Alex Plate 2020-10-16 10:27:12 +03:00
parent b20bec610a
commit a43232ba6c
No known key found for this signature in database
GPG Key ID: 0B97153C8FFEC09F
2 changed files with 102 additions and 79 deletions
src/com/maddyhome/idea/vim

View File

@ -301,7 +301,7 @@ public class KeyHandler {
return node;
}
private static <T> boolean isPrefix(@NotNull List<T> list1, @NotNull List<T> list2) {
public static <T> boolean isPrefix(@NotNull List<T> list1, @NotNull List<T> list2) {
if (list1.size() > list2.size()) {
return false;
}
@ -438,84 +438,7 @@ public class KeyHandler {
final EditorDataContext currentContext = new EditorDataContext(editor);
if (mappingInfo instanceof ToKeysMappingInfo) {
final List<KeyStroke> toKeys = ((ToKeysMappingInfo)mappingInfo).getToKeys();
final boolean fromIsPrefix = isPrefix(mappingInfo.getFromKeys(), toKeys);
boolean first = true;
for (KeyStroke keyStroke : toKeys) {
final boolean recursive = mappingInfo.isRecursive() && !(first && fromIsPrefix);
handleKey(editor, keyStroke, currentContext, recursive);
first = false;
}
}
else if (mappingInfo instanceof ToHandlerMappingInfo) {
final VimExtensionHandler extensionHandler = ((ToHandlerMappingInfo)mappingInfo).getExtensionHandler();
final CommandProcessor processor = CommandProcessor.getInstance();
// Cache isOperatorPending in case the extension changes the mode while moving the caret
// See CommonExtensionTest
// TODO: Is this legal? Should we assert in this case?
final boolean shouldCalculateOffsets = commandState.isOperatorPending();
Map<Caret, Integer> startOffsets =
editor.getCaretModel().getAllCarets().stream().collect(Collectors.toMap(Function.identity(), Caret::getOffset));
if (extensionHandler.isRepeatable()) {
VimRepeater.Extension.INSTANCE.clean();
}
processor.executeCommand(editor.getProject(), () -> extensionHandler.execute(editor, context),
"Vim " + extensionHandler.getClass().getSimpleName(), null);
if (extensionHandler.isRepeatable()) {
VimRepeater.Extension.INSTANCE.setLastExtensionHandler(extensionHandler);
VimRepeater.Extension.INSTANCE.setArgumentCaptured(null);
VimRepeater.INSTANCE.setRepeatHandler(true);
}
if (shouldCalculateOffsets && !commandState.getCommandBuilder().hasCurrentCommandPartArgument()) {
Map<Caret, VimSelection> offsets = new HashMap<>();
for (Caret caret : editor.getCaretModel().getAllCarets()) {
@Nullable Integer startOffset = startOffsets.get(caret);
if (caret.hasSelection()) {
final VimSelection vimSelection = VimSelection.Companion
.create(UserDataManager.getVimSelectionStart(caret), caret.getOffset(),
SelectionType.fromSubMode(CommandStateHelper.getSubMode(editor)), editor);
offsets.put(caret, vimSelection);
commandState.popModes();
}
else if (startOffset != null && startOffset != caret.getOffset()) {
// Command line motions are always characterwise exclusive
int endOffset = caret.getOffset();
if (startOffset < endOffset) {
endOffset -= 1;
} else {
startOffset -= 1;
}
final VimSelection vimSelection = VimSelection.Companion
.create(startOffset, endOffset, SelectionType.CHARACTER_WISE, editor);
offsets.put(caret, vimSelection);
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
// Move caret to the initial offset for better undo action
// This is not a necessary thing, but without it undo action look less convenient
editor.getCaretModel().moveToOffset(startOffset);
}
}
}
if (!offsets.isEmpty()) {
commandState.getCommandBuilder().completeCommandPart(new Argument(offsets));
}
}
}
else if (mappingInfo instanceof ToActionMappingInfo) {
String action = ((ToActionMappingInfo)mappingInfo).getAction();
CaretSpecificDataContext dataContext =
new CaretSpecificDataContext(currentContext, editor.getCaretModel().getCurrentCaret());
KeyHandler.executeAction(action, dataContext);
}
mappingInfo.execute(editor, context);
// If we've just evaluated the previous key sequence, make sure to also handle the current key
if (mappingInfo != currentMappingInfo) {

View File

@ -17,9 +17,30 @@
*/
package com.maddyhome.idea.vim.key
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.CaretSpecificDataContext
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.action.change.VimRepeater.Extension.argumentCaptured
import com.maddyhome.idea.vim.action.change.VimRepeater.Extension.clean
import com.maddyhome.idea.vim.action.change.VimRepeater.Extension.lastExtensionHandler
import com.maddyhome.idea.vim.action.change.VimRepeater.repeatHandler
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.command.SelectionType
import com.maddyhome.idea.vim.command.SelectionType.Companion.fromSubMode
import com.maddyhome.idea.vim.extension.VimExtensionHandler
import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.group.visual.VimSelection.Companion.create
import com.maddyhome.idea.vim.helper.EditorDataContext
import com.maddyhome.idea.vim.helper.StringHelper.toKeyNotation
import com.maddyhome.idea.vim.helper.subMode
import com.maddyhome.idea.vim.helper.vimSelectionStart
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
import java.awt.event.KeyEvent
import java.util.*
import javax.swing.KeyStroke
import kotlin.math.min
@ -30,6 +51,8 @@ sealed class MappingInfo(val fromKeys: List<KeyStroke>, val isRecursive: Boolean
abstract fun getPresentableString(): String
abstract fun execute(editor: Editor, context: DataContext)
override fun compareTo(other: MappingInfo): Int {
val size = fromKeys.size
val otherSize = other.fromKeys.size
@ -63,6 +86,18 @@ class ToKeysMappingInfo(
owner: MappingOwner
) : MappingInfo(fromKeys, isRecursive, owner) {
override fun getPresentableString(): String = toKeyNotation(toKeys)
override fun execute(editor: Editor, context: DataContext) {
val editorDataContext = EditorDataContext(editor)
val fromIsPrefix = KeyHandler.isPrefix(fromKeys, toKeys)
var first = true
for (keyStroke in toKeys) {
val recursive = isRecursive && !(first && fromIsPrefix)
val keyHandler = KeyHandler.getInstance()
keyHandler.handleKey(editor, keyStroke, editorDataContext, recursive)
first = false
}
}
}
class ToHandlerMappingInfo(
@ -72,6 +107,65 @@ class ToHandlerMappingInfo(
owner: MappingOwner
) : MappingInfo(fromKeys, isRecursive, owner) {
override fun getPresentableString(): String = "call ${this.javaClass.canonicalName}"
override fun execute(editor: Editor, context: DataContext) {
val processor = CommandProcessor.getInstance()
val commandState = CommandState.getInstance(editor)
// Cache isOperatorPending in case the extension changes the mode while moving the caret
// See CommonExtensionTest
// TODO: Is this legal? Should we assert in this case?
// Cache isOperatorPending in case the extension changes the mode while moving the caret
// See CommonExtensionTest
// TODO: Is this legal? Should we assert in this case?
val shouldCalculateOffsets: Boolean = commandState.isOperatorPending
val startOffsets: Map<Caret, Int> = editor.caretModel.allCarets.associateWith { it.offset }
if (extensionHandler.isRepeatable) {
clean()
}
processor.executeCommand(editor.project, { extensionHandler.execute(editor, context) },
"Vim " + extensionHandler.javaClass.simpleName, null)
if (extensionHandler.isRepeatable) {
lastExtensionHandler = extensionHandler
argumentCaptured = null
repeatHandler = true
}
if (shouldCalculateOffsets && !commandState.commandBuilder.hasCurrentCommandPartArgument()) {
val offsets: MutableMap<Caret, VimSelection> = HashMap()
for (caret in editor.caretModel.allCarets) {
var startOffset = startOffsets[caret]
if (caret.hasSelection()) {
val vimSelection = create(caret.vimSelectionStart, caret.offset, fromSubMode(editor.subMode), editor)
offsets[caret] = vimSelection
commandState.popModes()
} else if (startOffset != null && startOffset != caret.offset) {
// Command line motions are always characterwise exclusive
var endOffset = caret.offset
if (startOffset < endOffset) {
endOffset -= 1
} else {
startOffset -= 1
}
val vimSelection = create(startOffset, endOffset, SelectionType.CHARACTER_WISE, editor)
offsets[caret] = vimSelection
SelectionVimListenerSuppressor.lock().use { ignored ->
// Move caret to the initial offset for better undo action
// This is not a necessary thing, but without it undo action look less convenient
editor.caretModel.moveToOffset(startOffset)
}
}
}
if (offsets.isNotEmpty()) {
commandState.commandBuilder.completeCommandPart(Argument(offsets))
}
}
}
}
class ToActionMappingInfo(
@ -81,4 +175,10 @@ class ToActionMappingInfo(
owner: MappingOwner
) : MappingInfo(fromKeys, isRecursive, owner) {
override fun getPresentableString(): String = "action $action"
override fun execute(editor: Editor, context: DataContext) {
val editorDataContext = EditorDataContext(editor)
val dataContext = CaretSpecificDataContext(editorDataContext, editor.caretModel.currentCaret)
KeyHandler.executeAction(action, dataContext)
}
}