From 0eb811f04e873beddbddaa91eecd657523cab0ae Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Fri, 15 Mar 2024 16:01:06 -0300 Subject: [PATCH 1/2] Adding option to "fix" the usability problem involving international keyboards and dead keys. https://youtrack.jetbrains.com/issue/VIM-1503 --- .../action/motion/object/MotionQuoteAction.kt | 35 +++++++++++++++---- .../idea/vim/api/OptionProperties.kt | 3 ++ .../com/maddyhome/idea/vim/api/Options.kt | 1 + .../vim/handler/EditorActionHandlerBase.kt | 16 +++++++++ 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/object/MotionQuoteAction.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/object/MotionQuoteAction.kt index 6c3b68499e..4aa88d498f 100644 --- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/object/MotionQuoteAction.kt +++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/action/motion/object/MotionQuoteAction.kt @@ -12,17 +12,39 @@ import com.intellij.vim.annotations.CommandOrMotion import com.intellij.vim.annotations.Mode import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.ImmutableVimCaret +import com.maddyhome.idea.vim.api.MutableVimEditor import com.maddyhome.idea.vim.api.VimEditor +import com.maddyhome.idea.vim.api.globalOptions import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.command.CommandFlags import com.maddyhome.idea.vim.command.TextObjectVisualType import com.maddyhome.idea.vim.common.TextRange +import com.maddyhome.idea.vim.common.offset import com.maddyhome.idea.vim.handler.TextObjectActionHandler import com.maddyhome.idea.vim.helper.enumSetOf import java.util.* +public abstract class BaseMotionTextObjectActionHandler : TextObjectActionHandler() { + + override fun preExecute(editor: VimEditor) { + if (injector.globalOptions().fixdeadkeys) { + val mutableEditor = editor as? MutableVimEditor + if (mutableEditor != null) { + val lastCharData = LastCaretPositionData ?: return + injector.application.runWriteAction { + mutableEditor.insertText( + lastCharData.startOffset.offset, + lastCharData.selected + ) + } + } + } + } +} + + @CommandOrMotion(keys = ["i`"], modes = [Mode.VISUAL, Mode.OP_PENDING]) -public class MotionInnerBlockBackQuoteAction : TextObjectActionHandler() { +public class MotionInnerBlockBackQuoteAction : BaseMotionTextObjectActionHandler() { override val flags: EnumSet = enumSetOf(CommandFlags.FLAG_TEXT_BLOCK) @@ -40,7 +62,7 @@ public class MotionInnerBlockBackQuoteAction : TextObjectActionHandler() { } @CommandOrMotion(keys = ["i\""], modes = [Mode.VISUAL, Mode.OP_PENDING]) -public class MotionInnerBlockDoubleQuoteAction : TextObjectActionHandler() { +public class MotionInnerBlockDoubleQuoteAction : BaseMotionTextObjectActionHandler() { override val flags: EnumSet = enumSetOf(CommandFlags.FLAG_TEXT_BLOCK) @@ -55,10 +77,11 @@ public class MotionInnerBlockDoubleQuoteAction : TextObjectActionHandler() { ): TextRange? { return injector.searchHelper.findBlockQuoteInLineRange(editor, caret, '"', false) } + } @CommandOrMotion(keys = ["i'"], modes = [Mode.VISUAL, Mode.OP_PENDING]) -public class MotionInnerBlockSingleQuoteAction : TextObjectActionHandler() { +public class MotionInnerBlockSingleQuoteAction : BaseMotionTextObjectActionHandler() { override val flags: EnumSet = enumSetOf(CommandFlags.FLAG_TEXT_BLOCK) @@ -76,7 +99,7 @@ public class MotionInnerBlockSingleQuoteAction : TextObjectActionHandler() { } @CommandOrMotion(keys = ["a`"], modes = [Mode.VISUAL, Mode.OP_PENDING]) -public class MotionOuterBlockBackQuoteAction : TextObjectActionHandler() { +public class MotionOuterBlockBackQuoteAction : BaseMotionTextObjectActionHandler() { override val flags: EnumSet = enumSetOf(CommandFlags.FLAG_TEXT_BLOCK) @@ -94,7 +117,7 @@ public class MotionOuterBlockBackQuoteAction : TextObjectActionHandler() { } @CommandOrMotion(keys = ["a\""], modes = [Mode.VISUAL, Mode.OP_PENDING]) -public class MotionOuterBlockDoubleQuoteAction : TextObjectActionHandler() { +public class MotionOuterBlockDoubleQuoteAction : BaseMotionTextObjectActionHandler() { override val flags: EnumSet = enumSetOf(CommandFlags.FLAG_TEXT_BLOCK) @@ -112,7 +135,7 @@ public class MotionOuterBlockDoubleQuoteAction : TextObjectActionHandler() { } @CommandOrMotion(keys = ["a'"], modes = [Mode.VISUAL, Mode.OP_PENDING]) -public class MotionOuterBlockSingleQuoteAction : TextObjectActionHandler() { +public class MotionOuterBlockSingleQuoteAction : BaseMotionTextObjectActionHandler() { override val flags: EnumSet = enumSetOf(CommandFlags.FLAG_TEXT_BLOCK) diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/OptionProperties.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/OptionProperties.kt index eb175f031c..301a494577 100644 --- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/OptionProperties.kt +++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/OptionProperties.kt @@ -51,6 +51,9 @@ public open class GlobalOptions(scope: OptionAccessScope): OptionsPropertiesBase // IdeaVim specific options. Put any editor or IDE specific options in IjOptionProperties + // I really tried to put this in IjOptionProperties, but I don't have access to it where I need it. + public val fixdeadkeys: Boolean by optionProperty(Options.fixdeadkeys) + // This is an experimental option that enables global mode for the editor. However, // for the moment it has issues and there is no quality garantee if this option is enabled public var ideaglobalmode: Boolean by optionProperty(Options.ideaglobalmode) diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/Options.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/Options.kt index 2920696933..8871123b2f 100644 --- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/Options.kt +++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api/Options.kt @@ -206,6 +206,7 @@ public object Options { ) public val wrapscan: ToggleOption = addOption(ToggleOption("wrapscan", GLOBAL, "ws", true)) + public val fixdeadkeys: ToggleOption= addOption(ToggleOption("fixdeadkeys", GLOBAL, "fdk", false)) // More complex options, with additional validation, etc. public val guicursor: StringListOption = addOption(object : StringListOption( diff --git a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/EditorActionHandlerBase.kt b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/EditorActionHandlerBase.kt index c0c46e76f5..0cb5888765 100644 --- a/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/EditorActionHandlerBase.kt +++ b/vim-engine/src/main/kotlin/com/maddyhome/idea/vim/handler/EditorActionHandlerBase.kt @@ -11,6 +11,7 @@ package com.maddyhome.idea.vim.handler import com.maddyhome.idea.vim.api.ExecutionContext import com.maddyhome.idea.vim.api.VimCaret import com.maddyhome.idea.vim.api.VimEditor +import com.maddyhome.idea.vim.api.getText import com.maddyhome.idea.vim.api.injector import com.maddyhome.idea.vim.command.Argument import com.maddyhome.idea.vim.command.Command @@ -84,9 +85,20 @@ public abstract class EditorActionHandlerBase(private val myRunForEachCaret: Boo operatorArguments: OperatorArguments, ) {} + public open fun preExecute(editor: VimEditor) { + val caret = editor.currentCaret() + val start = caret.selectionStart + val end = caret.selectionEnd + 1 + LastCaretPositionData = LastCaretPositionData( + editor.getText(start, end), + start + ) + } + public fun execute(editor: VimEditor, context: ExecutionContext.Editor, operatorArguments: OperatorArguments) { val action = { caret: VimCaret -> doExecute(editor, caret, context, operatorArguments) } + preExecute(editor) // IJ platform has one issue - recursive `runForEachCaret` is not allowed. Strictly speaking, at this moment // we don't know if we run this action inside of this run or not. val currentCaret = editor.currentCaret() @@ -155,6 +167,8 @@ public abstract class EditorActionHandlerBase(private val myRunForEachCaret: Boo @NonNls private const val VimActionPrefix = "Vim" + public var LastCaretPositionData: LastCaretPositionData? = null + @NonNls public fun getActionId(classFullName: String): String { return classFullName @@ -163,3 +177,5 @@ public abstract class EditorActionHandlerBase(private val myRunForEachCaret: Boo } } } + +public data class LastCaretPositionData(val selected: String, val startOffset: Int) From 5508472d2f60434e161897992be0ad6284e74a54 Mon Sep 17 00:00:00 2001 From: Bruno Ortiz Date: Fri, 15 Mar 2024 17:55:44 -0300 Subject: [PATCH 2/2] fixing tests --- .../ex/implementation/commands/SetCommandTest.kt | 11 ++++++----- .../implementation/commands/SetglobalCommandTest.kt | 11 ++++++----- .../ex/implementation/commands/SetlocalCommandTest.kt | 11 ++++++----- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetCommandTest.kt index 32e4a16004..c620e3ea37 100644 --- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetCommandTest.kt +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetCommandTest.kt @@ -164,10 +164,11 @@ class SetCommandTest : VimTestCase() { assertCommandOutput("set all", """ |--- Options --- - |noargtextobj noincsearch selectmode= notextobj-indent - |nocommentary nomatchit shellcmdflag=-x timeout - |nodigraph maxmapdepth=20 shellxescape=@ timeoutlen=1000 - |noexchange more shellxquote={ notrackactionids + |noargtextobj noignorecase scrolloff=0 notextobj-entire + |nocommentary noincsearch selectmode= notextobj-indent + |nodigraph nomatchit shellcmdflag=-x timeout + |noexchange maxmapdepth=20 shellxescape=@ timeoutlen=1000 + |nofixdeadkeys more shellxquote={ notrackactionids |nogdefault nomultiple-cursors showcmd undolevels=1000 |nohighlightedyank noNERDTree showmode virtualedit= | history=50 nrformats=hex sidescroll=0 novisualbell @@ -176,7 +177,6 @@ class SetCommandTest : VimTestCase() { |noideajoin norelativenumber nosneak wrapscan | ideamarks scroll=0 startofline | ideawrite=all scrolljump=1 nosurround - |noignorecase scrolloff=0 notextobj-entire | clipboard=ideaput,autoselect,exclude:cons\|linux | guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175 | ide=IntelliJ IDEA Community Edition @@ -227,6 +227,7 @@ class SetCommandTest : VimTestCase() { |nocommentary |nodigraph |noexchange + |nofixdeadkeys |nogdefault | guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175 |nohighlightedyank diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetglobalCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetglobalCommandTest.kt index b571f10664..9e67365a07 100644 --- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetglobalCommandTest.kt +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetglobalCommandTest.kt @@ -348,10 +348,11 @@ class SetglobalCommandTest : VimTestCase() { setOsSpecificOptionsToSafeValues() assertCommandOutput("setglobal all", """ |--- Global option values --- - |noargtextobj noincsearch selectmode= notextobj-indent - |nocommentary nomatchit shellcmdflag=-x timeout - |nodigraph maxmapdepth=20 shellxescape=@ timeoutlen=1000 - |noexchange more shellxquote={ notrackactionids + |noargtextobj noignorecase scrolloff=0 notextobj-entire + |nocommentary noincsearch selectmode= notextobj-indent + |nodigraph nomatchit shellcmdflag=-x timeout + |noexchange maxmapdepth=20 shellxescape=@ timeoutlen=1000 + |nofixdeadkeys more shellxquote={ notrackactionids |nogdefault nomultiple-cursors showcmd undolevels=1000 |nohighlightedyank noNERDTree showmode virtualedit= | history=50 nrformats=hex sidescroll=0 novisualbell @@ -360,7 +361,6 @@ class SetglobalCommandTest : VimTestCase() { |noideajoin norelativenumber nosneak wrapscan | ideamarks scroll=0 startofline | ideawrite=all scrolljump=1 nosurround - |noignorecase scrolloff=0 notextobj-entire | clipboard=ideaput,autoselect,exclude:cons\|linux | guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175 | ide=IntelliJ IDEA Community Edition @@ -421,6 +421,7 @@ class SetglobalCommandTest : VimTestCase() { |nocommentary |nodigraph |noexchange + |nofixdeadkeys |nogdefault | guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175 |nohighlightedyank diff --git a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetlocalCommandTest.kt b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetlocalCommandTest.kt index 6de947bb0d..b39118ec04 100644 --- a/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetlocalCommandTest.kt +++ b/src/test/java/org/jetbrains/plugins/ideavim/ex/implementation/commands/SetlocalCommandTest.kt @@ -381,10 +381,11 @@ class SetlocalCommandTest : VimTestCase() { setOsSpecificOptionsToSafeValues() assertCommandOutput("setlocal all", """ |--- Local option values --- - |noargtextobj noignorecase scrolloff=-1 notextobj-entire - |nocommentary noincsearch selectmode= notextobj-indent - |nodigraph nomatchit shellcmdflag=-x timeout - |noexchange maxmapdepth=20 shellxescape=@ timeoutlen=1000 + |noargtextobj ideawrite=all scrolljump=1 nosurround + |nocommentary noignorecase scrolloff=-1 notextobj-entire + |nodigraph noincsearch selectmode= notextobj-indent + |noexchange nomatchit shellcmdflag=-x timeout + |nofixdeadkeys maxmapdepth=20 shellxescape=@ timeoutlen=1000 |nogdefault more shellxquote={ notrackactionids |nohighlightedyank nomultiple-cursors showcmd virtualedit= | history=50 noNERDTree showmode novisualbell @@ -393,7 +394,6 @@ class SetlocalCommandTest : VimTestCase() { |--ideajoin operatorfunc= nosmartcase wrapscan | ideamarks norelativenumber nosneak | idearefactormode= scroll=0 startofline - | ideawrite=all scrolljump=1 nosurround | clipboard=ideaput,autoselect,exclude:cons\|linux | guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175 | ide=IntelliJ IDEA Community Edition @@ -446,6 +446,7 @@ class SetlocalCommandTest : VimTestCase() { |nocommentary |nodigraph |noexchange + |nofixdeadkeys |nogdefault | guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175 |nohighlightedyank