Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix edge cases for H, L and M and introduce 'startofline' option #275

Merged
merged 8 commits into from
Mar 3, 2021
6 changes: 4 additions & 2 deletions resources/META-INF/includes/VimActions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,10 @@
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionJumpNextAction" mappingModes="N" keys="«C-I»,«Tab»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.mark.MotionJumpPreviousAction" mappingModes="N" keys="«C-O»,«C-T»"/>
<!-- Screen -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionFirstScreenLineAction" mappingModes="NXO" keys="H"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionLastScreenLineAction" mappingModes="NXO" keys="L"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionFirstScreenLineAction" mappingModes="NX" keys="H"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionOpPendingFirstScreenLineAction" mappingModes="O" keys="H"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionLastScreenLineAction" mappingModes="NX" keys="L"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionOpPendingLastScreenLineAction" mappingModes="O" keys="L"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.screen.MotionMiddleScreenLineAction" mappingModes="NXO" keys="M"/>
<!-- Scroll -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollFirstScreenLinePageStartAction" mappingModes="NXO" keys="z+"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,24 @@ import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*

class MotionFirstScreenLineAction : MotionActionHandler.ForEachCaret() {
/*
*H*
H To line [count] from top (Home) of window (default:
first line on the window) on the first non-blank
character |linewise|. See also 'startofline' option.
Cursor is adjusted for 'scrolloff' option, unless an
operator is pending, in which case the text may
scroll. E.g. "yH" yanks from the first visible line
until the cursor line (inclusive).
*/
abstract class MotionFirstScreenLineActionBase(private val operatorPending: Boolean) : MotionActionHandler.ForEachCaret() {
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_JUMP)

override val motionType: MotionType = MotionType.LINE_WISE
Expand All @@ -41,6 +52,20 @@ class MotionFirstScreenLineAction : MotionActionHandler.ForEachCaret() {
rawCount: Int,
argument: Argument?
): Int {
return VimPlugin.getMotion().moveCaretToFirstScreenLine(editor, count)

// Only apply scrolloff for NX motions. For op pending, use the actual first line and apply scrolloff after.
// E.g. yH will yank from first visible line to current line, but it also moves the caret to the first visible line.
// This is inside scrolloff, so Vim scrolls
return VimPlugin.getMotion().moveCaretToFirstScreenLine(editor, caret, count, !operatorPending)
}

override fun postMove(editor: Editor, caret: Caret, context: DataContext, cmd: Command) {
if (operatorPending) {
// Convert current caret line from a 0-based logical line to a 1-based logical line
VimPlugin.getMotion().scrollLineToFirstScreenLine(editor, caret.logicalPosition.line + 1, false)
}
}
}

class MotionFirstScreenLineAction : MotionFirstScreenLineActionBase(false)
class MotionOpPendingFirstScreenLineAction : MotionFirstScreenLineActionBase(true)
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,24 @@ import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*

class MotionLastScreenLineAction : MotionActionHandler.ForEachCaret() {
/*
*L*
L To line [count] from bottom of window (default: Last
line on the window) on the first non-blank character
|linewise|. See also 'startofline' option.
Cursor is adjusted for 'scrolloff' option, unless an
operator is pending, in which case the text may
scroll. E.g. "yL" yanks from the cursor to the last
visible line.
*/
abstract class MotionLastScreenLineActionBase(private val operatorPending: Boolean) : MotionActionHandler.ForEachCaret() {
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_JUMP)

override val motionType: MotionType = MotionType.LINE_WISE
Expand All @@ -41,6 +52,16 @@ class MotionLastScreenLineAction : MotionActionHandler.ForEachCaret() {
rawCount: Int,
argument: Argument?
): Int {
return VimPlugin.getMotion().moveCaretToLastScreenLine(editor, count)
return VimPlugin.getMotion().moveCaretToLastScreenLine(editor, caret, count, !operatorPending)
}

override fun postMove(editor: Editor, caret: Caret, context: DataContext, cmd: Command) {
if (operatorPending) {
// Convert current caret line from a 0-based logical line to a 1-based logical line
VimPlugin.getMotion().scrollLineToFirstScreenLine(editor, caret.logicalPosition.line + 1, false)
}
}
}

