Helix
Helix is a post-modern terminal text editor inspired by Kakoune and Vim but with a fresh take on modal editing. The key difference from Vim: in Helix you select first, then act — selections drive every operation. It ships with LSP integration and tree-sitter syntax highlighting out of the box with zero configuration.
Installation
# macOS (Homebrew)
brew install helix
# Linux (AppImage)
curl -L https://github.com/helix-editor/helix/releases/latest/download/helix-*.AppImage -o helix
chmod +x helix
sudo mv helix /usr/local/bin/hx
# Arch Linux
pacman -S helix
# Cargo (from source)
cargo install --locked --git https://github.com/helix-editor/helix helix-term
hx --grammar fetch
hx --grammar build
# Nix
nix-env -iA nixpkgs.helix
# Ubuntu/Debian PPA
sudo add-apt-repository ppa:maveonair/helix-editor
sudo apt update && sudo apt install helix
# Check version
hx --version
# Open Helix
hx
hx file.ts
hx src/ # open directory (file picker)
Configuration
Helix configuration lives at ~/.config/helix/config.toml:
# ~/.config/helix/config.toml
theme = "catppuccin_mocha"
[editor]
line-number = "relative" # "absolute" | "relative"
cursorline = true # highlight current line
cursorcolumn = false
gutters = ["diagnostics", "spacer", "line-numbers", "spacer", "diff"]
mouse = true
middle-click-paste = true
scroll-lines = 3
shell = ["bash", "-c"]
rulers = [100] # visual ruler at column 100
color-modes = true # colorize mode indicator
bufferline = "multiple" # show buffer tabs: "never" | "always" | "multiple"
idle-timeout = 400 # ms before LSP hints appear
completion-trigger-len = 2
[editor.cursor-shape]
insert = "bar"
normal = "block"
select = "underline"
[editor.file-picker]
hidden = false # show hidden files in picker
[editor.auto-save]
after-delay = { enable = true, timeout = 3000 }
[editor.statusline]
left = ["mode", "spinner", "file-name", "file-modification-indicator"]
center = []
right = ["diagnostics", "selections", "position", "file-encoding", "file-line-ending", "file-type"]
separator = "│"
mode.normal = "NORMAL"
mode.insert = "INSERT"
mode.select = "SELECT"
[editor.whitespace.render]
space = "none"
nbsp = "all"
nnbsp = "all"
tab = "all"
newline = "none"
[editor.indent-guides]
render = true
character = "╎"
skip-levels = 1
[editor.soft-wrap]
enable = true
[keys.normal]
# Remap keys
"C-/" = "toggle_comments"
"C-s" = ":w"
"space" = { f = "file_picker", e = "file_picker_in_current_directory", b = "buffer_picker", s = "symbol_picker", g = { g = "goto_first_line", e = "goto_last_line" } }
Language-Specific Settings
# ~/.config/helix/languages.toml
[[language]]
name = "javascript"
auto-format = true
formatter = { command = "biome", args = ["format", "--stdin-file-path", "file.js"] }
[[language]]
name = "typescript"
auto-format = true
formatter = { command = "biome", args = ["format", "--stdin-file-path", "file.ts"] }
[[language]]
name = "rust"
auto-format = true
[[language]]
name = "python"
language-servers = ["pyright"]
formatter = { command = "black", args = ["-", "--quiet"] }
auto-format = true
[[language]]
name = "go"
auto-format = true
formatter = { command = "goimports" }
Core Commands
Mode Switching
| Key | Mode | Description |
|---|---|---|
i | Normal → Insert | Insert before selection |
a | Normal → Insert | Insert after selection |
I | Normal → Insert | Insert at line start |
A | Normal → Insert | Insert at line end |
o | Normal → Insert | Open line below |
O | Normal → Insert | Open line above |
v | Normal → Select | Enter select mode (extend selection) |
Esc | Any → Normal | Return to normal mode |
: | Normal → Command | Enter command mode |
Movement (Normal Mode)
| Key | Description |
|---|---|
h j k l | Move left/down/up/right |
w / b | Next / previous word start |
e / ge | Next / previous word end |
W / B | Next / previous WORD start |
f{char} | Find next character on line |
t{char} | Move to before next character |
F{char} | Find previous character |
x | Select current line |
X | Extend selection to line |
gg / ge | Go to first / last line |
gh / gl | Go to line start / end |
Ctrl-d / Ctrl-u | Scroll half page down / up |
Ctrl-f / Ctrl-b | Scroll page down / up |
% | Match bracket |
m{motion} | Select text object (see below) |
Text Objects
| Key | Selection |
|---|---|
mi( or mib | Inside parentheses |
ma( or mab | Around parentheses |
mi[ | Inside brackets |
mi{ or miB | Inside braces |
mi" | Inside double quotes |
mi' | Inside single quotes |
mi`` | Inside backticks |
mif | Inside function |
maf | Around function |
mic | Inside class |
mat | Around tag (HTML/XML) |
mip | Inside paragraph |
Actions (Apply to Selection)
| Key | Description |
|---|---|
d | Delete selection |
c | Change selection (delete and enter insert) |
y | Yank (copy) selection |
p / P | Paste after / before cursor |
R | Replace selection with yanked text |
s | Select all matches of pattern in selection |
S | Split selection on regex matches |
K | Keep only matching selections |
~ | Toggle case |
``` `` | Lowercase selection |
Alt-` | Uppercase selection |
> / < | Indent / unindent selection |
= | Auto-format selection |
J | Join lines |
u / U | Undo / redo |
Advanced Usage
Multiple Cursors
# Add cursor on next match
, (select next match of current selection)
n (after search: next match)
N (after search: add cursor at next match)
# Add cursor on each line of selection
Ctrl-i (split selection by newline — creates cursor per line)
# Skip current selection, move to next
alt-,
# Remove a cursor
alt-(in select mode, remove main cursor)
# Select all occurrences in file
%s (select all, then: select regex)
# Practical multiple cursor workflow:
# 1. Search for a word: /myVariable
# 2. Select all matches: alt-, (while selecting)
# 3. Edit all at once (you now have N cursors)
LSP Integration
| Key | Action |
|---|---|
Space-k | Show hover documentation |
Space-d | Go to definition |
Space-D | Go to type definition |
Space-r | Rename symbol |
Space-a | Code actions |
Space-f | Format document |
[d / ]d | Previous / next diagnostic |
Space-l-d | Open diagnostics in picker |
Ctrl-space | Trigger completion |
Ctrl-x | Completion from signature |
Search and Replace
# In normal mode:
/pattern # search forward
?pattern # search backward
n / N # next / previous match
# Select all matches in selection
s # prompts for regex, creates multiple cursors
# Global search across files (picker)
<Space>/ or :global-search
# Command-line substitution
:%s/old/new/g # replace in entire file
:'<,'>s/old/new # replace in selection (use 's' in visual mode)
File Navigation
# File picker
<Space>f — open file picker
<Space>F — open file picker (cwd)
<Space>b — buffer picker
<Space>/ — global search (grep across files)
# Tree navigation in file picker:
# Enter — open file / expand dir
# Backspace — go up one directory
# Alt-Enter — open without closing picker
Command Mode
:w " save
:q " quit
:wq " save and quit
:q! " force quit (discard changes)
:e filename " open file
:r filename " read file into buffer
:set option=value
:split / :vsplit " split horizontally / vertically
:bp / :bn " previous / next buffer
:bd " close buffer (delete)
:theme name " change theme temporarily
:log " show log file
:health " check LSP/tree-sitter health
Tree-sitter Motions
# Navigate by syntax nodes (tree-sitter aware)
Alt-o — expand selection to parent node
Alt-i — shrink selection to child node
Alt-n — next sibling node
Alt-p — previous sibling node
# These are structurally aware — great for selecting
# entire function arguments, blocks, etc.
Common Workflows
Setting Up for a Project
# Open the project directory (shows file picker)
hx src/
# Check LSP status
:health
# Install language servers (done outside Helix)
# Node.js LSPs
npm install -g typescript-language-server typescript
npm install -g vscode-langservers-extracted # HTML, CSS, JSON, ESLint
npm install -g pyright # Python
Editing Multiple Files
# Open multiple files
hx file1.ts file2.ts file3.ts
# Navigate buffers
<Space>b — picker
:bp / :bn — prev/next
# Open split panes
Ctrl-w s — horizontal split
Ctrl-w v — vertical split
Ctrl-w h/j/k/l — move between splits
Recording and Running Macros (via Q)
Q — start recording macro
q — stop recording
Q — replay macro (after recording)
Tips and Best Practices
Think “select then act” — The core mental model is the opposite of Vim’s verb-object (e.g., dw). In Helix it’s object-verb: select the word with w, then delete with d. Get comfortable selecting before acting.
Use m text objects constantly — mib (match inside brackets), mif (match inside function), mi" (match inside quotes) are tree-sitter aware and dramatically faster than manual selection.
Learn s (select in selection) — Press s to run a regex on your selection and split it into multiple cursors. Combined with % (select all), this enables powerful multi-cursor bulk edits across the file.
Use <Space> as a leader — The space menu is discoverable — it shows all available commands with labels. Start there when you don’t remember a specific keybinding.
:health checks LSP status — If completion or go-to-definition isn’t working, run :health to see what language servers are installed and whether they’re working.
Use relative line numbers — Set line-number = "relative" in config.toml. This makes jump commands like 10j and 5k intuitive for navigating by count.
Swap Caps Lock to Escape — In a modal editor, Escape is pressed constantly. Remapping Caps Lock to Escape at the OS level (via keyd, karabiner, or xkb) prevents repetitive strain and speeds up mode switching.
Run :tutor to learn Helix — Helix ships with an interactive tutorial accessed via :tutor in the command line. It covers all core operations in 15–20 minutes and is the fastest way to get productive.