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

Fix Del and BS not working in Select mode

Fixes VIM-3618
This commit is contained in:
Matt Ellis 2025-01-02 10:18:02 +00:00 committed by Alex Pláte
parent f30e376e11
commit 1d5fc01d65
4 changed files with 197 additions and 11 deletions
src
main/java/com/maddyhome/idea/vim/group
test/java/org/jetbrains/plugins/ideavim/action/motion/select
vim-engine/src/main
kotlin/com/maddyhome/idea/vim/action/motion/select
resources/ksp-generated

View File

@ -314,7 +314,7 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
if (c instanceof JComponent) {
final List<AnAction> actions = ActionUtil.getActions((JComponent)c);
for (AnAction action : actions) {
if (action instanceof VimShortcutKeyAction) {
if (action instanceof VimShortcutKeyAction || action == VimShortcutKeyAction.getInstance()) {
continue;
}
final Shortcut[] shortcuts = action.getShortcutSet().getShortcuts();
@ -334,7 +334,11 @@ public class KeyGroup extends VimKeyGroupBase implements PersistentStateComponen
final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
for (String id : keymap.getActionIds(keyStroke)) {
final AnAction action = ActionManager.getInstance().getAction(id);
if (action != null) {
// EmptyAction is used to reserve a shortcut, but can't be executed. Code can ask for an action by ID and
// use its shortcut(s) to dynamically register a new action on a component in the UI hierarchy. It's not
// useful for our needs here - we'll pick up the real action when we get local actions.
if (action != null && !(action instanceof EmptyAction)) {
results.add(action);
}
}

View File

@ -0,0 +1,167 @@
/*
* Copyright 2003-2025 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/
package org.jetbrains.plugins.ideavim.action.motion.select
import com.maddyhome.idea.vim.state.mode.Mode
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Test
class SelectDeleteActionTest : VimTestCase() {
@Test
fun `test Delete removes text and returns to Normal mode`() {
doTest(
listOf("ve", "<C-G>", "<Del>"),
"""
|Lorem Ipsum
|
|I ${c}found it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
"""
|Lorem Ipsum
|
|I $c it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
Mode.NORMAL()
)
}
@Test
fun `test Delete removes text and returns to Insert mode when invoked from Insert Select`() {
doTest(
listOf("i", "<S-Right>".repeat(5), "<Del>"),
"""
|Lorem Ipsum
|
|I ${c}found it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
"""
|Lorem Ipsum
|
|I $c it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
Mode.INSERT
) {
enterCommand("set selectmode=key keymodel=startsel")
}
}
@Test
fun `test Delete removes text and returns to Replace mode when invoked from Select with pending Replace mode`() {
doTest(
listOf("R", "<S-Right>".repeat(5), "<Del>"),
"""
|Lorem Ipsum
|
|I ${c}found it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
"""
|Lorem Ipsum
|
|I $c it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
Mode.REPLACE
) {
enterCommand("set selectmode=key keymodel=startsel")
}
}
@Test
fun `test Backspace deletes text and returns to Normal mode`() {
doTest(
listOf("ve", "<C-G>", "<Del>"),
"""
|Lorem Ipsum
|
|I ${c}found it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
"""
|Lorem Ipsum
|
|I $c it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
Mode.NORMAL()
)
}
@Test
fun `test Backspace removes text and returns to Insert mode when invoked from Insert Select`() {
doTest(
listOf("i", "<S-Right>".repeat(5), "<BS>"),
"""
|Lorem Ipsum
|
|I ${c}found it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
"""
|Lorem Ipsum
|
|I $c it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
Mode.INSERT
) {
enterCommand("set selectmode=key keymodel=startsel")
}
}
@Test
fun `test Backspace removes text and returns to Replace mode when invoked from Select with pending Replace mode`() {
doTest(
listOf("R", "<S-Right>".repeat(5), "<BS>"),
"""
|Lorem Ipsum
|
|I ${c}found it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
"""
|Lorem Ipsum
|
|I $c it in a legendary land
|consectetur adipiscing elit
|Sed in orci mauris.
|Cras id tellus in ex imperdiet egestas.
""".trimMargin(),
Mode.REPLACE
) {
enterCommand("set selectmode=key keymodel=startsel")
}
}
}

View File

@ -19,30 +19,45 @@ import com.maddyhome.idea.vim.handler.VimActionHandler
import java.awt.event.KeyEvent
import javax.swing.KeyStroke
/**
* @author Alex Plate
*/
@CommandOrMotion(keys = ["<DEL>"], modes = [Mode.SELECT])
class SelectDeleteAction : SelectDeleteBackspaceActionBase() {
override val keyStroke: KeyStroke
get() = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)
}
@CommandOrMotion(keys = ["<BS>", "<DEL>"], modes = [Mode.SELECT])
class SelectDeleteAction : VimActionHandler.SingleExecution() {
@CommandOrMotion(keys = ["<BS>"], modes = [Mode.SELECT])
class SelectBackspaceAction : SelectDeleteBackspaceActionBase() {
override val keyStroke: KeyStroke
get() = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0)
}
abstract class SelectDeleteBackspaceActionBase : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.INSERT
abstract val keyStroke: KeyStroke
override fun execute(
editor: VimEditor,
context: ExecutionContext,
cmd: Command,
operatorArguments: OperatorArguments,
): Boolean {
val enterKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0)
val actions = injector.keyGroup.getActions(editor, enterKeyStroke)
// TODO: It would be nice to know _why_ we use native actions for Delete/Backspace
// If there's some kind of additional native editor action bound to Delete or Backspace (e.g. cancelling something,
// etc.) then we should of course invoke it, like we do with Escape or Enter. But if we do, we should reconsider
// unconditionally exiting Select mode. If the additional native editor action doesn't delete the text, then we're
// exiting Select mode incorrectly. If there isn't an additional native editor action, then would it just be simpler
// to delete the selected text using editor APIs?
val actions = injector.keyGroup.getActions(editor, keyStroke)
for (action in actions) {
if (injector.actionExecutor.executeAction(editor, action, context)) {
break
}
}
// Note that Vim returns to the pending mode. I.e., when starting Select from Normal/Visual, it will return to
// Normal. When returning from Insert or Replace pending Select (via shifted keys), it will return to Insert/Replace
editor.exitSelectModeNative(true)
injector.changeGroup.insertBeforeCursor(editor, context)
return true
}
}

View File

@ -126,7 +126,7 @@
},
{
"keys": "<BS>",
"class": "com.maddyhome.idea.vim.action.motion.select.SelectDeleteAction",
"class": "com.maddyhome.idea.vim.action.motion.select.SelectBackspaceAction",
"modes": "S"
},
{