class MotionLastScreenLineAction : MotionLastScreenLineActionBase(false)
class MotionOpPendingLastScreenLineAction : MotionLastScreenLineActionBase(true)
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*

/*
*M*
M To Middle line of window, on the first non-blank
character |linewise|. See also 'startofline' option.
*/
class MotionMiddleScreenLineAction : MotionActionHandler.ForEachCaret() {
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_JUMP)

Expand All @@ -41,6 +46,6 @@ class MotionMiddleScreenLineAction : MotionActionHandler.ForEachCaret() {
rawCount: Int,
argument: Argument?
): Int {
return VimPlugin.getMotion().moveCaretToMiddleScreenLine(editor)
return VimPlugin.getMotion().moveCaretToMiddleScreenLine(editor, caret)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ class MotionScrollHalfPageDownAction : VimActionHandler.SingleExecution() {
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_IGNORE_SCROLL_JUMP)

override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
return VimPlugin.getMotion().scrollScreen(editor, cmd.rawCount, true)
return VimPlugin.getMotion().scrollScreen(editor, editor.caretModel.primaryCaret, cmd.rawCount, true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ class MotionScrollHalfPageUpAction : VimActionHandler.SingleExecution() {
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_IGNORE_SCROLL_JUMP)

override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
return VimPlugin.getMotion().scrollScreen(editor, cmd.rawCount, false)
return VimPlugin.getMotion().scrollScreen(editor, editor.caretModel.primaryCaret, cmd.rawCount, false)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class MotionScrollPageDownAction : VimActionHandler.SingleExecution() {
override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_IGNORE_SCROLL_JUMP)

override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
return VimPlugin.getMotion().scrollFullPage(editor, cmd.count)
return VimPlugin.getMotion().scrollFullPage(editor, editor.caretModel.primaryCaret, cmd.count)
}
}

Expand All @@ -54,6 +54,6 @@ class MotionScrollPageDownInsertModeAction : VimActionHandler.SingleExecution(),
override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_IGNORE_SCROLL_JUMP, FLAG_CLEAR_STROKES)

override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
return VimPlugin.getMotion().scrollFullPage(editor, cmd.count)
return VimPlugin.getMotion().scrollFullPage(editor, editor.caretModel.primaryCaret, cmd.count)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class MotionScrollPageUpAction : VimActionHandler.SingleExecution() {
override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_IGNORE_SCROLL_JUMP)

override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
return VimPlugin.getMotion().scrollFullPage(editor, -cmd.count)
return VimPlugin.getMotion().scrollFullPage(editor, editor.caretModel.primaryCaret, -cmd.count)
}
}

Expand All @@ -54,6 +54,6 @@ class MotionScrollPageUpInsertModeAction : VimActionHandler.SingleExecution(), C
override val flags: EnumSet<CommandFlags> = enumSetOf(FLAG_IGNORE_SCROLL_JUMP, FLAG_CLEAR_STROKES)

override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
return VimPlugin.getMotion().scrollFullPage(editor, -cmd.count)
return VimPlugin.getMotion().scrollFullPage(editor, editor.caretModel.primaryCaret, -cmd.count)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*

Expand All @@ -42,7 +43,8 @@ class MotionGotoLineFirstAction : MotionActionHandler.ForEachCaret() {
rawCount: Int,
argument: Argument?
): Int {
return VimPlugin.getMotion().moveCaretGotoLineFirst(editor, count - 1)
val line = EditorHelper.normalizeLine(editor, count - 1)
return VimPlugin.getMotion().moveCaretToLineWithStartOfLineOption(editor, line, caret)
}
}

