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:
parent
b20bec610a
commit
a43232ba6c
src/com/maddyhome/idea/vim
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user