Skip to content

Feature: Helix mode#960

Draft
schlich wants to merge 22 commits intonushell:mainfrom
schlich:schlich/helix-mode
Draft

Feature: Helix mode#960
schlich wants to merge 22 commits intonushell:mainfrom
schlich:schlich/helix-mode

Conversation

@schlich
Copy link
Contributor

@schlich schlich commented Oct 10, 2025

Edit 2/17/2026: PR in progress!!! See comments below

@fdncred
Copy link
Contributor

fdncred commented Oct 10, 2025

whoa. helix mode sounds cool. thanks. this will be interesting to play around with.

@fdncred
Copy link
Contributor

fdncred commented Oct 11, 2025

the ci needs to be green to proceed. i think these are just formatting cargo fmt --all

@fdncred
Copy link
Contributor

fdncred commented Oct 11, 2025

The manual test sequence is helpful for those who don't use helix. However, on my mac, it doesn't work precisely as you've laid out in those bullets. I'm not sure if they're just wrong or if it's a bug.

@schlich
Copy link
Contributor Author

schlich commented Oct 11, 2025

I'll check it out! I've def been testing on windows layout so it's likely something I missed. That documentation there also might have gotten out of sequence in editing too. Good reminder to make sure the keyboard layout is consistent either way

@schlich schlich marked this pull request as draft October 12, 2025 21:59
@schlich schlich marked this pull request as ready for review October 12, 2025 23:56
@schlich
Copy link
Contributor Author

schlich commented Oct 12, 2025

@fdncred if you want to kick off CI again, i was able to fix a few bugs and think it's working pretty smoothly, ran cargo test and cargo fmt --all locally, hopefully it will pass this time, not sure how the spell checker works though

@schlich
Copy link
Contributor Author

schlich commented Oct 23, 2025

This is getting pretty close to complete w/comprehensive tests for the commands listed, but i'm gonna mark this as draft temporarily since i want to try to reconcile upstream Helix's architecture a little more closely with reedline's interface... hopefully it will reduce the footprint of this PR.

@schlich schlich marked this pull request as draft October 23, 2025 06:52
@freepicheep
Copy link

Are you planning on moving forward with this at all? I'd be willing to help, although my rust knowledge is very limited.

@schlich
Copy link
Contributor Author

schlich commented Dec 1, 2025

Hi, I am although I haven't had too much free time for open source lately. If youd like to pick it up I think right now it would just be helpful to try the demo and double check feature parity. I'm a total rust noob too so any refactoring that's more rust-idiomatic or reduces the size of the PR would definitely be welcome. Let me know if there's anything else I can help with, I'm gonna try to use some of the holiday season to catch up on things

@adaschma
Copy link
Contributor

adaschma commented Jan 18, 2026

I've just tested it. Things that work/don't:

  • everything in the tutorial
  • Mode switching:
    • v - Toggle select mode
    • i/a - Enter insert at cursor/after cursor
    • I/A - Enter insert at line start / after line end
  • Character motions (extend selection):
    • h/l - Move left/right with selection
  • Word motions (extend selection):
    • w - Next word start
    • b - Previous word start
    • e - Word end
    • W - Next WORD start (whitespace-delimited)
    • B - Previous WORD start
    • E - WORD end
  • Line motions (extend selection):
    • 0 - Line start -- 1. doesn't work; 2. this is not how helix does it. It is gh. And helix does not extend selection
    • $ - Line end -- 1. doesn't work; 2. this is not how helix does it. It is gl. And helix does not extend selection.
  • Find/till motions (extend selection):
    • f{char} - Find next occurrence of character
    • t{char} - Till next occurrence (stop before)
    • F{char} - Find previous occurrence
    • T{char} - Till previous occurrence (stop after)
  • Selection commands:
    • x - Select entire line
    • ; - Collapse selection to cursor; BUG: moves cursor to the left
    • Alt+; - Swap cursor and anchor (flip selection)
  • Edit commands:
    • d - Delete selection; BUG: doesn't delete single char; larger selections work
    • c - Change selection (delete and enter insert); BUG: doesn't delete single char; larger selections work
    • y - Yank/copy selection; BUG: doesn't delete single char; larger selections work
    • p - Paste after cursor
    • P - Paste before cursor
  • Other:
    • Enter - Accept/submit line
    • Ctrl+C - Exit
    • Ctrl+D - abort
  • Select Mode
    • v or Esc - Exit select mode (clear selection)
    • All motion keys work the same as Normal mode
    • i/a - Exit select and enter insert mode
    • I/A - Exit select and enter insert mode
    • d/c/y - Edit commands work the same
    • p - Edit commands work the same; BUG replaces selection - that should be done by R. p should just add text
  • Insert Mode
    • All printable characters - Insert text
    • Esc - Return to normal mode (cursor moves left, vi-style); DOCUMENTATION BUG Esc doesn't move cursor, however helix doesn't either
    • Backspace - Delete previous character
    • Enter - Accept/submit line
    • Ctrl+C - Exit
    • Ctrl+D - Exit/abort

