1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-06-05 13:34:04 +02:00

[New Typing Handler]: Switch j command to new typing handler

This commit is contained in:
Alex Plate 2022-09-05 15:22:10 +03:00
parent 2829a13187
commit 43a79dbad4
No known key found for this signature in database
GPG Key ID: 0B97153C8FFEC09F
16 changed files with 277 additions and 43 deletions

View File

@ -242,7 +242,7 @@ tasks {
// Don't forget to update plugin.xml // Don't forget to update plugin.xml
patchPluginXml { patchPluginXml {
sinceBuild.set("222") sinceBuild.set("222.4167.9")
} }
} }

View File

@ -56,6 +56,11 @@ class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandle
return return
} }
if (useNewHandler(editor.vim, charTyped)) {
(KeyHandlerKeeper.getInstance().originalHandler as? TypedActionHandlerEx)?.beforeExecute(editor, charTyped, context, plan)
return
}
LOG.trace("Executing before execute") LOG.trace("Executing before execute")
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0 val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers) val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
@ -80,12 +85,17 @@ class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandle
return return
} }
if (useNewHandler(editor.vim, charTyped)) {
KeyHandlerKeeper.getInstance().originalHandler.execute(editor, charTyped, context)
return
}
try { try {
LOG.trace("Executing typed action") LOG.trace("Executing typed action")
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0 val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers) val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
val startTime = if (traceTime) System.currentTimeMillis() else null val startTime = if (traceTime) System.currentTimeMillis() else null
handler.handleKey(editor.vim, keyStroke, EditorDataContext.init(editor, context).vim) handler.handleKeyInitial(editor.vim, keyStroke, EditorDataContext.init(editor, context).vim)
if (startTime != null) { if (startTime != null) {
val duration = System.currentTimeMillis() - startTime val duration = System.currentTimeMillis() - startTime
LOG.info("VimTypedAction '$charTyped': $duration ms") LOG.info("VimTypedAction '$charTyped': $duration ms")

View File

@ -0,0 +1,99 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2022 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 <https://www.gnu.org/licenses/>.
*/
package com.maddyhome.idea.vim
import com.intellij.codeInsight.editorActions.TypedHandlerDelegate
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.helper.EditorDataContext
import com.maddyhome.idea.vim.helper.inInsertMode
import com.maddyhome.idea.vim.helper.inNormalMode
import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
import com.maddyhome.idea.vim.newapi.vim
import javax.swing.KeyStroke
class VimTypedDelegateHandler : TypedHandlerDelegate() {
lateinit var stateUpdateResult: KeyHandler.StateUpdateResult
override fun checkAutoPopup(charTyped: Char, project: Project, editor: Editor, file: PsiFile): Result {
if (!VimPlugin.isEnabled()) return Result.CONTINUE
if (editor.isIdeaVimDisabledHere) return Result.CONTINUE
if (!useNewHandler(editor.vim, charTyped)) return Result.CONTINUE
return if (editor.inInsertMode) Result.CONTINUE else Result.STOP
}
override fun newTypingStarted(c: Char, editor: Editor, context: DataContext) {
if (!VimPlugin.isEnabled()) return
if (editor.isIdeaVimDisabledHere) return
if (!useNewHandler(editor.vim, c)) return
val keyStroke = KeyStroke.getKeyStroke(c)
val content = EditorDataContext.init(editor, context)
content.newTypingDelegate = true
stateUpdateResult = KeyHandler.getInstance()
.handleKeyInitial(editor.vim, keyStroke, content.vim, execute = false)
}
override fun beforeCharTyped(c: Char, project: Project, editor: Editor, file: PsiFile, fileType: FileType): Result {
if (!VimPlugin.isEnabled()) return Result.CONTINUE
if (editor.isIdeaVimDisabledHere) return Result.CONTINUE
if (!useNewHandler(editor.vim, c)) return Result.CONTINUE
val keyStroke = KeyStroke.getKeyStroke(c)
val context = EditorDataContext.init(editor)
context.newTypingDelegate = true
if (stateUpdateResult.continueExecution) {
KeyHandler.getInstance().finishedCommandPreparation(editor.vim, context.vim, keyStroke, stateUpdateResult.shouldRecord)
}
return Result.STOP
}
/*
override fun charTyped(c: Char, project: Project, editor: Editor, file: PsiFile): Result {
if (editor.isIdeaVimDisabledHere) return Result.CONTINUE
if (c !in charsByDelegate) return Result.CONTINUE
val keyStroke = KeyStroke.getKeyStroke(c)
KeyHandler.getInstance().handleKey(editor.vim, keyStroke, EditorDataContext.init(editor).vim)
return Result.STOP
}
*/
override fun isImmediatePaintingEnabled(editor: Editor, c: Char, context: DataContext): Boolean {
if (!VimPlugin.isEnabled()) return true
if (editor.isIdeaVimDisabledHere) return true
if (!useNewHandler(editor.vim, c)) return true
return editor.inInsertMode
}
}
internal val charsByDelegate = setOf('j')
internal fun useNewHandler(editor: VimEditor, c: Char): Boolean {
return c in charsByDelegate && editor.inNormalMode
}

View File

@ -82,7 +82,7 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
// Should we use HelperKt.getTopLevelEditor(editor) here, as we did in former EditorKeyHandler? // Should we use HelperKt.getTopLevelEditor(editor) here, as we did in former EditorKeyHandler?
try { try {
val start = if (traceTime) System.currentTimeMillis() else null val start = if (traceTime) System.currentTimeMillis() else null
KeyHandler.getInstance().handleKey(editor.vim, keyStroke, EditorDataContext.init(editor, e.dataContext).vim) KeyHandler.getInstance().handleKeyInitial(editor.vim, keyStroke, EditorDataContext.init(editor, e.dataContext).vim)
if (start != null) { if (start != null) {
val duration = System.currentTimeMillis() - start val duration = System.currentTimeMillis() - start
LOG.info("VimShortcut update '$keyStroke': $duration ms") LOG.info("VimShortcut update '$keyStroke': $duration ms")

View File

@ -73,7 +73,7 @@ public class MacroGroup extends VimMacroBase {
final Runnable run = () -> { final Runnable run = () -> {
// Handle one keystroke then queue up the next key // Handle one keystroke then queue up the next key
if (keyStack.hasStroke()) { if (keyStack.hasStroke()) {
KeyHandler.getInstance().handleKey(editor, keyStack.feedStroke(), context); KeyHandler.getInstance().handleKey(editor, keyStack.feedStroke(), context, true, false, true);
} }
if (keyStack.hasStroke()) { if (keyStack.hasStroke()) {
playbackKeys(editor, context, cnt, total); playbackKeys(editor, context, cnt, total);
@ -105,7 +105,7 @@ public class MacroGroup extends VimMacroBase {
return; return;
} }
ProgressManager.getInstance().executeNonCancelableSection(() -> { ProgressManager.getInstance().executeNonCancelableSection(() -> {
KeyHandler.getInstance().handleKey(editor, key, context); KeyHandler.getInstance().handleKey(editor, key, context, true, false, true);
}); });
} }
keyStack.resetFirst(); keyStack.resetFirst();

View File

@ -18,6 +18,7 @@
package com.maddyhome.idea.vim.helper package com.maddyhome.idea.vim.helper
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.DataKey
import com.intellij.openapi.actionSystem.PlatformDataKeys import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
@ -27,6 +28,9 @@ class EditorDataContext @Deprecated("Please use `init` method") constructor(
private val editor: Editor, private val editor: Editor,
private val contextDelegate: DataContext? = null, private val contextDelegate: DataContext? = null,
) : DataContext, UserDataHolder { ) : DataContext, UserDataHolder {
internal var newTypingDelegate = false
/** /**
* Returns the object corresponding to the specified data identifier. Some of the supported data identifiers are * Returns the object corresponding to the specified data identifier. Some of the supported data identifiers are
* defined in the [PlatformDataKeys] class. * defined in the [PlatformDataKeys] class.
@ -38,6 +42,7 @@ class EditorDataContext @Deprecated("Please use `init` method") constructor(
PlatformDataKeys.EDITOR.name == dataId -> editor PlatformDataKeys.EDITOR.name == dataId -> editor
PlatformDataKeys.PROJECT.name == dataId -> editor.project PlatformDataKeys.PROJECT.name == dataId -> editor.project
PlatformDataKeys.VIRTUAL_FILE.name == dataId -> EditorHelper.getVirtualFile(editor) PlatformDataKeys.VIRTUAL_FILE.name == dataId -> EditorHelper.getVirtualFile(editor)
NEW_DELEGATE.name == dataId -> newTypingDelegate
else -> contextDelegate?.getData(dataId) else -> contextDelegate?.getData(dataId)
} }
@ -71,3 +76,5 @@ class EditorDataContext @Deprecated("Please use `init` method") constructor(
} }
} }
} }
internal val NEW_DELEGATE = DataKey.create<Boolean>("IdeaVim.NEW_DELEGATE")

View File

@ -22,11 +22,16 @@ import com.intellij.openapi.actionSystem.DataContext
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.helper.EditorDataContext import com.maddyhome.idea.vim.helper.EditorDataContext
import com.maddyhome.idea.vim.helper.NEW_DELEGATE
class IjExecutionContext(override val context: DataContext) : ExecutionContext { class IjExecutionContext(override val context: DataContext) : ExecutionContext {
override fun updateEditor(editor: VimEditor): ExecutionContext { override fun updateEditor(editor: VimEditor): ExecutionContext {
return IjExecutionContext(EditorDataContext.init((editor as IjVimEditor).editor, context)) return IjExecutionContext(EditorDataContext.init((editor as IjVimEditor).editor, context))
} }
override fun isNewDelegate(): Boolean {
return context.getData(NEW_DELEGATE) ?: false
}
} }
val DataContext.vim val DataContext.vim

View File

@ -126,7 +126,7 @@
<!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version --> <!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version -->
<!-- Check for [Version Update] tag in YouTrack as well --> <!-- Check for [Version Update] tag in YouTrack as well -->
<!-- Also, please update the value in build.gradle.kts file--> <!-- Also, please update the value in build.gradle.kts file-->
<idea-version since-build="222"/> <idea-version since-build="222.4167.9"/>
<!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform (including CWM) --> <!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform (including CWM) -->
<depends>com.intellij.modules.platform</depends> <depends>com.intellij.modules.platform</depends>
@ -193,6 +193,8 @@
<statistics.applicationUsagesCollector implementation="com.maddyhome.idea.vim.statistic.ShortcutConflictState"/> <statistics.applicationUsagesCollector implementation="com.maddyhome.idea.vim.statistic.ShortcutConflictState"/>
<statistics.counterUsagesCollector implementationClass="com.maddyhome.idea.vim.statistic.ActionTracker"/> <statistics.counterUsagesCollector implementationClass="com.maddyhome.idea.vim.statistic.ActionTracker"/>
<typedHandler implementation="com.maddyhome.idea.vim.VimTypedDelegateHandler" order="first, before completionAutoPopup"/>
</extensions> </extensions>
<xi:include href="/META-INF/includes/ApplicationServices.xml" xpointer="xpointer(/idea-plugin/*)"/> <xi:include href="/META-INF/includes/ApplicationServices.xml" xpointer="xpointer(/idea-plugin/*)"/>

View File

@ -99,7 +99,7 @@ public abstract class JavaVimTestCase extends JavaCodeInsightFixtureTestCase {
exEntryPanel.handleKey(key); exEntryPanel.handleKey(key);
} }
else { else {
keyHandler.handleKey(new IjVimEditor(editor), key, new IjExecutionContext(dataContext)); keyHandler.handleKey(new IjVimEditor(editor), key, new IjExecutionContext(dataContext), true);
} }
} }
}, null, null); }, null, null);

View File

@ -29,6 +29,36 @@ import org.jetbrains.plugins.ideavim.VimTestCase
* @author Alex Plate * @author Alex Plate
*/ */
class MotionDownActionTest : VimTestCase() { class MotionDownActionTest : VimTestCase() {
fun `test simple motion down`() {
val keys = injector.parser.parseKeys("j")
val before = """
I found it in a ${c}legendary land
all rocks and lavender and tufted grass,
""".trimIndent()
val after = """
I found it in a legendary land
all rocks and la${c}vender and tufted grass,
""".trimIndent()
configureByText(before)
typeText(keys)
assertState(after)
}
fun `test simple motion down multicaret`() {
val keys = injector.parser.parseKeys("j")
val before = """
I found it in a ${c}legendary ${c}land
all rocks and lavender and tufted grass,
""".trimIndent()
val after = """
I found it in a legendary land
all rocks and la${c}vender and${c} tufted grass,
""".trimIndent()
configureByText(before)
typeText(keys)
assertState(after)
}
fun `test motion down in visual block mode`() { fun `test motion down in visual block mode`() {
val keys = "<C-V>2kjjj" val keys = "<C-V>2kjjj"
val before = """ val before = """

View File

@ -56,6 +56,11 @@ import java.awt.event.KeyEvent
import java.util.function.Consumer import java.util.function.Consumer
import javax.swing.KeyStroke import javax.swing.KeyStroke
data class KeyRoundInfo(
var commandState: CurrentCommandState,
)
/** /**
* This handles every keystroke that the user can argType except those that are still valid hotkeys for various Idea * This handles every keystroke that the user can argType except those that are still valid hotkeys for various Idea
* actions. This is a singleton. * actions. This is a singleton.
@ -67,6 +72,13 @@ class KeyHandler {
val keyStack = KeyStack() val keyStack = KeyStack()
val modalEntryKeys: MutableList<KeyStroke> = ArrayList() val modalEntryKeys: MutableList<KeyStroke> = ArrayList()
fun handleKeyInitial(editor: VimEditor, key: KeyStroke, context: ExecutionContext, execute: Boolean = true): StateUpdateResult {
if (editor.vimStateMachine.commandBuilder.isReady) {
editor.vimStateMachine.commandBuilder.resetAll(getKeyRoot(editor.vimStateMachine.mappingState.mappingMode))
}
return handleKey(editor, key, context, execute)
}
/** /**
* This is the main key handler for the Vim plugin. Every keystroke not handled directly by Idea is sent here for * This is the main key handler for the Vim plugin. Every keystroke not handled directly by Idea is sent here for
* processing. * processing.
@ -75,10 +87,15 @@ class KeyHandler {
* @param key The keystroke typed by the user * @param key The keystroke typed by the user
* @param context The data context * @param context The data context
*/ */
fun handleKey(editor: VimEditor, key: KeyStroke, context: ExecutionContext) { fun handleKey(editor: VimEditor, key: KeyStroke, context: ExecutionContext, execute: Boolean = true): StateUpdateResult {
handleKey(editor, key, context, allowKeyMappings = true, mappingCompleted = false) return handleKey(editor, key, context, allowKeyMappings = true, mappingCompleted = false, execute)
} }
data class StateUpdateResult(
val continueExecution: Boolean,
val shouldRecord: Boolean,
)
/** /**
* Handling input keys with additional parameters * Handling input keys with additional parameters
* *
@ -93,7 +110,8 @@ class KeyHandler {
context: ExecutionContext, context: ExecutionContext,
allowKeyMappings: Boolean, allowKeyMappings: Boolean,
mappingCompleted: Boolean, mappingCompleted: Boolean,
) { execute: Boolean = true,
): StateUpdateResult {
LOG.trace { LOG.trace {
""" """
------- Key Handler ------- ------- Key Handler -------
@ -112,7 +130,7 @@ class KeyHandler {
injector.messages.showStatusBarMessage(injector.messages.message("E223")) injector.messages.showStatusBarMessage(injector.messages.message("E223"))
injector.messages.indicateError() injector.messages.indicateError()
LOG.warn("Key handling, maximum recursion of the key received. maxdepth=$mapMapDepth") LOG.warn("Key handling, maximum recursion of the key received. maxdepth=$mapMapDepth")
return return StateUpdateResult(false, false)
} }
injector.messages.clearError() injector.messages.clearError()
@ -186,17 +204,20 @@ class KeyHandler {
} finally { } finally {
handleKeyRecursionCount-- handleKeyRecursionCount--
} }
finishedCommandPreparation(editor, context, editorState, commandBuilder, key, shouldRecord) if (execute) {
finishedCommandPreparation(editor, context, key, shouldRecord)
}
return StateUpdateResult(true, shouldRecord)
} }
fun finishedCommandPreparation( fun finishedCommandPreparation(
editor: VimEditor, editor: VimEditor,
context: ExecutionContext, context: ExecutionContext,
editorState: VimStateMachine,
commandBuilder: CommandBuilder,
key: KeyStroke?, key: KeyStroke?,
shouldRecord: Boolean, shouldRecord: Boolean,
) { ) {
val editorState = editor.vimStateMachine
val commandBuilder = editorState.commandBuilder
// 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.
if (commandBuilder.isReady) { if (commandBuilder.isReady) {
LOG.trace("Ready command builder. Execute command.") LOG.trace("Ready command builder. Execute command.")
@ -657,7 +678,7 @@ class KeyHandler {
editorState: VimStateMachine, editorState: VimStateMachine,
) { ) {
LOG.trace("Command execution") LOG.trace("Command execution")
val command = editorState.commandBuilder.buildCommand() val command = editorState.commandBuilder.buildCommand(context)
val operatorArguments = OperatorArguments( val operatorArguments = OperatorArguments(
editorState.mappingState.mappingMode == MappingMode.OP_PENDING, editorState.mappingState.mappingMode == MappingMode.OP_PENDING,
command.rawCount, editorState.mode, editorState.subMode command.rawCount, editorState.mode, editorState.subMode
@ -866,7 +887,9 @@ class KeyHandler {
) : Runnable { ) : Runnable {
override fun run() { override fun run() {
val editorState = getInstance(editor) val editorState = getInstance(editor)
editorState.commandBuilder.commandState = CurrentCommandState.NEW_COMMAND if (!context.isNewDelegate()) {
editorState.commandBuilder.commandState = CurrentCommandState.NEW_COMMAND
}
val register = cmd.register val register = cmd.register
if (register != null) { if (register != null) {
injector.registerGroup.selectRegister(register) injector.registerGroup.selectRegister(register)

View File

@ -27,6 +27,7 @@ interface ExecutionContext {
// TODO: 10.02.2022 Not sure about this method // TODO: 10.02.2022 Not sure about this method
fun updateEditor(editor: VimEditor): ExecutionContext fun updateEditor(editor: VimEditor): ExecutionContext
fun isNewDelegate(): Boolean
} }
interface ExecutionContextManager { interface ExecutionContextManager {

View File

@ -18,6 +18,7 @@
package com.maddyhome.idea.vim.command package com.maddyhome.idea.vim.command
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimActionsInitiator import com.maddyhome.idea.vim.api.VimActionsInitiator
import com.maddyhome.idea.vim.common.CurrentCommandState import com.maddyhome.idea.vim.common.CurrentCommandState
import com.maddyhome.idea.vim.diagnostic.debug import com.maddyhome.idea.vim.diagnostic.debug
@ -156,13 +157,14 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode<VimActi
return commandParts.peek()?.argument != null return commandParts.peek()?.argument != null
} }
fun buildCommand(): Command { fun buildCommand(context: ExecutionContext): Command {
if (commandParts.last.action.id == "VimInsertCompletedDigraphAction" || commandParts.last.action.id == "VimResetModeAction") { if (commandParts.last.action.id == "VimInsertCompletedDigraphAction" || commandParts.last.action.id == "VimResetModeAction") {
expectedArgumentType = prevExpectedArgumentType expectedArgumentType = prevExpectedArgumentType
prevExpectedArgumentType = null prevExpectedArgumentType = null
return commandParts.removeLast() return commandParts.removeLast()
} }
val original = ArrayDeque(commandParts)
var command: Command = commandParts.removeFirst() var command: Command = commandParts.removeFirst()
while (commandParts.size > 0) { while (commandParts.size > 0) {
val next = commandParts.removeFirst() val next = commandParts.removeFirst()
@ -174,17 +176,22 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode<VimActi
command = next command = next
} else { } else {
command.argument = Argument(next) command.argument = Argument(next)
assert(commandParts.size == 0) // assert(commandParts.size == 0)
} }
} }
if (context.isNewDelegate()) {
commandParts.addAll(original)
}
expectedArgumentType = null expectedArgumentType = null
return command return command
} }
fun resetAll(commandPartNode: CommandPartNode<VimActionsInitiator>) { fun resetAll(commandPartNode: CommandPartNode<VimActionsInitiator>, setNewCommand: Boolean = true) {
resetInProgressCommandPart(commandPartNode) resetInProgressCommandPart(commandPartNode)
commandState = CurrentCommandState.NEW_COMMAND if (setNewCommand) {
commandParts.clear() commandState = CurrentCommandState.NEW_COMMAND
commandParts.clear()
}
keyList.clear() keyList.clear()
expectedArgumentType = null expectedArgumentType = null
prevExpectedArgumentType = null prevExpectedArgumentType = null

View File

@ -76,10 +76,14 @@ abstract class EditorActionHandlerBase(private val myRunForEachCaret: Boolean) {
fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
val action = { caret: VimCaret -> doExecute(editor, caret, context, operatorArguments) } val action = { caret: VimCaret -> doExecute(editor, caret, context, operatorArguments) }
if (myRunForEachCaret) { if (!context.isNewDelegate()) {
editor.forEachCaret(action) if (myRunForEachCaret) {
editor.forEachCaret(action)
} else {
action(editor.primaryCaret())
}
} else { } else {
action(editor.primaryCaret()) action(editor.currentCaret())
} }
} }

View File

@ -152,6 +152,7 @@ sealed class MotionActionHandler : EditorActionHandlerBase(false) {
when (this) { when (this) {
is SingleExecution -> run { is SingleExecution -> run {
if (context.isNewDelegate() && !(caret == editor.currentCaret())) return@run
if (!preOffsetComputation(editor, context, cmd)) return@run if (!preOffsetComputation(editor, context, cmd)) return@run
val offset = getOffset(editor, context, cmd.argument, operatorArguments) val offset = getOffset(editor, context, cmd.argument, operatorArguments)
@ -177,34 +178,77 @@ sealed class MotionActionHandler : EditorActionHandlerBase(false) {
} }
} }
is ForEachCaret -> run { is ForEachCaret -> run {
when { if (!context.isNewDelegate()) {
blockSubmodeActive || editor.carets().size == 1 -> { when {
val primaryCaret = editor.primaryCaret() blockSubmodeActive || editor.carets().size == 1 -> {
doExecuteForEach(editor, primaryCaret, context, cmd, operatorArguments) val primaryCaret = editor.primaryCaret()
} doExecuteForEach(editor, primaryCaret, context, cmd, operatorArguments)
else -> { }
try { else -> {
editor.addCaretListener(CaretMergingWatcher) try {
editor.forEachCaret { caret -> editor.addCaretListener(CaretMergingWatcher)
doExecuteForEach( editor.forEachCaret { caret ->
editor, doExecuteForEach(
caret, editor,
context, caret,
cmd, context,
operatorArguments cmd,
) operatorArguments
)
}
} finally {
editor.removeCaretListener(CaretMergingWatcher)
} }
} finally {
editor.removeCaretListener(CaretMergingWatcher)
} }
} }
} }
else {
doExecuteForEach(
editor,
caret,
context,
cmd,
operatorArguments
)
}
} }
} }
return true return true
} }
private fun SingleExecution.singleAction(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
) {
if (!preOffsetComputation(editor, context, cmd)) return
val offset = getOffset(editor, context, cmd.argument, operatorArguments)
when (offset) {
is Motion.AbsoluteOffset -> {
var resultOffset = offset.offset
if (resultOffset < 0) {
logger.error("Offset is less than 0. $resultOffset. ${this.javaClass.name}")
}
if (CommandFlags.FLAG_SAVE_JUMP in cmd.flags) {
injector.markGroup.saveJumpLocation(editor)
}
if (!editor.isEndAllowed) {
resultOffset = injector.engineEditorHelper.normalizeOffset(editor, resultOffset, false)
}
preMove(editor, context, cmd)
editor.primaryCaret().moveToOffset(resultOffset)
postMove(editor, context, cmd)
}
is Motion.Error -> injector.messages.indicateError()
is Motion.NoMotion -> Unit
}
}
private fun doExecuteForEach( private fun doExecuteForEach(
editor: VimEditor, editor: VimEditor,
caret: VimCaret, caret: VimCaret,

View File

@ -175,7 +175,9 @@ class ToHandlerMappingInfo(
injector.application.invokeLater { injector.application.invokeLater {
KeyHandler.getInstance().finishedCommandPreparation( KeyHandler.getInstance().finishedCommandPreparation(
editor, editor,
context, VimStateMachine.getInstance(editor), VimStateMachine.getInstance(editor).commandBuilder, null, false context,
null,
false
) )
} }
} }