mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-06-02 13:34:07 +02:00
Use KeyProcessResultBuilder
It will help us to build the KeyProcessResult that we need for asynchronous key processing
This commit is contained in:
parent
275c5d28e1
commit
19fa00837c
src/main/java/com/maddyhome/idea/vim/group
vim-engine/src/main/kotlin/com/maddyhome/idea/vim
@ -20,6 +20,7 @@ import com.intellij.openapi.progress.ProgressManager
|
|||||||
import com.intellij.util.execution.ParametersListUtil
|
import com.intellij.util.execution.ParametersListUtil
|
||||||
import com.intellij.util.text.CharSequenceReader
|
import com.intellij.util.text.CharSequenceReader
|
||||||
import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance
|
import com.maddyhome.idea.vim.KeyHandler.Companion.getInstance
|
||||||
|
import com.maddyhome.idea.vim.KeyProcessResult
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
@ -90,19 +91,22 @@ public class ProcessGroup : VimProcessGroupBase() {
|
|||||||
panel.activate(editor.ij, context.ij, ":", initText, 1)
|
panel.activate(editor.ij, context.ij, ":", initText, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun processExKey(editor: VimEditor, stroke: KeyStroke): Boolean {
|
public override fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean {
|
||||||
// This will only get called if somehow the key focus ended up in the editor while the ex entry window
|
// This will only get called if somehow the key focus ended up in the editor while the ex entry window
|
||||||
// is open. So I'll put focus back in the editor and process the key.
|
// is open. So I'll put focus back in the editor and process the key.
|
||||||
|
|
||||||
val panel = ExEntryPanel.getInstance()
|
val panel = ExEntryPanel.getInstance()
|
||||||
if (panel.isActive) {
|
if (panel.isActive) {
|
||||||
requestFocus(panel.entry)
|
processResultBuilder.addExecutionStep { _, _, _ ->
|
||||||
panel.handleKey(stroke)
|
requestFocus(panel.entry)
|
||||||
|
panel.handleKey(stroke)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
editor.mode = NORMAL()
|
processResultBuilder.addExecutionStep { _, lambdaEditor, _ ->
|
||||||
getInstance().reset(editor)
|
lambdaEditor.mode = NORMAL()
|
||||||
|
getInstance().reset(lambdaEditor)
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,19 @@ public class KeyHandler {
|
|||||||
mappingCompleted: Boolean,
|
mappingCompleted: Boolean,
|
||||||
keyState: KeyHandlerState,
|
keyState: KeyHandlerState,
|
||||||
) {
|
) {
|
||||||
|
val result = processKey(key, editor, allowKeyMappings, mappingCompleted, KeyProcessResult.SynchronousKeyProcessBuilder(keyState))
|
||||||
|
if (result is KeyProcessResult.Executable) {
|
||||||
|
result.execute(editor, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processKey(
|
||||||
|
key: KeyStroke,
|
||||||
|
editor: VimEditor,
|
||||||
|
allowKeyMappings: Boolean,
|
||||||
|
mappingCompleted: Boolean,
|
||||||
|
processBuilder: KeyProcessResult.KeyProcessResultBuilder,
|
||||||
|
): KeyProcessResult {
|
||||||
LOG.trace {
|
LOG.trace {
|
||||||
"""
|
"""
|
||||||
------- Key Handler -------
|
------- Key Handler -------
|
||||||
@ -95,59 +108,69 @@ public class KeyHandler {
|
|||||||
}
|
}
|
||||||
val maxMapDepth = injector.globalOptions().maxmapdepth
|
val maxMapDepth = injector.globalOptions().maxmapdepth
|
||||||
if (handleKeyRecursionCount >= maxMapDepth) {
|
if (handleKeyRecursionCount >= maxMapDepth) {
|
||||||
injector.messages.showStatusBarMessage(editor, injector.messages.message("E223"))
|
processBuilder.addExecutionStep { _, lambdaEditor, _ ->
|
||||||
injector.messages.indicateError()
|
LOG.warn("Key handling, maximum recursion of the key received. maxdepth=$maxMapDepth")
|
||||||
LOG.warn("Key handling, maximum recursion of the key received. maxdepth=$maxMapDepth")
|
injector.messages.showStatusBarMessage(lambdaEditor, injector.messages.message("E223"))
|
||||||
return
|
injector.messages.indicateError()
|
||||||
|
}
|
||||||
|
return processBuilder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
val newState = keyState ?: this.keyHandlerState
|
|
||||||
injector.messages.clearError()
|
injector.messages.clearError()
|
||||||
val editorState = editor.vimStateMachine
|
val editorState = editor.vimStateMachine
|
||||||
val commandBuilder = newState.commandBuilder
|
val commandBuilder = processBuilder.state.commandBuilder
|
||||||
|
|
||||||
// If this is a "regular" character keystroke, get the character
|
// If this is a "regular" character keystroke, get the character
|
||||||
val chKey: Char = if (key.keyChar == KeyEvent.CHAR_UNDEFINED) 0.toChar() else key.keyChar
|
val chKey: Char = if (key.keyChar == KeyEvent.CHAR_UNDEFINED) 0.toChar() else key.keyChar
|
||||||
|
|
||||||
// We only record unmapped keystrokes. If we've recursed to handle mapping, don't record anything.
|
// We only record unmapped keystrokes. If we've recursed to handle mapping, don't record anything.
|
||||||
var shouldRecord = handleKeyRecursionCount == 0 && injector.registerGroup.isRecording
|
var shouldRecord = handleKeyRecursionCount == 0 && injector.registerGroup.isRecording
|
||||||
|
var isProcessed = false
|
||||||
handleKeyRecursionCount++
|
handleKeyRecursionCount++
|
||||||
try {
|
try {
|
||||||
LOG.trace("Start key processing...")
|
LOG.trace("Start key processing...")
|
||||||
if (!allowKeyMappings || !MappingProcessor.handleKeyMapping(editor, key, newState, context, mappingCompleted)) {
|
if (!MappingProcessor.handleKeyMapping(key, editor, allowKeyMappings, mappingCompleted, processBuilder)) {
|
||||||
LOG.trace("Mappings processed, continue processing key.")
|
LOG.trace("Mappings processed, continue processing key.")
|
||||||
if (isCommandCountKey(chKey, newState, editorState)) {
|
if (isCommandCountKey(chKey, processBuilder.state, editorState)) {
|
||||||
commandBuilder.addCountCharacter(key)
|
commandBuilder.addCountCharacter(key)
|
||||||
} else if (isDeleteCommandCountKey(key, newState, editorState.mode)) {
|
isProcessed = true
|
||||||
|
} else if (isDeleteCommandCountKey(key, processBuilder.state, editorState.mode)) {
|
||||||
commandBuilder.deleteCountCharacter()
|
commandBuilder.deleteCountCharacter()
|
||||||
|
isProcessed = true
|
||||||
} else if (isEditorReset(key, editorState)) {
|
} else if (isEditorReset(key, editorState)) {
|
||||||
handleEditorReset(editor, key, newState, context)
|
processBuilder.addExecutionStep { lambdaKeyState, lambdaEditor, lambdaContext -> handleEditorReset(lambdaEditor, key, lambdaKeyState, lambdaContext) }
|
||||||
|
isProcessed = true
|
||||||
} else if (isExpectingCharArgument(commandBuilder)) {
|
} else if (isExpectingCharArgument(commandBuilder)) {
|
||||||
handleCharArgument(key, chKey, newState, editor)
|
handleCharArgument(key, chKey, processBuilder.state, editor)
|
||||||
|
isProcessed = true
|
||||||
} else if (editorState.isRegisterPending) {
|
} else if (editorState.isRegisterPending) {
|
||||||
LOG.trace("Pending mode.")
|
LOG.trace("Pending mode.")
|
||||||
commandBuilder.addKey(key)
|
commandBuilder.addKey(key)
|
||||||
handleSelectRegister(editorState, chKey, newState)
|
handleSelectRegister(editorState, chKey, processBuilder.state)
|
||||||
} else if (!handleDigraph(editor, key, newState, context)) {
|
isProcessed = true
|
||||||
|
} else if (!handleDigraph(editor, key, processBuilder)) {
|
||||||
LOG.debug("Digraph is NOT processed")
|
LOG.debug("Digraph is NOT processed")
|
||||||
|
|
||||||
// Ask the key/action tree if this is an appropriate key at this point in the command and if so,
|
// Ask the key/action tree if this is an appropriate key at this point in the command and if so,
|
||||||
// return the node matching this keystroke
|
// return the node matching this keystroke
|
||||||
val node: Node<LazyVimCommand>? = mapOpCommand(key, commandBuilder.getChildNode(key), editorState.mode, newState)
|
val node: Node<LazyVimCommand>? = mapOpCommand(key, commandBuilder.getChildNode(key), editorState.mode, processBuilder.state)
|
||||||
LOG.trace("Get the node for the current mode")
|
LOG.trace("Get the node for the current mode")
|
||||||
|
|
||||||
if (node is CommandNode<LazyVimCommand>) {
|
if (node is CommandNode<LazyVimCommand>) {
|
||||||
LOG.trace("Node is a command node")
|
LOG.trace("Node is a command node")
|
||||||
handleCommandNode(editor, context, key, node, newState, editorState)
|
handleCommandNode(key, node, processBuilder)
|
||||||
commandBuilder.addKey(key)
|
commandBuilder.addKey(key)
|
||||||
|
isProcessed = true
|
||||||
} else if (node is CommandPartNode<LazyVimCommand>) {
|
} else if (node is CommandPartNode<LazyVimCommand>) {
|
||||||
LOG.trace("Node is a command part node")
|
LOG.trace("Node is a command part node")
|
||||||
commandBuilder.setCurrentCommandPartNode(node)
|
commandBuilder.setCurrentCommandPartNode(node)
|
||||||
commandBuilder.addKey(key)
|
commandBuilder.addKey(key)
|
||||||
} else if (isSelectRegister(key, newState, editorState)) {
|
isProcessed = true
|
||||||
|
} else if (isSelectRegister(key, processBuilder.state, editorState)) {
|
||||||
LOG.trace("Select register")
|
LOG.trace("Select register")
|
||||||
editorState.isRegisterPending = true
|
editorState.isRegisterPending = true
|
||||||
commandBuilder.addKey(key)
|
commandBuilder.addKey(key)
|
||||||
|
isProcessed = true
|
||||||
} else {
|
} else {
|
||||||
// node == null
|
// node == null
|
||||||
LOG.trace("We are not able to find a node for this key")
|
LOG.trace("We are not able to find a node for this key")
|
||||||
@ -155,37 +178,55 @@ public class KeyHandler {
|
|||||||
// If we are in insert/replace mode send this key in for processing
|
// If we are in insert/replace mode send this key in for processing
|
||||||
if (editorState.mode == Mode.INSERT || editorState.mode == Mode.REPLACE) {
|
if (editorState.mode == Mode.INSERT || editorState.mode == Mode.REPLACE) {
|
||||||
LOG.trace("Process insert or replace")
|
LOG.trace("Process insert or replace")
|
||||||
shouldRecord = injector.changeGroup.processKey(editor, context, key) && shouldRecord
|
shouldRecord = injector.changeGroup.processKey(editor, key, processBuilder) && shouldRecord
|
||||||
|
isProcessed = true
|
||||||
} else if (editorState.mode is Mode.SELECT) {
|
} else if (editorState.mode is Mode.SELECT) {
|
||||||
LOG.trace("Process select")
|
LOG.trace("Process select")
|
||||||
shouldRecord = injector.changeGroup.processKeyInSelectMode(editor, context, key) && shouldRecord
|
shouldRecord = injector.changeGroup.processKeyInSelectMode(editor, key, processBuilder) && shouldRecord
|
||||||
|
isProcessed = true
|
||||||
} else if (editor.mode is Mode.CMD_LINE) {
|
} else if (editor.mode is Mode.CMD_LINE) {
|
||||||
LOG.trace("Process cmd line")
|
LOG.trace("Process cmd line")
|
||||||
shouldRecord = injector.processGroup.processExKey(editor, key) && shouldRecord
|
shouldRecord = injector.processGroup.processExKey(editor, key, processBuilder) && shouldRecord
|
||||||
|
isProcessed = true
|
||||||
} else {
|
} else {
|
||||||
LOG.trace("Set command state to bad_command")
|
LOG.trace("Set command state to bad_command")
|
||||||
commandBuilder.commandState = CurrentCommandState.BAD_COMMAND
|
commandBuilder.commandState = CurrentCommandState.BAD_COMMAND
|
||||||
}
|
}
|
||||||
partialReset(newState, editorState.mode)
|
partialReset(processBuilder.state, editorState.mode)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
isProcessed = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isProcessed = true
|
||||||
|
}
|
||||||
|
if (isProcessed) {
|
||||||
|
processBuilder.addExecutionStep { lambdaKeyState, lambdaEditor, lambdaContext ->
|
||||||
|
finishedCommandPreparation(lambdaEditor, lambdaContext, key, shouldRecord, lambdaKeyState)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Key wasn't processed by any of the consumers, so we reset our key state
|
||||||
|
// and tell IDE that the key is Unknown (handle key for us)
|
||||||
|
onUnknownKey(editor, processBuilder.state)
|
||||||
|
return KeyProcessResult.Unknown.apply {
|
||||||
|
handleKeyRecursionCount-- // because onFinish will now be executed for unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
handleKeyRecursionCount--
|
processBuilder.onFinish = { handleKeyRecursionCount-- }
|
||||||
}
|
}
|
||||||
finishedCommandPreparation(editor, context, editorState, key, shouldRecord, newState)
|
return processBuilder.build()
|
||||||
updateState(newState)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun finishedCommandPreparation(
|
internal fun finishedCommandPreparation(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
editorState: VimStateMachine,
|
|
||||||
key: KeyStroke?,
|
key: KeyStroke?,
|
||||||
shouldRecord: Boolean,
|
shouldRecord: Boolean,
|
||||||
keyState: KeyHandlerState,
|
keyState: KeyHandlerState,
|
||||||
) {
|
) {
|
||||||
// Do we have a fully entered command at this point? If so, let's execute it.
|
// Do we have a fully entered command at this point? If so, let's execute it.
|
||||||
|
val editorState = editor.vimStateMachine
|
||||||
val commandBuilder = keyState.commandBuilder
|
val commandBuilder = keyState.commandBuilder
|
||||||
if (commandBuilder.isReady) {
|
if (commandBuilder.isReady) {
|
||||||
LOG.trace("Ready command builder. Execute command.")
|
LOG.trace("Ready command builder. Execute command.")
|
||||||
@ -211,6 +252,21 @@ public class KeyHandler {
|
|||||||
LOG.trace("----------- Key Handler Finished -----------")
|
LOG.trace("----------- Key Handler Finished -----------")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onUnknownKey(editor: VimEditor, keyState: KeyHandlerState) {
|
||||||
|
keyState.commandBuilder.commandState = CurrentCommandState.BAD_COMMAND
|
||||||
|
LOG.trace("Command builder is set to BAD")
|
||||||
|
editor.resetOpPending()
|
||||||
|
editor.vimStateMachine.resetRegisterPending()
|
||||||
|
editor.isReplaceCharacter = false
|
||||||
|
reset(keyState, editor.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun setBadCommand(editor: VimEditor, keyState: KeyHandlerState) {
|
||||||
|
onUnknownKey(editor, keyState)
|
||||||
|
injector.messages.indicateError()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the description for [com.maddyhome.idea.vim.command.DuplicableOperatorAction]
|
* See the description for [com.maddyhome.idea.vim.command.DuplicableOperatorAction]
|
||||||
*/
|
*/
|
||||||
@ -357,18 +413,16 @@ public class KeyHandler {
|
|||||||
private fun handleDigraph(
|
private fun handleDigraph(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
key: KeyStroke,
|
key: KeyStroke,
|
||||||
keyState: KeyHandlerState,
|
keyProcessResultBuilder: KeyProcessResult.KeyProcessResultBuilder,
|
||||||
context: ExecutionContext,
|
|
||||||
): Boolean {
|
): Boolean {
|
||||||
LOG.debug("Handling digraph")
|
|
||||||
// Support starting a digraph/literal sequence if the operator accepts one as an argument, e.g. 'r' or 'f'.
|
// Support starting a digraph/literal sequence if the operator accepts one as an argument, e.g. 'r' or 'f'.
|
||||||
// Normally, we start the sequence (in Insert or CmdLine mode) through a VimAction that can be mapped. Our
|
// Normally, we start the sequence (in Insert or CmdLine mode) through a VimAction that can be mapped. Our
|
||||||
// VimActions don't work as arguments for operators, so we have to special case here. Helpfully, Vim appears to
|
// VimActions don't work as arguments for operators, so we have to special case here. Helpfully, Vim appears to
|
||||||
// hardcode the shortcuts, and doesn't support mapping, so everything works nicely.
|
// hardcode the shortcuts, and doesn't support mapping, so everything works nicely.
|
||||||
|
val keyState = keyProcessResultBuilder.state
|
||||||
val commandBuilder = keyState.commandBuilder
|
val commandBuilder = keyState.commandBuilder
|
||||||
val digraphSequence = keyState.digraphSequence
|
val digraphSequence = keyState.digraphSequence
|
||||||
if (commandBuilder.expectedArgumentType == Argument.Type.DIGRAPH) {
|
if (commandBuilder.expectedArgumentType == Argument.Type.DIGRAPH) {
|
||||||
LOG.trace("Expected argument is digraph")
|
|
||||||
if (digraphSequence.isDigraphStart(key)) {
|
if (digraphSequence.isDigraphStart(key)) {
|
||||||
digraphSequence.startDigraphSequence()
|
digraphSequence.startDigraphSequence()
|
||||||
commandBuilder.addKey(key)
|
commandBuilder.addKey(key)
|
||||||
@ -381,34 +435,54 @@ public class KeyHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val res = digraphSequence.processKey(key, editor)
|
val res = digraphSequence.processKey(key, editor)
|
||||||
if (injector.exEntryPanel.isActive()) {
|
|
||||||
when (res.result) {
|
|
||||||
DigraphResult.RES_HANDLED -> setPromptCharacterEx(if (commandBuilder.isPuttingLiteral()) '^' else key.keyChar)
|
|
||||||
DigraphResult.RES_DONE, DigraphResult.RES_BAD -> if (key.keyCode == KeyEvent.VK_C && key.modifiers and InputEvent.CTRL_DOWN_MASK != 0) {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
injector.exEntryPanel.clearCurrentAction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
when (res.result) {
|
when (res.result) {
|
||||||
DigraphResult.RES_HANDLED -> {
|
DigraphResult.RES_HANDLED -> {
|
||||||
commandBuilder.addKey(key)
|
keyProcessResultBuilder.addExecutionStep { lambdaKeyState, _, _ ->
|
||||||
|
if (injector.exEntryPanel.isActive()) {
|
||||||
|
setPromptCharacterEx(if (lambdaKeyState.commandBuilder.isPuttingLiteral()) '^' else key.keyChar)
|
||||||
|
}
|
||||||
|
lambdaKeyState.commandBuilder.addKey(key)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
DigraphResult.RES_DONE -> {
|
DigraphResult.RES_DONE -> {
|
||||||
if (commandBuilder.expectedArgumentType === Argument.Type.DIGRAPH) {
|
if (injector.exEntryPanel.isActive()) {
|
||||||
commandBuilder.fallbackToCharacterArgument()
|
if (key.keyCode == KeyEvent.VK_C && key.modifiers and InputEvent.CTRL_DOWN_MASK != 0) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
keyProcessResultBuilder.addExecutionStep { _, _, _ ->
|
||||||
|
injector.exEntryPanel.clearCurrentAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keyProcessResultBuilder.addExecutionStep { lambdaKeyState, _, _ ->
|
||||||
|
if (lambdaKeyState.commandBuilder.expectedArgumentType === Argument.Type.DIGRAPH) {
|
||||||
|
lambdaKeyState.commandBuilder.fallbackToCharacterArgument()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val stroke = res.stroke ?: return false
|
val stroke = res.stroke ?: return false
|
||||||
commandBuilder.addKey(key)
|
keyProcessResultBuilder.addExecutionStep { lambdaKeyState, lambdaEditorState, lambdaContext ->
|
||||||
handleKey(editor, stroke, context, keyState)
|
lambdaKeyState.commandBuilder.addKey(key)
|
||||||
|
handleKey(lambdaEditorState, stroke, lambdaContext, lambdaKeyState)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
DigraphResult.RES_BAD -> {
|
DigraphResult.RES_BAD -> {
|
||||||
// BAD is an error. We were expecting a valid character, and we didn't get it.
|
if (injector.exEntryPanel.isActive()) {
|
||||||
if (commandBuilder.expectedArgumentType != null) {
|
if (key.keyCode == KeyEvent.VK_C && key.modifiers and InputEvent.CTRL_DOWN_MASK != 0) {
|
||||||
commandBuilder.commandState = CurrentCommandState.BAD_COMMAND
|
return false
|
||||||
|
} else {
|
||||||
|
keyProcessResultBuilder.addExecutionStep { _, _, _ ->
|
||||||
|
injector.exEntryPanel.clearCurrentAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keyProcessResultBuilder.addExecutionStep { lambdaKeyState, lambdaEditor, _ ->
|
||||||
|
// BAD is an error. We were expecting a valid character, and we didn't get it.
|
||||||
|
if (lambdaKeyState.commandBuilder.expectedArgumentType != null) {
|
||||||
|
setBadCommand(lambdaEditor, lambdaKeyState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -417,7 +491,9 @@ public class KeyHandler {
|
|||||||
// state. E.g. waiting for {char} <BS> {char}. Let the key handler have a go at it.
|
// state. E.g. waiting for {char} <BS> {char}. Let the key handler have a go at it.
|
||||||
if (commandBuilder.expectedArgumentType === Argument.Type.DIGRAPH) {
|
if (commandBuilder.expectedArgumentType === Argument.Type.DIGRAPH) {
|
||||||
commandBuilder.fallbackToCharacterArgument()
|
commandBuilder.fallbackToCharacterArgument()
|
||||||
handleKey(editor, key, context, keyState)
|
keyProcessResultBuilder.addExecutionStep { lambdaKeyState, lambdaEditor, lambdaContext ->
|
||||||
|
handleKey(lambdaEditor, key, lambdaContext, lambdaKeyState)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -469,58 +545,63 @@ public class KeyHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleCommandNode(
|
private fun handleCommandNode(
|
||||||
editor: VimEditor,
|
|
||||||
context: ExecutionContext,
|
|
||||||
key: KeyStroke,
|
key: KeyStroke,
|
||||||
node: CommandNode<LazyVimCommand>,
|
node: CommandNode<LazyVimCommand>,
|
||||||
keyState: KeyHandlerState,
|
processBuilder: KeyProcessResult.KeyProcessResultBuilder,
|
||||||
editorState: VimStateMachine,
|
|
||||||
) {
|
) {
|
||||||
LOG.trace("Handle command node")
|
LOG.trace("Handle command node")
|
||||||
// The user entered a valid command. Create the command and add it to the stack.
|
// The user entered a valid command. Create the command and add it to the stack.
|
||||||
val action = node.actionHolder.instance
|
val action = node.actionHolder.instance
|
||||||
|
val keyState = processBuilder.state
|
||||||
val commandBuilder = keyState.commandBuilder
|
val commandBuilder = keyState.commandBuilder
|
||||||
val expectedArgumentType = commandBuilder.expectedArgumentType
|
val expectedArgumentType = commandBuilder.expectedArgumentType
|
||||||
commandBuilder.pushCommandPart(action)
|
commandBuilder.pushCommandPart(action)
|
||||||
if (!checkArgumentCompatibility(expectedArgumentType, action)) {
|
if (!checkArgumentCompatibility(expectedArgumentType, action)) {
|
||||||
LOG.trace("Return from command node handling")
|
LOG.trace("Return from command node handling")
|
||||||
commandBuilder.commandState = CurrentCommandState.BAD_COMMAND
|
processBuilder.addExecutionStep { lamdaKeyState, lambdaEditor, _ ->
|
||||||
|
setBadCommand(lambdaEditor, lamdaKeyState)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (action.argumentType == null || stopMacroRecord(node)) {
|
if (action.argumentType == null || stopMacroRecord(node)) {
|
||||||
LOG.trace("Set command state to READY")
|
LOG.trace("Set command state to READY")
|
||||||
commandBuilder.commandState = CurrentCommandState.READY
|
commandBuilder.commandState = CurrentCommandState.READY
|
||||||
} else {
|
} else {
|
||||||
LOG.trace("Set waiting for the argument")
|
processBuilder.addExecutionStep { lambdaKeyState, lambdaEditor, lambdaContext ->
|
||||||
val argumentType = action.argumentType
|
LOG.trace("Set waiting for the argument")
|
||||||
startWaitingForArgument(editor, context, key.keyChar, action, argumentType!!, keyState, editorState)
|
val argumentType = action.argumentType
|
||||||
partialReset(keyState, editorState.mode)
|
val editorState = lambdaEditor.vimStateMachine
|
||||||
|
startWaitingForArgument(lambdaEditor, lambdaContext, key.keyChar, action, argumentType!!, lambdaKeyState, editorState)
|
||||||
|
lambdaKeyState.partialReset(editorState.mode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO In the name of God, get rid of EX_STRING, FLAG_COMPLETE_EX and all the related staff
|
processBuilder.addExecutionStep { _, lambdaEditor, _ ->
|
||||||
if (expectedArgumentType === Argument.Type.EX_STRING && action.flags.contains(CommandFlags.FLAG_COMPLETE_EX)) {
|
// TODO In the name of God, get rid of EX_STRING, FLAG_COMPLETE_EX and all the related staff
|
||||||
/* The only action that implements FLAG_COMPLETE_EX is ProcessExEntryAction.
|
if (expectedArgumentType === Argument.Type.EX_STRING && action.flags.contains(CommandFlags.FLAG_COMPLETE_EX)) {
|
||||||
* When pressing ':', ExEntryAction is chosen as the command. Since it expects no arguments, it is invoked and
|
/* The only action that implements FLAG_COMPLETE_EX is ProcessExEntryAction.
|
||||||
calls ProcessGroup#startExCommand, pushes CMD_LINE mode, and the action is popped. The ex handler will push
|
* When pressing ':', ExEntryAction is chosen as the command. Since it expects no arguments, it is invoked and
|
||||||
the final <CR> through handleKey, which chooses ProcessExEntryAction. Because we're not expecting EX_STRING,
|
calls ProcessGroup#startExCommand, pushes CMD_LINE mode, and the action is popped. The ex handler will push
|
||||||
this branch does NOT fire, and ProcessExEntryAction handles the ex cmd line entry.
|
the final <CR> through handleKey, which chooses ProcessExEntryAction. Because we're not expecting EX_STRING,
|
||||||
* When pressing '/' or '?', SearchEntry(Fwd|Rev)Action is chosen as the command. This expects an argument of
|
this branch does NOT fire, and ProcessExEntryAction handles the ex cmd line entry.
|
||||||
EX_STRING, so startWaitingForArgument calls ProcessGroup#startSearchCommand. The ex handler pushes the final
|
* When pressing '/' or '?', SearchEntry(Fwd|Rev)Action is chosen as the command. This expects an argument of
|
||||||
<CR> through handleKey, which chooses ProcessExEntryAction, and we hit this branch. We don't invoke
|
EX_STRING, so startWaitingForArgument calls ProcessGroup#startSearchCommand. The ex handler pushes the final
|
||||||
ProcessExEntryAction, but pop it, set the search text as an argument on SearchEntry(Fwd|Rev)Action and invoke
|
<CR> through handleKey, which chooses ProcessExEntryAction, and we hit this branch. We don't invoke
|
||||||
that instead.
|
ProcessExEntryAction, but pop it, set the search text as an argument on SearchEntry(Fwd|Rev)Action and invoke
|
||||||
* When using '/' or '?' as part of a motion (e.g. "d/foo"), the above happens again, and all is good. Because
|
that instead.
|
||||||
the text has been applied as an argument on the last command, '.' will correctly repeat it.
|
* When using '/' or '?' as part of a motion (e.g. "d/foo"), the above happens again, and all is good. Because
|
||||||
|
the text has been applied as an argument on the last command, '.' will correctly repeat it.
|
||||||
|
|
||||||
It's hard to see how to improve this. Removing EX_STRING means starting ex input has to happen in ExEntryAction
|
It's hard to see how to improve this. Removing EX_STRING means starting ex input has to happen in ExEntryAction
|
||||||
and SearchEntry(Fwd|Rev)Action, and the ex command invoked in ProcessExEntryAction, but that breaks any initial
|
and SearchEntry(Fwd|Rev)Action, and the ex command invoked in ProcessExEntryAction, but that breaks any initial
|
||||||
operator, which would be invoked first (e.g. 'd' in "d/foo").
|
operator, which would be invoked first (e.g. 'd' in "d/foo").
|
||||||
*/
|
*/
|
||||||
LOG.trace("Processing ex_string")
|
LOG.trace("Processing ex_string")
|
||||||
val text = injector.processGroup.endSearchCommand()
|
val text = injector.processGroup.endSearchCommand()
|
||||||
commandBuilder.popCommandPart() // Pop ProcessExEntryAction
|
commandBuilder.popCommandPart() // Pop ProcessExEntryAction
|
||||||
commandBuilder.completeCommandPart(Argument(text)) // Set search text on SearchEntry(Fwd|Rev)Action
|
commandBuilder.completeCommandPart(Argument(text)) // Set search text on SearchEntry(Fwd|Rev)Action
|
||||||
editor.mode = editorState.mode.returnTo()
|
lambdaEditor.mode = lambdaEditor.mode.returnTo()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.maddyhome.idea.vim.api
|
package com.maddyhome.idea.vim.api
|
||||||
|
|
||||||
|
import com.maddyhome.idea.vim.KeyProcessResult
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.command.OperatorArguments
|
import com.maddyhome.idea.vim.command.OperatorArguments
|
||||||
@ -65,9 +66,9 @@ public interface VimChangeGroup {
|
|||||||
operatorArguments: OperatorArguments,
|
operatorArguments: OperatorArguments,
|
||||||
): Boolean
|
): Boolean
|
||||||
|
|
||||||
public fun processKey(editor: VimEditor, context: ExecutionContext, key: KeyStroke): Boolean
|
public fun processKey(editor: VimEditor, key: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean
|
||||||
|
|
||||||
public fun processKeyInSelectMode(editor: VimEditor, context: ExecutionContext, key: KeyStroke): Boolean
|
public fun processKeyInSelectMode(editor: VimEditor, key: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean
|
||||||
|
|
||||||
public fun deleteLine(editor: VimEditor, caret: VimCaret, count: Int, operatorArguments: OperatorArguments): Boolean
|
public fun deleteLine(editor: VimEditor, caret: VimCaret, count: Int, operatorArguments: OperatorArguments): Boolean
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
package com.maddyhome.idea.vim.api
|
package com.maddyhome.idea.vim.api
|
||||||
|
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
|
import com.maddyhome.idea.vim.KeyProcessResult
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.command.CommandFlags
|
import com.maddyhome.idea.vim.command.CommandFlags
|
||||||
@ -715,18 +716,18 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
|
|||||||
*/
|
*/
|
||||||
override fun processKey(
|
override fun processKey(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
context: ExecutionContext,
|
|
||||||
key: KeyStroke,
|
key: KeyStroke,
|
||||||
|
processResultBuilder: KeyProcessResult.KeyProcessResultBuilder,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
logger.debug { "processKey($key)" }
|
logger.debug { "processKey($key)" }
|
||||||
if (key.keyChar != KeyEvent.CHAR_UNDEFINED) {
|
if (key.keyChar != KeyEvent.CHAR_UNDEFINED) {
|
||||||
type(editor, context, key.keyChar)
|
processResultBuilder.addExecutionStep { _, lambdaEditor, lambdaContext -> type(lambdaEditor, lambdaContext, key.keyChar) }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shift-space
|
// Shift-space
|
||||||
if (key.keyCode == 32 && key.modifiers and KeyEvent.SHIFT_DOWN_MASK != 0) {
|
if (key.keyCode == 32 && key.modifiers and KeyEvent.SHIFT_DOWN_MASK != 0) {
|
||||||
type(editor, context, ' ')
|
processResultBuilder.addExecutionStep { _, lambdaEditor, lambdaContext -> type(lambdaEditor, lambdaContext, ' ') }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -734,16 +735,18 @@ public abstract class VimChangeGroupBase : VimChangeGroup {
|
|||||||
|
|
||||||
override fun processKeyInSelectMode(
|
override fun processKeyInSelectMode(
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
context: ExecutionContext,
|
|
||||||
key: KeyStroke,
|
key: KeyStroke,
|
||||||
|
processResultBuilder: KeyProcessResult.KeyProcessResultBuilder
|
||||||
): Boolean {
|
): Boolean {
|
||||||
var res: Boolean
|
var res: Boolean
|
||||||
SelectionVimListenerSuppressor.lock().use {
|
SelectionVimListenerSuppressor.lock().use {
|
||||||
res = processKey(editor, context, key)
|
res = processKey(editor, key, processResultBuilder)
|
||||||
editor.exitSelectModeNative(false)
|
processResultBuilder.addExecutionStep { _, lambdaEditor, lambdaContext ->
|
||||||
KeyHandler.getInstance().reset(editor)
|
lambdaEditor.exitSelectModeNative(false)
|
||||||
if (isPrintableChar(key.keyChar) || activeTemplateWithLeftRightMotion(editor, key)) {
|
KeyHandler.getInstance().reset(lambdaEditor)
|
||||||
injector.changeGroup.insertBeforeCursor(editor, context)
|
if (isPrintableChar(key.keyChar) || activeTemplateWithLeftRightMotion(lambdaEditor, key)) {
|
||||||
|
injector.changeGroup.insertBeforeCursor(lambdaEditor, lambdaContext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.maddyhome.idea.vim.api
|
package com.maddyhome.idea.vim.api
|
||||||
|
|
||||||
|
import com.maddyhome.idea.vim.KeyProcessResult
|
||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
@ -18,7 +19,7 @@ public interface VimProcessGroup {
|
|||||||
|
|
||||||
public fun startSearchCommand(editor: VimEditor, context: ExecutionContext, count: Int, leader: Char)
|
public fun startSearchCommand(editor: VimEditor, context: ExecutionContext, count: Int, leader: Char)
|
||||||
public fun endSearchCommand(): String
|
public fun endSearchCommand(): String
|
||||||
public fun processExKey(editor: VimEditor, stroke: KeyStroke): Boolean
|
public fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean
|
||||||
public fun startFilterCommand(editor: VimEditor, context: ExecutionContext, cmd: Command)
|
public fun startFilterCommand(editor: VimEditor, context: ExecutionContext, cmd: Command)
|
||||||
public fun startExCommand(editor: VimEditor, context: ExecutionContext, cmd: Command)
|
public fun startExCommand(editor: VimEditor, context: ExecutionContext, cmd: Command)
|
||||||
public fun processExEntry(editor: VimEditor, context: ExecutionContext): Boolean
|
public fun processExEntry(editor: VimEditor, context: ExecutionContext): Boolean
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim.api.stubs
|
package com.maddyhome.idea.vim.api.stubs
|
||||||
|
|
||||||
|
import com.maddyhome.idea.vim.KeyProcessResult
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.VimProcessGroupBase
|
import com.maddyhome.idea.vim.api.VimProcessGroupBase
|
||||||
@ -36,7 +37,7 @@ public class VimProcessGroupStub : VimProcessGroupBase() {
|
|||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun processExKey(editor: VimEditor, stroke: KeyStroke): Boolean {
|
override fun processExKey(editor: VimEditor, stroke: KeyStroke, processResultBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
package com.maddyhome.idea.vim.command
|
package com.maddyhome.idea.vim.command
|
||||||
|
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
|
import com.maddyhome.idea.vim.KeyProcessResult
|
||||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||||
import com.maddyhome.idea.vim.api.VimEditor
|
import com.maddyhome.idea.vim.api.VimEditor
|
||||||
import com.maddyhome.idea.vim.api.injector
|
import com.maddyhome.idea.vim.api.injector
|
||||||
@ -19,8 +20,8 @@ import com.maddyhome.idea.vim.diagnostic.vimLogger
|
|||||||
import com.maddyhome.idea.vim.helper.vimStateMachine
|
import com.maddyhome.idea.vim.helper.vimStateMachine
|
||||||
import com.maddyhome.idea.vim.impl.state.toMappingMode
|
import com.maddyhome.idea.vim.impl.state.toMappingMode
|
||||||
import com.maddyhome.idea.vim.key.KeyMappingLayer
|
import com.maddyhome.idea.vim.key.KeyMappingLayer
|
||||||
|
import com.maddyhome.idea.vim.key.MappingInfoLayer
|
||||||
import com.maddyhome.idea.vim.state.KeyHandlerState
|
import com.maddyhome.idea.vim.state.KeyHandlerState
|
||||||
import com.maddyhome.idea.vim.state.VimStateMachine
|
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
public object MappingProcessor {
|
public object MappingProcessor {
|
||||||
@ -28,13 +29,16 @@ public object MappingProcessor {
|
|||||||
private val log = vimLogger<MappingProcessor>()
|
private val log = vimLogger<MappingProcessor>()
|
||||||
|
|
||||||
internal fun handleKeyMapping(
|
internal fun handleKeyMapping(
|
||||||
editor: VimEditor,
|
|
||||||
key: KeyStroke,
|
key: KeyStroke,
|
||||||
keyState: KeyHandlerState,
|
editor: VimEditor,
|
||||||
context: ExecutionContext,
|
allowKeyMappings: Boolean,
|
||||||
mappingCompleted: Boolean,
|
mappingCompleted: Boolean,
|
||||||
|
keyProcessResultBuilder: KeyProcessResult.KeyProcessResultBuilder,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
if (!allowKeyMappings) return false
|
||||||
|
|
||||||
log.debug("Start processing key mappings.")
|
log.debug("Start processing key mappings.")
|
||||||
|
val keyState = keyProcessResultBuilder.state
|
||||||
val commandState = editor.vimStateMachine
|
val commandState = editor.vimStateMachine
|
||||||
val mappingState = keyState.mappingState
|
val mappingState = keyState.mappingState
|
||||||
val commandBuilder = keyState.commandBuilder
|
val commandBuilder = keyState.commandBuilder
|
||||||
@ -58,9 +62,9 @@ public object MappingProcessor {
|
|||||||
// Returns true if any of these methods handle the key. False means that the key is unrelated to mapping and should
|
// Returns true if any of these methods handle the key. False means that the key is unrelated to mapping and should
|
||||||
// be processed as normal.
|
// be processed as normal.
|
||||||
val mappingProcessed =
|
val mappingProcessed =
|
||||||
handleUnfinishedMappingSequence(editor, keyState, mappingState, mapping, mappingCompleted) ||
|
handleUnfinishedMappingSequence(keyProcessResultBuilder, mapping, mappingCompleted) ||
|
||||||
handleCompleteMappingSequence(editor, keyState, context, mappingState, mapping, key) ||
|
handleCompleteMappingSequence(keyProcessResultBuilder, mapping, key) ||
|
||||||
handleAbandonedMappingSequence(editor, keyState, mappingState, context)
|
handleAbandonedMappingSequence(keyProcessResultBuilder)
|
||||||
log.debug { "Finish mapping processing. Return $mappingProcessed" }
|
log.debug { "Finish mapping processing. Return $mappingProcessed" }
|
||||||
|
|
||||||
return mappingProcessed
|
return mappingProcessed
|
||||||
@ -76,9 +80,7 @@ public object MappingProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleUnfinishedMappingSequence(
|
private fun handleUnfinishedMappingSequence(
|
||||||
editor: VimEditor,
|
processBuilder: KeyProcessResult.KeyProcessResultBuilder,
|
||||||
keyState: KeyHandlerState,
|
|
||||||
mappingState: MappingState,
|
|
||||||
mapping: KeyMappingLayer,
|
mapping: KeyMappingLayer,
|
||||||
mappingCompleted: Boolean,
|
mappingCompleted: Boolean,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
@ -93,7 +95,7 @@ public object MappingProcessor {
|
|||||||
// mapping is a prefix, it will get evaluated when the next character is entered.
|
// mapping is a prefix, it will get evaluated when the next character is entered.
|
||||||
// Note that currentlyUnhandledKeySequence is the same as the state after commandState.getMappingKeys().add(key). It
|
// Note that currentlyUnhandledKeySequence is the same as the state after commandState.getMappingKeys().add(key). It
|
||||||
// would be nice to tidy ths up
|
// would be nice to tidy ths up
|
||||||
if (!mapping.isPrefix(mappingState.keys)) {
|
if (!mapping.isPrefix(processBuilder.state.mappingState.keys)) {
|
||||||
log.debug("There are no mappings that start with the current sequence. Returning false.")
|
log.debug("There are no mappings that start with the current sequence. Returning false.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -102,6 +104,11 @@ public object MappingProcessor {
|
|||||||
// Every time a key is pressed and handled, the timer is stopped. E.g. if there is a mapping for "dweri", and the
|
// Every time a key is pressed and handled, the timer is stopped. E.g. if there is a mapping for "dweri", and the
|
||||||
// user has typed "dw" wait for the timeout, and then replay "d" and "w" without any mapping (which will of course
|
// user has typed "dw" wait for the timeout, and then replay "d" and "w" without any mapping (which will of course
|
||||||
// delete a word)
|
// delete a word)
|
||||||
|
processBuilder.addExecutionStep { lambdaKeyState, lambdaEditor, _ -> processUnfinishedMappingSequence(lambdaEditor, lambdaKeyState) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processUnfinishedMappingSequence(editor: VimEditor, keyState: KeyHandlerState) {
|
||||||
if (injector.options(editor).timeout) {
|
if (injector.options(editor).timeout) {
|
||||||
log.trace("timeout is set. schedule a mapping timer")
|
log.trace("timeout is set. schedule a mapping timer")
|
||||||
// XXX There is a strange issue that reports that mapping state is empty at the moment of the function call.
|
// XXX There is a strange issue that reports that mapping state is empty at the moment of the function call.
|
||||||
@ -109,6 +116,7 @@ public object MappingProcessor {
|
|||||||
// but before invoke later is handled. This is a rare case, so I'll just add a check to isPluginMapping.
|
// but before invoke later is handled. This is a rare case, so I'll just add a check to isPluginMapping.
|
||||||
// But this "unexpected behaviour" exists, and it would be better not to relay on mutable state with delays.
|
// But this "unexpected behaviour" exists, and it would be better not to relay on mutable state with delays.
|
||||||
// https://youtrack.jetbrains.com/issue/VIM-2392
|
// https://youtrack.jetbrains.com/issue/VIM-2392
|
||||||
|
val mappingState = keyState.mappingState
|
||||||
mappingState.startMappingTimer {
|
mappingState.startMappingTimer {
|
||||||
injector.application.invokeLater(
|
injector.application.invokeLater(
|
||||||
{
|
{
|
||||||
@ -127,7 +135,8 @@ public object MappingProcessor {
|
|||||||
// of waiting for `abc` mapping.
|
// of waiting for `abc` mapping.
|
||||||
val lastKeyInSequence = index == unhandledKeys.lastIndex
|
val lastKeyInSequence = index == unhandledKeys.lastIndex
|
||||||
|
|
||||||
KeyHandler.getInstance().handleKey(
|
val keyHandler = KeyHandler.getInstance()
|
||||||
|
keyHandler.handleKey(
|
||||||
editor,
|
editor,
|
||||||
keyStroke,
|
keyStroke,
|
||||||
injector.executionContextManager.onEditor(editor),
|
injector.executionContextManager.onEditor(editor),
|
||||||
@ -142,19 +151,16 @@ public object MappingProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.trace("Unfinished mapping processing finished")
|
log.trace("Unfinished mapping processing finished")
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleCompleteMappingSequence(
|
private fun handleCompleteMappingSequence(
|
||||||
editor: VimEditor,
|
processBuilder: KeyProcessResult.KeyProcessResultBuilder,
|
||||||
keyState: KeyHandlerState,
|
|
||||||
context: ExecutionContext,
|
|
||||||
mappingState: MappingState,
|
|
||||||
mapping: KeyMappingLayer,
|
mapping: KeyMappingLayer,
|
||||||
key: KeyStroke,
|
key: KeyStroke,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
log.trace("Processing complete mapping sequence...")
|
log.trace("Processing complete mapping sequence...")
|
||||||
// The current sequence isn't a prefix, check to see if it's a completed sequence.
|
// The current sequence isn't a prefix, check to see if it's a completed sequence.
|
||||||
|
val mappingState = processBuilder.state.mappingState
|
||||||
val currentMappingInfo = mapping.getLayer(mappingState.keys)
|
val currentMappingInfo = mapping.getLayer(mappingState.keys)
|
||||||
var mappingInfo = currentMappingInfo
|
var mappingInfo = currentMappingInfo
|
||||||
if (mappingInfo == null) {
|
if (mappingInfo == null) {
|
||||||
@ -180,6 +186,19 @@ public object MappingProcessor {
|
|||||||
log.trace("Cannot find any mapping info for the sequence. Return false.")
|
log.trace("Cannot find any mapping info for the sequence. Return false.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
processBuilder.addExecutionStep { b, c, d -> processCompleteMappingSequence(key, b, c, d, mappingInfo, currentMappingInfo) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processCompleteMappingSequence(
|
||||||
|
key: KeyStroke,
|
||||||
|
keyState: KeyHandlerState,
|
||||||
|
editor: VimEditor,
|
||||||
|
context: ExecutionContext,
|
||||||
|
mappingInfo: MappingInfoLayer,
|
||||||
|
currentMappingInfo: MappingInfoLayer?,
|
||||||
|
) {
|
||||||
|
val mappingState = keyState.mappingState
|
||||||
mappingState.resetMappingSequence()
|
mappingState.resetMappingSequence()
|
||||||
val currentContext = context.updateEditor(editor)
|
val currentContext = context.updateEditor(editor)
|
||||||
log.trace("Executing mapping info")
|
log.trace("Executing mapping info")
|
||||||
@ -216,20 +235,14 @@ public object MappingProcessor {
|
|||||||
KeyHandler.getInstance().handleKey(editor, key, currentContext, allowKeyMappings = true, false, keyState)
|
KeyHandler.getInstance().handleKey(editor, key, currentContext, allowKeyMappings = true, false, keyState)
|
||||||
}
|
}
|
||||||
log.trace("Success processing of mapping")
|
log.trace("Success processing of mapping")
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleAbandonedMappingSequence(
|
private fun handleAbandonedMappingSequence(processBuilder: KeyProcessResult.KeyProcessResultBuilder): Boolean {
|
||||||
editor: VimEditor,
|
|
||||||
keyState: KeyHandlerState,
|
|
||||||
mappingState: MappingState,
|
|
||||||
context: ExecutionContext,
|
|
||||||
): Boolean {
|
|
||||||
log.debug("Processing abandoned mapping sequence")
|
log.debug("Processing abandoned mapping sequence")
|
||||||
// The user has terminated a mapping sequence with an unexpected key
|
// The user has terminated a mapping sequence with an unexpected key
|
||||||
// E.g. if there is a mapping for "hello" and user enters command "help" the processing of "h", "e" and "l" will be
|
// E.g. if there is a mapping for "hello" and user enters command "help" the processing of "h", "e" and "l" will be
|
||||||
// prevented by this handler. Make sure the currently unhandled keys are processed as normal.
|
// prevented by this handler. Make sure the currently unhandled keys are processed as normal.
|
||||||
val unhandledKeyStrokes = mappingState.detachKeys()
|
val unhandledKeyStrokes = processBuilder.state.mappingState.detachKeys()
|
||||||
|
|
||||||
// If there is only the current key to handle, do nothing
|
// If there is only the current key to handle, do nothing
|
||||||
if (unhandledKeyStrokes.size == 1) {
|
if (unhandledKeyStrokes.size == 1) {
|
||||||
@ -244,6 +257,12 @@ public object MappingProcessor {
|
|||||||
// If user enters `dI`, the first `d` will be caught be this handler because it's a prefix for `ds` command.
|
// If user enters `dI`, the first `d` will be caught be this handler because it's a prefix for `ds` command.
|
||||||
// After the user enters `I`, the caught `d` should be processed without mapping, and the rest of keys
|
// After the user enters `I`, the caught `d` should be processed without mapping, and the rest of keys
|
||||||
// should be processed with mappings (to make I work)
|
// should be processed with mappings (to make I work)
|
||||||
|
processBuilder.addExecutionStep { lambdaKeyState, lambdaEditor, lambdaContext ->
|
||||||
|
processAbondonedMappingSequence(unhandledKeyStrokes, lambdaEditor, lambdaContext, lambdaKeyState) }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processAbondonedMappingSequence(unhandledKeyStrokes: List<KeyStroke>, editor: VimEditor, context: ExecutionContext, keyState: KeyHandlerState) {
|
||||||
if (isPluginMapping(unhandledKeyStrokes)) {
|
if (isPluginMapping(unhandledKeyStrokes)) {
|
||||||
log.trace("This is a plugin mapping, process it")
|
log.trace("This is a plugin mapping, process it")
|
||||||
KeyHandler.getInstance().handleKey(
|
KeyHandler.getInstance().handleKey(
|
||||||
@ -264,7 +283,6 @@ public object MappingProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.trace("Return true from abandoned keys processing.")
|
log.trace("Return true from abandoned keys processing.")
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The <Plug>mappings are not executed if they fail to map to something.
|
// The <Plug>mappings are not executed if they fail to map to something.
|
||||||
|
Loading…
Reference in New Issue
Block a user