mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-05-14 15:34:06 +02:00
[New Typing Handler]: Switch j
command to new typing handler
This commit is contained in:
parent
2829a13187
commit
43a79dbad4
build.gradle.kts
src
main
java/com/maddyhome/idea/vim
resources/META-INF
test/java/org/jetbrains/plugins/ideavim
vim-engine/src/main/kotlin/com/maddyhome/idea/vim
@ -242,7 +242,7 @@ tasks {
|
||||
|
||||
// Don't forget to update plugin.xml
|
||||
patchPluginXml {
|
||||
sinceBuild.set("222")
|
||||
sinceBuild.set("222.4167.9")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,11 @@ class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandle
|
||||
return
|
||||
}
|
||||
|
||||
if (useNewHandler(editor.vim, charTyped)) {
|
||||
(KeyHandlerKeeper.getInstance().originalHandler as? TypedActionHandlerEx)?.beforeExecute(editor, charTyped, context, plan)
|
||||
return
|
||||
}
|
||||
|
||||
LOG.trace("Executing before execute")
|
||||
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
|
||||
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
|
||||
@ -80,12 +85,17 @@ class VimTypedActionHandler(origHandler: TypedActionHandler) : TypedActionHandle
|
||||
return
|
||||
}
|
||||
|
||||
if (useNewHandler(editor.vim, charTyped)) {
|
||||
KeyHandlerKeeper.getInstance().originalHandler.execute(editor, charTyped, context)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
LOG.trace("Executing typed action")
|
||||
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
|
||||
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
|
||||
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) {
|
||||
val duration = System.currentTimeMillis() - startTime
|
||||
LOG.info("VimTypedAction '$charTyped': $duration ms")
|
||||
|
@ -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
|
||||
}
|
@ -82,7 +82,7 @@ class VimShortcutKeyAction : AnAction(), DumbAware/*, LightEditCompatible*/ {
|
||||
// Should we use HelperKt.getTopLevelEditor(editor) here, as we did in former EditorKeyHandler?
|
||||
try {
|
||||
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) {
|
||||
val duration = System.currentTimeMillis() - start
|
||||
LOG.info("VimShortcut update '$keyStroke': $duration ms")
|
||||
|
@ -73,7 +73,7 @@ public class MacroGroup extends VimMacroBase {
|
||||
final Runnable run = () -> {
|
||||
// Handle one keystroke then queue up the next key
|
||||
if (keyStack.hasStroke()) {
|
||||
KeyHandler.getInstance().handleKey(editor, keyStack.feedStroke(), context);
|
||||
KeyHandler.getInstance().handleKey(editor, keyStack.feedStroke(), context, true, false, true);
|
||||
}
|
||||
if (keyStack.hasStroke()) {
|
||||
playbackKeys(editor, context, cnt, total);
|
||||
@ -105,7 +105,7 @@ public class MacroGroup extends VimMacroBase {
|
||||
return;
|
||||
}
|
||||
ProgressManager.getInstance().executeNonCancelableSection(() -> {
|
||||
KeyHandler.getInstance().handleKey(editor, key, context);
|
||||
KeyHandler.getInstance().handleKey(editor, key, context, true, false, true);
|
||||
});
|
||||
}
|
||||
keyStack.resetFirst();
|
||||
|
@ -18,6 +18,7 @@
|
||||
package com.maddyhome.idea.vim.helper
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.DataKey
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.Key
|
||||
@ -27,6 +28,9 @@ class EditorDataContext @Deprecated("Please use `init` method") constructor(
|
||||
private val editor: Editor,
|
||||
private val contextDelegate: DataContext? = null,
|
||||
) : DataContext, UserDataHolder {
|
||||
|
||||
internal var newTypingDelegate = false
|
||||
|
||||
/**
|
||||
* Returns the object corresponding to the specified data identifier. Some of the supported data identifiers are
|
||||
* defined in the [PlatformDataKeys] class.
|
||||
@ -38,6 +42,7 @@ class EditorDataContext @Deprecated("Please use `init` method") constructor(
|
||||
PlatformDataKeys.EDITOR.name == dataId -> editor
|
||||
PlatformDataKeys.PROJECT.name == dataId -> editor.project
|
||||
PlatformDataKeys.VIRTUAL_FILE.name == dataId -> EditorHelper.getVirtualFile(editor)
|
||||
NEW_DELEGATE.name == dataId -> newTypingDelegate
|
||||
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")
|
||||
|
@ -22,11 +22,16 @@ import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.maddyhome.idea.vim.api.ExecutionContext
|
||||
import com.maddyhome.idea.vim.api.VimEditor
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext
|
||||
import com.maddyhome.idea.vim.helper.NEW_DELEGATE
|
||||
|
||||
class IjExecutionContext(override val context: DataContext) : ExecutionContext {
|
||||
override fun updateEditor(editor: VimEditor): ExecutionContext {
|
||||
return IjExecutionContext(EditorDataContext.init((editor as IjVimEditor).editor, context))
|
||||
}
|
||||
|
||||
override fun isNewDelegate(): Boolean {
|
||||
return context.getData(NEW_DELEGATE) ?: false
|
||||
}
|
||||
}
|
||||
|
||||
val DataContext.vim
|
||||
|
@ -126,7 +126,7 @@
|
||||
<!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version -->
|
||||
<!-- Check for [Version Update] tag in YouTrack as well -->
|
||||
<!-- 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) -->
|
||||
<depends>com.intellij.modules.platform</depends>
|
||||
@ -193,6 +193,8 @@
|
||||
<statistics.applicationUsagesCollector implementation="com.maddyhome.idea.vim.statistic.ShortcutConflictState"/>
|
||||
|
||||
<statistics.counterUsagesCollector implementationClass="com.maddyhome.idea.vim.statistic.ActionTracker"/>
|
||||
|
||||
<typedHandler implementation="com.maddyhome.idea.vim.VimTypedDelegateHandler" order="first, before completionAutoPopup"/>
|
||||
</extensions>
|
||||
|
||||
<xi:include href="/META-INF/includes/ApplicationServices.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
@ -99,7 +99,7 @@ public abstract class JavaVimTestCase extends JavaCodeInsightFixtureTestCase {
|
||||
exEntryPanel.handleKey(key);
|
||||
}
|
||||
else {
|
||||
keyHandler.handleKey(new IjVimEditor(editor), key, new IjExecutionContext(dataContext));
|
||||
keyHandler.handleKey(new IjVimEditor(editor), key, new IjExecutionContext(dataContext), true);
|
||||
}
|
||||
}
|
||||
}, null, null);
|
||||
|
@ -29,6 +29,36 @@ import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
* @author Alex Plate
|
||||
*/
|
||||
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`() {
|
||||
val keys = "<C-V>2kjjj"
|
||||
val before = """
|
||||
|
@ -56,6 +56,11 @@ import java.awt.event.KeyEvent
|
||||
import java.util.function.Consumer
|
||||
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
|
||||
* actions. This is a singleton.
|
||||
@ -67,6 +72,13 @@ class KeyHandler {
|
||||
val keyStack = KeyStack()
|
||||
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
|
||||
* processing.
|
||||
@ -75,10 +87,15 @@ class KeyHandler {
|
||||
* @param key The keystroke typed by the user
|
||||
* @param context The data context
|
||||
*/
|
||||
fun handleKey(editor: VimEditor, key: KeyStroke, context: ExecutionContext) {
|
||||
handleKey(editor, key, context, allowKeyMappings = true, mappingCompleted = false)
|
||||
fun handleKey(editor: VimEditor, key: KeyStroke, context: ExecutionContext, execute: Boolean = true): StateUpdateResult {
|
||||
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
|
||||
*
|
||||
@ -93,7 +110,8 @@ class KeyHandler {
|
||||
context: ExecutionContext,
|
||||
allowKeyMappings: Boolean,
|
||||
mappingCompleted: Boolean,
|
||||
) {
|
||||
execute: Boolean = true,
|
||||
): StateUpdateResult {
|
||||
LOG.trace {
|
||||
"""
|
||||
------- Key Handler -------
|
||||
@ -112,7 +130,7 @@ class KeyHandler {
|
||||
injector.messages.showStatusBarMessage(injector.messages.message("E223"))
|
||||
injector.messages.indicateError()
|
||||
LOG.warn("Key handling, maximum recursion of the key received. maxdepth=$mapMapDepth")
|
||||
return
|
||||
return StateUpdateResult(false, false)
|
||||
}
|
||||
|
||||
injector.messages.clearError()
|
||||
@ -186,17 +204,20 @@ class KeyHandler {
|
||||
} finally {
|
||||
handleKeyRecursionCount--
|
||||
}
|
||||
finishedCommandPreparation(editor, context, editorState, commandBuilder, key, shouldRecord)
|
||||
if (execute) {
|
||||
finishedCommandPreparation(editor, context, key, shouldRecord)
|
||||
}
|
||||
return StateUpdateResult(true, shouldRecord)
|
||||
}
|
||||
|
||||
fun finishedCommandPreparation(
|
||||
editor: VimEditor,
|
||||
context: ExecutionContext,
|
||||
editorState: VimStateMachine,
|
||||
commandBuilder: CommandBuilder,
|
||||
key: KeyStroke?,
|
||||
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.
|
||||
if (commandBuilder.isReady) {
|
||||
LOG.trace("Ready command builder. Execute command.")
|
||||
@ -657,7 +678,7 @@ class KeyHandler {
|
||||
editorState: VimStateMachine,
|
||||
) {
|
||||
LOG.trace("Command execution")
|
||||
val command = editorState.commandBuilder.buildCommand()
|
||||
val command = editorState.commandBuilder.buildCommand(context)
|
||||
val operatorArguments = OperatorArguments(
|
||||
editorState.mappingState.mappingMode == MappingMode.OP_PENDING,
|
||||
command.rawCount, editorState.mode, editorState.subMode
|
||||
@ -866,7 +887,9 @@ class KeyHandler {
|
||||
) : Runnable {
|
||||
override fun run() {
|
||||
val editorState = getInstance(editor)
|
||||
editorState.commandBuilder.commandState = CurrentCommandState.NEW_COMMAND
|
||||
if (!context.isNewDelegate()) {
|
||||
editorState.commandBuilder.commandState = CurrentCommandState.NEW_COMMAND
|
||||
}
|
||||
val register = cmd.register
|
||||
if (register != null) {
|
||||
injector.registerGroup.selectRegister(register)
|
||||
|
@ -27,6 +27,7 @@ interface ExecutionContext {
|
||||
|
||||
// TODO: 10.02.2022 Not sure about this method
|
||||
fun updateEditor(editor: VimEditor): ExecutionContext
|
||||
fun isNewDelegate(): Boolean
|
||||
}
|
||||
|
||||
interface ExecutionContextManager {
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
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.common.CurrentCommandState
|
||||
import com.maddyhome.idea.vim.diagnostic.debug
|
||||
@ -156,13 +157,14 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode<VimActi
|
||||
return commandParts.peek()?.argument != null
|
||||
}
|
||||
|
||||
fun buildCommand(): Command {
|
||||
fun buildCommand(context: ExecutionContext): Command {
|
||||
if (commandParts.last.action.id == "VimInsertCompletedDigraphAction" || commandParts.last.action.id == "VimResetModeAction") {
|
||||
expectedArgumentType = prevExpectedArgumentType
|
||||
prevExpectedArgumentType = null
|
||||
return commandParts.removeLast()
|
||||
}
|
||||
|
||||
val original = ArrayDeque(commandParts)
|
||||
var command: Command = commandParts.removeFirst()
|
||||
while (commandParts.size > 0) {
|
||||
val next = commandParts.removeFirst()
|
||||
@ -174,17 +176,22 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode<VimActi
|
||||
command = next
|
||||
} else {
|
||||
command.argument = Argument(next)
|
||||
assert(commandParts.size == 0)
|
||||
// assert(commandParts.size == 0)
|
||||
}
|
||||
}
|
||||
if (context.isNewDelegate()) {
|
||||
commandParts.addAll(original)
|
||||
}
|
||||
expectedArgumentType = null
|
||||
return command
|
||||
}
|
||||
|
||||
fun resetAll(commandPartNode: CommandPartNode<VimActionsInitiator>) {
|
||||
fun resetAll(commandPartNode: CommandPartNode<VimActionsInitiator>, setNewCommand: Boolean = true) {
|
||||
resetInProgressCommandPart(commandPartNode)
|
||||
commandState = CurrentCommandState.NEW_COMMAND
|
||||
commandParts.clear()
|
||||
if (setNewCommand) {
|
||||
commandState = CurrentCommandState.NEW_COMMAND
|
||||
commandParts.clear()
|
||||
}
|
||||
keyList.clear()
|
||||
expectedArgumentType = null
|
||||
prevExpectedArgumentType = null
|
||||
|
@ -76,10 +76,14 @@ abstract class EditorActionHandlerBase(private val myRunForEachCaret: Boolean) {
|
||||
|
||||
fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) {
|
||||
val action = { caret: VimCaret -> doExecute(editor, caret, context, operatorArguments) }
|
||||
if (myRunForEachCaret) {
|
||||
editor.forEachCaret(action)
|
||||
if (!context.isNewDelegate()) {
|
||||
if (myRunForEachCaret) {
|
||||
editor.forEachCaret(action)
|
||||
} else {
|
||||
action(editor.primaryCaret())
|
||||
}
|
||||
} else {
|
||||
action(editor.primaryCaret())
|
||||
action(editor.currentCaret())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,6 +152,7 @@ sealed class MotionActionHandler : EditorActionHandlerBase(false) {
|
||||
|
||||
when (this) {
|
||||
is SingleExecution -> run {
|
||||
if (context.isNewDelegate() && !(caret == editor.currentCaret())) return@run
|
||||
if (!preOffsetComputation(editor, context, cmd)) return@run
|
||||
|
||||
val offset = getOffset(editor, context, cmd.argument, operatorArguments)
|
||||
@ -177,34 +178,77 @@ sealed class MotionActionHandler : EditorActionHandlerBase(false) {
|
||||
}
|
||||
}
|
||||
is ForEachCaret -> run {
|
||||
when {
|
||||
blockSubmodeActive || editor.carets().size == 1 -> {
|
||||
val primaryCaret = editor.primaryCaret()
|
||||
doExecuteForEach(editor, primaryCaret, context, cmd, operatorArguments)
|
||||
}
|
||||
else -> {
|
||||
try {
|
||||
editor.addCaretListener(CaretMergingWatcher)
|
||||
editor.forEachCaret { caret ->
|
||||
doExecuteForEach(
|
||||
editor,
|
||||
caret,
|
||||
context,
|
||||
cmd,
|
||||
operatorArguments
|
||||
)
|
||||
if (!context.isNewDelegate()) {
|
||||
when {
|
||||
blockSubmodeActive || editor.carets().size == 1 -> {
|
||||
val primaryCaret = editor.primaryCaret()
|
||||
doExecuteForEach(editor, primaryCaret, context, cmd, operatorArguments)
|
||||
}
|
||||
else -> {
|
||||
try {
|
||||
editor.addCaretListener(CaretMergingWatcher)
|
||||
editor.forEachCaret { caret ->
|
||||
doExecuteForEach(
|
||||
editor,
|
||||
caret,
|
||||
context,
|
||||
cmd,
|
||||
operatorArguments
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
editor.removeCaretListener(CaretMergingWatcher)
|
||||
}
|
||||
} finally {
|
||||
editor.removeCaretListener(CaretMergingWatcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
doExecuteForEach(
|
||||
editor,
|
||||
caret,
|
||||
context,
|
||||
cmd,
|
||||
operatorArguments
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
editor: VimEditor,
|
||||
caret: VimCaret,
|
||||
|
@ -175,7 +175,9 @@ class ToHandlerMappingInfo(
|
||||
injector.application.invokeLater {
|
||||
KeyHandler.getInstance().finishedCommandPreparation(
|
||||
editor,
|
||||
context, VimStateMachine.getInstance(editor), VimStateMachine.getInstance(editor).commandBuilder, null, false
|
||||
context,
|
||||
null,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user