diff --git a/doc/IdeaVim Plugins.md b/doc/IdeaVim Plugins.md index de38d59d6..91b1c7a43 100644 --- a/doc/IdeaVim Plugins.md +++ b/doc/IdeaVim Plugins.md @@ -423,6 +423,24 @@ https://plugins.jetbrains.com/plugin/19417-ideavim-quickscope </details> +<details> +<summary><h2>Mini.ai: Extend and create a/i textobjects (IMPORTANT: The plugin is not related with artificial intelligence)</h2></summary> + +### Features: +Provides additional text object motions for handling quotes and brackets. The following motions are included: + +- aq: Around any quotes. +- iq: Inside any quotes. +- ab: Around any parentheses, curly braces, and square brackets. +- ib: Inside any parentheses, curly braces, and square brackets. + +Original plugin: [mini.ai](https://github.com/echasnovski/mini.ai). + +### Setup: +- Add the following command to `~/.ideavimrc`: `set mini-ai` + +</details> + <details> <summary><h2>Which-Key</h2></summary> @@ -484,4 +502,3 @@ or restart the IDE. https://plugins.jetbrains.com/plugin/25899-vim-switch -</details> diff --git a/src/main/java/com/maddyhome/idea/vim/extension/miniai/MiniAI.kt b/src/main/java/com/maddyhome/idea/vim/extension/miniai/MiniAI.kt new file mode 100644 index 000000000..2bda28f6c --- /dev/null +++ b/src/main/java/com/maddyhome/idea/vim/extension/miniai/MiniAI.kt @@ -0,0 +1,230 @@ +/* + * Copyright 2003-2023 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 com.maddyhome.idea.vim.extension.miniai + +import com.maddyhome.idea.vim.KeyHandler +import com.maddyhome.idea.vim.api.ExecutionContext +import com.maddyhome.idea.vim.api.ImmutableVimCaret +import com.maddyhome.idea.vim.api.VimEditor +import com.maddyhome.idea.vim.api.injector +import com.maddyhome.idea.vim.command.CommandFlags +import com.maddyhome.idea.vim.command.MappingMode +import com.maddyhome.idea.vim.command.OperatorArguments +import com.maddyhome.idea.vim.command.TextObjectVisualType +import com.maddyhome.idea.vim.common.TextRange +import com.maddyhome.idea.vim.extension.ExtensionHandler +import com.maddyhome.idea.vim.extension.VimExtension +import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping +import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping +import com.maddyhome.idea.vim.group.findBlockRange +import com.maddyhome.idea.vim.handler.TextObjectActionHandler +import com.maddyhome.idea.vim.helper.enumSetOf +import com.maddyhome.idea.vim.state.KeyHandlerState +import java.util.* + +/** + * Extend and create a/i textobjects + * + * <p> + * mini ai provides the next motions: + * <ul> + * <li>aq around any quotes.</li> + * <li>iq inside any quotes.</li> + * <li>ab around any parentheses, curly braces and square brackets.</li> + * <li>ib inside any parentheses, curly braces and square brackets.</li> + * </ul> + * + * @author Osvaldo Cordova Aburto (@oca159) + */ +internal class MiniAI : VimExtension { + + companion object { + // Constants for key mappings + private const val PLUG_AQ = "<Plug>mini-ai-aq" + private const val PLUG_IQ = "<Plug>mini-ai-iq" + private const val PLUG_AB = "<Plug>mini-ai-ab" + private const val PLUG_IB = "<Plug>mini-ai-ib" + + // Constants for key sequences + private const val KEY_AQ = "aq" + private const val KEY_IQ = "iq" + private const val KEY_AB = "ab" + private const val KEY_IB = "ib" + } + + override fun getName() = "mini-ai" + + override fun init() { + registerMappings() + } + + private fun registerMappings() { + val mappings = listOf( + PLUG_AQ to AroundAnyQuotesHandler(), + PLUG_IQ to InsideAnyQuotesHandler(), + PLUG_AB to AroundAnyBracketsHandler(), + PLUG_IB to InsideAnyBracketsHandler() + ) + + mappings.forEach { (key, handler) -> + putExtensionHandlerMapping(MappingMode.XO, injector.parser.parseKeys(key), owner, handler, false) + } + + val keyMappings = listOf( + KEY_AQ to PLUG_AQ, + KEY_IQ to PLUG_IQ, + KEY_AB to PLUG_AB, + KEY_IB to PLUG_IB + ) + + keyMappings.forEach { (key, plug) -> + putKeyMapping(MappingMode.XO, injector.parser.parseKeys(key), owner, injector.parser.parseKeys(plug), true) + } + } + + + private class InsideAnyQuotesHandler : ExtensionHandler { + override val isRepeatable = true + + override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { + addAction(MotionInnerAnyQuoteProximityAction()) + } + } + + private class AroundAnyQuotesHandler : ExtensionHandler { + override val isRepeatable = true + + override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { + addAction(MotionOuterAnyQuoteProximityAction()) + } + } + + private class InsideAnyBracketsHandler : ExtensionHandler { + override val isRepeatable = true + + override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { + addAction(MotionInnerAnyBracketProximityAction()) + } + } + + private class AroundAnyBracketsHandler : ExtensionHandler { + override val isRepeatable = true + + override fun execute(editor: VimEditor, context: ExecutionContext, operatorArguments: OperatorArguments) { + addAction(MotionOuterAnyBracketProximityAction()) + } + } + + class MotionInnerAnyQuoteProximityAction : TextObjectActionHandler() { + + override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_TEXT_BLOCK) + + override val visualType: TextObjectVisualType = TextObjectVisualType.CHARACTER_WISE + + override fun getRange( + editor: VimEditor, + caret: ImmutableVimCaret, + context: ExecutionContext, + count: Int, + rawCount: Int, + ): TextRange? { + return findClosestQuoteRange(editor, caret, false) + } + } + + class MotionOuterAnyQuoteProximityAction : TextObjectActionHandler() { + + override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_TEXT_BLOCK) + + override val visualType: TextObjectVisualType = TextObjectVisualType.CHARACTER_WISE + + override fun getRange( + editor: VimEditor, + caret: ImmutableVimCaret, + context: ExecutionContext, + count: Int, + rawCount: Int, + ): TextRange? { + return findClosestQuoteRange(editor, caret, true) + } + } + + class MotionInnerAnyBracketProximityAction : TextObjectActionHandler() { + + override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_TEXT_BLOCK) + + override val visualType: TextObjectVisualType = TextObjectVisualType.CHARACTER_WISE + + override fun getRange( + editor: VimEditor, + caret: ImmutableVimCaret, + context: ExecutionContext, + count: Int, + rawCount: Int, + ): TextRange? { + return findClosestBracketRange(editor, caret, count, false) + } + } +} + +class MotionOuterAnyBracketProximityAction : TextObjectActionHandler() { + + override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_TEXT_BLOCK) + + override val visualType: TextObjectVisualType = TextObjectVisualType.CHARACTER_WISE + + override fun getRange( + editor: VimEditor, + caret: ImmutableVimCaret, + context: ExecutionContext, + count: Int, + rawCount: Int, + ): TextRange? { + return findClosestBracketRange(editor, caret, count, true) + } +} + +private fun addAction(action: TextObjectActionHandler) { + val keyHandlerState: KeyHandlerState = KeyHandler.getInstance().keyHandlerState + keyHandlerState.commandBuilder.addAction(action) +} + +private fun findClosestBracketRange( + editor: VimEditor, + caret: ImmutableVimCaret, + count: Int, + isOuter: Boolean, +): TextRange? { + return listOf('(', '[', '{') + .mapNotNull { char -> + findBlockRange(editor, caret, char, count, isOuter)?.let { range -> range to char } + } + .minByOrNull { it.first.distanceTo(caret.offset) }?.first +} + +private fun findClosestQuoteRange( + editor: VimEditor, + caret: ImmutableVimCaret, + isOuter: Boolean, +): TextRange? { + return listOf('`', '"', '\'') + .mapNotNull { char -> + injector.searchHelper.findBlockQuoteInLineRange(editor, caret, char, isOuter)?.let { range -> range to char } + } + .minByOrNull { it.first.distanceTo(caret.offset) }?.first +} + +private fun TextRange.distanceTo(caretOffset: Int): Int { + return if (caretOffset < startOffset) { + startOffset - caretOffset + } else if (caretOffset > endOffset) { + caretOffset - endOffset + } else { + 0 // Caret is inside the range + } +} \ No newline at end of file diff --git a/src/main/java/com/maddyhome/idea/vim/statistic/PluginState.kt b/src/main/java/com/maddyhome/idea/vim/statistic/PluginState.kt index 355516a12..035036fec 100644 --- a/src/main/java/com/maddyhome/idea/vim/statistic/PluginState.kt +++ b/src/main/java/com/maddyhome/idea/vim/statistic/PluginState.kt @@ -45,7 +45,8 @@ internal class PluginState : ApplicationUsagesCollector() { "surround", "commentary", "matchit", - "textobj-indent" + "textobj-indent", + "mini-ai" ) internal val enabledExtensions = HashSet<String>() } diff --git a/src/main/resources/META-INF/includes/VimExtensions.xml b/src/main/resources/META-INF/includes/VimExtensions.xml index f7d15720a..7a14af971 100644 --- a/src/main/resources/META-INF/includes/VimExtensions.xml +++ b/src/main/resources/META-INF/includes/VimExtensions.xml @@ -49,6 +49,15 @@ </aliases> </vimExtension> + + <vimExtension implementation="com.maddyhome.idea.vim.extension.miniai.MiniAI" + name="mini-ai"> + <aliases> + <alias name="https://github.com/echasnovski/mini.ai"/> + <alias name="echasnovski/mini.ai"/> + </aliases> + </vimExtension> + <vimExtension implementation="com.maddyhome.idea.vim.extension.argtextobj.VimArgTextObjExtension" name="argtextobj"> <aliases> 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 ba0f965f9..c9d0f6253 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 @@ -183,20 +183,21 @@ class SetCommandTest : VimTestCase() { "set all", """ |--- Options --- - |noargtextobj ideamarks scroll=0 nosurround - |nobomb ideawrite=all scrolljump=1 notextobj-entire - |nobreakindent noignorecase scrolloff=0 notextobj-indent - | colorcolumn= noincsearch selectmode= textwidth=0 - |nocommentary nolist shellcmdflag=-x timeout - |nocursorline nomatchit shellxescape=@ timeoutlen=1000 - |nodigraph maxmapdepth=20 shellxquote={ notrackactionids - |noexchange more showcmd undolevels=1000 - | fileformat=unix nomultiple-cursors showmode virtualedit= - |nogdefault noNERDTree sidescroll=0 novisualbell - |nohighlightedyank nrformats=hex sidescrolloff=0 visualdelay=100 - | history=50 nonumber nosmartcase whichwrap=b,s - |nohlsearch operatorfunc= nosneak wrap - |noideajoin norelativenumber startofline wrapscan + |noargtextobj ideawrite=all scrolljump=1 notextobj-indent + |nobomb noignorecase scrolloff=0 textwidth=0 + |nobreakindent noincsearch selectmode= timeout + | colorcolumn= nolist shellcmdflag=-x timeoutlen=1000 + |nocommentary nomatchit shellxescape=@ notrackactionids + |nocursorline maxmapdepth=20 shellxquote={ undolevels=1000 + |nodigraph nomini-ai showcmd virtualedit= + |noexchange more showmode novisualbell + | fileformat=unix nomultiple-cursors sidescroll=0 visualdelay=100 + |nogdefault noNERDTree sidescrolloff=0 whichwrap=b,s + |nohighlightedyank nrformats=hex nosmartcase wrap + | history=50 nonumber nosneak wrapscan + |nohlsearch operatorfunc= startofline + |noideajoin norelativenumber nosurround + | ideamarks scroll=0 notextobj-entire | clipboard=ideaput,autoselect | fileencoding=utf-8 | 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 @@ -288,6 +289,7 @@ class SetCommandTest : VimTestCase() { |nomatchit | matchpairs=(:),{:},[:] | maxmapdepth=20 + |nomini-ai | more |nomultiple-cursors |noNERDTree 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 48afdcb8a..54132197b 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 @@ -432,21 +432,21 @@ class SetglobalCommandTest : VimTestCase() { assertCommandOutput( "setglobal all", """ |--- Global option values --- - |noargtextobj ideamarks scrolljump=1 notextobj-indent - |nobomb ideawrite=all scrolloff=0 textwidth=0 - |nobreakindent noignorecase selectmode= timeout - | colorcolumn= noincsearch shellcmdflag=-x timeoutlen=1000 - |nocommentary nolist shellxescape=@ notrackactionids - |nocursorline nomatchit shellxquote={ undolevels=1000 - |nodigraph maxmapdepth=20 showcmd virtualedit= - |noexchange more showmode novisualbell - | fileencoding= nomultiple-cursors sidescroll=0 visualdelay=100 - | fileformat=unix noNERDTree sidescrolloff=0 whichwrap=b,s - |nogdefault nrformats=hex nosmartcase wrap - |nohighlightedyank nonumber nosneak wrapscan - | history=50 operatorfunc= startofline - |nohlsearch norelativenumber nosurround - |noideajoin scroll=0 notextobj-entire + |noargtextobj ideamarks scroll=0 notextobj-entire + |nobomb ideawrite=all scrolljump=1 notextobj-indent + |nobreakindent noignorecase scrolloff=0 textwidth=0 + | colorcolumn= noincsearch selectmode= timeout + |nocommentary nolist shellcmdflag=-x timeoutlen=1000 + |nocursorline nomatchit shellxescape=@ notrackactionids + |nodigraph maxmapdepth=20 shellxquote={ undolevels=1000 + |noexchange nomini-ai showcmd virtualedit= + | fileencoding= more showmode novisualbell + | fileformat=unix nomultiple-cursors sidescroll=0 visualdelay=100 + |nogdefault noNERDTree sidescrolloff=0 whichwrap=b,s + |nohighlightedyank nrformats=hex nosmartcase wrap + | history=50 nonumber nosneak wrapscan + |nohlsearch operatorfunc= startofline + |noideajoin norelativenumber nosurround | clipboard=ideaput,autoselect | 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 @@ -539,6 +539,7 @@ class SetglobalCommandTest : VimTestCase() { |nomatchit | matchpairs=(:),{:},[:] | maxmapdepth=20 + |nomini-ai | more |nomultiple-cursors |noNERDTree 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 7f51d520e..b1d785c66 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 @@ -483,20 +483,21 @@ class SetlocalCommandTest : VimTestCase() { assertCommandOutput( "setlocal all", """ |--- Local option values --- - |noargtextobj ideamarks norelativenumber startofline - |nobomb idearefactormode= scroll=0 nosurround - |nobreakindent ideawrite=all scrolljump=1 notextobj-entire - | colorcolumn= noignorecase scrolloff=-1 notextobj-indent - |nocommentary noincsearch selectmode= textwidth=0 - |nocursorline nolist shellcmdflag=-x timeout - |nodigraph nomatchit shellxescape=@ timeoutlen=1000 - |noexchange maxmapdepth=20 shellxquote={ notrackactionids - | fileformat=unix more showcmd virtualedit= - |nogdefault nomultiple-cursors showmode novisualbell - |nohighlightedyank noNERDTree sidescroll=0 visualdelay=100 - | history=50 nrformats=hex sidescrolloff=-1 whichwrap=b,s - |nohlsearch nonumber nosmartcase wrap - |--ideajoin operatorfunc= nosneak wrapscan + |noargtextobj idearefactormode= scroll=0 notextobj-entire + |nobomb ideawrite=all scrolljump=1 notextobj-indent + |nobreakindent noignorecase scrolloff=-1 textwidth=0 + | colorcolumn= noincsearch selectmode= timeout + |nocommentary nolist shellcmdflag=-x timeoutlen=1000 + |nocursorline nomatchit shellxescape=@ notrackactionids + |nodigraph maxmapdepth=20 shellxquote={ virtualedit= + |noexchange nomini-ai showcmd novisualbell + | fileformat=unix more showmode visualdelay=100 + |nogdefault nomultiple-cursors sidescroll=0 whichwrap=b,s + |nohighlightedyank noNERDTree sidescrolloff=-1 wrap + | history=50 nrformats=hex nosmartcase wrapscan + |nohlsearch nonumber nosneak + |--ideajoin operatorfunc= startofline + | ideamarks norelativenumber nosurround | clipboard=ideaput,autoselect | fileencoding=utf-8 | 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 @@ -588,6 +589,7 @@ class SetlocalCommandTest : VimTestCase() { |nomatchit | matchpairs=(:),{:},[:] | maxmapdepth=20 + |nomini-ai | more |nomultiple-cursors |noNERDTree diff --git a/tests/java-tests/src/test/kotlin/org/jetbrains/plugins/ideavim/extension/miniai/MiniAIExtensionTest.kt b/tests/java-tests/src/test/kotlin/org/jetbrains/plugins/ideavim/extension/miniai/MiniAIExtensionTest.kt new file mode 100644 index 000000000..03c00adbf --- /dev/null +++ b/tests/java-tests/src/test/kotlin/org/jetbrains/plugins/ideavim/extension/miniai/MiniAIExtensionTest.kt @@ -0,0 +1,644 @@ +/* + * Copyright 2003-2024 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.extension.miniai + +import com.intellij.ide.highlighter.JavaFileType +import com.maddyhome.idea.vim.state.mode.Mode +import org.jetbrains.plugins.ideavim.VimJavaTestCase +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInfo + +@Suppress("SpellCheckingInspection") +class MiniAIExtensionTest : VimJavaTestCase() { + @BeforeEach + override fun setUp(testInfo: TestInfo) { + super.setUp(testInfo) + enableExtensions("mini-ai") + } + + @Test + fun testChangeInsideSingleQuote() { + doTest( + "ciq", + "<caret>This is a 'simple' test", + "This is a '<caret>' test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeInsideSingleQuoteNotBalanced() { + doTest( + "ciq", + "<caret>This is a 'simple test", + "<caret>This is a 'simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeInsideDoubleQuote() { + doTest( + "ciq", + "<caret>This is a \"simple\" test", + "This is a \"<caret>\" test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeInsideDoubleQuoteNotBalanced() { + doTest( + "ciq", + "<caret>This is a \"simple test", + "<caret>This is a \"simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeInsideBackQuote() { + doTest( + "ciq", + "<caret>This is a `simple` test", + "This is a `<caret>` test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeInsideBackQuoteNotBalanced() { + doTest( + "ciq", + "<caret>This is a `simple test", + "<caret>This is a `simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeAroundSingleQuote() { + doTest( + "caq", + "<caret>This is a 'simple' test", + "This is a <caret> test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeAroundSingleQuoteNotBalanced() { + doTest( + "caq", + "<caret>This is a 'simple test", + "<caret>This is a 'simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeAroundDoubleQuote() { + doTest( + "caq", + "<caret>This is a \"simple\" test", + "This is a <caret> test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeAroundDoubleQuoteNotBalanced() { + doTest( + "caq", + "<caret>This is a \"simple test", + "<caret>This is a \"simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + @Test + fun testChangeAroundBackQuote() { + doTest( + "caq", + "<caret>This is a `simple` test", + "This is a <caret> test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeAroundBackQuoteNotBalanced() { + doTest( + "caq", + "<caret>This is a `simple test", + "<caret>This is a `simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + @Test + fun testChangeInsideRoundBrackets() { + doTest( + "cib", + "<caret>This is a (simple) test", + "This is a (<caret>) test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeInsideRoundBracketsNotBalanced() { + doTest( + "cib", + "<caret>This is a (simple test", + "<caret>This is a (simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeInsideSquareBrackets() { + doTest( + "cib", + "<caret>This is a [simple] test", + "This is a [<caret>] test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeInsideSquareBracketsNotBalanced() { + doTest( + "cib", + "<caret>This is a [simple test", + "<caret>This is a [simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeInsideCurlyBrackets() { + doTest( + "cib", + "<caret>This is a {simple} test", + "This is a {<caret>} test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeInsideCurlyBracketsNotBalanced() { + doTest( + "cib", + "<caret>This is a {simple test", + "<caret>This is a {simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeAroundRoundBrackets() { + doTest( + "cab", + "<caret>This is a (simple) test", + "This is a <caret> test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeAroundRoundBracketsNotBalanced() { + doTest( + "cab", + "<caret>This is a (simple test", + "<caret>This is a (simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeAroundSquareBrackets() { + doTest( + "cab", + "<caret>This is a [simple] test", + "This is a <caret> test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + @Test + fun testChangeAroundSquareBracketsNotBalanced() { + doTest( + "cab", + "<caret>This is a [simple test", + "<caret>This is a [simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeAroundCurlyBrackets() { + doTest( + "cab", + "<caret>This is a {simple} test", + "This is a <caret> test", + Mode.INSERT, + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testChangeAroundCurlyBracketsNotBalanced() { + doTest( + "cab", + "<caret>This is a {simple test", + "<caret>This is a {simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + // DELETE tests + @Test + fun testDeleteInsideSingleQuote() { + doTest( + "diq", + "<caret>This is a 'simple' test", + "This is a '<caret>' test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteInsideSingleQuoteNotBalanced() { + doTest( + "diq", + "<caret>This is a 'simple test", + "<caret>This is a 'simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteInsideDoubleQuote() { + doTest( + "diq", + "<caret>This is a \"simple\" test", + "This is a \"<caret>\" test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteInsideDoubleQuoteNotBalanced() { + doTest( + "diq", + "<caret>This is a \"simple test", + "<caret>This is a \"simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteInsideBackQuote() { + doTest( + "diq", + "<caret>This is a `simple` test", + "This is a `<caret>` test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteInsideBackQuoteNotBalanced() { + doTest( + "diq", + "<caret>This is a `simple test", + "<caret>This is a `simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteAroundSingleQuote() { + doTest( + "daq", + "<caret>This is a 'simple' test", + "This is a <caret> test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + @Test + fun testDeleteAroundSingleQuoteNotBalanced() { + doTest( + "daq", + "<caret>This is a 'simple test", + "<caret>This is a 'simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteAroundDoubleQuote() { + doTest( + "daq", + "<caret>This is a \"simple\" test", + "This is a <caret> test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteAroundDoubleQuoteNotBalanced() { + doTest( + "daq", + "<caret>This is a \"simple test", + "<caret>This is a \"simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteAroundBackQuote() { + doTest( + "daq", + "<caret>This is a `simple` test", + "This is a <caret> test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteAroundBackQuoteNotBalanced() { + doTest( + "daq", + "<caret>This is a `simple test", + "<caret>This is a `simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteInsideRoundBrackets() { + doTest( + "dib", + "<caret>This is a (simple) test", + "This is a (<caret>) test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteInsideRoundBracketsNotBalanced() { + doTest( + "dib", + "<caret>This is a (simple test", + "<caret>This is a (simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteInsideSquareBrackets() { + doTest( + "dib", + "<caret>This is a [simple] test", + "This is a [<caret>] test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteInsideSquareBracketsNotBalanced() { + doTest( + "dib", + "<caret>This is a [simple test", + "<caret>This is a [simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteInsideCurlyBrackets() { + doTest( + "dib", + "<caret>This is a {simple} test", + "This is a {<caret>} test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteInsideCurlyBracketsNotBalanced() { + doTest( + "dib", + "<caret>This is a {simple test", + "<caret>This is a {simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteAroundRoundBrackets() { + doTest( + "dab", + "<caret>This is a (simple) test", + "This is a <caret> test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteAroundRoundBracketsNotBalanced() { + doTest( + "dab", + "<caret>This is a (simple test", + "<caret>This is a (simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteAroundSquareBrackets() { + doTest( + "dab", + "<caret>This is a [simple] test", + "This is a <caret> test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteAroundSquareBracketsNotBalanced() { + doTest( + "dab", + "<caret>This is a [simple test", + "<caret>This is a [simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteAroundCurlyBrackets() { + doTest( + "dab", + "<caret>This is a {simple} test", + "This is a <caret> test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } + + + @Test + fun testDeleteAroundCurlyBracketsNotBalanced() { + doTest( + "dab", + "<caret>This is a {simple test", + "<caret>This is a {simple test", + Mode.NORMAL(), + JavaFileType.INSTANCE, + ) + assertSelection(null) + } +}