Thank you for working on this. Looking forward to having helix mode on nushell some day :-). I too always wish I would spend more time to work on open source.

@kronberger-droid
Copy link

Hey there, I have been dreaming of a helix mode in nushell for a long time now. There is the possibility to use ctrl-o to open helix and edit the prompt, but it does not feel natural.
I have been looking for projects to contribute to and this feature is close to my heart. I would start with some of the things mentioned in @adaschma's post.
@schlich @freepicheep are any of you already working on some of those?

@freepicheep
Copy link

@kronberger-droid
I would love if you work on this. I haven't been able to do anything with it. Thanks for checking.

@schlich
Copy link
Contributor Author

schlich commented Feb 12, 2026

I have some time to revisit this. I will see what to-dos are left before this might be merge ready and will let the thread know if I find areas to help!

@schlich
Copy link
Contributor Author

schlich commented Feb 13, 2026

OK, i've made significantly more process but i feel like i'm running in circles with the w/b logic. The abstraction logic is a little odd in terms of where the word/whitespace boundaries are defined, e.g. the move_word functions defined here

i believe theres some underspecified and overabstracted definitions here; What we need to do is settle on/reconcile definitions of the selection and cursor indices. helix defines its model with indexes for an "anchor" and a "cursor" with the selection being defined as the characters between these indexes. I'm running into limitations here both in terms of how vi handles these boundaries and my Rust knowledge in terms of what an appropriate abstraction might look like (e.g. passing "select" as a bool to these methods seems a little out of place here).

After we resolve this i think this should be ready to merge. If anyone wants to propose a way forward that would be great! The cleaner the better, if we can brute force a solution without changing the abstrations thats fine; but i need to take a break after trying that approach for a bit. If someone can propose (and we can agree on) a good abstraction model that reconciles the two modes I think that's the ideal path forward.

edit: a good entrypoint to the issue would be the cursor position for "w" when there are multiple space between words. I tried mapping this to EditCommand::MoveWordRight after trying EditCommand::MoveWordRightStart incorrectly placed the cursor at the beginning of the next word instead of the space right before it (latter is correct behavior for Helix, presumably not for vi)

@kronberger-droid
Copy link

I've been looking into this a little bit.

The w/W problem comes down to none of the existing word_*_index functions returning the right position for Helix -- it needs the last char before the next word, not the first char of it. Reusing a lot of Vi's motions makes sense since they work for most cases, but long term it could make sense to replicate helix's semantics a little more closely.

It would mean new EditCommand variants in the core, but we touch core anyways so why not make changes which are easier to reason about.

I am thinking about adding two commands:

  • SetAnchor: sets selection_anchor = Some(cursor_pos). The difference between normal and select mode would be whether this precedes the motion or not.
  • MoveCursor(MotionTarget): pure cursor move, no selection side effects. This could include at first just MotionTargets to fix the w/W position, but could be extended in the future to make helix mode more independent.

This would give Helix its own motion path with no dependency on select: bool, which would align with #1028 point 2, as I understood it.

@fdncred
Copy link
Contributor

fdncred commented Feb 14, 2026

In my perfect world we'd find a crate the implements well known emacs, vim, and kakoune/helix keybindings and implement that crate and let it do the heavy lifting. Anyone know of such a crate or even multiple crates? I know about modalkit for vim.

@schlich
Copy link
Contributor Author

schlich commented Feb 14, 2026

@fdncred I know we've talked about using modalkit for this feature before. i think now that the work is a little more scoped it's probably a good idea to revisit.

I believe ratatui uses crossterm for this kind of stuff, think it could work? my initial thought is that it might be more designed for a terminal app than a shell app but initially it seems like it has at least a superset of the functionality we want. i'd probably lean towards modalkit for that reason

edit: interestingly, there's a ratatui-modalkit project that combines the approaches; seems liike modalkit might be the way to go

@fdncred
Copy link
Contributor

fdncred commented Feb 14, 2026

I could be wrong because I haven't studied it deeply but I thought modalkit was only for vim. Do you think it could work for helix as well?

Ratatui definitely can use crossterm as a backend, we use it in nushell commands like explore, explore regex and explore config and clearly reedline uses crossterm so clearly there is some overlap. Of course, we don't want to turn reedline into a tui but since it already uses crossterm there should be a way to make it work, but I'm just guessing.

@schlich
Copy link
Contributor Author

schlich commented Feb 14, 2026

modalkit has built-in configurations for vim and emacs but its main modules are the lower level primitives we want. maybe at some point we can even push a custom helix configuration upstream

@kronberger-droid
Copy link

