Skip to content

Add support for moving selected lines up and down - #14121

Open
omentic wants to merge 2 commits into
helix-editor:masterfrom
omentic:move-lines-selection
Open

omentic wants to merge 2 commits into
helix-editor:masterfrom
omentic:move-lines-selection

Conversation

@omentic

@omentic omentic commented Jul 29, 2025

Copy link
Copy Markdown
Contributor

Hi! I'd like to reopen discussion on #2245.

This patch adds support for moving the current line (or some arbitrary selected lines) up and down.

It is a rebased, bugfixed version of #4545. It's bugfixed, stable, behaves predictably with multiple cursors, doesn't have issues with Unicode or with moving before/after a file start/end, etc. I've been running this as a patch on my personal fork for a couple of years now and rely on it heavily.

However, Helix has recently been merging a lot of PRs that I was running the fork for -- so I'm trying to move away from using a fork. I thought I would reopen discussion on upstreaming this because I think it may be useful to others.


There are two lines of argumentation that have been made in the past for keeping this out of Helix core.

This kind of editing breaks with select-then-act.

This is a matter of perspective. Some operations -- like this one -- are inherently line-based. If an operation is inherently line-based, it doesn't operate on the full selection. This still fits into the select-than-act paradigm, in my mind -- your initial selection is the current line, which can be expanded to multiple lines across continuous or discontinuous selections.

It's the same thing as insert_at_line_start, imo.

This can be implemented in terms of existing commands.

It can't. It can be roughly implemented in terms of existing commands -- but only roughly.

Per discussion on #2245 (which has been ongoing -- a testament to this patch's utility, imo) the closest someone's gotten is ["extend_to_line_bounds", "delete_selection", "paste_after", "select_mode", "goto_line_start", "normal_mode"]. But even that doesn't preserve the cursor position after moving lines: which is really important!

Line movement is a core enough utility with enough edge cases that I think it is worth being built in.

@icorbrey

icorbrey commented Jul 30, 2025 •

Copy link
Copy Markdown
Contributor

Would love native support for this, I currently have this roughly sketched out with macros but it would be nice for it not to destroy my selections while doing so:

keys.normal.A-j = "@Xdp"
keys.normal.A-k = "@XdkP"
keys.select.A-j = "@<esc>Xdp"
keys.select.A-k = "@<esc>XdkP"

@omentic
omentic force-pushed the move-lines-selection branch from 7154c0e to 970f5ae Compare July 30, 2025 21:13
@omentic
omentic force-pushed the move-lines-selection branch from 970f5ae to f72aad7 Compare July 30, 2025 21:56
@Axlefublr

Copy link
Copy Markdown
Contributor

trying this pr out and am enjoying it more than I expected to... it's really smooth 😋

@npupko

npupko commented Aug 21, 2025 •

Copy link
Copy Markdown
Contributor

Maybe we should consider making the default keybinds for that as in the vim-unimpaired (link)?

There are already linewise mappings in Helix done the same way: [<Space> and ]<Space> for adding newlines before and after the cursor line.

So, what about making the[e and ]eto exchange the current line with the one above or below the same way?

@Axlefublr

Copy link
Copy Markdown
Contributor

that makes the pr a harder sell to the maintainers, I'd love for this to get merged first and then possibly add mappings :3

@mi2ebi

mi2ebi commented Aug 21, 2025

Copy link
Copy Markdown
Contributor

the pr already has mappings @Axlefublr, <C-j> <C-k>. fwiw i like them as-is

@Axlefublr

Copy link
Copy Markdown
Contributor

dear github,
please add the 🤯 react for this exact usecase.
thank you.

@omentic

omentic commented Aug 29, 2025 •

Copy link
Copy Markdown
Contributor Author

@the-mikedavis I'd like to request a review, if you don't mind. I know you all tend to leave PRs you don't want open so others can pull from them, but I would be interested to hear what you think in response to the points in the description here. I think this is a good fit for a core function of the editor and subsequently a bad fit for bindings / a future plugin.

