in vim-engine/src/main/kotlin/com/maddyhome/idea/vim/group/visual/EngineVisualGroup.kt [28:93]
fun setVisualSelection(selectionStart: Int, selectionEnd: Int, caret: VimCaret) {
val (start, end) = if (selectionStart > selectionEnd) selectionEnd to selectionStart else selectionStart to selectionEnd
val editor = caret.editor
val selectionType = editor.mode.selectionType ?: SelectionType.CHARACTER_WISE
val mode = editor.mode
when (selectionType) {
SelectionType.CHARACTER_WISE -> {
val (nativeStart, nativeEnd) = charToNativeSelection(editor, start, end, mode)
caret.vimSetSystemSelectionSilently(nativeStart, nativeEnd)
}
SelectionType.LINE_WISE -> {
val (nativeStart, nativeEnd) = lineToNativeSelection(editor, start, end)
caret.vimSetSystemSelectionSilently(nativeStart, nativeEnd)
}
SelectionType.BLOCK_WISE -> {
// This will invalidate any secondary carets, but we shouldn't have any of these cached in local variables, etc.
editor.removeSecondaryCarets()
// Set system selection
val (blockStart, blockEnd) = blockToNativeSelection(editor, selectionStart, selectionEnd, mode)
val lastColumn = editor.primaryCaret().vimLastColumn
// WARNING! This can invalidate the primary caret! I.e. the `caret` parameter will no longer be the primary caret.
// Given an existing visual block selection, moving the caret will first remove all secondary carets (above) then
// this method will ask IntelliJ to create a new multi-caret block selection. If we're moving up (`k`) a new caret
// is added, and becomes the new primary caret. The current `caret` parameter remains valid, but is no longer the
// primary caret. Make sure to fetch the new primary caret if necessary.
editor.vimSetSystemBlockSelectionSilently(blockStart, blockEnd)
// We've just added secondary carets again, hide them to better emulate block selection
injector.editorGroup.updateCaretsVisualAttributes(editor)
for (aCaret in editor.nativeCarets()) {
if (!aCaret.isValid) continue
val line = aCaret.getBufferPosition().line
val lineEndOffset = editor.getLineEndOffset(line, true)
val lineStartOffset = editor.getLineStartOffset(line)
// Extend selection to line end if it was made with `$` command
if (lastColumn >= VimMotionGroupBase.LAST_COLUMN) {
aCaret.vimSetSystemSelectionSilently(aCaret.selectionStart, lineEndOffset)
val newOffset = (lineEndOffset - injector.visualMotionGroup.selectionAdj).coerceAtLeast(lineStartOffset)
aCaret.moveToInlayAwareOffset(newOffset)
}
val visualPosition = editor.offsetToVisualPosition(aCaret.selectionEnd)
if (aCaret.offset == aCaret.selectionEnd && visualPosition != aCaret.getVisualPosition()) {
// Put right caret position for tab character
aCaret.moveToVisualPosition(visualPosition)
}
if (mode !is Mode.SELECT &&
!editor.isLineEmpty(line, false) &&
aCaret.offset == aCaret.selectionEnd &&
aCaret.selectionEnd - 1 >= lineStartOffset &&
aCaret.selectionEnd - aCaret.selectionStart != 0
) {
// Move all carets one char left in case if it's on selection end
aCaret.moveToVisualPosition(VimVisualPosition(visualPosition.line, visualPosition.column - 1))
}
}
editor.primaryCaret().moveToInlayAwareOffset(selectionEnd)
}
}
}