Notes on learning the Helix text editor.

Keymap: https://docs.helix-editor.com/keymap.html

Up to date as of Feb 2023, Helix release 22.12.


Language server integration is a breeze. For me this is kinda Helix’s killer feature, and it is good enough to make language servers actually worth using. In vim you need a plugin for the language server, and then you need a plugin for LSP support to exist at all, and then you need a plugin to manage your plugins, and then often in my experience the language server plugin still doesn’t work great and actually getting anything useful out of it requires learning a whole new set of keybindings that are different for each LSP plugin. Then if the language server, any of those three plugins, or something else changes or breaks unexpectedly you get to spend a fun afternoon fixing it instead of doing whatever it was you wanted to do. In Helix language server support is built-in so it all just slots together automatically. If you have the language server for a particular language installed somewhere in your path, Helix will detect it and use it transparently. If you don’t, hx --health will tell you about it. That’s the beginning and end of the process.

I gotta say, I am digging the more explorable approach and more combinator-y keybindings. Most of Helix’s more seldom-used options are accessible through a small number of menus or searchable text interfaces, and tell you what the shortcuts for them are if they exist. Many times now I have gone “I should be able to do…” or “I bet this is part of…” or “I know I saw there was a way to do…” and was correct. And the tutorial, while incomplete, does a pretty darn good job of getting your brain plugged into how Helix works.

Combinators generally Just Work how you expect! And you find little uses for them all the time. I found myself mashing J a bunch to join lines together while writing this up, and was like “…I can just select all the lines and hit J once, can’t I?” Lo and behold, it can. x in normal mode just selects an entire line, vx enters selection mode and selects the entire line and then leaves you in selection mode to do other things.

Selection mode in general is a joy, the multiple cursors are something I appreciate even if I am not good at using them yet. AND getting out of multiple-cursor mode is just ; which is very easy to remember and very easy to reach, so if you fat-finger something and get into a weird state it’s fast and easy to undo.

wc and Wc are probably my most commonly used commands now, and I can’t even remember how to do the equivalent in vim.

The most common keybindings are the same as vim. This is good because they’re also some of vim’s best keybindings.

For many things, if there’s a single-button command like w or n, the upper-case version of it is the same operation with some common, complementary difference. Search upward instead of downward, select a word breaking on different characters, etc. There’s some exceptions of course, like c (change) and C (add Cursor). But the mnemonics are also usually pretty good.

Its handling of “enclosing things” like brackets and quotes is generally correct by default. That makes me pretty damn happy. Vim can do this but it’s arcane as fuck and I can never remember the correct keybindings for it. In Helix it’s m for match mode and then whatever menu option you want.

Many of the basic things Just Work The Right Way. Autoformatting on save. Indentation. Syntax highlighting. Displaying messages. Split-window operations. File buffers. Useful status line. Inline menus and file/buffer pickers show help docs, useful previews, and shortcuts if applicable. Displaying filtered options when typing into a list.

Differences (from vim)

You don’t seem to have vim’s C-v box-selection mode. Instead you use multiple cursors for it. Works just fine. Potentially better when you are good at using multiple cursors, you can do things like select jaggy boxes.

