fun findBlockRange()

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)
}