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

Select leading whitespace with vaw

Outer word/WORD motions will select leading whitespace at the start of a line
This commit is contained in:
Matt Ellis 2025-01-15 22:01:49 +00:00 committed by Alex Pláte
parent 81e094ac8f
commit 1769804c8a
5 changed files with 90 additions and 33 deletions
src/test/java/org/jetbrains/plugins/ideavim/action/motion/object
vim-engine/src/main/kotlin/com/maddyhome/idea/vim/api

View File

@ -106,14 +106,12 @@ class MotionInnerBigWordActionTest : VimTestCase() {
)
}
// TODO: Fix this bug
@VimBehaviorDiffers(originalVimAfter = "Lorem ipsum dolor sit amet, ${s}${c}consectet${se}ur adipiscing elit")
@Test
fun `test select WORD with existing right-to-left selection selects start of word`() {
doTest(
listOf("v", "h", "iW"),
"Lorem ipsum dolor sit amet, consecte${c}tur adipiscing elit",
"Lorem ipsum dolor sit amet,${s}${c} consectet${se}ur adipiscing elit",
"Lorem ipsum dolor sit amet, ${s}${c}consectet${se}ur adipiscing elit",
Mode.VISUAL(SelectionType.CHARACTER_WISE),
)
}

View File

@ -106,14 +106,12 @@ class MotionInnerWordActionTest : VimTestCase() {
)
}
// TODO: Fix this bug
@VimBehaviorDiffers(originalVimAfter = "Lorem ipsum dolor sit amet, ${s}${c}consectet${se}ur adipiscing elit")
@Test
fun `test select word with existing right-to-left selection selects start of word`() {
doTest(
listOf("v", "h", "iw"),
"Lorem ipsum dolor sit amet, consecte${c}tur adipiscing elit",
"Lorem ipsum dolor sit amet,${s}${c} consectet${se}ur adipiscing elit",
"Lorem ipsum dolor sit amet, ${s}${c}consectet${se}ur adipiscing elit",
Mode.VISUAL(SelectionType.CHARACTER_WISE),
)
}

View File