There are many modes! Normal/command and insert mode like vim, but also select mode, goto mode, match mode, window mode, space mode (a general catch-all for menu commands invoked by hitting the spacebar in normal mode), and view mode (which I don’t even know what it’s for). All of these except the most-common select-mode have a popup menu that tells you keybindings, which I think really might be a perfect juxtaposition of GUI and CLI. vim and emacs implicitly have most of these modes as well, is the thing, they just hide them as command line sub-languages (s// being the archetypical example) or special keybinding sub-languages (C-w $FOO in vim).

Upon consideration, this actually is pretty useful because it results in less pressure for good key mnemonics, by making the command tree deeper rather than broader. (Keystroke golf is a specific anti-goal of Helix; it does optimize for common operations, but doesn’t see the need to make an uncommon operation one keystroke when there’s a more-orthogonal 3-keystroke combination for it.) For example, in vim % switches to a matching paren if there’s a paren under the cursor. Or, in the s// sublanguage, % means “match the entire file”. In Helix, % always means “do X to the entire file” for whatever valid X. (There was more to this or another example but I forget what.)

Turning the stunningly ubiquitous s/foo/bar regex replacement into s to search for and select a regex and then c to change it to something else is… well, it Feels Weird. Seems to work pretty well in practice though, at least so far! I can think of some things I don’t know how to do with it, like ask for confirmation or un-select a single instance of the selection, but I also haven’t needed those in the last couple weeks. Apparently the Helix Way to do those things is to do your multi-cursor selection, and then you can alter your selection as you wish.



No automatic hard-wrap for lines, you have to select them and use the :reflow command, and its reflowing is not as good as vim’s. However, I can bind :reflow to C-r in selection mode, so I can just do x C-r to reflow text. Not quite Vg, but, oh well.

Hitting enter when in a comment block doesn’t extend the comment block correctly, which is weird and also irks me. Can you make it so that if you hit enter while in a comment it adds the comment character at the start of the next line? There’s a PR open for it that from the look of it the originator really wants to get done, and it’s been on their “to do soon” list for like six months. (Issue #1730.) Big ol mood. On the upside, doing a :reflow over a block of comments does seem to work correctly… usually. On the downside, using J to join lines that contain comments leaves the leadin comment characters on the new line, presumably for the same reason. In general it looks like the interaction between comments and commands that change lines (J, :reflow, etc) is still being worked on.

Also, auto hard-wrapping of text files would still be nice. Maybe I can make it trigger on file save at least? Ehhhhhhh, its hard-wrap doesn’t handle markdown well ’cause it eliminates the blank newlines between paragraphs. Very annoying, it makes editing Markdown in general way more narsty than it should be. I should find a shortcut for “select all non-blank lines in file”, then I could do that and then :reflow. v%s to select the whole file, modify selection by regex, and then using the regex .+ to select lines with one or more character will do the job. Seems to be the closest I can get right now. Pretty oogly though.

On the other hand, moving the cursor up and down in a long soft-wrapped line moves the cursor up and down in the window, not up and down an actual line, so maybe you can just not hard-wrap things and it’s fine? Nah, then it’s annoying to go to the end or beginning of a line and end up far away from where you want to be.

Movement and selection by line

The emacs C-k and C-u for “delete to end of line” and “delete to beginning of line” work in insertion mode, and in prompts, but not in normal mode. Weird.

Another pet peeve I need to solve: x selects the current line. Repeating x selects subsequent lines. I need a good way to select the current lines and previous lines as well. vx and starts the selection at the beginning of the current line, so doing that and then moving the cursor upwards requires you to start on the line after the one you want to select.

I do wish there were more convenient selection-mode shortcuts for “beginning of line” and “end of line” than home and end, something closer to the home row. Might have to conjure forth my own?

Nit: At the end of the line, doing a to enter insert mode enters insert mode after the end of the current line, which is thus far never what I want. vim does the end of the current line, basically when a does “insert after current char” it means “insert after current char, except when current char is a newline”. There’s an article somewhere talking about consistency vs. “what you actually want” in a very similar case, that of Emacs’s “swap characters” operation when done at the end of a line.


How do you go to a named buffer? Doing space b and moving the cursor down to select hir.rs from the list is entirely inferior to typing :b hi<tab>. …maybe I’ll make a PR for it. Well, you can do space b and then type the first bit of your buffer name and it will filter for it, so… maybe almost as nice? Very different feel. We’ll leave it a few more weeks and see if I get used to it.

Apparently Helix has a PR in-progress to let you do :b or such to switch to another buffer… by numeric index, not by name. That is just so bad it seems useless. Sure you can have multiple buffers with the same name, that’s why you disambiguate those ones with a trailing number.


No Debian package, gotta build it from source. Maybe I can fix this someday?

I figured out a bug in syntax highlighting that turned out to be a problem for tmux being bug-for-bug compatible with screen, and then not reloading the config file if I start a new session while one already exists. So! Apparently vim detects tmux and works around the workaround.

Helix’s paren matching uses the LSP and thus is actually kinda worse than vim’s in some situations. If I do /* thing { ... } and want to flip to the end of the block to put the comment at the end of it then I can’t use mm for it, because it doesn’t parse successfully and thus can’t find the matching }. Hmmmm. Okay, what I CAN do is do vx to select the first line, mm to select to the end of the block, and THEN use C-c to comment it all out. Hmmmmmmmmmmmmmmmmm. Not sure if it’s better or worse, but certainly different. OTOH, vim’s lack of any ability to toggle comments on and off for a region without a plugin is one of its more irksome features. All in all the broader take seems to be that relying on a LSP to do everything is both a blessing and a curse. Usually worth it so far though.

I’d ask for help on some of these on the Helix Matrix chat, but… man, someday I would love to see a Matrix client that just actually works without any bullshit involved. I really fucking hate it. So far nheko and neochat still fail that test on my Pinebook Pro running sway; nheko complains about not being able to connect to a password key service that isn’t running, neochat sits there saying “loading” for an eon and when it eventually does something the UI is broken. The github discussions and issues are pretty good though. The community appears surprisingly large and active.


Is Helix Good(tm)? Yes.

Does Helix still have rough edges you will encounter in day-to-day life? Yes.

Is it good enough to use despite those? For me, so far, yes. This is the test kakoune failed for me at the time I tried it, its rough edges were too rough.

Is it good enough to be worth retraining your fingers away from ubiquitous vim keybindings? For me, so far, yes.

There is no single killer feature that’s Better Than Vim, the LSP integration is the closest it has, and for me that’s a pretty good convenience but not a requirement. But when you combine it with the combinator-rich command style, the actively helpful UI, the generally excellent array of “it’s 2023, the editor should be able to…” features, and the generally lightweight and speedy feel of it… I think it’s actually a worthy successor to vim.