mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-05-06 12:34:03 +02:00
Handle blank and empty lines for text objects
Fixes VIM-1642
This commit is contained in:
parent
f25c56b545
commit
426c2fd006
src/test/java/org/jetbrains/plugins/ideavim/action/motion/object
vim-engine/src/main/kotlin/com/maddyhome/idea/vim
@ -338,6 +338,116 @@ class MotionInnerBigWordActionTest : VimTestCase() {
|
||||
)
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(originalVimAfter =
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${s}${c}
|
||||
|${se}
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""",
|
||||
description = "The caret and selection should not be at the same offset. This indicates that IdeaVim is " +
|
||||
"shortening the selection range to (incorrectly) avoid selecting the end of line char. Once IdeaVim allows " +
|
||||
"this, both caret and selection end offset will be incorrect, indicating an off-by-ine error somewhere, " +
|
||||
"which will need fixing." +
|
||||
"Fix when IdeaVim supports selecting end of line char"
|
||||
)
|
||||
@Test
|
||||
fun `test select empty line`() {
|
||||
doTest(
|
||||
"viW",
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${c}
|
||||
|
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin(),
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${s}
|
||||
|${c}${se}
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin(),
|
||||
Mode.VISUAL(SelectionType.CHARACTER_WISE),
|
||||
)
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(originalVimAfter =
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${s}
|
||||
|
|
||||
|
|
||||
|
|
||||
|${c}${se}
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""",
|
||||
description = "I don't understand Vim's logic for selecting mulitple empty lines with 'iw'." +
|
||||
"E.g. `v3iw` will select 5 lines, `v4iw` will select 7, `v5iw` selects 9. " +
|
||||
"Fix only when/if Vim's behaviour is clarified"
|
||||
)
|
||||
@Test
|
||||
fun `test select multiple empty lines`() {
|
||||
doTest(
|
||||
"v3iW",
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${c}
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin(),
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${s}
|
||||
|
|
||||
|
|
||||
|${c}${se}
|
||||
|
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin(),
|
||||
Mode.VISUAL(SelectionType.CHARACTER_WISE),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test select blank line`() {
|
||||
doTest(
|
||||
"viW",
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|..${c}..
|
||||
|
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin().dotToSpace(),
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${s}...${c}.${se}
|
||||
|
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin().dotToSpace(),
|
||||
Mode.VISUAL(SelectionType.CHARACTER_WISE),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@VimBehaviorDiffers(
|
||||
originalVimAfter = "${s}Lorem${c}${se} ipsum dolor sit amet, consectetur adipiscing elit",
|
||||
description = "Text objects are implicitly inclusive, because they set the selection." +
|
||||
|
@ -338,6 +338,115 @@ class MotionInnerWordActionTest : VimTestCase() {
|
||||
)
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(originalVimAfter =
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${s}${c}
|
||||
|${se}
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""",
|
||||
description = "The caret and selection should not be at the same offset. This indicates that IdeaVim is " +
|
||||
"shortening the selection range to (incorrectly) avoid selecting the end of line char. Once IdeaVim allows " +
|
||||
"this, both caret and selection end offset will be incorrect, indicating an off-by-ine error somewhere, " +
|
||||
"which will need fixing." +
|
||||
"Fix when IdeaVim supports selecting end of line char"
|
||||
)
|
||||
@Test
|
||||
fun `test select empty line`() {
|
||||
doTest(
|
||||
"viw",
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${c}
|
||||
|
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin(),
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${s}
|
||||
|${c}${se}
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin(),
|
||||
Mode.VISUAL(SelectionType.CHARACTER_WISE),
|
||||
)
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(originalVimAfter =
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${s}
|
||||
|
|
||||
|
|
||||
|
|
||||
|${c}${se}
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""",
|
||||
description = "I don't understand Vim's logic for selecting mulitple empty lines with 'iw'." +
|
||||
"E.g. `v3iw` will select 5 lines, `v4iw` will select 7, `v5iw` selects 9. " +
|
||||
"Fix only when/if Vim's behaviour is clarified"
|
||||
)
|
||||
@Test
|
||||
fun `test select multiple empty lines`() {
|
||||
doTest(
|
||||
"v3iw",
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${c}
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin(),
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${s}
|
||||
|
|
||||
|
|
||||
|${c}${se}
|
||||
|
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin(),
|
||||
Mode.VISUAL(SelectionType.CHARACTER_WISE),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test select blank line`() {
|
||||
doTest(
|
||||
"viw",
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|..${c}..
|
||||
|
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin().dotToSpace(),
|
||||
"""
|
||||
|Lorem ipsum dolor sit amet,
|
||||
|
|
||||
|${s}...${c}.${se}
|
||||
|
|
||||
|
|
||||
|consectetur adipiscing elit
|
||||
""".trimMargin().dotToSpace(),
|
||||
Mode.VISUAL(SelectionType.CHARACTER_WISE),
|
||||
)
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(
|
||||
originalVimAfter = "${s}Lorem${c}${se} ipsum dolor sit amet, consectetur adipiscing elit",
|
||||
description = "Text objects are implicitly inclusive, because they set the selection." +
|
||||
|
@ -379,13 +379,13 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
||||
private fun findNextWordEndOne(
|
||||
chars: CharSequence,
|
||||
editor: VimEditor,
|
||||
pos: Int,
|
||||
start: Int,
|
||||
size: Int,
|
||||
step: Int,
|
||||
bigWord: Boolean,
|
||||
spaceWords: Boolean,
|
||||
): Int {
|
||||
var pos = pos
|
||||
var pos = start
|
||||
var found = false
|
||||
// For forward searches, skip any current whitespace so we start at the start of a word
|
||||
if (step > 0 && pos < size - 1) {
|
||||
@ -430,7 +430,10 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
||||
&& (newChar == lastChar
|
||||
|| (spaceWords && charType(editor, lastChar, bigWord) === CharacterHelper.CharacterType.WHITESPACE))
|
||||
) {
|
||||
res = if (step < 0) pos + 1 else pos - 1
|
||||
// Add/subtract one so we don't take the current new line char as our end of word char.
|
||||
// However, if we're matching empty lines and we start on an empty line, then we'll get two consecutive new line
|
||||
// chars. Subtracting will put us back at the start character
|
||||
res = if (step < 0) pos + 1 else (pos - 1).coerceAtLeast(start + 1)
|
||||
found = true
|
||||
}
|
||||
lastChar = newChar
|
||||
@ -1515,6 +1518,13 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
||||
logger.debug("start=$start")
|
||||
logger.debug("end=$end")
|
||||
|
||||
// TODO: Remove this when IdeaVim supports selecting the new line character
|
||||
// A selection with start == end is perfectly valid, and will select a single character. However, IdeaVim
|
||||
// unnecessarily prevents selecting the new line character at the end of a line. If the selection is just that new
|
||||
// line character, then nothing is selected (we end up with a selection with range start==endInclusive, rather than
|
||||
// start==endExclusive). This little hack makes sure that `viw` will (mostly) work on a single empty line
|
||||
if (start == end && chars[start] == '\n') end++
|
||||
|
||||
// Text range's end offset is exclusive
|
||||
return TextRange(start, end + 1)
|
||||
}
|
||||
|
@ -19,9 +19,10 @@ import com.maddyhome.idea.vim.state.mode.Mode
|
||||
|
||||
fun charToNativeSelection(editor: VimEditor, start: Int, end: Int, mode: Mode): Pair<Int, Int> {
|
||||
val (nativeStart, nativeEnd) = sort(start, end)
|
||||
// TODO: Remove this unnecessary restriction on not selecting the new line character
|
||||
// When doing so, please remove the hack in VimSearchHelperBase.findWordUnderCursor
|
||||
val lineEnd = editor.getLineEndForOffset(nativeEnd)
|
||||
val adj =
|
||||
if (isExclusiveSelection() || nativeEnd == lineEnd || mode is Mode.SELECT) 0 else 1
|
||||
val adj = if (isExclusiveSelection() || nativeEnd == lineEnd || mode is Mode.SELECT) 0 else 1
|
||||
val adjEnd = (nativeEnd + adj).coerceAtMost(editor.fileSize().toInt())
|
||||
return nativeStart to adjEnd
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user