neovim-into-helix.mdx
1 --- 2 title: From Neovim to Helix 3 date: 2025-10-28 4 description: Reasons why I'm leaving Neovim in favour of Helix editor due to its simplicity 5 tags: 6 - neovim 7 - editor 8 - helix 9 --- 10 11 Sup! :) 12 13 Text editors have been something that has made me obsessed for the past few years. It all started from this notion of _"Vim is hard to learn"_ as a lot of people have been saying, so out of pure egotistical view, I decided to learn it. Another reason was that my first laptop was so bad that I literally couldn't use any other editor but Vim. Long story short, ever since I discovered it, I became obsessed with text editors, how they're set up, how they work, tinkering with their components, and many other things. 14 15 Text editors are... weirdly personal, as I found out. Everyone seems to have their own thing going on, apparently. For some, they're just a tool to edit some text, nothing more, nothing less. For others... It's half tool, and half identity, and they swear by it. 16 17 # The (Neo)vim experience 18 19 Initially, I was using Vim before I discovered Neovim not long after I started using it. Back then, Neovim was just a fork of Vim with architectural differences, the most notable one being `libuv` that enables non-blocking plugins and background tasks. It also introduced an RPC interface which allows you to write plugins in other languages (remember [coc.nvim](https://github.com/neoclide/coc.nvim)? It was _the shit_ that people talked about back then, before we finally have built-in LSP client support in Neovim). 20 21 Neovim also splits the core and the UI, so people can make their own GUI with actual Neovim embedded instead of trying to make yet another sub-par emulation. There are probably many other differences that I didn't mention, but my point is that there was not that much of a difference between Neovim 0.4 and Vim 8.0, unlike how it is right now with Neovim 0.11.x and Vim 9.x. 22 23 I later found out that the master branch contained a bunch of good stuff, including Lua scripting support[^1], built-in LSP client, Treesitter integration, and many more! That's where the rabbit hole begins... I started exploring much of the not-yet-stable latest and greatest Neovim API. If you're using Neovim before 0.5 was released, you'll know how long it was before 0.5 finally gets merged, hahah. Not to mention the support for a first-class `init.lua` instead of `init.vim`, that one was quite a ride. 24 25 [^1]: Technically, 0.4 already has Lua support; it just doesn't expose the API, so you can't really do much with it 26 27 It was a pretty fun experience overall! Using these bare-bones editors made me realise the moving parts that most mainstream IDE-like editors give you out of the box. Although that comes at a cost, maintenance. 28 29 ## Freedom vs Fatigue 30 31 Neovim gives you sooo much freedom, I can't exaggerate enough how much freedom it gives you. You can change almost every part of it with plugins, in whatever language you want. Each person will have their own unique configurations, their own setup, to a point where it becomes their identity. 32 33 > It's just an editor bro it ain't that deep 34 35 I've had my fair share of that; I had like dozens of Lua files just to configure my editor. It gets out of hand very quickly. What started out as a single `init.vim` became `init.lua` that imports a bunch of other files with abstractions all over the place, all that just to configure a text editor. 36 37 While it's good at the start, like, you get excited over a lot of things. Each week, or even each day! People are coming up with new plugins that supposedly improve your _✨productivity✨_ and _✨Quality of Life✨_ in using the text editor. At the end of the day, it depends on how you actually use it. I believe people work differently, and you can't just force how other people use their editor on yourself. 38 39 At one point, I felt like it was fatiguing. What's even the point of having a so-called perfect setup, like, what's the end goal here? I know, I know, sometimes it's just because it's fun! While I do agree on that, there's a point where it stopped becoming fun and became more fatiguing instead. 40 41 Eventually, curiosity turned into obsession. 42 43 ## Rabbit Hole 44 45 I went deep into this rabbit hole of configuration, or should I say, scripting. See, it's not a configuration anymore when you have a turing complete language to customise your editor. Most mainstream editors out there are using configuration languages like YAML, JSON, TOML, or some other variations of them. 46 47 I have mixed feelings about this. Although I'm leaning towards configuration over scripting. While scripting gives you the freedom to extend the behaviour of your text editor, it gets messy quickly because you literally have no limits whatsoever. 48 49 I met a guy years ago in Neovim's #matrix, and he was arguing that configuration is better than scripting. Configuration should be purely data, not some turing-complete code you can execute. He [wrote a good article](https://strongly-typed-thoughts.net/blog/editors-in-2021) about this topic; you should probably read it to get what I mean. I came to an agreement with most of what he's saying; I used to be on the other side of the argument. 50 51 Trying to make Neovim work like an IDE was not a good idea in the slightest. I had a bunch of plugins that basically implement features you'd find in an IDE, like a debugger, database client, and a bunch of other things. There are quite a few problems with this, most notably [the stability of the plugins](https://strongly-typed-thoughts.net/blog/neovim-plugins-stability). Of course, the solution is to pin them and just never update them, but then you'll miss out on improvements and bug fixes that the author has added. To be fair, it was also due to my lack of self-control that I updated the plugins almost daily hahah. 52 53 ## The cost of chasing perfection 54 55 I keep chasing this mythical ✨perfect setup✨ with a bunch of plugins and scripts I keep adding almost every day. It has become a well-known meme, so to speak, that people using these editors end up spending their time configuring their editors instead of actually being productive. This is a common issue with software that gives you a lot of flexibility, be it an editor, a window manager (which I also had my fair share of), or any type of software, really. 56 57 > Just one more plugin, boss. Just one more line of config to make you more productive, boss 58 59 I swear I'm finally done configuring my editor, like, this is it, this is the perfect setup, and every time I thought of that, a new idea came up, and I just _had_ to tinker around with it. 60 61 ## Communities 62 63 In hindsight, going down that rabbit hole was not all that bad. I met a lot of people in the community thanks to that! I love interacting with people who share the same interests as I do. 64 65 I never thought there would be a day when someone would recognise me because of the things I do with my editor. I had my contributions to the ecosystem, but I just never thought people would end up recognising me because of that. 66 67 # That's enough, I gave up 68 69 After using Neovim for years, crafting my own configurations, I finally had enough of it. I miss when something _Just Works™_ without having me tinker around with a bunch of configuration options and all. 70 71 ## I got burnt out 72 73 I stopped using Neovim as I looked for other alternatives. I tried using "normal" editors or IDEs that most people use. I'm done with having my "own" text editor; it's just a text editor, mate, why over-complicate it, I said to myself. 74 75 It's probably time to say goodbye to those years-old configurations and embrace the default. 76 77 ## Trying mainstream editors 78 79 I said to myself, let's just try out what most people are using, let's just be normal this time, don't be obsessed with it. 80 81 ### Visual Studio Code 82 83 I started using VSCode because I had to handle a Typescript project at the time. Back then, Typescript support outside of VSCode (and its forks) was just horrible because `tsserver` doesn't implement a full LSP interface [even until today](https://github.com/microsoft/TypeScript/issues/39459). Me trying to figure out how to make it work in Neovim can be a post on its own. I tried so many different things, different servers, but in the end, I just gave up and used VSCode. 84 85 ### JetBrains IDE 86 87 Since I have a university account, I can claim JetBrains' student offer, so I decided to try the holy grail of the IDE world. I mean, it's an IDE, it does what you would expect an IDE would do. 88 89 There's not much to say, really; it works, albeit taking up my entire computing resources. I used IntelliJ IDEA for everything, btw, and I tried disabling a lot of things to the point where what's supposed to be a Java IDE can't be used to code Java. I only use it for web things. Even with a lot of things disabled, it still consumed a lot of resources. 90 91 Also, I don't like the fact that it makes me feel like I work for a boring corporation; that's just depressing. 92 93 ## Going back 94 95 I was not satisfied with the options. Years of those muscle memories, I did not enjoy using the defaults. I decided to go back with Neovim, and reworked my config to be more minimal than before (though arguably it's still dozens of Lua files, it's just a bit more manageable). 96 97 Here I am, back with Neovim for a few months. Thought I was over with it, but I guess not, aye. 98 99 ## AI sloppery slop 100 101 Along with the advancement of Generative AI / LLM, there has been an influx of "AI text editors" popping up in recent years. You might be familiar with [Cursor](https://cursor.com/), which was the first time the concept of "AI-assisted IDE" was introduced. Since then, there have been countless companies trying to do the same, whether it be a new text editor, a new IDE, new extensions, and a lot of people are going all in with the hype. 102 103 When I was still using Neovim, I felt like it lacked good support for AI chat. There are extensions like [codecompanion.nvim](https://github.com/olimorris/codecompanion.nvim), [avante.nvim](https://github.com/yetone/avante.nvim), and probably others I failed to mention, but still, I wasn't quite satisfied. 104 105 For this reason, I went back to VSCode, well, Cursor, actually. I looove its autocompletion, it's stupid fast and quite accurate too. There's one issue, though, I don't use AI enough to warrant a subscription for it, so I looked for alternatives that have a pay-as-you-go scheme, and I found two at the time, which are Cline and RooCode. 106 107 Now, I won't go deep into them in this post because that's basically just reviewing AI coding tools. The point is, I liked RooCode enough that it makes me stay to use VSCode due to it being a VSCode extension. I even ended up [contributing a bunch of stuff to them](https://github.com/RooCodeInc/Roo-Code/pulls?q=sort%3Aupdated-desc+is%3Apr+is%3Aopen+author%3Aelianiva)! 108 109 I liked it for a while; it made me get things done quickly, and I used it quite frequently. I learned to navigate through the shortcomings of these models, context management, and all those new buzzwords that cool kids are talking about these days. 110 111 # Into the post-modern era 112 113 At this point, LLM writes more code than I do myself. I'll let you decide whether it's a good thing or not. I liked the fact that it makes me more productive in shipping things. 114 115 I have an issue with using LLM, though; it takes the fun out of programming, at least for me. I stopped editing the code manually, and I stopped navigating the code manually; all of it has been assisted by LLM. 116 117 After editing my prompt more than I edit my code, I decided to step back a bit and try to find the joy in programming like I used to. 118 119 ## How I discovered Helix 120 121 I discovered [Helix](https://helix-editor.com/) years ago when the project was still quite young, circa 2022, only a year after it was made. Someone mentioned it in Neovim's #matrix for it being a _post-modern text editor_. 122 123 At first, it didn't really catch my attention to make me consider using it. Although it did catch my attention a little, the fact that it has first-class Treesitter support, [Rope data structure](https://en.wikipedia.org/wiki/Rope_(data_structure)) for the text, which should make things faster, _and_ an LSP client by default makes it quite interesting. 124 125 I decided to pay close attention to it because who knows, it might be the next new shiny thing for me (spoiler alert: yes it does). 126 127 There are two things that really convinced me to try it. The first one is that someone shared their experience using it and how comfortable they are with the keybind, and that came from a former (Neo)Vim user. The second reason is the fact that it's integrated in the [Zed Editor](https://zed.dev), and I've been trying it out for a few days. 128 129 I thought to myself, I think this is it! This is the missing piece that I was looking for, after trying it out for a few minutes. 130 131 ## Adjusting mindset 132 133 I used (Neo)vim for years, so the muscle memory has been ingrained in me. I didn't even know what my fingers were doing, I could just think what I wanted to do and they just did it for me. If the keypresses were visualised, I'd also get confused, wdym I pressed `ggVg=<C-o>` or `vipddjjjjjjp`, like, seriously, what is that nonsense. 134 135 Helix has a flipped mindset. Instead of doing action->selection, kinda like how you'd do Verb->Object, in Helix, you'd do selection->action. At first, I thought this didn't make sense at all. Only after I realised that in Helix, you're always in select mode, each time you move using the navigation keys like `b` or `w`, you will always be selecting something, until you explicitly say not to. 136 137 This screwed my muscle memory at first because I'm so used to the action->selection mindset like `dw` for deleting words, now I had to adjust to using `wd`, but worry not! Since Helix is always in select mode, it ended up saving me some keystrokes because the things that I want to delete are sometimes already being selected, so I just simply need to press `d` to delete them, cool! 138 139 The selection->action mindset makes more sense for text editing because you can clearly visualise the text that you want to operate on, as opposed to having to memorise what certain movements do, like we have in (Neo)vim. 140 141 Some of the keybindings are also more consistent. Going to the start of a (non whitespace) line is `gs` instead of `^`, going to the end of a line is `gl` instead of `$`. I know, I know, the reason why Vim picked `^` and `$` is because it borrows the idea from regular expressions. Even though it's faster because it's only a single keypress as opposed to two, it can feel a bit cryptic to some people. I like that Helix favours a more consistent way of doing things, even if it sacrifices efficiency for a bit[^2]. 142 143 [^2]: I mean, let's be real here, it probably doesn't even matter when it's just a few keypresses of difference. 144 145 ## Appreciating minimalism 146 147 Helix doesn't have the concept of plugins ([yet! It's still in the works](https://github.com/helix-editor/helix/discussions/3806)), so it has a few missing things for me coming from Neovim. Thankfully, though, it already has a bunch of features baked in by default! Treesitter, LSP client, pickers, popup menus, diagnostics, and a bunch of other things. 148 149 This made me come to the realisation after using it for a while. Wow, I didn't actually use most of my custom configuration. A bunch of them are just there because I thought that'd be useful in the future. 150 151 I also tried not to change the keybind to suit my personal needs. I don't want to maintain yet another gigantic config files with dozens of custom keybinds, no, no, no, I'm done with that. 152 153 That being said, I still wrote some configurations that's just nice to have. You know what, here's my config, it's so short that I can fit it in this post as opposed to my [Neovim config](https://github.com/elianiva/dotfiles/tree/master/nvim), which is an entire folder of its own. 154 155 ```toml 156 theme = "my_rose_pine" 157 158 [editor] 159 bufferline = "multiple" 160 line-number = "relative" 161 cursorline = true 162 popup-border = "all" 163 color-modes = true 164 default-yank-register = "+" 165 166 [editor.statusline] 167 left = ["spacer", "separator", "spacer", "file-name", "read-only-indicator", "file-modification-indicator"] 168 right = ["spinner", "spacer", "version-control", "spacer", "diagnostics", "register", "position", "file-encoding", "file-line-ending", "file-type"] 169 mode.normal = "NORMAL" 170 mode.insert = "INSERT" 171 mode.select = "SELECT" 172 separator = "" 173 174 [editor.cursor-shape] 175 insert = "bar" 176 normal = "block" 177 select = "block" 178 179 [editor.lsp] 180 auto-signature-help = false 181 display-messages = true 182 183 [editor.indent-guides] 184 render = true 185 character = "▏" 186 187 [keys.normal.space] 188 i = ":toggle lsp.display-inlay-hints" 189 # replace <space>-e with yazi instead of default file manager 190 e = [ 191 ':sh rm -f /tmp/unique-file', 192 ':insert-output yazi "%{buffer_name}" --chooser-file=/tmp/unique-file', 193 ':insert-output echo "\x1b[?1049h\x1b[?2004h" > /dev/tty', 194 ':open %sh{cat /tmp/unique-file}', 195 ':redraw', 196 ] 197 ``` 198 199 I used a slightly customised rose pine colour because I want my statusline to be pink! Here's how it looks. 200 201 ```toml 202 inherits = "rose_pine_dawn" 203 204 "ui.virtual.indent-guide" = { fg = "overlay" } 205 206 "ui.statusline" = { fg = "love", bg = "love_10" } 207 "ui.statusline.separator" = { fg = "love" } 208 209 "ui.popup" = {} 210 "ui.popup.info" = {} 211 212 "ui.menu" = { fg = "subtle" } 213 "ui.help" = { fg = "subtle" } 214 ``` 215 216 ...aaand here's how the editor looks, I'm using Ghostty as my terminal emulator. 217 218  219 220 As for the language configuration, here it is. I configured a bunch of language servers for Typst, Biome, and Tailwind, so they work properly. 221 222 ```toml 223 [language-server.tinymist] 224 command = "tinymist" 225 226 [language-server.tinymist.config] 227 preview.background.enabled = true 228 preview.background.args = ["--data-plane-host=127.0.0.1:9898", "--invert-colors=never"] 229 230 [language-server.harper-ls] 231 command = "harper-ls" 232 args = [ "--stdio" ] 233 234 [language-server.vtsls] 235 command = "vtsls" 236 args = [ "--stdio" ] 237 238 [language-server.vtsls.config.typescript.inlayHints] 239 parameterNames.enabled = "literals" 240 parameterTypes.enabled = true 241 variableTypes.enabled = true 242 properlyDeclarationTypes.enabled = true 243 functionLikeReturnTypes.enabled = true 244 enummemberValues.enabled = true 245 246 [language-server.biome] 247 command = "biome" 248 args = ["lsp-proxy"] 249 250 # ------------------ 251 252 [[language]] 253 name = "html" 254 language-servers = [ "vscode-html-language-server", "tailwindcss-ls" ] 255 256 [[language]] 257 name = "css" 258 language-servers = [ "vscode-css-language-server", "tailwindcss-ls" ] 259 260 [[language]] 261 name = "typst" 262 language-servers = ["tinymist", "harper-ls"] 263 formatter.command = "typstyle" 264 rulers = [80] 265 auto-format = true 266 soft-wrap.enable = true 267 soft-wrap.wrap-at-text-width = true 268 269 [[language]] 270 name = "markdown" 271 language-servers = ["harper-ls"] 272 rulers = [80] 273 soft-wrap.enable = true 274 soft-wrap.wrap-at-text-width = true 275 276 [[language]] 277 name = "typescript" 278 language-servers = [{ name = "vtsls", except-features = ["format"] }, "biome", "tailwindcss-ls"] 279 formatter = { command = "biome", args = ["format", "--stdin-file-path", "index.ts"] } 280 281 [[language]] 282 name = "javascript" 283 language-servers = [{ name = "vtsls", except-features = ["format"] }, "biome", "tailwindcss-ls"] 284 formatter = { command = "biome", args = ["format", "--stdin-file-path", "index.js"] } 285 286 [[language]] 287 name = "tsx" 288 language-servers = [{ name = "vtsls", except-features = ["format"] }, "biome", "tailwindcss-ls"] 289 formatter = { command = "biome", args = ["format", "--stdin-file-path", "index.tsx"] } 290 291 [[language]] 292 name = "jsx" 293 language-servers = [{ name = "vtsls", except-features = ["format"] }, "biome", "tailwindcss-ls"] 294 formatter = { command = "biome", args = ["format", "--stdin-file-path", "index.jsx"] } 295 296 [[language]] 297 name = "astro" 298 language-servers = [{ name = "astro-ls", except-features = ["format"] }, "biome", "tailwindcss-ls"] 299 formatter = { command = "biome", args = ["format", "--stdin-file-path", "index.astro"] } 300 ``` 301 302 That's about it, really. I can achieve like 80% of my Neovim experience with those configs, I don't need anything else. I don't have to worry that some day some of these will break because those are just built-in features! It's not someone else's code that I attached to the editor. As long as I'm on the stable branch, I won't have to worry :) 303 304 It will probably get outdated very quickly since I might have to change a thing or two as I'm adjusting to use it, so you can check it yourself in [my dotfiles repository](https://github.com/elianiva/dotfiles) 305 306 ## Having fun! 307 308 In the midst of the rise of AI slop, I can find joy in text editing again! I now look forward to editing text again, thanks to Helix. 309 310 There are still some issues, though: I much prefer GUI to TUI, but there's gonna be some time for a Helix GUI to happen. The second best option is to just use Zed, which is what I'm doing now, but it doesn't have (or even try to) the same level of parity with Helix itself. 311 312 For now, I'm just enjoying editing text again, with all these fancy pants AST movements, selections, and whatnot. 313 314 # What I learned 315 316 If there are one or two things that I learned from this, being productive isn't the same as feeling productive. You might feel like you're chasing one with the amount of how much you tinker around with your config, but at the end of the day, you're only productive if you've actually shipped what you've built! 317 318 Don't be obsessed with this mythical "most productive workflows", just get shit done in the way that you enjoy the most! I'm not saying you shouldn't try to optimise your setup, just be careful before it takes over your life, hahah. And before you know it, you're eight hours deep into editing your configuration instead of actually building things. 319 320 If you’re still in your config-tinkering era, enjoy it. Just know there’s peace on the other side ;)