What is the plan now?

I figure the semantic changes I suggested are something we can work on in the future since it is quite overkill for only solving the w/W motions. Since we could solve the problem just by keeping the semantics the same and adding two entries to EditCommand:

  • MoveWordRightGapEnd { select: bool }
  • MoveBigWordRightGapEnd { select: bool }
    Plus the two new LineBuffer methods.

Am I seeing this right that b/B are also still off by one? Since the selection does not include the current cursor position, which is the case in helix.

I also found a small bug which happens for p/P where the char under the cursor gets replaced instead of paste before or after cursor. This would need to be fixed also.

Otherwise it feels like there are enough stable features to start maybe going over the code to look if we can find some things which might be able to be refactored like @schlich mentioned before.

@schlich
Copy link
Contributor Author

schlich commented Feb 14, 2026

Excellent suggestions. Perhaps the new plan could just be to essentially start from a fresh branch of main and do small red/green/refactor cycles for review, that way we can get this feature out and accomplish the refactoring in parallel

@kronberger-droid
Copy link

Should we try to fix the last bugs on here, or go forward with your plan? @schlich

@schlich
Copy link
Contributor Author

schlich commented Feb 15, 2026

If you can submit a patch to get things to work go for it! I can work on tidying things up before review. Should have time in the next few days.

@schlich
Copy link
Contributor Author

schlich commented Feb 17, 2026

alrighty, i just pushed up a total rework focused on a 100% implementation of helix mode based on modalkit's primitives. it took me a while to figure out some patterns and i still dont think i'm all the way there, but this feels a lot more sustainable and workable, with the bonus that it's not going to rely on any vi/emacs code so we can fully gate it on the feature flag. It's regressed on some of the implementation technically but i'm gonna try to get at least the common normal/visual mode and mode switching actions done before i take a break.

This has been a great project for learning me some Rust!

@freepicheep
Copy link

I would love to test this, but I'm not sure how to enable the helix mode when building (I'm not very familiar with Cargo). Any advice?

@fdncred
Copy link
Contributor

fdncred commented Feb 17, 2026

I'd like to test it too. You'd have to compile with --features=hx but that won't get you all the way to testing. We'd need a new example, or augment of the existing examples like demo.rs.

@schlich
Copy link
Contributor Author

schlich commented Feb 17, 2026

I'm working on a demo, but as-is it's gonna require a patch to the w/b logic to be any use, even with modalkits the logic is still proving tricky. Should be able to get a demo up this afternoon though

@kronberger-droid
Copy link

kronberger-droid commented Feb 17, 2026

I can handle the word motions, I have been fighting with it for a bit and I feel like I could help there.
But I would need to guidance I guess... on where exactly to handle things.

Is there now some semantic comparable to Helix's Range { anchor: usize, head: usize }? Which encodes direction. And do we have a CharType Enum? If we have it, I should be fine.

@schlich
Copy link
Contributor Author

schlich commented Feb 17, 2026

@kronberger-droid best place to start would probably be in the prelude section
https://docs.rs/modalkit/latest/modalkit/prelude/index.html

@schlich
Copy link
Contributor Author

schlich commented Feb 17, 2026

gonna wait on adding this to this PR because it still has kinks and i want to look more into rusts module/export system BUT here is WIP on the tutorial example if anyone is picking up on this; need to log off for today

https://github.com/schlich/reedline/tree/schlich/helix-example

@kronberger-droid
Copy link

kronberger-droid commented Feb 18, 2026

I just want to mention that I had to do some tweaking in the apply_motion calls for word motions to work. Since there is no real solution for them using only modalkits primitives alone.

For testing I adapted Helix's cursor notation to make it easier to reason about test cases:

#[case("#[h|}ello world\n", "#[hello |]#world\n"

And scraped some of the test cases from helix's source.
I will soon put it onto a fork so you can take a look. @schlich
Should I do a PR on here or on the helix-example fork?

@fdncred
Copy link
Contributor

fdncred commented Feb 18, 2026

gonna wait on adding this to this PR because it still has kinks and i want to look more into rusts module/export system BUT here is WIP on the tutorial example if anyone is picking up on this; need to log off for today

schlich/reedline@schlich/helix-example

that was fun. i love the tutorial mode. very helpful for those who don't quite know the helix motions.

it does remind me that our selection foreground and background are just terrible without any way to change the color. maybe someday we can fix that too. ugh.

And scraped some of the test cases from helix's source.

I think having some of helix's own test cases is a great idea. So, I'm supportive of having them in whichever branch we think is best.

@kronberger-droid
Copy link

kronberger-droid commented Feb 18, 2026

Its now a descendant of the schlich/helix-example branch
see: https://github.com/kronberger-droid/reedline/tree/helix-example