@the-mikedavis

Copy link
Copy Markdown
Member

In terms of the feature itself I wasn't a fan of it in the past but I understand it's a popular ask. I haven't looked at the code yet but if it can be done in a way that's fairly clean and it's not buggy then I'm not opposed to it

let slice = text.slice(..);
let mut last_step_changes: Vec<ExtendedChange> = vec![];
let mut at_doc_edge = false;
let all_changes = selection.into_iter().map(|range| {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than iterating over all ranges here, Selection::line_ranges might be simpler. It's meant for line-wise stuff like this: it gives an iterator which merges overlapping and adjacent ranges. See its use in :reset-diff-change for an example

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For one test case let's have at least two selections on the same line. I haven't looked through the command's impl fully yet but it's something that might break some assumptions and maybe lead to a panic

iniw added a commit to iniw/helix that referenced this pull request Oct 26, 2025
Add support for moving selected lines up and down

don't trim trailing whitespace in tests lol
iniw added a commit to iniw/helix that referenced this pull request Oct 27, 2025
Add support for moving selected lines up and down

don't trim trailing whitespace in tests lol
iniw added a commit to iniw/helix that referenced this pull request Oct 27, 2025
Add support for moving selected lines up and down

don't trim trailing whitespace in tests lol
iniw pushed a commit to iniw/helix that referenced this pull request Oct 27, 2025
Add support for moving selected lines up and down

don't trim trailing whitespace in tests lol

Co-authored-by: sireliah <sajuukk@gmail.com>
@taylorplewe

Copy link
Copy Markdown

Haven't seen this posted yet; to @the-mikedavis's point about testing multiple cursors on one line, it looks like doing that does indeed mess up the buffer pretty bad. In my vid you can see I have a few cursors on one line, then move the line up a few times, try undoing all the way, and :redrawing, but the buffer is broken beyond repair, I have to close and reopen the buffer to get back to normal.

Maybe @the-mikedavis's suggestion about Selection::line_ranges would fix this?

move_lines_up-multi-cursor-bug.mp4

@nobriot

nobriot commented Oct 28, 2025 •

Copy link
Copy Markdown

Haven't seen this posted yet; to @the-mikedavis's point about testing multiple cursors on one line, it looks like doing that does indeed mess up the buffer pretty bad. In my vid you can see I have a few cursors on one line, then move the line up a few times, try undoing all the way, and :redrawing, but the buffer is broken beyond repair, I have to close and reopen the buffer to get back to normal.

Maybe @the-mikedavis's suggestion about Selection::line_ranges would fix this?

I think my change works with cursors all over the place, if you want to get some inspiration: #14656

iniw pushed a commit to iniw/helix that referenced this pull request Nov 3, 2025
Add support for moving selected lines up and down

don't trim trailing whitespace in tests lol

Co-authored-by: sireliah <sajuukk@gmail.com>
iniw pushed a commit to iniw/helix that referenced this pull request Dec 4, 2025
Add support for moving selected lines up and down

don't trim trailing whitespace in tests lol

Co-authored-by: sireliah <sajuukk@gmail.com>
iniw pushed a commit to iniw/helix that referenced this pull request Dec 4, 2025
Add support for moving selected lines up and down

don't trim trailing whitespace in tests lol

Co-authored-by: sireliah <sajuukk@gmail.com>
iniw pushed a commit to iniw/helix that referenced this pull request Dec 30, 2025
Add support for moving selected lines up and down

don't trim trailing whitespace in tests lol

Co-authored-by: sireliah <sajuukk@gmail.com>
iniw pushed a commit to iniw/helix that referenced this pull request Dec 30, 2025
Add support for moving selected lines up and down

don't trim trailing whitespace in tests lol

Co-authored-by: sireliah <sajuukk@gmail.com>
MyCodeUsuallyWorksOnMyOwnMachine added a commit to MyCodeUsuallyWorksOnMyOwnMachine/helix that referenced this pull request Sep 26, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants