1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-02-27 17:45:59 +01:00

Remove more postMove overrides

This commit is contained in:
Matt Ellis 2022-10-11 20:21:30 +11:00 committed by Alex Pláte
parent a49631e986
commit b5aba454ae
10 changed files with 120 additions and 51 deletions
src/main/java/com/maddyhome/idea/vim
vim-engine/src/main/kotlin/com/maddyhome/idea/vim

View File

@ -43,6 +43,7 @@ import com.maddyhome.idea.vim.newapi.IjVimEditor;
import com.maddyhome.idea.vim.options.LocalOptionChangeListener;
import com.maddyhome.idea.vim.options.OptionConstants;
import com.maddyhome.idea.vim.options.OptionScope;
import com.maddyhome.idea.vim.options.helpers.StrictMode;
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimDataType;
import com.maddyhome.idea.vim.vimscript.model.datatypes.VimInt;
@ -370,7 +371,12 @@ public class MotionGroup extends VimMotionGroupBase {
if (offset < 0 || offset > editor.getDocument().getTextLength() || !caret.isValid()) return;
if (CommandStateHelper.inBlockSubMode(editor)) {
StrictMode.INSTANCE.assertTrue(caret == editor.getCaretModel().getPrimaryCaret(),
"Block selection can only be moved with primary caret!");
// Note that this call replaces ALL carets, so any local caret instances will be invalid!
VisualGroupKt.vimMoveBlockSelectionToOffset(editor, offset);
Caret primaryCaret = editor.getCaretModel().getPrimaryCaret();
UserDataManager.setVimLastColumn(primaryCaret, primaryCaret.getVisualPosition().column);
scrollCaretIntoView(editor);
@ -381,6 +387,11 @@ public class MotionGroup extends VimMotionGroupBase {
// changes in surrounding text, especially with inline inlays.
final int oldOffset = caret.getOffset();
InlayHelperKt.moveToInlayAwareOffset(caret, offset);
// TODO: Remove this. If setting LAST_COLUMN, we have to set it both before and after moveCaret
// But not all handlers set vimLastColumn (e.g. ShiftedArrowKeyHandler). It would be better if all handlers reset
// vimLastColumn so that it is either calculated on demand (current column) or explicitly set by a handler that needs
// special handling
if (oldOffset != offset) {
UserDataManager.setVimLastColumn(caret, InlayHelperKt.getInlayAwareVisualColumn(caret));
}

View File

@ -58,7 +58,9 @@ fun Caret.vimMoveSelectionToCaret() {
/**
* Move selection end to current primary caret position
* This method is created only for block mode
*
* This method is created only for block mode. Note that this method will invalidate all carets!
*
* @see vimMoveSelectionToCaret for character and line selection
*/
fun vimMoveBlockSelectionToOffset(editor: Editor, offset: Int) {
@ -163,11 +165,18 @@ private fun setVisualSelection(selectionStart: Int, selectionEnd: Int, caret: Ca
caret.vimSetSystemSelectionSilently(nativeStart, nativeEnd)
}
VimStateMachine.SubMode.VISUAL_BLOCK -> {
// This will invalidate any secondary carets, but we shouldn't have any of these cached in local variables, etc.
editor.caretModel.removeSecondaryCarets()
// Set system selection
val (blockStart, blockEnd) = blockToNativeSelection(vimEditor, selectionStart, selectionEnd, mode)
val lastColumn = editor.caretModel.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.
vimEditor.vimSetSystemBlockSelectionSilently(blockStart, blockEnd)
// We've just added secondary carets again, hide them to better emulate block selection

View File

@ -31,6 +31,10 @@ import kotlin.reflect.KProperty
* @author Alex Plate
*/
// NOTE: Carets, including the primary caret, can be replaced when the caret is moved in visual block mode.
// No attempt is made to maintain this user data (apart from vimLastColum which is set during caret movement)
// Non-transient data will be lost!
//region Vim selection start ----------------------------------------------------
/**
* Caret's offset when entering visual mode
@ -55,15 +59,18 @@ fun Caret.vimSelectionStartClear() {
private var Caret._vimSelectionStart: Int? by userDataCaretToEditor()
//endregion ----------------------------------------------------
// Last column excluding inlays before the caret
// Last column excluding inlays before the caret. Reset during visual block motion
var Caret.vimLastColumn: Int by userDataCaretToEditorOr { (this as Caret).inlayAwareVisualColumn }
// TODO: Is this a per-caret setting? This data is non-transient, so could be lost during visual block motion
var Caret.vimLastVisualOperatorRange: VisualChange? by userDataCaretToEditor()
// Transient data. Does not need to be restored during visual block motion
var Caret.vimInsertStart: RangeMarker by userDataOr {
(this as Caret).editor.document.createRangeMarker(
this.offset,
this.offset
)
(this as Caret).editor.document.createRangeMarker(this.offset, this.offset)
}
// TODO: Data could be lost during visual block motion
var Caret.registerStorage: CaretRegisterStorageBase? by userDataCaretToEditor()
// ------------------ Editor

View File

@ -12,13 +12,14 @@ import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.Motion
import com.maddyhome.idea.vim.handler.MotionActionHandler
class MotionColumnAction : MotionActionHandler.ForEachCaret() {
override val motionType: MotionType = MotionType.EXCLUSIVE
override fun getOffset(
editor: VimEditor,
caret: VimCaret,
@ -26,17 +27,10 @@ class MotionColumnAction : MotionActionHandler.ForEachCaret() {
argument: Argument?,
operatorArguments: OperatorArguments,
): Motion {
return injector.motion.moveCaretToColumn(editor, caret, operatorArguments.count1 - 1, false)
val motion = injector.motion.moveCaretToColumn(editor, caret, operatorArguments.count1 - 1, false)
return when (motion) {
is Motion.AbsoluteOffset -> Motion.AdjustedOffset(motion.offset, operatorArguments.count1 - 1)
else -> motion
}
}
override fun postMove(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
cmd: Command,
) {
caret.vimLastColumn = cmd.count - 1
}
override val motionType: MotionType = MotionType.EXCLUSIVE
}

View File

@ -20,7 +20,6 @@ import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.Motion
import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.handler.toMotion
import com.maddyhome.idea.vim.helper.enumSetOf
import com.maddyhome.idea.vim.helper.inVisualMode
import com.maddyhome.idea.vim.helper.isEndAllowed
@ -55,11 +54,8 @@ open class MotionLastColumnAction : MotionActionHandler.ForEachCaret() {
if (operatorArguments.isOperatorPending) false else editor.isEndAllowed
}
return injector.motion.moveCaretToRelativeLineEnd(editor, caret, operatorArguments.count1 - 1, allow).toMotion()
}
override fun postMove(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {
caret.vimLastColumn = VimMotionGroupBase.LAST_COLUMN
val offset = injector.motion.moveCaretToRelativeLineEnd(editor, caret, operatorArguments.count1 - 1, allow)
return Motion.AdjustedOffset(offset, VimMotionGroupBase.LAST_COLUMN)
}
override fun preMove(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {

View File

@ -13,7 +13,6 @@ import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimMotionGroupBase
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.handler.Motion
@ -46,16 +45,11 @@ class MotionLastScreenColumnAction : MotionActionHandler.ForEachCaret() {
allow = true
}
}
return injector.motion.moveCaretToCurrentDisplayLineEnd(editor, caret, allow)
}
override fun postMove(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
cmd: Command,
) {
caret.vimLastColumn = VimMotionGroupBase.LAST_COLUMN
val motion = injector.motion.moveCaretToCurrentDisplayLineEnd(editor, caret, allow)
return when (motion) {
is Motion.AbsoluteOffset -> Motion.AdjustedOffset(motion.offset, VimMotionGroupBase.LAST_COLUMN)
else -> motion
}
}
override val motionType: MotionType = MotionType.INCLUSIVE

View File

@ -24,6 +24,28 @@ import com.maddyhome.idea.vim.handler.toMotionOrError
sealed class MotionDownBase : MotionActionHandler.ForEachCaret() {
private var col: Int = 0
final override fun getOffset(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
argument: Argument?,
operatorArguments: OperatorArguments
): Motion {
val motion = getMotion(editor, caret, context, argument, operatorArguments)
return when (motion) {
is Motion.AbsoluteOffset -> Motion.AdjustedOffset(motion.offset, col)
else -> motion
}
}
abstract fun getMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
argument: Argument?,
operatorArguments: OperatorArguments
): Motion
override fun preOffsetComputation(
editor: VimEditor,
caret: VimCaret,
@ -33,17 +55,13 @@ sealed class MotionDownBase : MotionActionHandler.ForEachCaret() {
col = injector.engineEditorHelper.prepareLastColumn(caret)
return true
}
override fun postMove(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {
injector.engineEditorHelper.updateLastColumn(caret, col)
}
}
open class MotionDownAction : MotionDownBase() {
override val motionType: MotionType = MotionType.LINE_WISE
override fun getOffset(
override fun getMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
@ -55,7 +73,7 @@ open class MotionDownAction : MotionDownBase() {
}
class MotionDownCtrlNAction : MotionDownAction() {
override fun getOffset(
override fun getMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
@ -79,7 +97,7 @@ class MotionDownNotLineWiseAction : MotionDownBase() {
override val motionType: MotionType = MotionType.EXCLUSIVE
override fun getOffset(
override fun getMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,

View File

@ -24,6 +24,28 @@ import com.maddyhome.idea.vim.handler.toMotionOrError
sealed class MotionUpBase : MotionActionHandler.ForEachCaret() {
private var col: Int = 0
final override fun getOffset(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
argument: Argument?,
operatorArguments: OperatorArguments
): Motion {
val motion = getMotion(editor, caret, context, argument, operatorArguments)
return when (motion) {
is Motion.AbsoluteOffset -> Motion.AdjustedOffset(motion.offset, col)
else -> motion
}
}
abstract fun getMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
argument: Argument?,
operatorArguments: OperatorArguments
): Motion
override fun preOffsetComputation(
editor: VimEditor,
caret: VimCaret,
@ -33,16 +55,12 @@ sealed class MotionUpBase : MotionActionHandler.ForEachCaret() {
col = injector.engineEditorHelper.prepareLastColumn(caret)
return true
}
override fun postMove(editor: VimEditor, caret: VimCaret, context: ExecutionContext, cmd: Command) {
injector.engineEditorHelper.updateLastColumn(caret, col)
}
}
open class MotionUpAction : MotionUpBase() {
override val motionType: MotionType = MotionType.LINE_WISE
override fun getOffset(
override fun getMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
@ -54,7 +72,7 @@ open class MotionUpAction : MotionUpBase() {
}
class MotionUpCtrlPAction : MotionUpAction() {
override fun getOffset(
override fun getMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,
@ -77,7 +95,7 @@ class MotionUpCtrlPAction : MotionUpAction() {
class MotionUpNotLineWiseAction : MotionUpBase() {
override val motionType: MotionType = MotionType.EXCLUSIVE
override fun getOffset(
override fun getMotion(
editor: VimEditor,
caret: VimCaret,
context: ExecutionContext,

View File

@ -186,10 +186,29 @@ sealed class MotionActionHandler : EditorActionHandlerBase(false) {
cmd: Command,
offset: Motion.AdjustedOffset,
) {
// Block selection mode is emulated with multiple carets, but we only ever operate on the primary caret. Changing
// the selection (by moving the primary caret) can result in IntelliJ invalidating, replacing or adding a new
// primary caret
if (editor.inBlockSubMode) {
StrictMode.assert(caret.isPrimary, "Block selection mode must only operate on primary caret")
}
val normalisedOffset = prepareMoveToAbsoluteOffset(editor, cmd, offset)
StrictMode.assert(normalisedOffset == offset.offset, "Adjusted offset should be normalised by action")
caret.moveToOffset(normalisedOffset)
// Set before moving, so it can be applied during move, especially important for LAST_COLUMN and visual block mode
caret.vimLastColumn = offset.intendedColumn
caret.moveToOffset(normalisedOffset)
// Visual block movement can replace the primary caret when moving the selection up, so reset the last column
if (editor.inBlockSubMode) {
editor.primaryCaret().vimLastColumn = offset.intendedColumn
}
else {
// TODO: Remove this - we've already set it before moving the caret, but currently moving the caret sets last col
caret.vimLastColumn = offset.intendedColumn
}
}
private fun moveToAbsoluteOffset(
@ -209,6 +228,8 @@ sealed class MotionActionHandler : EditorActionHandlerBase(false) {
val normalisedOffset = prepareMoveToAbsoluteOffset(editor, cmd, offset)
preMove(editor, caret, context, cmd)
caret.moveToOffset(normalisedOffset)
// Block selection mode might cause IntelliJ to invalidate/replace/add a new primary caret. Refresh if necessary
val postMoveCaret = if (editor.inBlockSubMode) editor.primaryCaret() else caret
postMove(editor, postMoveCaret, context, cmd)
}

View File

@ -5,6 +5,7 @@ import com.maddyhome.idea.vim.options.OptionConstants
import com.maddyhome.idea.vim.options.OptionScope
object StrictMode {
@JvmName("assertTrue")
fun assert(condition: Boolean, message: String) {
if (!condition) {
fail(message)