in vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/SearchGroup.kt [348:443]
fun findBlockRange(
editor: VimEditor,
caret: ImmutableVimCaret,
type: Char,
count: Int,
isOuter: Boolean,
): TextRange? {
val chars: CharSequence = editor.text()
var pos: Int = caret.offset
var start: Int = caret.selectionStart
var end: Int = caret.selectionEnd
val loc = BLOCK_CHARS.indexOf(type)
val close = BLOCK_CHARS[loc + 1]
// extend the range for blank line after type and before close, as they are excluded when inner match
if (!isOuter) {
if (start > 1 && chars[start - 2] == type && chars[start - 1] == '\n') {
start--
}
if (end < chars.length && chars[end] == '\n') {
var isSingleLineAllWhiteSpaceUntilClose = false
var countWhiteSpaceCharacter = 1
while (end + countWhiteSpaceCharacter < chars.length) {
if (Character.isWhitespace(chars[end + countWhiteSpaceCharacter]) &&
chars[end + countWhiteSpaceCharacter] != '\n'
) {
countWhiteSpaceCharacter++
continue
}
if (chars[end + countWhiteSpaceCharacter] == close) {
isSingleLineAllWhiteSpaceUntilClose = true
}
break
}
if (isSingleLineAllWhiteSpaceUntilClose) {
end += countWhiteSpaceCharacter
}
}
}
var rangeSelection = end - start > 1
if (rangeSelection && start == 0) // early return not only for optimization
{
return null // but also not to break the interval semantic on this edge case (see below)
}
/* In case of successive inner selection. We want to break out of
* the block delimiter of the current inner selection.
* In other terms, for the rest of the algorithm, a previous inner selection of a block
* if equivalent to an outer one. */
/* In case of successive inner selection. We want to break out of
* the block delimiter of the current inner selection.
* In other terms, for the rest of the algorithm, a previous inner selection of a block
* if equivalent to an outer one. */if (!isOuter && start - 1 >= 0 && type == chars[start - 1] && end < chars.length && close == chars[end]) {
start -= 1
pos = start
rangeSelection = true
}
/* when one char is selected, we want to find the enclosing block of (start,end]
* although when a range of characters is selected, we want the enclosing block of [start, end]
* shifting the position allow to express which kind of interval we work on */
/* when one char is selected, we want to find the enclosing block of (start,end]
* although when a range of characters is selected, we want the enclosing block of [start, end]
* shifting the position allow to express which kind of interval we work on */if (rangeSelection) pos =
max(0.0, (start - 1).toDouble()).toInt()
var (blockStart, blockEnd) = findBlock(editor, pos, type, close, count) ?: return null
if (!isOuter) {
blockStart++
// exclude first line break after start for inner match
if (chars[blockStart] == '\n') {
blockStart++
}
val o = editor.getLineStartForOffset(blockEnd)
var allWhite = true
for (i in o until blockEnd) {
if (!Character.isWhitespace(chars[i])) {
allWhite = false
break
}
}
if (allWhite) {
blockEnd = o - 2
} else {
blockEnd--
}
}
// End offset exclusive
return TextRange(blockStart, blockEnd + 1)
}