mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-13 06:16:58 +02:00
Define main caret for visual block selection
This commit is contained in:
src/com/maddyhome/idea/vim
action
group
handler
helper
@@ -25,7 +25,6 @@ import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.action.VimCommandAction;
|
||||
import com.maddyhome.idea.vim.command.Command;
|
||||
import com.maddyhome.idea.vim.command.CommandFlags;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.command.MappingMode;
|
||||
import com.maddyhome.idea.vim.command.SelectionType;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
@@ -51,7 +50,7 @@ public class DeleteVisualLinesEndAction extends VimCommandAction {
|
||||
@NotNull DataContext context,
|
||||
@NotNull Command cmd,
|
||||
@NotNull VimSelection range) {
|
||||
if (CommandState.inVisualBlockMode(editor)) {
|
||||
if (range.getType() == SelectionType.BLOCK_WISE) {
|
||||
TextRange vimTextRange = range.toVimTextRange();
|
||||
final int[] starts = vimTextRange.getStartOffsets();
|
||||
final int[] ends = vimTextRange.getEndOffsets();
|
||||
|
@@ -54,18 +54,13 @@ public class MotionDownAction extends MotionEditorAction {
|
||||
int count,
|
||||
int rawCount,
|
||||
@Nullable Argument argument) {
|
||||
Caret lastDownCaret = EditorData.getLastDownCaret(editor);
|
||||
EditorData.setLastDownCaret(editor, caret);
|
||||
if (CommandState.inVisualBlockMode(editor) && EditorData.shouldIgnoreNextMove(editor)) {
|
||||
EditorData.dontIgnoreNextMove(editor);
|
||||
if (lastDownCaret != caret) {
|
||||
return caret.getOffset();
|
||||
}
|
||||
return caret.getOffset();
|
||||
}
|
||||
if (CommandState.inVisualBlockMode(editor)) {
|
||||
Caret primaryCaret = editor.getCaretModel().getPrimaryCaret();
|
||||
int blockEndOffset = CaretDataKt.getVimSelectionStart(primaryCaret);
|
||||
int blockStartOffset = primaryCaret.getOffset();
|
||||
int blockEndOffset = CaretDataKt.getVimSelectionStart(caret);
|
||||
int blockStartOffset = caret.getOffset();
|
||||
VisualPosition blockEndPosition = editor.offsetToVisualPosition(blockEndOffset);
|
||||
VisualPosition blockStartPosition = editor.offsetToVisualPosition(blockStartOffset);
|
||||
if (blockEndPosition.getLine() < blockStartPosition.getLine()) {
|
||||
|
@@ -356,6 +356,15 @@ public class MotionGroup {
|
||||
|
||||
public static void moveCaret(@NotNull Editor editor, @NotNull Caret caret, int offset, boolean forceKeepVisual) {
|
||||
if (offset >= 0 && offset <= editor.getDocument().getTextLength()) {
|
||||
|
||||
if (CommandState.inVisualBlockMode(editor)) {
|
||||
scrollCaretIntoView(editor);
|
||||
UtilsKt.vimMoveBlockSelectionToOffset(editor, offset);
|
||||
Caret blockMainCaret = CaretDataKt.getVimBlockMainCaret(editor);
|
||||
CaretData.setLastColumn(editor, blockMainCaret, blockMainCaret.getVisualPosition().column);
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean keepVisual = forceKeepVisual || keepVisual(editor);
|
||||
if (caret.getOffset() != offset) {
|
||||
caret.moveToOffset(offset);
|
||||
|
@@ -97,8 +97,14 @@ object PutCopyGroup {
|
||||
): Boolean {
|
||||
val res = Ref.create(true)
|
||||
val caret = editor.caretModel.primaryCaret
|
||||
|
||||
val range = selection.toVimTextRange().normalize()
|
||||
val line = if (insertBefore) {
|
||||
editor.offsetToLogicalPosition(range.startOffset).line
|
||||
} else {
|
||||
editor.offsetToLogicalPosition(range.endOffset).line
|
||||
}
|
||||
|
||||
|
||||
VimPlugin.getChange().deleteRange(editor, caret, range, SelectionType.BLOCK_WISE, false)
|
||||
|
||||
val type = register?.type ?: return false
|
||||
@@ -124,9 +130,9 @@ object PutCopyGroup {
|
||||
}
|
||||
SelectionType.LINE_WISE -> {
|
||||
val startOffset = if (insertBefore) {
|
||||
EditorHelper.getLineStartForOffset(editor, range.startOffset)
|
||||
EditorHelper.getLineStartOffset(editor, line)
|
||||
} else {
|
||||
EditorHelper.getLineEndForOffset(editor, range.endOffset)
|
||||
EditorHelper.getLineEndOffset(editor, line, true)
|
||||
}
|
||||
|
||||
var text = register.text ?: run {
|
||||
|
@@ -34,6 +34,7 @@ import com.maddyhome.idea.vim.group.MotionGroup
|
||||
import com.maddyhome.idea.vim.helper.CaretData
|
||||
import com.maddyhome.idea.vim.helper.EditorData
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.vimBlockMainCaretSetToNull
|
||||
import com.maddyhome.idea.vim.helper.vimSelectionStart
|
||||
import com.maddyhome.idea.vim.helper.vimSelectionStartSetToNull
|
||||
import com.maddyhome.idea.vim.helper.vimStartSelectionAtPoint
|
||||
@@ -283,6 +284,7 @@ object VisualMotionGroup {
|
||||
val vimSelectionStart = primaryCaret.vimSelectionStart
|
||||
VimPlugin.getMark().setVisualSelectionMarks(editor, TextRange(vimSelectionStart, primaryCaret.offset))
|
||||
editor.caretModel.allCarets.forEach { it.vimSelectionStartSetToNull() }
|
||||
editor.vimBlockMainCaretSetToNull()
|
||||
|
||||
CommandState.getInstance(editor).subMode = CommandState.SubMode.NONE
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ import com.maddyhome.idea.vim.command.CommandFlags
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.group.MotionGroup
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.vimBlockMainCaret
|
||||
import com.maddyhome.idea.vim.helper.vimSelectionStart
|
||||
|
||||
/**
|
||||
@@ -47,7 +48,10 @@ abstract class MotionEditorActionHandler : EditorActionHandlerBase(false) {
|
||||
final override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
|
||||
val visualBlockActive = CommandState.inVisualBlockMode(editor)
|
||||
|
||||
if (visualBlockActive || editor.caretModel.caretCount == 1 || alwaysBatchExecution) {
|
||||
if (visualBlockActive) {
|
||||
val primaryCaret = editor.vimBlockMainCaret
|
||||
doExecute(editor, primaryCaret, context, cmd)
|
||||
} else if (editor.caretModel.caretCount == 1 || alwaysBatchExecution) {
|
||||
val primaryCaret = editor.caretModel.primaryCaret
|
||||
doExecute(editor, primaryCaret, context, cmd)
|
||||
} else {
|
||||
|
@@ -34,6 +34,7 @@ import com.maddyhome.idea.vim.group.motion.VisualMotionGroup
|
||||
import com.maddyhome.idea.vim.helper.CaretData
|
||||
import com.maddyhome.idea.vim.helper.EditorData
|
||||
import com.maddyhome.idea.vim.helper.VimSelection
|
||||
import com.maddyhome.idea.vim.helper.vimBlockMainCaret
|
||||
import com.maddyhome.idea.vim.helper.vimSelectionStart
|
||||
import com.maddyhome.idea.vim.helper.visualBlockRange
|
||||
|
||||
@@ -68,7 +69,7 @@ abstract class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
|
||||
val res = Ref.create(true)
|
||||
when {
|
||||
selections.keys.isEmpty() -> return false
|
||||
selections.keys.size == 1 -> res.set(executeAction(editor, editor.caretModel.primaryCaret, context, cmd, selections.values.first()))
|
||||
selections.keys.size == 1 -> res.set(executeAction(editor, selections.keys.first(), context, cmd, selections.values.first()))
|
||||
else -> editor.caretModel.runForEachCaret({ caret ->
|
||||
val range = selections.getValue(caret)
|
||||
val loopRes = executeAction(editor, caret, context, cmd, range)
|
||||
@@ -95,8 +96,8 @@ abstract class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
|
||||
|
||||
if (CommandState.inVisualBlockMode(this)) {
|
||||
val adj = if (VisualMotionGroup.exclusiveSelection) 0 else 1
|
||||
val primaryCaret = caretModel.primaryCaret
|
||||
return mapOf(primaryCaret to VimSelection(primaryCaret.vimSelectionStart, primaryCaret.offset + adj, SelectionType.BLOCK_WISE, this))
|
||||
val (start, end) = vimBlockMainCaret.run { if (vimSelectionStart > offset) vimSelectionStart + adj to offset else vimSelectionStart to offset + adj }
|
||||
return mapOf(vimBlockMainCaret to VimSelection(start, end, SelectionType.BLOCK_WISE, this))
|
||||
}
|
||||
|
||||
return this.caretModel.allCarets.associateWith { caret ->
|
||||
|
@@ -59,7 +59,8 @@ public class CaretData {
|
||||
caret.putUserData(LAST_COLUMN, col);
|
||||
}
|
||||
else {
|
||||
editor.getCaretModel().getPrimaryCaret().putUserData(LAST_COLUMN, col);
|
||||
Caret vimBlockMainCaret = CaretDataKt.getVimBlockMainCaret(editor);
|
||||
vimBlockMainCaret.putUserData(LAST_COLUMN, col);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -47,3 +47,17 @@ fun Caret.vimSelectionStartSetToNull() {
|
||||
|
||||
private var Caret._vimSelectionStart: Int? by userData()
|
||||
private var Editor._vimBlockSelectinoStart: Int? by userData()
|
||||
|
||||
|
||||
var Editor.vimBlockMainCaret: Caret
|
||||
get() = _vimBlockMainCaret
|
||||
?: throw AssertionError("Trying to access block main caret, but it's not set")
|
||||
set(value) {
|
||||
_vimBlockMainCaret = value
|
||||
}
|
||||
|
||||
fun Editor.vimBlockMainCaretSetToNull() {
|
||||
this._vimBlockMainCaret = null
|
||||
}
|
||||
|
||||
private var Editor._vimBlockMainCaret: Caret? by userData()
|
||||
|
@@ -270,21 +270,6 @@ public class EditorData {
|
||||
editor.putUserData(WAS_VISUAL_BLOCK_MODE, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last caret used in down movement.
|
||||
*/
|
||||
@Nullable
|
||||
public static Caret getLastDownCaret(@NotNull Editor editor) {
|
||||
return editor.getUserData(LAST_DOWN_CARET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last caret used in down movement.
|
||||
*/
|
||||
public static void setLastDownCaret(@NotNull Editor editor, @NotNull Caret caret) {
|
||||
editor.putUserData(LAST_DOWN_CARET, caret);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a static helper - no instances needed
|
||||
*/
|
||||
@@ -307,7 +292,6 @@ public class EditorData {
|
||||
private static final Key<Boolean> IS_KEEPING_VISUAL_OPERATOR_ACTION = new Key<>("isKeepingVisualOperatorAction");
|
||||
private static final Key<CommandState.Mode> CHANGE_ACTION_SWITCH_MODE = new Key<>("changeActionSwitchMode");
|
||||
private static final Key<Boolean> WAS_VISUAL_BLOCK_MODE = new Key<>("wasVisualBlockMode");
|
||||
private static final Key<Caret> LAST_DOWN_CARET = new Key<>("lastDownCaret");
|
||||
|
||||
private static final Logger logger = Logger.getInstance(EditorData.class.getName());
|
||||
|
||||
|
@@ -38,19 +38,31 @@ val Editor.visualBlockRange: TextRange
|
||||
get() = selectionModel.run { TextRange(blockSelectionStarts, blockSelectionEnds) }
|
||||
|
||||
fun Caret.vimStartSelectionAtPoint(point: Int) {
|
||||
setVisualSelection(point, point, this)
|
||||
vimSelectionStart = point
|
||||
setVisualSelection(point, point, this)
|
||||
}
|
||||
|
||||
fun Caret.vimMoveSelectionToCaret() {
|
||||
if (CommandState.getInstance(editor).mode != CommandState.Mode.VISUAL)
|
||||
throw RuntimeException("Attempt to extent selection in non-visual mode")
|
||||
if (CommandState.inVisualBlockMode(editor))
|
||||
throw RuntimeException("Move caret with [vimMoveBlockSelectionToOffset]")
|
||||
|
||||
val startOffsetMark = vimSelectionStart
|
||||
|
||||
setVisualSelection(startOffsetMark, offset, this)
|
||||
}
|
||||
|
||||
fun vimMoveBlockSelectionToOffset(editor: Editor, offset: Int) {
|
||||
if (!CommandState.inVisualBlockMode(editor))
|
||||
throw RuntimeException("Move caret with [vimMoveSelectionToCaret]")
|
||||
|
||||
val vimBlockMainCaret = editor.vimBlockMainCaret
|
||||
val startOffsetMark = vimBlockMainCaret.vimSelectionStart
|
||||
|
||||
setVisualSelection(startOffsetMark, offset, vimBlockMainCaret)
|
||||
}
|
||||
|
||||
fun Caret.vimUpdateEditorSelection() {
|
||||
val startOffsetMark = vimSelectionStart
|
||||
setVisualSelection(startOffsetMark, offset, this)
|
||||
@@ -73,12 +85,14 @@ private fun setVisualSelection(firstOffset: Int, secondOffset: Int, caret: Caret
|
||||
caret.setSelection(start, adjEnd)
|
||||
}
|
||||
CommandState.SubMode.VISUAL_BLOCK -> {
|
||||
if (caret != editor.caretModel.primaryCaret) return // Block operations are performed only on primary caret
|
||||
|
||||
editor.caretModel.removeSecondaryCarets()
|
||||
|
||||
val blockStart = editor.offsetToLogicalPosition(start)
|
||||
var blockStart = editor.offsetToLogicalPosition(start)
|
||||
var blockEnd = editor.offsetToLogicalPosition(end)
|
||||
if (blockStart.column > blockEnd.column) {
|
||||
// swap variables
|
||||
blockStart = blockEnd.also { blockEnd = blockStart }
|
||||
}
|
||||
if (!VisualMotionGroup.exclusiveSelection) {
|
||||
blockEnd = LogicalPosition(blockEnd.line, blockEnd.column + 1)
|
||||
}
|
||||
@@ -95,6 +109,17 @@ private fun setVisualSelection(firstOffset: Int, secondOffset: Int, caret: Caret
|
||||
aCaret.moveToOffset(aCaret.selectionEnd - 1)
|
||||
}
|
||||
}
|
||||
|
||||
val startLine = editor.offsetToLogicalPosition(caret.vimSelectionStart).line
|
||||
val startColumn = editor.offsetToLogicalPosition(caret.vimSelectionStart).column
|
||||
if (editor.caretModel.allCarets.first().logicalPosition.line == startLine) {
|
||||
editor.vimBlockMainCaret = editor.caretModel.allCarets.last()
|
||||
} else {
|
||||
editor.vimBlockMainCaret = editor.caretModel.allCarets.first()
|
||||
}
|
||||
if (editor.vimBlockMainCaret.logicalPosition.column <= startColumn) {
|
||||
editor.vimBlockMainCaret.moveToOffset(editor.vimBlockMainCaret.selectionStart)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user