Turning Helix into an IDE with the help of Zellij

February 11, 2025

A while ago I wrote about how I switched to doing basically everything in the terminal. I kept that post pretty high-level since I didn't want to get lost in the technical weeds. But now? Let's dive into those config details.

For people that just want to get the config files, they are available here.

Helix

The reason I choose helix was its default features. It has a very nice fuzzy search and sane and intuitive configs. One of those features is LSP support. To setup autocomplete and syntax highlighting you just need to setup your languages.toml file.

Just a disclaimer, the examples will revolve around ruby since it’s what I use the most when developing.

[language-server.ruby-lsp]
command = "ruby-lsp"
config = { diagnostics = true, formatting = true }

[[language]]
name = "ruby"
language-servers = ["ruby-lsp"]
auto-format = true

On top of easy LSP setup, the config for the editor itself is also very simple. Everything is done via a TOML file. My config file is less than 50 lines long and that includes some custom commands I will share down the line. My basic config just to set how I want the editor to behave is the following.

[editor]
line-number = "relative"
mouse = true
end-of-line-diagnostics = "hint"

[editor.cursor-shape]
insert = "bar"
normal = "block"
select = "underline"

[editor.file-picker]
hidden = false

[editor.lsp]
display-inlay-hints = true
display-messages = true

[editor.inline-diagnostics]
cursor-line = "error"

[editor.soft-wrap]
enable = true

Simple and readable right? If you have any doubts you can check the docs which are very detailed.

Other than that, it comes with the usual bells and whistles of a terminal code editor, all except some major issues I have with it that I will explain.

It doesn’t support a plugin system, yet. That means that if you want something that’s not yet implemented you have to either do it yourself, find a workaround with some of the things that I’m going to show next, or just learn to live without it.

Also doesn’t support external live code reloading. So if a linter or something modifies your code outside of helix, you will have to run the manual :reload command to see those changes in the buffer.

Although it has a very fast and incredible file-picker with fuzzy search, helix doesn’t support any type for file-tree with file system control. I miss the file tree specially with big projects, where exploring the codebase since you may not be familiar with it, is a must.

The final big missing feature I miss the most is global search & replace. You can global search very efficiently and fast, but there is no way of modifying multiple files at the same time.

I will show my workarounds for the last two in the next part.

If you are interested and want to be involved in the development of helix, here’s a list of good features that have long standing open PRs/issues:

Zellij

Zellij in my setup journey is the successor of tmux. It basically gives you a layer on top of any terminal app you use, to be able to configure and have some extra features.

Zellij in particular has very verbose and simple configs together with floating panes and a tmux mode. This meant I could very easily make the switch from tmux without the need of new commands or muscle memory. I just prefix my way in zellij the same way I did in tmux.

Zellij being highly customizable makes it the ideal partner to bridge the gap helix has, in order to have those key missing features.

File picker with yazi

The first workaround I’m going to explain is getting a floating pane to open yazi, and then be able to open a file within helix.

The main idea is to be able to explore the file structure of a project and open a specific file in helix. I just followed yazi’s own docs and it worked perfectly. My only change is that I wanted to have it under space + q instead of ctrl.

If you want to replicate it, my config is the following.

[keys.normal.space.q]
q = ":sh zellij run -c -f -x 10% -y 10% --width 80% --height 80% -- bash ~/.config/helix/yazi-picker.sh open" # Open the file(s) in the current window
v = ":sh zellij run -c -f -x 10% -y 10% --width 80% --height 80% -- bash ~/.config/helix/yazi-picker.sh vsplit" # Open the file(s) in a horizontal pane
s = ":sh zellij run -c -f -x 10% -y 10% --width 80% --height 80% -- bash ~/.config/helix/yazi-picker.sh hsplit" # Open the file(s) in a vertical pane

There’s currently an open PR to add custom labels to commands that would make the command prettier when appearing in the space modal, so when that gets merged we can stop worrying about appearances.

File picker with yazi

Global Search & Replace with serpl

After having configured the yazi floating pane I started to think what else could I do with this type of workaround. After some trial and error I implemented my own search & replace using serpl.

This workaround actually works fine opening a floating pane manually and running serpl in it, but I wanted something a little more streamlined that also refreshed the buffers after the replacement.

Using the same concept as with yazi, I created a serpl-replace script in ~/.config/helix/serpl-replace.sh with the following code.

#!/usr/bin/env bash

serpl
exit_code=$?

if [[ $exit_code -eq 0 ]]; then
    zellij action toggle-floating-panes
    zellij action write-chars ":reload-all"
    zellij action write 13 # send <Enter> key
fi

The script itself is extremely simple but runs the reload-all command. This means that if you have unsaved changes you will lose them, so be careful with that.

Global search and replace with serpl

Alternatives

While writing this I found several projects that aim to fix the same issues I see in helix, a lot of them in a very similar way. I will try them out in the future to see if I make the switch.

Anyways, here’s the list of all the notable alternatives I could find:

Be sure to check them out!