But all the motions are still only defined in terms of the TestBuf since i was not sure how to bridge the gap to reedline. But it is a step in the right direction i think. Tests at the end of hx/mod.rs should be easy to validate if you are using hx as editor since i have placed the raw strings above the case.

@kronberger-droid
Copy link

I still don't really understand how we will bridge the gap from modalkit to reedline. Modalkit works on the premise that you have access to the buffer. Since we have to always go through reedline events we always land on the same problem that we will have to extend EditCommands anyways. Or am I just not understanding something? @schlich

@schlich
Copy link
Contributor Author

schlich commented Feb 19, 2026

I haven't looked too much into that aspect of it yet, good questions. It might leverage the fact that they both use cross term under the hood (modalkit re-exports it) but that's just a hunch, I haven't looked into specifics. Very well may need to add to EditCommand idk. Unfortunately I've been I'll today but will jump back into this asap

@schlich
Copy link
Contributor Author

schlich commented Feb 20, 2026

PS feel free to add commits to this PR as long as they include tests/ leave things in a working state!

@schlich
Copy link
Contributor Author

schlich commented Feb 20, 2026

I still don't really understand how we will bridge the gap from modalkit to reedline. Modalkit works on the premise that you have access to the buffer. Since we have to always go through reedline events we always land on the same problem that we will have to extend EditCommands anyways. Or am I just not understanding something? @schlich

perhaps we could adjust Reedline.run_edit_commands to pass buffer context along with the commands? that seems like the easiest entrypoint if we need buffer context. What do you think?

@kronberger-droid
Copy link

All good, take your time to get well.

I will look into it and try to test some possible solutions.
But the more i understand the structure, I get the feeling that without major changes in the way Reedline handles events, selection and its buffer. I don't think bolting modalkit onto it gives us much of a benefit.
Right now we always have to get back into Reedline's way of doing things.
Plus the fact that modalkit is very much vi focused, thus we have to solve the same problems as for Reedline.
Sure if we can strip away most of Reedline's Editor abstractions and replace them with modalkit it could work, but I don't know if we could get major changes like this approved.

I think making a more isolated selection abstraction inside Reedline's current architecture, will make it pretty easy to implement all of the proposed helix motions, and more if we get the selection abstraction versatile enough to handle multi cursors etc.

But I am very open for modalkit if we find a nice solution, but right now I can't seem to find one.

@schlich
Copy link
Contributor Author

schlich commented Feb 20, 2026

The more I look into it the more i tend to agree even though i'm a little fuzzy on where the mismatch is, precisely.

i've pushed up the branch w/earlier iteration of this PR that contained my reedline-only code but it's a bit AI slop-ish. it should be helpful but as we mentioned before we might want to iterate on changes from a fresh branch off of main. https://github.com/schlich/reedline/tree/hx-reedline

@fdncred
Copy link
Contributor

fdncred commented Feb 20, 2026

I'm up for gutting how reedline handles all the keystroke related stuff in favor of modalkit or some other crate that standardizes a way to use keystrokes for emacs, vim, and helix (and works with well-known vim/helix/emacs standards). However, we need to figure out a way to do that in a somewhat non-breaking change way. What I'm thinking is feature-gating the changes so the default is close to what it is today, and the feature is the gutted version that may work very different on the inside but to the end user, it just works (tm) just better.

The thing that makes major code surgery difficult is we don't have enough test cases to ensure non-breaking changes plus it's extremely difficult to review huge PRs. If it's in a feature, we could ship close-to-working stuff that isn't ready for prime-time (but compiles), piece-by-piece without the fear of it breaking every user who uses reedline whether in nushell or just as a crate in their cool app.

Thoughts?

@kronberger-droid
Copy link

You mean the missmatch between vi and hx or the missmatch between modalkit and reedline?

I think it makes sense to use modalkit as a backend and make it feature gated. But right now it feels like we would have to do major changes in the Editor, i am not sure if it can be easily feature gated without a lot of code dublication. But it is definitely the best option.

I will now work on a clean branch and implement a feature gated helix mode, since i have now identified a possible minimal implementation, based on some new EditorCommands and a new Selection struct in the Editor.
But will now spend some time reading helix's implementation, but tell you if i have something working.

@schlich
Copy link
Contributor Author

schlich commented Feb 20, 2026

missmatch between modalkit and reedline?

yeah, between modalkit and reedline. i spent some time reading the architecture on deepwiki but am having a bit of trouble mapping concepts. I understand the bit about "modalkit assumes we have access to the linebuffer" but i'm trying to figure out what these architecture differences look like at a higher level, why they were designed the way they were, etc. I would want to have a deeper understanding of what reedline's mechanisms would look like if it were more fully integrated with modalkit before advocating for an overhaul

here are some diagrams that i think are helpful to ground the conversation:
reedline event transformation pipeline
modalkit system component overview
modalkit action processing flow

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.

5 participants