mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-05-28 08:34:02 +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(
|
@VimBehaviorDiffers(
|
||||||
originalVimAfter = "${s}Lorem${c}${se} ipsum dolor sit amet, consectetur adipiscing elit",
|
originalVimAfter = "${s}Lorem${c}${se} ipsum dolor sit amet, consectetur adipiscing elit",
|
||||||
description = "Text objects are implicitly inclusive, because they set the selection." +
|
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(
|
@VimBehaviorDiffers(
|
||||||
originalVimAfter = "${s}Lorem${c}${se} ipsum dolor sit amet, consectetur adipiscing elit",
|
originalVimAfter = "${s}Lorem${c}${se} ipsum dolor sit amet, consectetur adipiscing elit",
|
||||||
description = "Text objects are implicitly inclusive, because they set the selection." +
|
description = "Text objects are implicitly inclusive, because they set the selection." +
|
||||||
|
@ -379,13 +379,13 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
|||||||
private fun findNextWordEndOne(
|
private fun findNextWordEndOne(
|
||||||
chars: CharSequence,
|
chars: CharSequence,
|
||||||
editor: VimEditor,
|
editor: VimEditor,
|
||||||
pos: Int,
|
start: Int,
|
||||||
size: Int,
|
size: Int,
|
||||||
step: Int,
|
step: Int,
|
||||||
bigWord: Boolean,
|
bigWord: Boolean,
|
||||||
spaceWords: Boolean,
|
spaceWords: Boolean,
|
||||||
): Int {
|
): Int {
|
||||||
var pos = pos
|
var pos = start
|
||||||
var found = false
|
var found = false
|
||||||
// For forward searches, skip any current whitespace so we start at the start of a word
|
// For forward searches, skip any current whitespace so we start at the start of a word
|
||||||
if (step > 0 && pos < size - 1) {
|
if (step > 0 && pos < size - 1) {
|
||||||
@ -430,7 +430,10 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
|||||||
&& (newChar == lastChar
|
&& (newChar == lastChar
|
||||||
|| (spaceWords && charType(editor, lastChar, bigWord) === CharacterHelper.CharacterType.WHITESPACE))
|
|| (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
|
found = true
|
||||||
}
|
}
|
||||||
lastChar = newChar
|
lastChar = newChar
|
||||||
@ -1515,6 +1518,13 @@ abstract class VimSearchHelperBase : VimSearchHelper {
|
|||||||
logger.debug("start=$start")
|
logger.debug("start=$start")
|
||||||
logger.debug("end=$end")
|
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
|
// Text range's end offset is exclusive
|
||||||
return TextRange(start, end + 1)
|
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> {
|
fun charToNativeSelection(editor: VimEditor, start: Int, end: Int, mode: Mode): Pair<Int, Int> {
|
||||||
val (nativeStart, nativeEnd) = sort(start, end)
|
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 lineEnd = editor.getLineEndForOffset(nativeEnd)
|
||||||
val adj =
|
val adj = if (isExclusiveSelection() || nativeEnd == lineEnd || mode is Mode.SELECT) 0 else 1
|
||||||
if (isExclusiveSelection() || nativeEnd == lineEnd || mode is Mode.SELECT) 0 else 1
|
|
||||||
val adjEnd = (nativeEnd + adj).coerceAtMost(editor.fileSize().toInt())
|
val adjEnd = (nativeEnd + adj).coerceAtMost(editor.fileSize().toInt())
|
||||||
return nativeStart to adjEnd
|
return nativeStart to adjEnd
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user