Expand All @@ -59,6 +61,7 @@ class MotionGotoLineFirstInsertAction : MotionActionHandler.ForEachCaret() {
rawCount: Int,
argument: Argument?
): Int {
return VimPlugin.getMotion().moveCaretGotoLineFirst(editor, count - 1)
val line = EditorHelper.normalizeLine(editor, count - 1)
return VimPlugin.getMotion().moveCaretToLineStart(editor, line)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.*

Expand All @@ -41,6 +42,14 @@ class MotionGotoLineLastAction : MotionActionHandler.ForEachCaret() {
rawCount: Int,
argument: Argument?
): Int {
return VimPlugin.getMotion().moveCaretGotoLineLast(editor, rawCount)
val line = EditorHelper.normalizeLine(
editor,
if (rawCount == 0) {
EditorHelper.getLineCount(editor) - 1
} else {
rawCount - 1
}
)
return VimPlugin.getMotion().moveCaretToLineWithStartOfLineOption(editor, line, caret)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class MotionPercentOrMatchAction : MotionActionHandler.ForEachCaret() {
return if (rawCount == 0) {
VimPlugin.getMotion().moveCaretToMatchingPair(editor, caret)
} else {
VimPlugin.getMotion().moveCaretToLinePercent(editor, count)
VimPlugin.getMotion().moveCaretToLinePercent(editor, caret, count)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ class MotionShiftDownAction : ShiftedArrowKeyHandler() {
}

override fun motionWithoutKeyModel(editor: Editor, context: DataContext, cmd: Command) {
VimPlugin.getMotion().scrollFullPage(editor, cmd.count)
VimPlugin.getMotion().scrollFullPage(editor, editor.caretModel.primaryCaret, cmd.count)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ class MotionShiftUpAction : ShiftedArrowKeyHandler() {
}

override fun motionWithoutKeyModel(editor: Editor, context: DataContext, cmd: Command) {
VimPlugin.getMotion().scrollFullPage(editor, -cmd.count)
VimPlugin.getMotion().scrollFullPage(editor, editor.caretModel.primaryCaret, -cmd.count)
}
}
10 changes: 5 additions & 5 deletions src/com/maddyhome/idea/vim/ex/CommandParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ object CommandParser {
val argument = StringBuilder() // The command's argument(s)
var location: StringBuffer? = null // The current range text
var offsetSign = 1 // Sign of current range offset
var offsetNumber = 0 // The value of the current range offset
var offsetNumber = -1 // The value of the current range offset
var offsetTotal = 0 // The sum of all the current range offsets
var move = false // , vs. ; separated ranges (true=; false=,)
var patternType = 0.toChar() // ? or /
Expand Down Expand Up @@ -251,7 +251,7 @@ object CommandParser {
State.RANGE -> {
location = StringBuffer()
offsetTotal = 0
offsetNumber = 0
offsetNumber = -1
move = false
if (ch in '0'..'9') {
state = State.RANGE_LINE
Expand Down Expand Up @@ -401,7 +401,7 @@ object CommandParser {
}
State.RANGE_OFFSET -> {
// Figure out the sign of the offset and reset the offset value
offsetNumber = 0
offsetNumber = -1
if (ch == '+') {
offsetSign = 1
} else if (ch == '-') {
Expand All @@ -418,7 +418,7 @@ object CommandParser {
}
State.RANGE_OFFSET_DONE -> {
// No number implies a one
if (offsetNumber == 0) {
if (offsetNumber == -1) {
offsetNumber = 1
}
// Update offset total for this range
Expand All @@ -433,7 +433,7 @@ object CommandParser {
}
State.RANGE_OFFSET_NUM -> // Update the value of the current offset
if (ch in '0'..'9') {
offsetNumber = offsetNumber * 10 + (ch - '0')
offsetNumber = (if (offsetNumber == -1) 0 else offsetNumber) * 10 + (ch - '0')
state = State.RANGE_OFFSET_MAYBE_DONE
reprocess = false
} else if (ch == '+' || ch == '-') {
Expand Down
3 changes: 2 additions & 1 deletion src/com/maddyhome/idea/vim/ex/handler/GotoLineHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ class GotoLineHandler : CommandHandler.ForEachCaret() {
val line = min(cmd.getLine(editor, caret), EditorHelper.getLineCount(editor) - 1)

if (line >= 0) {
MotionGroup.moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, line))
val offset = VimPlugin.getMotion().moveCaretToLineWithStartOfLineOption(editor, line, caret)
MotionGroup.moveCaret(editor, caret, offset)
return true
}

Expand Down
2 changes: 1 addition & 1 deletion src/com/maddyhome/idea/vim/ex/handler/RepeatHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class RepeatHandler : CommandHandler.ForEachCaret() {
MotionGroup.moveCaret(
editor,
caret,
VimPlugin.getMotion().moveCaretToLine(editor, line, editor.caretModel.primaryCaret)
VimPlugin.getMotion().moveCaretToLineWithSameColumn(editor, line, editor.caretModel.primaryCaret)
)

if (arg == ':') {
Expand Down
4 changes: 2 additions & 2 deletions src/com/maddyhome/idea/vim/ex/ranges/Ranges.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class Ranges {
if (range.isMove) {
MotionGroup.moveCaret(
editor, editor.caretModel.primaryCaret,
VimPlugin.getMotion().moveCaretToLine(editor, endLine, editor.caretModel.primaryCaret)
VimPlugin.getMotion().moveCaretToLineWithSameColumn(editor, endLine, editor.caretModel.primaryCaret)
)
}
// Did that last range represent the start of the file?
Expand All @@ -177,7 +177,7 @@ class Ranges {
if (range.isMove) MotionGroup.moveCaret(
editor,
caret,
VimPlugin.getMotion().moveCaretToLine(editor, endLine, editor.caretModel.primaryCaret)
VimPlugin.getMotion().moveCaretToLineWithSameColumn(editor, endLine, editor.caretModel.primaryCaret)
)
lastZero = endLine < 0
++count
Expand Down
10 changes: 8 additions & 2 deletions src/com/maddyhome/idea/vim/group/ChangeGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,9 @@ public boolean deleteRange(@NotNull Editor editor,
@Nullable SelectionType type,
boolean isChange) {

// Update the last column before we delete, or we might be retrieving the data for a line that no longer exists
UserDataManager.setVimLastColumn(caret, InlayHelperKt.getInlayAwareVisualColumn(caret));

boolean removeLastNewLine = removeLastNewLine(editor, range, type);
final boolean res = deleteText(editor, range, type);
if (removeLastNewLine) {
Expand All @@ -1150,7 +1153,7 @@ public boolean deleteRange(@NotNull Editor editor,
if (res) {
int pos = EditorHelper.normalizeOffset(editor, range.getStartOffset(), isChange);
if (type == SelectionType.LINE_WISE) {
pos = VimPlugin.getMotion().moveCaretToLineStart(editor, editor.offsetToLogicalPosition(pos).line);
pos = VimPlugin.getMotion().moveCaretToLineWithStartOfLineOption(editor, editor.offsetToLogicalPosition(pos).line, caret);
}
MotionGroup.moveCaret(editor, caret, pos);
}
Expand Down Expand Up @@ -1649,6 +1652,9 @@ public void indentRange(@NotNull Editor editor,
logger.debug("count=" + count);
}

// Update the last column before we indent, or we might be retrieving the data for a line that no longer exists
UserDataManager.setVimLastColumn(caret, InlayHelperKt.getInlayAwareVisualColumn(caret));

IndentConfig indentConfig = IndentConfig.create(editor, context);

final int sline = editor.offsetToLogicalPosition(range.getStartOffset()).line;
Expand Down Expand Up @@ -1710,7 +1716,7 @@ public void indentRange(@NotNull Editor editor,

if (!CommandStateHelper.inInsertMode(editor)) {
if (!range.isMultiple()) {
MotionGroup.moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, sline));
MotionGroup.moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLineWithStartOfLineOption(editor, sline, caret));
}
else {
MotionGroup.moveCaret(editor, caret, range.getStartOffset());
Expand Down
Loading