helix
helix copied to clipboard
Implement Vim like movement commands
This adds a few new movement commands to allow hjkl
to be rebound so that they follow more the movement of vim. left/right movement cannot move between lines and up/down either follows newline or does not accidentally move onto the newline character. See #4268 for motivation. This however does not emulate vim, it just has some vim inspired movements. In particular it retains the entire behavior of Helix with regards to how newlines are otherwise handled (they stay selectable, and these alternative movement commands to not interfere with other commands in use or change their assumptions).
The following commands are added:
-
move_line_up_anchored
(bind tok
): implements anchored movement up -
move_line_down_anchored
(bind toj
): implements anchored movement down -
move_char_left_same_line
(bind toh
): implements same-line movement left -
move_char_right_same_line
(bind tol
): implements same-line movement right -
extend_line_up_anchored
(bind tok
): implements anchored selection extension up -
extend_line_down_anchored
(bind toj
): implements anchored selection extension down -
extend_char_left_same_line
(bind toh
): implements same-line selection extension left -
extend_char_right_same_line
(bind tol
): implements same-line selection extension right
Bindings
[keys.normal]
j = ["move_line_down_anchored"]
k = ["move_line_up_anchored"]
h = ["move_char_left_same_line"]
l = ["move_char_right_same_line"]
[keys.select]
j = ["extend_line_down_anchored"]
k = ["extend_line_up_anchored"]
h = ["extend_char_left_same_line"]
l = ["extend_char_right_same_line"]
Same Line Movement
The default movement behavior of helix will happily place you in the next line. Vim users are not used to that and are likely to prefer the cursor not to move into the next line (that is at least my expectation). This is what move_char_left_same_line
and move_char_right_same_line
do. They will however let you move to the rightmost character (the newline) which is not what vim does (except of virtualedit=oneline
is used). However this pairs up nicely with the anchored movement (see next).
Anchored Vertical Movements
The two commands move_line_up_anchored
and move_line_down_anchored
implement what I would call anchored movement. If you are on the newline character (eg: one to the right of where goto_line_end
would place you) then as you move up and down you end up on the newline of the target line again. This emulates the behavior of vim of what happens if you move to the line end with $
. If however you are placed to the left of the newline, as you move up and down you will not move onto the newline.
The main difference is that in vim if you use $
and go up/down you stay anchored to the line end. In Helix with this change you need to do gll
to stay anchored to the newline. I do however have to say that I prefer this actually because in Vim you do not actually know if you are newline anchored visually. That is as far as I can tell an internal state.
I played around with this a bit now and I really like the combination of the two. It stays true to the helix character model, but it also feels very natural for vim users.
Limitation: Movements across empty lines are ambiguous. To help with this I put the horizontal column to the largest possible integer on the range to tell these cases apart. From what I can tell this does not create negative impact and avoids extra complication on the range.
Notes on Mixed Use
I think these move/extend commands are interesting to use independent of each other. For instance I find the anchored newline movement functionality useful even if one does not like restricting the movements within a line. For instance line based selection works really nice as once the visual selection is on the newline, any further moving down will continue to capture entire lines. That means for instance vx
will continue to move downwards which I think also addresses some points of #1638 as it basically keeps you in line mode for as long as you don't move left.
Newlines being selectable like any other character is intended behavior and I don't like adding options or alternate movement commands to skirt them. See https://github.com/helix-editor/helix/issues/2956 and https://github.com/helix-editor/helix/issues/3076.
@the-mikedavis this does not change the behavior about select-ability. They are still selectable as before. The new commands add a way that for horizontal movements you can stay constrained within the line but you still move onto them and for vertical movement it even anchors you onto them if you already are on one.
Just to be clear since maybe my comment on the other issue was misleading: i think the visible newlines are a good thing and I wouldn't want to change this. This is also not changing the behavior of helix to bring in vim semantics that do not feel native. The movements here are vim inspired, but they are actually following the helix model. Rather than using hidden internal flags for the newline behavior, this actually takes advantage of the visible and selectable newline instead.
(Also fixes https://github.com/helix-editor/helix/issues/768)
Curious about what the odds are of this merging. I'm happy to incorporate necessary changes if there is something I can do here.
An open question I have is how this should be documented best. Is there a place to document unmapped commands that do not have a typable alias?
Not currently, see: https://github.com/helix-editor/helix/pull/997
Then I suppose this is ready.
Sorry for the delay, this looks fine to me but needs a rebase now that the soft wrap changes landed!
The rebase will be slightly more challenging than usual because the vertical movement commands were essentially reimplemented in #5420. It would also be good to have a version that does visual line movement and document line movement like we have for the normal movement command
I will update this. Unfortunately the changes are a bit more involved so it will take a bit until I find the time for it.
i was also thinking about this topic the other day - that why don't n/vi/m stop the horizontal movement at line ends and don't wrap it around - is there any benefit to that way while editing etc...
I think a better way to configure it is to add all of those binding into one option.
I would definitely like to still see this feature. Unfortunately, this branch is quite stale as the refactor in #5420 entirely changed how movement works so somebody would need to start almost from scratch. Thank you for contributing!