@ -502,6 +502,36 @@ class MotionOuterBigWordActionTest : VimTestCase() {
)
}
@Test
fun `test select outer WORD with existing right-to-left selection with caret at start of word selects previous word`() {
doTest(
listOf("v", "h", "aW"),
"Lorem i${c}psum dolor sit amet",
"${s}${c}Lorem ip${se}sum dolor sit amet",
Mode.VISUAL(SelectionType.CHARACTER_WISE),
)
}
@Test
fun `test select outer WORD with existing right-to-left selection selects rest of word and leading whitespace at start of line`() {
doTest(
listOf("v", "h", "aW"),
" Lo${c}rem ipsum dolor sit amet",
"${s}${c} Lor${se}em ipsum dolor sit amet",
Mode.VISUAL(SelectionType.CHARACTER_WISE),
)
}
@Test
fun `test select outer WORD with existing right-to-left selection on only word on line selects rest of word and leading whitespace`() {
doTest(
listOf("v", "h", "aW"),
" Lo${c}rem",
"${s}${c} Lor${se}em",
Mode.VISUAL(SelectionType.CHARACTER_WISE),
)
}
@Test
fun `test select outer WORD with existing right-to-left selection in whitespace selects rest of whitespace and preceding word`() {
doTest(

View File

@ -509,6 +509,36 @@ class MotionOuterWordActionTest : VimTestCase() {
)
}
@Test
fun `test select outer word with existing right-to-left selection with caret at start of word selects previous word`() {
doTest(
listOf("v", "h", "aw"),
"Lorem i${c}psum dolor sit amet",
"${s}${c}Lorem ip${se}sum dolor sit amet",
Mode.VISUAL(SelectionType.CHARACTER_WISE),
)
}
@Test
fun `test select outer word with existing right-to-left selection selects rest of word and leading whitespace at start of line`() {
doTest(
listOf("v", "h", "aw"),
" Lo${c}rem ipsum dolor sit amet",
"${s}${c} Lor${se}em ipsum dolor sit amet",
Mode.VISUAL(SelectionType.CHARACTER_WISE),
)
}
@Test
fun `test select outer word with existing right-to-left selection on only word on line selects rest of word and leading whitespace`() {
doTest(
listOf("v", "h", "aw"),
" Lo${c}rem",
"${s}${c} Lor${se}em",
Mode.VISUAL(SelectionType.CHARACTER_WISE),
)
}
@Test
fun `test select outer word with existing right-to-left selection in whitespace selects rest of whitespace and preceding word`() {
doTest(

View File

@ -26,7 +26,6 @@ import org.jetbrains.annotations.Range
import java.util.*
import java.util.regex.Pattern
import kotlin.math.abs
import kotlin.math.min
// todo all this methods should return Long since editor.fileSize is long
// todo same for TextRange and motions
@ -1402,7 +1401,7 @@ abstract class VimSearchHelperBase : VimSearchHelper {
val startSpace = charType(editor, chars[pos], isBig) === CharacterHelper.CharacterType.WHITESPACE
// Find word start
// Find word start. Note that the caret might be on the word start, but the selection start might not be!
val onWordStart = pos == 0 || charType(editor, chars[pos - 1], isBig) !== charType(editor, chars[pos], isBig)
var start = pos
@ -1438,28 +1437,29 @@ abstract class VimSearchHelperBase : VimSearchHelper {
logger.debug("end=$end")
var goBack = (startSpace && !hasSelection) || (!startSpace && hasSelection && !onWordStart)
if (dir == 1 && isOuter) {
// If there's no whitespace after the end of the word/WORD to select, go back for the leading whitespace
// This behaviour isn't strictly documented, but you can see it mentioned in `:help v_a'`
if (end < max - 1) {
val c = chars[end + 1]
if (charType(editor, c, false) !== CharacterHelper.CharacterType.WHITESPACE || c == '\n') {
goBack = true
}
}
else {
goBack = true
}
val hasForwardHeadingSelection = dir == 1 && hasSelection
val hasBackwardHeadingSelection = dir == -1 && hasSelection
val hasFollowingWhitespace = if (end < max - 1) {
val c = chars[end + 1]
charType(editor, c, false) === CharacterHelper.CharacterType.WHITESPACE && c != '\n'
}
if (dir == -1 && isOuter && startSpace) {
if (pos > 0) {
if (charType(editor, chars[pos - 1], false) !== CharacterHelper.CharacterType.WHITESPACE) {
goBack = true
}
}
else {
false
}
// Include preceding whitespace:
// ✓ Outer text object (`aw`) AND
// ✓ No forward heading selection AND
// ✓ Started on space, and there's no selection
// ✓ OR backward heading selection, but didn't start on space
// ✓ OR no whitespace after the word under cursor (see `:help v_a'`), as long as there's another word before
val includePrecedingWhitespace = isOuter
&& !hasForwardHeadingSelection
&& ((startSpace && !hasSelection)
|| (hasBackwardHeadingSelection && !startSpace)
|| (!hasFollowingWhitespace && editor.anyNonWhitespace(start, -1))
)
var goForward = dir == 1 && isOuter && ((!startSpace && !onWordEnd) || (startSpace && onWordEnd && hasSelection))
if (!goForward && dir == 1 && isOuter) {
var firstEnd = end
@ -1480,7 +1480,7 @@ abstract class VimSearchHelperBase : VimSearchHelper {
}
}
logger.debug("goBack=$goBack")
logger.debug("goBack=$includePrecedingWhitespace")
logger.debug("goForward=$goForward")
if (goForward) {
@ -1491,11 +1491,12 @@ abstract class VimSearchHelperBase : VimSearchHelper {
end++
}
}
if (goBack) {
if (editor.anyNonWhitespace(start, -1)) {
while (start > 0 && charType(editor, chars[start - 1], false) === CharacterHelper.CharacterType.WHITESPACE) {
start--
}
if (includePrecedingWhitespace) {
while (start > 0
&& chars[start - 1] != '\n'
&& charType(editor, chars[start - 1], false) === CharacterHelper.CharacterType.WHITESPACE
) {
start--
}
}