Helix
+Docs for bleeding edge master can be found at +https://docs.helix-editor.com/master.
+See the usage section for a quick overview of the editor, keymap +section for all available keybindings and the configuration section +for defining custom keybindings, setting themes, etc. +For everything else (e.g., how to install supported language servers), see the Helix Wiki.
+Refer the FAQ for common questions.
+Installing Helix
+ +-
+
- Pre-built binaries +
- Linux, macOS, Windows and OpenBSD packaging status +
- Linux
+
-
+
- Ubuntu +
- Fedora/RHEL +
- Arch Linux extra +
- NixOS +
- Flatpak +
- Snap +
- AppImage +
+ - macOS + + +
- Windows
+
-
+
- Winget +
- Scoop +
- Chocolatey +
- MSYS2 +
+ - Building from source + + +
To install Helix, follow the instructions specific to your operating system. +Note that:
+-
+
-
+
To get the latest nightly version of Helix, you need to +build from source.
+
+ -
+
To take full advantage of Helix, install the language servers for your +preferred programming languages. See the +wiki +for instructions.
+
+
Pre-built binaries
+Download pre-built binaries from the
+GitHub Releases page. Add the binary to your system's $PATH
to use it from the command
+line.
Linux, macOS, Windows and OpenBSD packaging status
+ +Linux
+The following third party repositories are available:
+Ubuntu
+Add the PPA
for Helix:
sudo add-apt-repository ppa:maveonair/helix-editor
+sudo apt update
+sudo apt install helix
+
+Fedora/RHEL
+Enable the COPR
repository for Helix:
sudo dnf copr enable varlad/helix
+sudo dnf install helix
+
+Arch Linux extra
+Releases are available in the extra
repository:
sudo pacman -S helix
+
+Additionally, a helix-git package is available +in the AUR, which builds the master branch.
+NixOS
+Helix is available in nixpkgs through the helix
attribute,
+the unstable channel usually carries the latest release.
Helix is also available as a flake in the project
+root. Use nix develop
to spin up a reproducible development shell. Outputs are
+cached for each push to master using Cachix. The
+flake is configured to automatically make use of this cache assuming the user
+accepts the new settings on first use.
If you are using a version of Nix without flakes enabled,
+install Cachix CLI and use
+cachix use helix
to configure Nix to use cached outputs when possible.
Flatpak
+Helix is available on Flathub:
+flatpak install flathub com.helix_editor.Helix
+flatpak run com.helix_editor.Helix
+
+Snap
+Helix is available on Snapcraft and can be installed with:
+snap install --classic helix
+
+This will install Helix as both /snap/bin/helix
and /snap/bin/hx
, so make sure /snap/bin
is in your PATH
.
AppImage
+Install Helix using the Linux AppImage format. +Download the official Helix AppImage from the latest releases page.
+chmod +x helix-*.AppImage # change permission for executable mode
+./helix-*.AppImage # run helix
+
+macOS
+Homebrew Core
+brew install helix
+
+Windows
+Install on Windows using Winget, Scoop, Chocolatey +or MSYS2.
+Winget
+Windows Package Manager winget command-line tool is by default available on Windows 11 and modern versions of Windows 10 as a part of the App Installer. +You can get App Installer from the Microsoft Store. If it's already installed, make sure it is updated with the latest version.
+winget install Helix.Helix
+
+Scoop
+scoop install helix
+
+Chocolatey
+choco install helix
+
+MSYS2
+For 64-bit Windows 8.1 or above:
+pacman -S mingw-w64-ucrt-x86_64-helix
+
+Building from source
+Requirements:
+Clone the Helix GitHub repository into a directory of your choice. The
+examples in this documentation assume installation into either ~/src/
on
+Linux and macOS, or %userprofile%\src\
on Windows.
-
+
- The Rust toolchain +
- The Git version control system +
- A C++14 compatible compiler to build the tree-sitter grammars, for example GCC or Clang +
If you are using the musl-libc
standard library instead of glibc
the following environment variable must be set during the build to ensure tree-sitter grammars can be loaded correctly:
RUSTFLAGS="-C target-feature=-crt-static"
+
+-
+
-
+
Clone the repository:
+
+git clone https://github.com/helix-editor/helix +cd helix +
+ -
+
Compile from source:
+
+cargo install --path helix-term --locked +
This command will create the
+hx
executable and construct the tree-sitter +grammars in the localruntime
folder.
+
++๐ก Tree-sitter grammars can be fetched and compiled if not pre-packaged. Fetch +grammars with
+hx --grammar fetch
and compile them with +hx --grammar build
. This will install them in +theruntime
directory within the user's helix config directory (more +details below).
Configuring Helix's runtime files
+Linux and macOS
+The runtime directory is one below the Helix source, so either set a
+HELIX_RUNTIME
environment variable to point to that directory and add it to
+your ~/.bashrc
or equivalent:
HELIX_RUNTIME=~/src/helix/runtime
+
+Or, create a symbolic link:
+ln -Ts $PWD/runtime ~/.config/helix/runtime
+
+If the above command fails to create a symbolic link because the file exists either move ~/.config/helix/runtime
to a new location or delete it, then run the symlink command above again.
Windows
+Either set the HELIX_RUNTIME
environment variable to point to the runtime files using the Windows setting (search for
+Edit environment variables for your account
) or use the setx
command in
+Cmd:
setx HELIX_RUNTIME "%userprofile%\source\repos\helix\runtime"
+
+++๐ก
+%userprofile%
resolves to your user directory like +C:\Users\Your-Name\
for example.
Or, create a symlink in %appdata%\helix\
that links to the source code directory:
Method | Command |
---|---|
PowerShell | New-Item -ItemType Junction -Target "runtime" -Path "$Env:AppData\helix\runtime" |
Cmd | cd %appdata%\helix mklink /D runtime "%userprofile%\src\helix\runtime" |
++๐ก On Windows, creating a symbolic link may require running PowerShell or +Cmd as an administrator.
+
Multiple runtime directories
+When Helix finds multiple runtime directories it will search through them for files in the +following order:
+-
+
runtime/
sibling directory to$CARGO_MANIFEST_DIR
directory (this is intended for +developing and testing helix only).
+runtime/
subdirectory of OS-dependent helix user config directory.
+$HELIX_RUNTIME
+- Distribution-specific fallback directory (set at compile timeโnot run timeโ
+with the
HELIX_DEFAULT_RUNTIME
environment variable)
+ runtime/
subdirectory of path to Helix executable.
+
This order also sets the priority for selecting which file will be used if multiple runtime +directories have files with the same name.
+Note to packagers
+If you are making a package of Helix for end users, to provide a good out of
+the box experience, you should set the HELIX_DEFAULT_RUNTIME
environment
+variable at build time (before invoking cargo build
) to a directory which
+will store the final runtime files after installation. For example, say you want
+to package the runtime into /usr/lib/helix/runtime
. The rough steps a build
+script could follow are:
-
+
export HELIX_DEFAULT_RUNTIME=/usr/lib/helix/runtime
+cargo build --profile opt --locked --path helix-term
+cp -r runtime $BUILD_DIR/usr/lib/helix/
+cp target/opt/hx $BUILD_DIR/usr/bin/hx
+
This way the resulting hx
binary will always look for its runtime directory in
+/usr/lib/helix/runtime
if the user has no custom runtime in ~/.config/helix
+or HELIX_RUNTIME
.
Validating the installation
+To make sure everything is set up as expected you should run the Helix health +check:
+hx --health
+
+For more information on the health check results refer to +Health check.
+Configure the desktop shortcut
+If your desktop environment supports the
+XDG desktop menu
+you can configure Helix to show up in the application menu by copying the
+provided .desktop
and icon files to their correct folders:
cp contrib/Helix.desktop ~/.local/share/applications
+cp contrib/helix.png ~/.icons # or ~/.local/share/icons
+
+To use another terminal than the system default, you can modify the .desktop
+file. For example, to use kitty
:
sed -i "s|Exec=hx %F|Exec=kitty hx %F|g" ~/.local/share/applications/Helix.desktop
+sed -i "s|Terminal=true|Terminal=false|g" ~/.local/share/applications/Helix.desktop
+
+Using Helix
+ +-
+
- Registers + + +
- Surround +
- Selecting and manipulating text with textobjects +
- Navigating using tree-sitter textobjects +
- Moving the selection with syntax-aware motions +
For a full interactive introduction to Helix, refer to the
+tutor which
+can be accessed via the command hx --tutor
or :tutor
.
++๐ก Currently, not all functionality is fully documented, please refer to the +key mappings list.
+
Registers
+In Helix, registers are storage locations for text and other data, such as the
+result of a search. Registers can be used to cut, copy, and paste text, similar
+to the clipboard in other text editors. Usage is similar to Vim, with "
being
+used to select a register.
User-defined registers
+Helix allows you to create your own named registers for storing text, for +example:
+-
+
"ay
- Yank the current selection to registera
.
+"op
- Paste the text in registero
after the selection.
+
If a register is selected before invoking a change or delete command, the selection will be stored in the register and the action will be carried out:
+-
+
"hc
- Store the selection in registerh
and then change it (delete and enter insert mode).
+"md
- Store the selection in registerm
and delete it.
+
Default registers
+Commands that use registers, like yank (y
), use a default register if none is specified.
+These registers are used as defaults:
Register character | Contains |
---|---|
/ | Last search |
: | Last executed command |
" | Last yanked text |
@ | Last recorded macro |
Special registers
+Some registers have special behavior when read from and written to.
+Register character | When read | When written |
---|---|---|
_ | No values are returned | All values are discarded |
# | Selection indices (first selection is 1 , second is 2 , etc.) | This register is not writable |
. | Contents of the current selections | This register is not writable |
% | Name of the current file | This register is not writable |
* | Reads from the system clipboard | Joins and yanks to the system clipboard |
+ | Reads from the primary clipboard | Joins and yanks to the primary clipboard |
When yanking multiple selections to the clipboard registers, the selections +are joined with newlines. Pasting from these registers will paste multiple +selections if the clipboard was last yanked to by the Helix session. Otherwise +the clipboard contents are pasted as one selection.
+Surround
+Helix includes built-in functionality similar to vim-surround. +The keymappings have been inspired from vim-sandwich:
+ +Key Sequence | Action |
---|---|
ms<char> (after selecting text) | Add surround characters to selection |
mr<char_to_replace><new_char> | Replace the closest surround characters |
md<char_to_delete> | Delete the closest surround characters |
You can use counts to act on outer pairs.
+Surround can also act on multiple selections. For example, to change every occurrence of (use)
to [use]
:
-
+
%
to select the whole file
+s
to split the selections on a search term
+- Input
use
and hit Enter
+ mr([
to replace the parentheses with square brackets
+
Multiple characters are currently not supported, but planned for future release.
+Selecting and manipulating text with textobjects
+In Helix, textobjects are a way to select, manipulate and operate on a piece of +text in a structured way. They allow you to refer to blocks of text based on +their structure or purpose, such as a word, sentence, paragraph, or even a +function or block of code.
++
+-
+
ma
- Select around the object (va
in Vim,<alt-a>
in Kakoune)
+mi
- Select inside the object (vi
in Vim,<alt-i>
in Kakoune)
+
Key after mi or ma | Textobject selected |
---|---|
w | Word |
W | WORD |
p | Paragraph |
( , [ , ' , etc. | Specified surround pairs |
m | The closest surround pair |
f | Function |
t | Type (or Class) |
a | Argument/parameter |
c | Comment |
T | Test |
g | Change |
++๐ก
+f
,t
, etc. need a tree-sitter grammar active for the current +document and a special tree-sitter query file to work properly. Only +some grammars currently have the query file implemented. +Contributions are welcome!
Navigating using tree-sitter textobjects
+Navigating between functions, classes, parameters, and other elements is
+possible using tree-sitter and textobject queries. For
+example to move to the next function use ]f
, to move to previous
+type use [t
, and so on.
For the full reference see the unimpaired section of the key bind +documentation.
+++๐ก This feature relies on tree-sitter textobjects +and requires the corresponding query file to work properly.
+
Moving the selection with syntax-aware motions
+Alt-p
, Alt-o
, Alt-i
, and Alt-n
(or Alt
and arrow keys) allow you to move the
+selection according to its location in the syntax tree. For example, many languages have the
+following syntax for function calls:
func(arg1, arg2, arg3);
+
+A function call might be parsed by tree-sitter into a tree like the following.
+(call
+ function: (identifier) ; func
+ arguments:
+ (arguments ; (arg1, arg2, arg3)
+ (identifier) ; arg1
+ (identifier) ; arg2
+ (identifier))) ; arg3
+
+Use :tree-sitter-subtree
to view the syntax tree of the primary selection. In
+a more intuitive tree format:
โโโโโโ
+ โcallโ
+ โโโโโโโดโโโโโดโโโโโโ
+ โ โ
+โโโโโโโผโโโโโ โโโโโโผโโโโโ
+โidentifierโ โargumentsโ
+โ "func" โ โโโโโโดโโโโฌโโโโโโดโโโโ
+โโโโโโโโโโโโ โ โ โ
+ โ โ โ
+ โโโโโโโโโโโผโ โโโโโโผโโโโโโ โโผโโโโโโโโโโ
+ โidentifierโ โidentifierโ โidentifierโ
+ โ "arg1" โ โ "arg2" โ โ "arg3" โ
+ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ
+
+If you have a selection that wraps arg1
(see the tree above), and you use
+Alt-n
, it will select the next sibling in the syntax tree: arg2
.
// before
+func([arg1], arg2, arg3)
+// after
+func(arg1, [arg2], arg3);
+
+Similarly, Alt-o
will expand the selection to the parent node, in this case, the
+arguments node.
func[(arg1, arg2, arg3)];
+
+There is also some nuanced behavior that prevents you from getting stuck on a
+node with no sibling. When using Alt-p
with a selection on arg1
, the previous
+child node will be selected. In the event that arg1
does not have a previous
+sibling, the selection will move up the syntax tree and select the previous
+element. As a result, using Alt-p
with a selection on arg1
will move the
+selection to the "func" identifier
.
Keymap
+-
+
- Normal mode
+
-
+
- Movement +
- Changes
+
-
+
- Shell +
+ - Selection manipulation +
- Search +
- Minor modes
+
-
+
- View mode +
- Goto mode +
- Match mode +
- Window mode +
- Space mode
+
-
+
- Popup +
+ - Unimpaired +
+
+ - Insert mode +
- Select / extend mode +
- Picker +
- Prompt +
++๐ก Mappings marked (LSP) require an active language server for the file.
+
++๐ก Mappings marked (TS) require a tree-sitter grammar for the file type.
+
Normal mode
+Normal mode is the default mode when you launch helix. Return to it from other modes by typing Escape
.
Movement
+++NOTE: Unlike Vim,
+f
,F
,t
andT
are not confined to the current line.
Key | Description | Command |
---|---|---|
h , Left | Move left | move_char_left |
j , Down | Move down | move_visual_line_down |
k , Up | Move up | move_visual_line_up |
l , Right | Move right | move_char_right |
w | Move next word start | move_next_word_start |
b | Move previous word start | move_prev_word_start |
e | Move next word end | move_next_word_end |
W | Move next WORD start | move_next_long_word_start |
B | Move previous WORD start | move_prev_long_word_start |
E | Move next WORD end | move_next_long_word_end |
t | Find 'till next char | find_till_char |
f | Find next char | find_next_char |
T | Find 'till previous char | till_prev_char |
F | Find previous char | find_prev_char |
G | Go to line number <n> | goto_line |
Alt-. | Repeat last motion (f , t or m ) | repeat_last_motion |
Home | Move to the start of the line | goto_line_start |
End | Move to the end of the line | goto_line_end |
Ctrl-b , PageUp | Move page up | page_up |
Ctrl-f , PageDown | Move page down | page_down |
Ctrl-u | Move half page up | half_page_up |
Ctrl-d | Move half page down | half_page_down |
Ctrl-i | Jump forward on the jumplist | jump_forward |
Ctrl-o | Jump backward on the jumplist | jump_backward |
Ctrl-s | Save the current selection to the jumplist | save_selection |
Changes
+Key | Description | Command |
---|---|---|
r | Replace with a character | replace |
R | Replace with yanked text | replace_with_yanked |
~ | Switch case of the selected text | switch_case |
` | Set the selected text to lower case | switch_to_lowercase |
Alt-` | Set the selected text to upper case | switch_to_uppercase |
i | Insert before selection | insert_mode |
a | Insert after selection (append) | append_mode |
I | Insert at the start of the line | insert_at_line_start |
A | Insert at the end of the line | insert_at_line_end |
\ | Insert digraphs | insert_digraph |
o | Open new line below selection | open_below |
O | Open new line above selection | open_above |
. | Repeat last insert | N/A |
u | Undo change | undo |
U | Redo change | redo |
Alt-u | Move backward in history | earlier |
Alt-U | Move forward in history | later |
y | Yank selection | yank |
p | Paste after selection | paste_after |
P | Paste before selection | paste_before |
" <reg> | Select a register to yank to or paste from | select_register |
> | Indent selection | indent |
< | Unindent selection | unindent |
= | Format selection (currently nonfunctional/disabled) (LSP) | format_selections |
d | Delete selection | delete_selection |
Alt-d | Delete selection, without yanking | delete_selection_noyank |
c | Change selection (delete and enter insert mode) | change_selection |
Alt-c | Change selection (delete and enter insert mode, without yanking) | change_selection_noyank |
Ctrl-a | Increment object (number) under cursor | increment |
Ctrl-x | Decrement object (number) under cursor | decrement |
Q | Start/stop macro recording to the selected register (experimental) | record_macro |
q | Play back a recorded macro from the selected register (experimental) | replay_macro |
Shell
+Key | Description | Command |
---|---|---|
| | Pipe each selection through shell command, replacing with output | shell_pipe |
Alt-| | Pipe each selection into shell command, ignoring output | shell_pipe_to |
! | Run shell command, inserting output before each selection | shell_insert_output |
Alt-! | Run shell command, appending output after each selection | shell_append_output |
$ | Pipe each selection into shell command, keep selections where command returned 0 | shell_keep_pipe |
Selection manipulation
+Key | Description | Command |
---|---|---|
s | Select all regex matches inside selections | select_regex |
S | Split selection into sub selections on regex matches | split_selection |
Alt-s | Split selection on newlines | split_selection_on_newline |
Alt-minus | Merge selections | merge_selections |
Alt-_ | Merge consecutive selections | merge_consecutive_selections |
& | Align selection in columns | align_selections |
_ | Trim whitespace from the selection | trim_selections |
; | Collapse selection onto a single cursor | collapse_selection |
Alt-; | Flip selection cursor and anchor | flip_selections |
Alt-: | Ensures the selection is in forward direction | ensure_selections_forward |
, | Keep only the primary selection | keep_primary_selection |
Alt-, | Remove the primary selection | remove_primary_selection |
C | Copy selection onto the next line (Add cursor below) | copy_selection_on_next_line |
Alt-C | Copy selection onto the previous line (Add cursor above) | copy_selection_on_prev_line |
( | Rotate main selection backward | rotate_selections_backward |
) | Rotate main selection forward | rotate_selections_forward |
Alt-( | Rotate selection contents backward | rotate_selection_contents_backward |
Alt-) | Rotate selection contents forward | rotate_selection_contents_forward |
% | Select entire file | select_all |
x | Select current line, if already selected, extend to next line | extend_line_below |
X | Extend selection to line bounds (line-wise selection) | extend_to_line_bounds |
Alt-x | Shrink selection to line bounds (line-wise selection) | shrink_to_line_bounds |
J | Join lines inside selection | join_selections |
Alt-J | Join lines inside selection and select the inserted space | join_selections_space |
K | Keep selections matching the regex | keep_selections |
Alt-K | Remove selections matching the regex | remove_selections |
Ctrl-c | Comment/uncomment the selections | toggle_comments |
Alt-o , Alt-up | Expand selection to parent syntax node (TS) | expand_selection |
Alt-i , Alt-down | Shrink syntax tree object selection (TS) | shrink_selection |
Alt-p , Alt-left | Select previous sibling node in syntax tree (TS) | select_prev_sibling |
Alt-n , Alt-right | Select next sibling node in syntax tree (TS) | select_next_sibling |
Search
+Search commands all operate on the /
register by default. To use a different register, use "<char>
.
Key | Description | Command |
---|---|---|
/ | Search for regex pattern | search |
? | Search for previous pattern | rsearch |
n | Select next search match | search_next |
N | Select previous search match | search_prev |
* | Use current selection as the search pattern | search_selection |
Minor modes
+These sub-modes are accessible from normal mode and typically switch back to normal mode after a command.
+Key | Description | Command |
---|---|---|
v | Enter select (extend) mode | select_mode |
g | Enter goto mode | N/A |
m | Enter match mode | N/A |
: | Enter command mode | command_mode |
z | Enter view mode | N/A |
Z | Enter sticky view mode | N/A |
Ctrl-w | Enter window mode | N/A |
Space | Enter space mode | N/A |
These modes (except command mode) can be configured by +remapping keys.
+View mode
+Accessed by typing z
in normal mode.
View mode is intended for scrolling and manipulating the view without changing
+the selection. The "sticky" variant of this mode (accessed by typing Z
in
+normal mode) is persistent and can be exited using the escape key. This is
+useful when you're simply looking over text and not actively editing it.
Key | Description | Command |
---|---|---|
z , c | Vertically center the line | align_view_center |
t | Align the line to the top of the screen | align_view_top |
b | Align the line to the bottom of the screen | align_view_bottom |
m | Align the line to the middle of the screen (horizontally) | align_view_middle |
j , down | Scroll the view downwards | scroll_down |
k , up | Scroll the view upwards | scroll_up |
Ctrl-f , PageDown | Move page down | page_down |
Ctrl-b , PageUp | Move page up | page_up |
Ctrl-d | Move half page down | half_page_down |
Ctrl-u | Move half page up | half_page_up |
Goto mode
+Accessed by typing g
in normal mode.
Jumps to various locations.
+Key | Description | Command |
---|---|---|
g | Go to line number <n> else start of file | goto_file_start |
e | Go to the end of the file | goto_last_line |
f | Go to files in the selection | goto_file |
h | Go to the start of the line | goto_line_start |
l | Go to the end of the line | goto_line_end |
s | Go to first non-whitespace character of the line | goto_first_nonwhitespace |
t | Go to the top of the screen | goto_window_top |
c | Go to the middle of the screen | goto_window_center |
b | Go to the bottom of the screen | goto_window_bottom |
d | Go to definition (LSP) | goto_definition |
y | Go to type definition (LSP) | goto_type_definition |
r | Go to references (LSP) | goto_reference |
i | Go to implementation (LSP) | goto_implementation |
a | Go to the last accessed/alternate file | goto_last_accessed_file |
m | Go to the last modified/alternate file | goto_last_modified_file |
n | Go to next buffer | goto_next_buffer |
p | Go to previous buffer | goto_previous_buffer |
. | Go to last modification in current file | goto_last_modification |
j | Move down textual (instead of visual) line | move_line_down |
k | Move up textual (instead of visual) line | move_line_up |
Match mode
+Accessed by typing m
in normal mode.
See the relevant section in Usage for an explanation about +surround and textobject usage.
+Key | Description | Command |
---|---|---|
m | Goto matching bracket (TS) | match_brackets |
s <char> | Surround current selection with <char> | surround_add |
r <from><to> | Replace surround character <from> with <to> | surround_replace |
d <char> | Delete surround character <char> | surround_delete |
a <object> | Select around textobject | select_textobject_around |
i <object> | Select inside textobject | select_textobject_inner |
TODO: Mappings for selecting syntax nodes (a superset of [
).
Window mode
+Accessed by typing Ctrl-w
in normal mode.
This layer is similar to Vim keybindings as Kakoune does not support windows.
+Key | Description | Command |
---|---|---|
w , Ctrl-w | Switch to next window | rotate_view |
v , Ctrl-v | Vertical right split | vsplit |
s , Ctrl-s | Horizontal bottom split | hsplit |
f | Go to files in the selection in horizontal splits | goto_file |
F | Go to files in the selection in vertical splits | goto_file |
h , Ctrl-h , Left | Move to left split | jump_view_left |
j , Ctrl-j , Down | Move to split below | jump_view_down |
k , Ctrl-k , Up | Move to split above | jump_view_up |
l , Ctrl-l , Right | Move to right split | jump_view_right |
q , Ctrl-q | Close current window | wclose |
o , Ctrl-o | Only keep the current window, closing all the others | wonly |
H | Swap window to the left | swap_view_left |
J | Swap window downwards | swap_view_down |
K | Swap window upwards | swap_view_up |
L | Swap window to the right | swap_view_right |
Space mode
+Accessed by typing Space
in normal mode.
This layer is a kludge of mappings, mostly pickers.
+Key | Description | Command |
---|---|---|
f | Open file picker | file_picker |
F | Open file picker at current working directory | file_picker_in_current_directory |
b | Open buffer picker | buffer_picker |
j | Open jumplist picker | jumplist_picker |
g | Debug (experimental) | N/A |
k | Show documentation for item under cursor in a popup (LSP) | hover |
s | Open document symbol picker (LSP) | symbol_picker |
S | Open workspace symbol picker (LSP) | workspace_symbol_picker |
d | Open document diagnostics picker (LSP) | diagnostics_picker |
D | Open workspace diagnostics picker (LSP) | workspace_diagnostics_picker |
r | Rename symbol (LSP) | rename_symbol |
a | Apply code action (LSP) | code_action |
h | Select symbol references (LSP) | select_references_to_symbol_under_cursor |
' | Open last fuzzy picker | last_picker |
w | Enter window mode | N/A |
p | Paste system clipboard after selections | paste_clipboard_after |
P | Paste system clipboard before selections | paste_clipboard_before |
y | Yank selections to clipboard | yank_to_clipboard |
Y | Yank main selection to clipboard | yank_main_selection_to_clipboard |
R | Replace selections by clipboard contents | replace_selections_with_clipboard |
/ | Global search in workspace folder | global_search |
? | Open command palette | command_palette |
e | Reveal current file in explorer | reveal_current_file |
++๐ก Global search displays results in a fuzzy picker, use
+Space + '
to bring it back up after opening a file.
Popup
+Displays documentation for item under cursor.
+Key | Description |
---|---|
Ctrl-u | Scroll up |
Ctrl-d | Scroll down |
Unimpaired
+These mappings are in the style of vim-unimpaired.
+Key | Description | Command |
---|---|---|
]d | Go to next diagnostic (LSP) | goto_next_diag |
[d | Go to previous diagnostic (LSP) | goto_prev_diag |
]D | Go to last diagnostic in document (LSP) | goto_last_diag |
[D | Go to first diagnostic in document (LSP) | goto_first_diag |
]f | Go to next function (TS) | goto_next_function |
[f | Go to previous function (TS) | goto_prev_function |
]t | Go to next type definition (TS) | goto_next_class |
[t | Go to previous type definition (TS) | goto_prev_class |
]a | Go to next argument/parameter (TS) | goto_next_parameter |
[a | Go to previous argument/parameter (TS) | goto_prev_parameter |
]c | Go to next comment (TS) | goto_next_comment |
[c | Go to previous comment (TS) | goto_prev_comment |
]T | Go to next test (TS) | goto_next_test |
[T | Go to previous test (TS) | goto_prev_test |
]p | Go to next paragraph | goto_next_paragraph |
[p | Go to previous paragraph | goto_prev_paragraph |
]g | Go to next change | goto_next_change |
[g | Go to previous change | goto_prev_change |
]G | Go to last change | goto_last_change |
[G | Go to first change | goto_first_change |
]Space | Add newline below | add_newline_below |
[Space | Add newline above | add_newline_above |
Insert mode
+Accessed by typing i
in normal mode.
Insert mode bindings are minimal by default. Helix is designed to +be a modal editor, and this is reflected in the user experience and internal +mechanics. Changes to the text are only saved for undos when +escaping from insert mode to normal mode.
+++๐ก New users are strongly encouraged to learn the modal editing paradigm +to get the smoothest experience.
+
Key | Description | Command |
---|---|---|
Escape | Switch to normal mode | normal_mode |
Ctrl-s | Commit undo checkpoint | commit_undo_checkpoint |
Ctrl-x | Autocomplete | completion |
Ctrl-r | Insert a register content | insert_register |
Ctrl-w , Alt-Backspace | Delete previous word | delete_word_backward |
Alt-d , Alt-Delete | Delete next word | delete_word_forward |
Ctrl-u | Delete to start of line | kill_to_line_start |
Ctrl-k | Delete to end of line | kill_to_line_end |
Ctrl-h , Backspace , Shift-Backspace | Delete previous char | delete_char_backward |
Ctrl-d , Delete | Delete next char | delete_char_forward |
Ctrl-j , Enter | Insert new line | insert_newline |
These keys are not recommended, but are included for new users less familiar +with modal editors.
+Key | Description | Command |
---|---|---|
Up | Move to previous line | move_line_up |
Down | Move to next line | move_line_down |
Left | Backward a char | move_char_left |
Right | Forward a char | move_char_right |
PageUp | Move one page up | page_up |
PageDown | Move one page down | page_down |
Home | Move to line start | goto_line_start |
End | Move to line end | goto_line_end_newline |
As you become more comfortable with modal editing, you may want to disable some
+insert mode bindings. You can do this by editing your config.toml
file.
[keys.insert]
+up = "no_op"
+down = "no_op"
+left = "no_op"
+right = "no_op"
+pageup = "no_op"
+pagedown = "no_op"
+home = "no_op"
+end = "no_op"
+
+Select / extend mode
+Accessed by typing v
in normal mode.
Select mode echoes Normal mode, but changes any movements to extend
+selections rather than replace them. Goto motions are also changed to
+extend, so that vgl
, for example, extends the selection to the end of
+the line.
Search is also affected. By default, n
and N
will remove the current
+selection and select the next instance of the search term. Toggling this
+mode before pressing n
or N
makes it possible to keep the current
+selection. Toggling it on and off during your iterative searching allows
+you to selectively add search terms to your selections.
Picker
+Keys to use within picker. Remapping currently not supported.
+Key | Description |
---|---|
Shift-Tab , Up , Ctrl-p | Previous entry |
Tab , Down , Ctrl-n | Next entry |
PageUp , Ctrl-u | Page up |
PageDown , Ctrl-d | Page down |
Home | Go to first entry |
End | Go to last entry |
Enter | Open selected |
Alt-Enter | Open selected in the background without closing the picker |
Ctrl-s | Open horizontally |
Ctrl-v | Open vertically |
Ctrl-t | Toggle preview |
Escape , Ctrl-c | Close picker |
Prompt
+Keys to use within prompt, Remapping currently not supported.
+Key | Description |
---|---|
Escape , Ctrl-c | Close prompt |
Alt-b , Ctrl-Left | Backward a word |
Ctrl-b , Left | Backward a char |
Alt-f , Ctrl-Right | Forward a word |
Ctrl-f , Right | Forward a char |
Ctrl-e , End | Move prompt end |
Ctrl-a , Home | Move prompt start |
Ctrl-w , Alt-Backspace , Ctrl-Backspace | Delete previous word |
Alt-d , Alt-Delete , Ctrl-Delete | Delete next word |
Ctrl-u | Delete to start of line |
Ctrl-k | Delete to end of line |
Backspace , Ctrl-h , Shift-Backspace | Delete previous char |
Delete , Ctrl-d | Delete next char |
Ctrl-s | Insert a word under doc cursor, may be changed to Ctrl-r Ctrl-w later |
Ctrl-p , Up | Select previous history |
Ctrl-n , Down | Select next history |
Ctrl-r | Insert the content of the register selected by following input char |
Tab | Select next completion item |
BackTab | Select previous completion item |
Enter | Open selected |
File explorer
+Press ?
to see keymaps. Remapping currently not supported.
Commands
+Command mode can be activated by pressing :
. The built-in commands are:
Name | Description |
---|---|
:quit , :q | Close the current view. |
:quit! , :q! | Force close the current view, ignoring unsaved changes. |
:open , :o | Open a file from disk into the current view. |
:buffer-close , :bc , :bclose | Close the current buffer. |
:buffer-close! , :bc! , :bclose! | Close the current buffer forcefully, ignoring unsaved changes. |
:buffer-close-others , :bco , :bcloseother | Close all buffers but the currently focused one. |
:buffer-close-others! , :bco! , :bcloseother! | Force close all buffers but the currently focused one. |
:buffer-close-all , :bca , :bcloseall | Close all buffers without quitting. |
:buffer-close-all! , :bca! , :bcloseall! | Force close all buffers ignoring unsaved changes without quitting. |
:buffer-next , :bn , :bnext | Goto next buffer. |
:buffer-previous , :bp , :bprev | Goto previous buffer. |
:write , :w | Write changes to disk. Accepts an optional path (:write some/path.txt) |
:write! , :w! | Force write changes to disk creating necessary subdirectories. Accepts an optional path (:write! some/path.txt) |
:write-buffer-close , :wbc | Write changes to disk and closes the buffer. Accepts an optional path (:write-buffer-close some/path.txt) |
:write-buffer-close! , :wbc! | Force write changes to disk creating necessary subdirectories and closes the buffer. Accepts an optional path (:write-buffer-close! some/path.txt) |
:new , :n | Create a new scratch buffer. |
:format , :fmt | Format the file using the LSP formatter. |
:indent-style | Set the indentation style for editing. ('t' for tabs or 1-8 for number of spaces.) |
:line-ending | Set the document's default line ending. Options: crlf, lf. |
:earlier , :ear | Jump back to an earlier point in edit history. Accepts a number of steps or a time span. |
:later , :lat | Jump to a later point in edit history. Accepts a number of steps or a time span. |
:write-quit , :wq , :x | Write changes to disk and close the current view. Accepts an optional path (:wq some/path.txt) |
:write-quit! , :wq! , :x! | Write changes to disk and close the current view forcefully. Accepts an optional path (:wq! some/path.txt) |
:write-all , :wa | Write changes from all buffers to disk. |
:write-all! , :wa! | Forcefully write changes from all buffers to disk creating necessary subdirectories. |
:write-quit-all , :wqa , :xa | Write changes from all buffers to disk and close all views. |
:write-quit-all! , :wqa! , :xa! | Write changes from all buffers to disk and close all views forcefully (ignoring unsaved changes). |
:quit-all , :qa | Close all views. |
:quit-all! , :qa! | Force close all views ignoring unsaved changes. |
:cquit , :cq | Quit with exit code (default 1). Accepts an optional integer exit code (:cq 2). |
:cquit! , :cq! | Force quit with exit code (default 1) ignoring unsaved changes. Accepts an optional integer exit code (:cq! 2). |
:theme | Change the editor theme (show current theme if no name specified). |
:yank-join | Yank joined selections. A separator can be provided as first argument. Default value is newline. |
:clipboard-yank | Yank main selection into system clipboard. |
:clipboard-yank-join | Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline. |
:primary-clipboard-yank | Yank main selection into system primary clipboard. |
:primary-clipboard-yank-join | Yank joined selections into system primary clipboard. A separator can be provided as first argument. Default value is newline. |
:clipboard-paste-after | Paste system clipboard after selections. |
:clipboard-paste-before | Paste system clipboard before selections. |
:clipboard-paste-replace | Replace selections with content of system clipboard. |
:primary-clipboard-paste-after | Paste primary clipboard after selections. |
:primary-clipboard-paste-before | Paste primary clipboard before selections. |
:primary-clipboard-paste-replace | Replace selections with content of system primary clipboard. |
:show-clipboard-provider | Show clipboard provider name in status bar. |
:change-current-directory , :cd | Change the current working directory. |
:show-directory , :pwd | Show the current working directory. |
:encoding | Set encoding. Based on https://encoding.spec.whatwg.org . |
:character-info , :char | Get info about the character under the primary cursor. |
:reload , :rl | Discard changes and reload from the source file. |
:reload-all , :rla | Discard changes and reload all documents from the source files. |
:update , :u | Write changes only if the file has been modified. |
:lsp-workspace-command | Open workspace command picker |
:lsp-restart | Restarts the language servers used by the current doc |
:lsp-stop | Stops the language servers that are used by the current doc |
:tree-sitter-scopes | Display tree sitter scopes, primarily for theming and development. |
:tree-sitter-highlight-name | Display name of tree-sitter highlight scope under the cursor. |
:debug-start , :dbg | Start a debug session from a given template with given parameters. |
:debug-remote , :dbg-tcp | Connect to a debug adapter by TCP address and start a debugging session from a given template with given parameters. |
:debug-eval | Evaluate expression in current debug context. |
:vsplit , :vs | Open the file in a vertical split. |
:vsplit-new , :vnew | Open a scratch buffer in a vertical split. |
:hsplit , :hs , :sp | Open the file in a horizontal split. |
:hsplit-new , :hnew | Open a scratch buffer in a horizontal split. |
:tutor | Open the tutorial. |
:goto , :g | Goto line number. |
:set-language , :lang | Set the language of current buffer (show current language if no value specified). |
:set-option , :set | Set a config option at runtime. For example to disable smart case search, use :set search.smart-case false . |
:toggle-option , :toggle | Toggle a boolean config option at runtime. For example to toggle smart case search, use :toggle search.smart-case . |
:get-option , :get | Get the current value of a config option. |
:sort | Sort ranges in selection. |
:rsort | Sort ranges in selection in reverse order. |
:reflow | Hard-wrap the current selection of lines to a given width. |
:tree-sitter-subtree , :ts-subtree | Display tree sitter subtree under cursor, primarily for debugging queries. |
:config-reload | Refresh user config. |
:config-open | Open the user config.toml file. |
:config-open-workspace | Open the workspace config.toml file. |
:log-open | Open the helix log file. |
:insert-output | Run shell command, inserting output before each selection. |
:append-output | Run shell command, appending output after each selection. |
:pipe | Pipe each selection to the shell command. |
:pipe-to | Pipe each selection to the shell command, ignoring output. |
:run-shell-command , :sh | Run a shell command |
:reset-diff-change , :diffget , :diffg | Reset the diff change at the cursor position. |
:clear-register | Clear given register. If no argument is provided, clear all registers. |
:redraw | Clear and re-render the whole UI |
Language Support
+The following languages and Language Servers are supported. To use +Language Server features, you must first install the +appropriate Language Server.
+You can check the language support in your installed helix version with hx --health
.
Also see the Language Configuration docs and the Adding +Languages guide for more language configuration information.
+Language | Syntax Highlighting | Treesitter Textobjects | Auto Indent | Rainbow Brackets | Default LSP |
---|---|---|---|---|---|
astro | โ | ||||
awk | โ | โ | awk-language-server | ||
bash | โ | โ | โ | โ | bash-language-server |
bass | โ | bass | |||
beancount | โ | ||||
bibtex | โ | texlab | |||
bicep | โ | bicep-langserver | |||
blueprint | โ | blueprint-compiler | |||
c | โ | โ | โ | โ | clangd |
c-sharp | โ | โ | OmniSharp | ||
cabal | |||||
cairo | โ | โ | โ | cairo-language-server | |
capnp | โ | โ | |||
clojure | โ | โ | clojure-lsp | ||
cmake | โ | โ | โ | cmake-language-server | |
comment | โ | ||||
common-lisp | โ | โ | cl-lsp | ||
cpon | โ | โ | |||
cpp | โ | โ | โ | โ | clangd |
crystal | โ | โ | crystalline | ||
css | โ | โ | vscode-css-language-server | ||
cue | โ | cuelsp | |||
d | โ | โ | โ | serve-d | |
dart | โ | โ | dart | ||
devicetree | โ | ||||
dhall | โ | โ | dhall-lsp-server | ||
diff | โ | ||||
dockerfile | โ | docker-langserver | |||
dot | โ | dot-language-server | |||
dtd | โ | ||||
edoc | โ | ||||
eex | โ | ||||
ejs | โ | ||||
elixir | โ | โ | โ | โ | elixir-ls |
elm | โ | โ | elm-language-server | ||
elvish | โ | elvish | |||
env | โ | ||||
erb | โ | ||||
erlang | โ | โ | โ | erlang_ls | |
esdl | โ | ||||
fish | โ | โ | โ | ||
forth | โ | forth-lsp | |||
fortran | โ | โ | fortls | ||
fsharp | โ | fsautocomplete | |||
gas | โ | โ | |||
gdscript | โ | โ | โ | ||
gemini | โ | ||||
git-attributes | โ | ||||
git-commit | โ | โ | |||
git-config | โ | ||||
git-ignore | โ | ||||
git-rebase | โ | ||||
gleam | โ | โ | gleam | ||
glsl | โ | โ | โ | ||
go | โ | โ | โ | โ | gopls |
godot-resource | โ | ||||
gomod | โ | gopls | |||
gotmpl | โ | gopls | |||
gowork | โ | gopls | |||
graphql | โ | graphql-lsp | |||
hare | โ | ||||
haskell | โ | โ | haskell-language-server-wrapper | ||
haskell-persistent | โ | ||||
hcl | โ | โ | terraform-ls | ||
heex | โ | โ | elixir-ls | ||
hosts | โ | ||||
html | โ | โ | vscode-html-language-server | ||
hurl | โ | โ | |||
idris | idris2-lsp | ||||
iex | โ | ||||
ini | โ | ||||
java | โ | โ | โ | โ | jdtls |
javascript | โ | โ | โ | โ | typescript-language-server |
jinja | โ | ||||
jsdoc | โ | ||||
json | โ | โ | โ | vscode-json-language-server | |
json5 | โ | ||||
jsonnet | โ | jsonnet-language-server | |||
jsx | โ | โ | โ | โ | typescript-language-server |
julia | โ | โ | โ | julia | |
just | โ | โ | โ | ||
kdl | โ | ||||
kotlin | โ | kotlin-language-server | |||
latex | โ | โ | texlab | ||
lean | โ | lean | |||
ledger | โ | ||||
llvm | โ | โ | โ | ||
llvm-mir | โ | โ | โ | ||
llvm-mir-yaml | โ | โ | |||
lua | โ | โ | โ | lua-language-server | |
make | โ | ||||
markdoc | โ | markdoc-ls | |||
markdown | โ | marksman | |||
markdown.inline | โ | ||||
matlab | โ | โ | โ | ||
mermaid | โ | ||||
meson | โ | โ | |||
mint | mint | ||||
msbuild | โ | โ | |||
nasm | โ | โ | |||
nickel | โ | โ | nls | ||
nim | โ | โ | โ | nimlangserver | |
nix | โ | โ | nil | ||
nu | โ | ||||
nunjucks | โ | ||||
ocaml | โ | โ | ocamllsp | ||
ocaml-interface | โ | ocamllsp | |||
odin | โ | โ | ols | ||
opencl | โ | โ | โ | clangd | |
openscad | โ | openscad-lsp | |||
org | โ | ||||
pascal | โ | โ | pasls | ||
passwd | โ | ||||
pem | โ | ||||
perl | โ | โ | โ | perlnavigator | |
php | โ | โ | โ | intelephense | |
po | โ | โ | |||
pod | โ | ||||
ponylang | โ | โ | โ | ||
prisma | โ | prisma-language-server | |||
prolog | swipl | ||||
protobuf | โ | โ | bufls , pb | ||
prql | โ | ||||
purescript | โ | โ | purescript-language-server | ||
python | โ | โ | โ | โ | pylsp |
qml | โ | โ | qmlls | ||
r | โ | R | |||
racket | โ | โ | racket | ||
regex | โ | โ | |||
rego | โ | regols | |||
rescript | โ | โ | rescript-language-server | ||
rmarkdown | โ | โ | R | ||
robot | โ | robotframework_ls | |||
ron | โ | โ | |||
rst | โ | ||||
ruby | โ | โ | โ | โ | solargraph |
rust | โ | โ | โ | โ | rust-analyzer |
sage | โ | โ | |||
scala | โ | โ | metals | ||
scheme | โ | โ | |||
scss | โ | โ | vscode-css-language-server | ||
slint | โ | โ | slint-lsp | ||
smithy | โ | cs | |||
sml | โ | ||||
solidity | โ | solc | |||
sql | โ | ||||
sshclientconfig | โ | ||||
starlark | โ | โ | โ | ||
strace | โ | ||||
svelte | โ | โ | svelteserver | ||
sway | โ | โ | โ | forc | |
swift | โ | sourcekit-lsp | |||
t32 | โ | ||||
tablegen | โ | โ | โ | ||
task | โ | ||||
templ | โ | templ | |||
tfvars | โ | โ | terraform-ls | ||
todotxt | โ | ||||
toml | โ | โ | taplo | ||
tsq | โ | โ | |||
tsx | โ | โ | โ | โ | typescript-language-server |
twig | โ | ||||
typescript | โ | โ | โ | โ | typescript-language-server |
ungrammar | โ | ||||
unison | โ | ||||
uxntal | โ | ||||
v | โ | โ | โ | v-analyzer | |
vala | โ | vala-language-server | |||
verilog | โ | โ | svlangserver | ||
vhdl | โ | vhdl_ls | |||
vhs | โ | ||||
vue | โ | vue-language-server | |||
wast | โ | ||||
wat | โ | ||||
webc | โ | ||||
wgsl | โ | wgsl_analyzer | |||
wit | โ | โ | |||
wren | โ | โ | โ | ||
xit | โ | ||||
xml | โ | โ | โ | ||
yaml | โ | โ | โ | yaml-language-server , ansible-language-server | |
yuck | โ | ||||
zig | โ | โ | โ | โ | zls |
Migrating from Vim
+Helix's editing model is strongly inspired from Vim and Kakoune, and a notable
+difference from Vim (and the most striking similarity to Kakoune) is that Helix
+follows the selection โ action
model. This means that whatever you are
+going to act on (a word, a paragraph, a line, etc.) is selected first and the
+action itself (delete, change, yank, etc.) comes second. A cursor is simply a
+single width selection.
See also Kakoune's Migrating from Vim and Helix's Migrating from Vim.
+++TODO: Mention textobjects, surround, registers
+
Configuration
+To override global configuration parameters, create a config.toml
file located in your config directory:
-
+
- Linux and Mac:
~/.config/helix/config.toml
+ - Windows:
%AppData%\helix\config.toml
+
++๐ก You can easily open the config file by typing
+:config-open
within Helix normal mode.
Example config:
+theme = "onedark"
+
+[editor]
+line-number = "relative"
+mouse = false
+
+[editor.cursor-shape]
+insert = "bar"
+normal = "block"
+select = "underline"
+
+[editor.file-picker]
+hidden = false
+
+You can use a custom configuration file by specifying it with the -c
or
+--config
command line argument, for example hx -c path/to/custom-config.toml
.
+Additionally, you can reload the configuration file by sending the USR1
+signal to the Helix process on Unix operating systems, such as by using the command pkill -USR1 hx
.
Finally, you can have a config.toml
local to a project by putting it under a .helix
directory in your repository.
+Its settings will be merged with the configuration directory config.toml
and the built-in configuration.
Editor
+[editor]
Section
+Key | Description | Default |
---|---|---|
scrolloff | Number of lines of padding around the edge of the screen when scrolling | 5 |
mouse | Enable mouse mode | true |
middle-click-paste | Middle click paste support | true |
scroll-lines | Number of lines to scroll per scroll wheel step | 3 |
shell | Shell to use when running external commands | Unix: ["sh", "-c"] Windows: ["cmd", "/C"] |
line-number | Line number display: absolute simply shows each line's number, while relative shows the distance from the current line. When unfocused or in insert mode, relative will still show absolute line numbers | absolute |
cursorline | Highlight all lines with a cursor | false |
cursorcolumn | Highlight all columns with a cursor | false |
gutters | Gutters to display: Available are diagnostics and diff and line-numbers and spacer , note that diagnostics also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | ["diagnostics", "spacer", "line-numbers", "spacer", "diff"] |
auto-completion | Enable automatic pop up of auto-completion | true |
auto-format | Enable automatic formatting on save | true |
auto-save | Enable automatic saving on the focus moving away from Helix. Requires focus event support from your terminal | false |
idle-timeout | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant | 400 |
preview-completion-insert | Whether to apply completion item instantly when selected | true |
completion-trigger-len | The min-length of word under cursor to trigger autocompletion | 2 |
completion-replace | Set to true to make completions always replace the entire word and not just the part before the cursor | false |
auto-info | Whether to display info boxes | true |
true-color | Set to true to override automatic detection of terminal truecolor support in the event of a false negative | false |
undercurl | Set to true to override automatic detection of terminal undercurl support in the event of a false negative | false |
rulers | List of column positions at which to display the rulers. Can be overridden by language specific rulers in languages.toml file | [] |
bufferline | Renders a line at the top of the editor displaying open buffers. Can be always , never or multiple (only shown if more than one buffer is in use) | never |
initial-mode | The initial mode for newly opened editors. | "normal" |
color-modes | Whether to color the mode indicator with different colors depending on the mode itself | false |
text-width | Maximum line length. Used for the :reflow command and soft-wrapping if soft-wrap.wrap-at-text-width is set | 80 |
workspace-lsp-roots | Directories relative to the workspace root that are treated as LSP roots. Should only be set in .helix/config.toml | [] |
default-line-ending | The line ending to use for new documents. Can be native , lf , crlf , ff , cr or nel . native uses the platform's native line ending (crlf on Windows, otherwise lf ). | native |
insert-final-newline | Whether to automatically insert a trailing line-ending on write if missing | true |
rainbow-brackets | Whether to render rainbow colors for matching brackets. Requires tree-sitter rainbows.scm queries for the language. | false |
[editor.statusline]
Section
+Allows configuring the statusline at the bottom of the editor.
+The configuration distinguishes between three areas of the status line:
+[ ... ... LEFT ... ... | ... ... ... ... CENTER ... ... ... ... | ... ... RIGHT ... ... ]
Statusline elements can be defined as follows:
+[editor.statusline]
+left = ["mode", "spinner"]
+center = ["file-name"]
+right = ["diagnostics", "selections", "position", "file-encoding", "file-line-ending", "file-type"]
+separator = "โ"
+mode.normal = "NORMAL"
+mode.insert = "INSERT"
+mode.select = "SELECT"
+
+The [editor.statusline]
key takes the following sub-keys:
Key | Description | Default |
---|---|---|
left | A list of elements aligned to the left of the statusline | ["mode", "spinner", "file-name", "read-only-indicator", "file-modification-indicator"] |
center | A list of elements aligned to the middle of the statusline | [] |
right | A list of elements aligned to the right of the statusline | ["diagnostics", "selections", "register", "position", "file-encoding"] |
separator | The character used to separate elements in the statusline | "โ" |
mode.normal | The text shown in the mode element for normal mode | "NOR" |
mode.insert | The text shown in the mode element for insert mode | "INS" |
mode.select | The text shown in the mode element for select mode | "SEL" |
The following statusline elements can be configured:
+Key | Description |
---|---|
mode | The current editor mode (mode.normal /mode.insert /mode.select ) |
spinner | A progress spinner indicating LSP activity |
file-name | The path/name of the opened file |
file-base-name | The basename of the opened file |
file-modification-indicator | The indicator to show whether the file is modified (a [+] appears when there are unsaved changes) |
file-encoding | The encoding of the opened file if it differs from UTF-8 |
file-line-ending | The file line endings (CRLF or LF) |
read-only-indicator | An indicator that shows [readonly] when a file cannot be written |
total-line-numbers | The total line numbers of the opened file |
file-type | The type of the opened file |
diagnostics | The number of warnings and/or errors |
workspace-diagnostics | The number of warnings and/or errors on workspace |
selections | The number of active selections |
primary-selection-length | The number of characters currently in primary selection |
position | The cursor position |
position-percentage | The cursor position as a percentage of the total number of lines |
separator | The string defined in editor.statusline.separator (defaults to "โ" ) |
spacer | Inserts a space between elements (multiple/contiguous spacers may be specified) |
version-control | The current branch name or detached commit hash of the opened workspace |
register | The current selected register |
[editor.lsp]
Section
+Key | Description | Default |
---|---|---|
enable | Enables LSP integration. Setting to false will completely disable language servers regardless of language settings. | true |
display-messages | Display LSP progress messages below statusline1 | false |
auto-signature-help | Enable automatic popup of signature help (parameter hints) | true |
display-inlay-hints | Display inlay hints2 | false |
display-signature-help-docs | Display docs under signature help popup | true |
snippets | Enables snippet completions. Requires a server restart (:lsp-restart ) to take effect after :config-reload /:set . | true |
goto-reference-include-declaration | Include declaration in the goto references popup. | true |
By default, a progress spinner is shown in the statusline beside the file path.
+You may also have to activate them in the LSP config for them to appear, not just in Helix. Inlay hints in Helix are still being improved on and may be a little bit laggy/janky under some circumstances. Please report any bugs you see so we can fix them!
+[editor.cursor-shape]
Section
+Defines the shape of cursor in each mode.
+Valid values for these options are block
, bar
, underline
, or hidden
.
++๐ก Due to limitations of the terminal environment, only the primary cursor can +change shape.
+
Key | Description | Default |
---|---|---|
normal | Cursor shape in normal mode | block |
insert | Cursor shape in insert mode | block |
select | Cursor shape in select mode | block |
[editor.file-picker]
Section
+Set options for file picker and global search. Ignoring a file means it is +not visible in the Helix file picker and global search.
+All git related options are only enabled in a git repository.
+Key | Description | Default |
---|---|---|
hidden | Enables ignoring hidden files | true |
follow-symlinks | Follow symlinks instead of ignoring them | true |
deduplicate-links | Ignore symlinks that point at files already shown in the picker | true |
parents | Enables reading ignore files from parent directories | true |
ignore | Enables reading .ignore files | true |
git-ignore | Enables reading .gitignore files | true |
git-global | Enables reading global .gitignore , whose path is specified in git's config: core.excludefile option | true |
git-exclude | Enables reading .git/info/exclude files | true |
max-depth | Set with an integer value for maximum depth to recurse | Defaults to None . |
[editor.auto-pairs]
Section
+Enables automatic insertion of pairs to parentheses, brackets, etc. Can be a +simple boolean value, or a specific mapping of pairs of single characters.
+To disable auto-pairs altogether, set auto-pairs
to false
:
[editor]
+auto-pairs = false # defaults to `true`
+
+The default pairs are (){}[]''""``
, but these can be customized by
+setting auto-pairs
to a TOML table:
[editor.auto-pairs]
+'(' = ')'
+'{' = '}'
+'[' = ']'
+'"' = '"'
+'`' = '`'
+'<' = '>'
+
+Additionally, this setting can be used in a language config. Unless
+the editor setting is false
, this will override the editor config in
+documents with this language.
Example languages.toml
that adds <> and removes ''
[[language]]
+name = "rust"
+
+[language.auto-pairs]
+'(' = ')'
+'{' = '}'
+'[' = ']'
+'"' = '"'
+'`' = '`'
+'<' = '>'
+
+[editor.search]
Section
+Search specific options.
+Key | Description | Default |
---|---|---|
smart-case | Enable smart case regex searching (case-insensitive unless pattern contains upper case characters) | true |
wrap-around | Whether the search should wrap after depleting the matches | true |
[editor.whitespace]
Section
+Options for rendering whitespace with visible characters. Use :set whitespace.render all
to temporarily enable visible whitespace.
Key | Description | Default |
---|---|---|
render | Whether to render whitespace. May either be "all" or "none" , or a table with sub-keys space , nbsp , tab , and newline | "none" |
characters | Literal characters to use when rendering whitespace. Sub-keys may be any of tab , space , nbsp , newline or tabpad | See example below |
Example
+[editor.whitespace]
+render = "all"
+# or control each character
+[editor.whitespace.render]
+space = "all"
+tab = "all"
+newline = "none"
+
+[editor.whitespace.characters]
+space = "ยท"
+nbsp = "โฝ"
+tab = "โ"
+newline = "โ"
+tabpad = "ยท" # Tabs will look like "โยทยทยท" (depending on tab width)
+
+[editor.indent-guides]
Section
+Options for rendering vertical indent guides.
+Key | Description | Default |
---|---|---|
render | Whether to render indent guides | false |
character | Literal character to use for rendering the indent guide | โ |
skip-levels | Number of indent levels to skip | 0 |
rainbow-option | Enum to set rainbow indentations. Options: normal , dim and none . | none |
Example:
+[editor.indent-guides]
+render = true
+character = "โ" # Some characters that work well: "โ", "โ", "โ", "โธฝ"
+skip-levels = 1
+rainbow-option = "normal"
+
+[editor.gutters]
Section
+For simplicity, editor.gutters
accepts an array of gutter types, which will
+use default settings for all gutter components.
[editor]
+gutters = ["diff", "diagnostics", "line-numbers", "spacer"]
+
+To customize the behavior of gutters, the [editor.gutters]
section must
+be used. This section contains top level settings, as well as settings for
+specific gutter components as subsections.
Key | Description | Default |
---|---|---|
layout | A vector of gutters to display | ["diagnostics", "spacer", "line-numbers", "spacer", "diff"] |
Example:
+[editor.gutters]
+layout = ["diff", "diagnostics", "line-numbers", "spacer"]
+
+[editor.gutters.line-numbers]
Section
+Options for the line number gutter
+Key | Description | Default |
---|---|---|
min-width | The minimum number of characters to use | 3 |
Example:
+[editor.gutters.line-numbers]
+min-width = 1
+
+[editor.gutters.diagnostics]
Section
+Currently unused
+[editor.gutters.diff]
Section
+Currently unused
+[editor.gutters.spacer]
Section
+Currently unused
+[editor.soft-wrap]
Section
+Options for soft wrapping lines that exceed the view width:
+Key | Description | Default |
---|---|---|
enable | Whether soft wrapping is enabled. | false |
max-wrap | Maximum free space left at the end of the line. | 20 |
max-indent-retain | Maximum indentation to carry over when soft wrapping a line. | 40 |
wrap-indicator | Text inserted before soft wrapped lines, highlighted with ui.virtual.wrap | โช |
wrap-at-text-width | Soft wrap at text-width instead of using the full viewport size. | false |
Example:
+[editor.soft-wrap]
+enable = true
+max-wrap = 25 # increase value to reduce forced mid-word wrapping
+max-indent-retain = 0
+wrap-indicator = "" # set wrap-indicator to "" to hide it
+
+[editor.explorer]
Section
+Sets explorer side width and style.
+Key | Description | Default |
---|---|---|
column-width | explorer side width | 30 |
position | explorer widget position, left or right | left |
[editor.smart-tab]
Section
+Key | Description | Default |
---|---|---|
enable | If set to true, then when the cursor is in a position with non-whitespace to its left, instead of inserting a tab, it will run move_parent_node_end . If there is only whitespace to the left, then it inserts a tab as normal. With the default bindings, to explicitly insert a tab character, press Shift-tab. | true |
supersede-menu | Normally, when a menu is on screen, such as when auto complete is triggered, the tab key is bound to cycling through the items. This means when menus are on screen, one cannot use the tab key to trigger the smart-tab command. If this option is set to true, the smart-tab command always takes precedence, which means one cannot use the tab key to cycle through menu items. One of the other bindings must be used instead, such as arrow keys or C-n /C-p . | false |
[editor.digraphs]
Section
+By default, special characters can be input using the insert_digraphs
command, bound to \
in normal mode.
+Custom digraphs can be added to the editor.digraphs
section of the config.
[editor.digraphs]
+ka = "ใ"
+ku = { symbols = "ใ", description = "The japanese character Ku" }
+shrug = "ยฏ\\_(ใ)_/ยฏ"
+
+Themes
+To use a theme add theme = "<name>"
to the top of your config.toml
file, or select it during runtime using :theme <name>
.
Creating a theme
+Create a file with the name of your theme as the file name (i.e mytheme.toml
) and place it in your themes
directory (i.e ~/.config/helix/themes
or %AppData%\helix\themes
on Windows). The directory might have to be created beforehand.
++๐ก The names "default" and "base16_default" are reserved for built-in themes +and cannot be overridden by user-defined themes.
+
Overview
+Each line in the theme file is specified as below:
+key = { fg = "#ffffff", bg = "#000000", underline = { color = "#ff0000", style = "curl"}, modifiers = ["bold", "italic"] }
+
+Where key
represents what you want to style, fg
specifies the foreground color, bg
the background color, underline
the underline style
/color
, and modifiers
is a list of style modifiers. bg
, underline
and modifiers
can be omitted to defer to the defaults.
To specify only the foreground color:
+key = "#ffffff"
+
+If the key contains a dot '.'
, it must be quoted to prevent it being parsed as a dotted key.
"key.key" = "#ffffff"
+
+For inspiration, you can find the default theme.toml
+here and
+user-submitted themes
+here.
Using the linter
+Use the supplied linting tool to check for errors and missing scopes:
+cargo xtask themelint onedark # replace onedark with <name>
+
+The details of theme creation
+Color palettes
+It's recommended to define a palette of named colors, and refer to them in the
+configuration values in your theme. To do this, add a table called
+palette
to your theme file:
"ui.background" = "white"
+"ui.text" = "black"
+
+[palette]
+white = "#ffffff"
+black = "#000000"
+
+Keep in mind that the [palette]
table includes all keys after its header,
+so it should be defined after the normal theme options.
The default palette uses the terminal's default 16 colors, and the colors names
+are listed below. The [palette]
section in the config file takes precedence
+over it and is merged into the default palette.
Color Name |
---|
default |
black |
red |
green |
yellow |
blue |
magenta |
cyan |
gray |
light-red |
light-green |
light-yellow |
light-blue |
light-magenta |
light-cyan |
light-gray |
white |
Modifiers
+The following values may be used as modifier, provided they are supported by +your terminal emulator.
+Modifier |
---|
bold |
dim |
italic |
underlined |
slow_blink |
rapid_blink |
reversed |
hidden |
crossed_out |
++๐ก The
+underlined
modifier is deprecated and only available for backwards compatibility. +Its behavior is equivalent to settingunderline.style="line"
.
Underline style
+One of the following values may be used as a value for underline.style
, providing it is
+supported by your terminal emulator.
Modifier |
---|
line |
curl |
dashed |
dotted |
double_line |
Inheritance
+Extend other themes by setting the inherits
property to an existing theme.
inherits = "boo_berry"
+
+# Override the theming for "keyword"s:
+"keyword" = { fg = "gold" }
+
+# Override colors in the palette:
+[palette]
+berry = "#2A2A4D"
+
+Rainbow
+The rainbow
key is used for rainbow highlight for matching brackets.
+The key is a list of styles.
rainbow = ["#ff0000", "#ffa500", "#fff000", { fg = "#00ff00", modifiers = ["bold"] }]
+
+Colors from the palette and modifiers may be used.
+Scopes
+The following is a list of scopes available to use for styling:
+Syntax highlighting
+These keys match tree-sitter scopes.
+When determining styling for a highlight, the longest matching theme key will be used. For example, if the highlight is function.builtin.static
, the key function.builtin
will be used instead of function
.
We use a similar set of scopes as +Sublime Text. See also +TextMate scopes.
+-
+
-
+
+attribute
- Class attributes, HTML tag attributes
+ -
+
+type
- Types-
+
builtin
- Primitive types provided by the language (int
,usize
)
+enum
+-
+
variant
+
+
+ -
+
+constructor
+ -
+
+constant
(TODO: constant.other.placeholder for%v
)-
+
builtin
Special constants provided by the language (true
,false
,nil
etc) +-
+
boolean
+
+character
+-
+
escape
+
+numeric
(numbers) +-
+
integer
+float
+
+
+ -
+
+string
(TODO: string.quoted.{single, double}, string.raw/.unquoted)?-
+
regexp
- Regular expressions
+special
+-
+
path
+url
+symbol
- Erlang/Elixir atoms, Ruby symbols, Clojure keywords
+
+
+ -
+
+comment
- Code comments-
+
line
- Single line comments (//
)
+block
- Block comments (e.g. (/* */
) +-
+
documentation
- Documentation comments (e.g.///
in Rust)
+
+
+ -
+
+variable
- Variables-
+
builtin
- Reserved language variables (self
,this
,super
, etc.)
+parameter
- Function parameters
+other
+-
+
member
- Fields of composite data types (e.g. structs, unions)
+
+
+ -
+
+label
+ -
+
+punctuation
-
+
delimiter
- Commas, colons
+bracket
- Parentheses, angle brackets, etc.
+special
- String interpolation brackets.
+
+ -
+
+keyword
-
+
control
+-
+
conditional
-if
,else
+repeat
-for
,while
,loop
+import
-import
,export
+return
+exception
+
+operator
-or
,in
+directive
- Preprocessor directives (#if
in C)
+function
-fn
,func
+storage
- Keywords describing how things are stored +-
+
type
- The type of something,class
,function
,var
,let
, etc.
+modifier
- Storage modifiers likestatic
,mut
,const
,ref
, etc.
+
+
+ -
+
+operator
-||
,+=
,>
+ -
+
+function
-
+
builtin
+method
+macro
+special
(preprocessor in C)
+
+ -
+
+tag
- Tags (e.g.<body>
in HTML)-
+
builtin
+
+ -
+
+namespace
+ -
+
+special
+ -
+
+markup
-
+
heading
+-
+
marker
+1
,2
,3
,4
,5
,6
- heading text for h1 through h6
+
+list
+-
+
unnumbered
+numbered
+checked
+unchecked
+
+bold
+italic
+strikethrough
+link
+-
+
url
- URLs pointed to by links
+label
- non-URL link references
+text
- URL and image descriptions in links
+
+quote
+raw
+-
+
inline
+block
+
+
+ -
+
+diff
- version control changes-
+
plus
- additions +-
+
gutter
- gutter indicator
+
+minus
- deletions +-
+
gutter
- gutter indicator
+
+delta
- modifications +-
+
moved
- renamed or moved files/changes
+gutter
- gutter indicator
+
+
+
Interface
+These scopes are used for theming the editor interface:
+-
+
markup
+-
+
normal
+-
+
completion
- for completion doc popup UI
+hover
- for hover popup UI
+
+heading
+-
+
completion
- for completion doc popup UI
+hover
- for hover popup UI
+
+raw
+-
+
inline
+-
+
completion
- for completion doc popup UI
+hover
- for hover popup UI
+
+
+
+
Key | Notes |
---|---|
ui.background | |
ui.background.separator | Picker separator below input line |
ui.cursor | |
ui.cursor.normal | |
ui.cursor.insert | |
ui.cursor.select | |
ui.cursor.match | Matching bracket etc. |
ui.cursor.primary | Cursor with primary selection |
ui.cursor.primary.normal | |
ui.cursor.primary.insert | |
ui.cursor.primary.select | |
ui.debug.breakpoint | Breakpoint indicator, found in the gutter |
ui.debug.active | Indicator for the line at which debugging execution is paused at, found in the gutter |
ui.gutter | Gutter |
ui.gutter.selected | Gutter for the line the cursor is on |
ui.highlight.frameline | Line at which debugging execution is paused at |
ui.linenr | Line numbers |
ui.linenr.selected | Line number for the line the cursor is on |
ui.statusline | Statusline |
ui.statusline.inactive | Statusline (unfocused document) |
ui.statusline.normal | Statusline mode during normal mode (only if editor.color-modes is enabled) |
ui.statusline.insert | Statusline mode during insert mode (only if editor.color-modes is enabled) |
ui.statusline.select | Statusline mode during select mode (only if editor.color-modes is enabled) |
ui.statusline.separator | Separator character in statusline |
ui.popup | Documentation popups (e.g. Space + k) |
ui.popup.info | Prompt for multiple key options |
ui.window | Borderlines separating splits |
ui.help | Description box for commands |
ui.text | Command prompts, popup text, etc. |
ui.text.focus | The currently selected line in the picker |
ui.text.inactive | Same as ui.text but when the text is inactive (e.g. suggestions) |
ui.text.info | The key: command text in ui.popup.info boxes |
ui.virtual.ruler | Ruler columns (see the editor.rulers config) |
ui.virtual.whitespace | Visible whitespace characters |
ui.virtual.indent-guide | Vertical indent width guides |
ui.virtual.inlay-hint | Default style for inlay hints of all kinds |
ui.virtual.inlay-hint.parameter | Style for inlay hints of kind parameter (LSPs are not required to set a kind) |
ui.virtual.inlay-hint.type | Style for inlay hints of kind type (LSPs are not required to set a kind) |
ui.virtual.wrap | Soft-wrap indicator (see the editor.soft-wrap config) |
ui.menu | Code and command completion menus |
ui.menu.selected | Selected autocomplete item |
ui.menu.scroll | fg sets thumb color, bg sets track color of scrollbar |
ui.selection | For selections in the editing area |
ui.selection.primary | |
ui.highlight | Highlighted lines in the picker preview |
ui.cursorline.primary | The line of the primary cursor (if cursorline is enabled) |
ui.cursorline.secondary | The lines of any other cursors (if cursorline is enabled) |
ui.cursorcolumn.primary | The column of the primary cursor (if cursorcolumn is enabled) |
ui.cursorcolumn.secondary | The columns of any other cursors (if cursorcolumn is enabled) |
warning | Diagnostics warning (gutter) |
error | Diagnostics error (gutter) |
info | Diagnostics info (gutter) |
hint | Diagnostics hint (gutter) |
diagnostic | Diagnostics fallback style (editing area) |
diagnostic.hint | Diagnostics hint (editing area) |
diagnostic.info | Diagnostics info (editing area) |
diagnostic.warning | Diagnostics warning (editing area) |
diagnostic.error | Diagnostics error (editing area) |
Key remapping
+Helix currently supports one-way key remapping through a simple TOML configuration +file. (More powerful solutions such as rebinding via commands will be +available in the future).
+To remap keys, create a config.toml
file in your helix
configuration
+directory (default ~/.config/helix
on Linux systems) with a structure like
+this:
# At most one section each of 'keys.normal', 'keys.insert' and 'keys.select'
+[keys.normal]
+C-s = ":w" # Maps Ctrl-s to the typable command :w which is an alias for :write (save file)
+C-o = ":open ~/.config/helix/config.toml" # Maps Ctrl-o to opening of the helix config file
+a = "move_char_left" # Maps the 'a' key to the move_char_left command
+w = "move_line_up" # Maps the 'w' key move_line_up
+"C-S-esc" = "extend_line" # Maps Ctrl-Shift-Escape to extend_line
+g = { a = "code_action" } # Maps `ga` to show possible code actions
+"ret" = ["open_below", "normal_mode"] # Maps the enter key to open_below then re-enter normal mode
+
+[keys.insert]
+"A-x" = "normal_mode" # Maps Alt-X to enter normal mode
+j = { k = "normal_mode" } # Maps `jk` to exit insert mode
+
+Minor modes
+Minor modes are accessed by pressing a key (usually from normal mode), giving access to dedicated bindings. Bindings +can be modified or added by nesting definitions.
+[keys.insert.j]
+k = "normal_mode" # Maps `jk` to exit insert mode
+
+[keys.normal.g]
+a = "code_action" # Maps `ga` to show possible code actions
+
+# invert `j` and `k` in view mode
+[keys.normal.z]
+j = "scroll_up"
+k = "scroll_down"
+
+# create a new minor mode bound to `+`
+[keys.normal."+"]
+m = ":run-shell-command make"
+c = ":run-shell-command cargo build"
+t = ":run-shell-command cargo test"
+
+Special keys and modifiers
+Ctrl, Shift and Alt modifiers are encoded respectively with the prefixes
+C-
, S-
and A-
. Special keys are encoded as follows:
Key name | Representation |
---|---|
Backspace | "backspace" |
Space | "space" |
Return/Enter | "ret" |
- | "minus" |
Left | "left" |
Right | "right" |
Up | "up" |
Down | "down" |
Home | "home" |
End | "end" |
Page Up | "pageup" |
Page Down | "pagedown" |
Tab | "tab" |
Delete | "del" |
Insert | "ins" |
Null | "null" |
Escape | "esc" |
Keys can be disabled by binding them to the no_op
command.
To remove all default bindings, unbind-default-keys = true
can be added to the top level configuration.
unbind-default-keys = true
+
+# Only these normal mode bindings will be used
+[keys.normal]
+n = "normal_mode"
+t = "goto_definition"
+
+# remember to add bindings to return to normal mode
+[keys.select]
+esc = "normal_mode"
+
+[keys.insert]
+esc = "normal_mode"
+
+A list of commands is available in the Keymap documentation
+and in the source code at helix-term/src/commands.rs
at the invocation of static_commands!
macro and the TypableCommandList
.
Languages
+Language-specific settings and settings for language servers are configured
+in languages.toml
files.
languages.toml
files
+There are three possible locations for a languages.toml
file:
-
+
-
+
In the Helix source code, which lives in the +Helix repository. +It provides the default configurations for languages and language servers.
+
+ -
+
In your configuration directory. This overrides values +from the built-in language configuration. For example, to disable +auto-LSP-formatting in Rust:
+
+# in <config_dir>/helix/languages.toml + +[language-server.mylang-lsp] +command = "mylang-lsp" + +[[language]] +name = "rust" +auto-format = false +
+ -
+
In a
+.helix
folder in your project. Language configuration may also be +overridden local to a project by creating alanguages.toml
file in a +.helix
folder. Its settings will be merged with the language configuration +in the configuration directory and the built-in configuration.
+
Language configuration
+Each language is configured by adding a [[language]]
section to a
+languages.toml
file. For example:
[[language]]
+name = "mylang"
+scope = "source.mylang"
+injection-regex = "mylang"
+file-types = ["mylang", "myl"]
+comment-token = "#"
+indent = { tab-width = 2, unit = " " }
+formatter = { command = "mylang-formatter" , args = ["--stdin"] }
+language-servers = [ "mylang-lsp" ]
+
+These configuration keys are available:
+Key | Description |
---|---|
name | The name of the language |
language-id | The language-id for language servers, checkout the table at TextDocumentItem for the right id |
scope | A string like source.js that identifies the language. Currently, we strive to match the scope names used by popular TextMate grammars and by the Linguist library. Usually source.<name> or text.<name> in case of markup languages |
injection-regex | regex pattern that will be tested against a language name in order to determine whether this language should be used for a potential language injection site. |
file-types | The filetypes of the language, for example ["yml", "yaml"] . See the file-type detection section below. |
shebangs | The interpreters from the shebang line, for example ["sh", "bash"] |
roots | A set of marker files to look for when trying to find the workspace root. For example Cargo.lock , yarn.lock |
auto-format | Whether to autoformat this language when saving |
diagnostic-severity | Minimal severity of diagnostic for it to be displayed. (Allowed values: Error , Warning , Info , Hint ) |
comment-token | The token to use as a comment-token |
indent | The indent to use. Has sub keys unit (the text inserted into the document when indenting; usually set to N spaces or "\t" for tabs) and tab-width (the number of spaces rendered for a tab) |
language-servers | The Language Servers used for this language. See below for more information in the section Configuring Language Servers for a language |
grammar | The tree-sitter grammar to use (defaults to the value of name ) |
formatter | The formatter for the language, it will take precedence over the lsp when defined. The formatter must be able to take the original file as input from stdin and write the formatted file to stdout |
text-width | Maximum line length. Used for the :reflow command and soft-wrapping if soft-wrap.wrap-at-text-width is set, defaults to editor.text-width |
workspace-lsp-roots | Directories relative to the workspace root that are treated as LSP roots. Should only be set in .helix/config.toml . Overwrites the setting of the same name in config.toml if set. |
rulers | Overrides the editor.rulers config key for the language. |
rainbow-brackets | Overrides the editor.rainbow-brackets config key for the language. |
File-type detection and the file-types
key
+Helix determines which language configuration to use based on the file-types
key
+from the above section. file-types
is a list of strings or tables, for
+example:
file-types = ["Makefile", "toml", { suffix = ".git/config" }]
+
+When determining a language configuration to use, Helix searches the file-types +with the following priorities:
+-
+
- Exact match: if the filename of a file is an exact match of a string in a
+
file-types
list, that language wins. In the example above,"Makefile"
+will match againstMakefile
files.
+ - Extension: if there are no exact matches, any
file-types
string that +matches the file extension of a given file wins. In the example above, the +"toml"
matches files likeCargo.toml
orlanguages.toml
.
+ - Suffix: if there are still no matches, any values in
suffix
tables +are checked against the full path of the given file. In the example above, +the{ suffix = ".git/config" }
would match against anyconfig
files +in.git
directories. Note:/
is used as the directory separator but is +replaced at runtime with the appropriate path separator for the operating +system, so this rule would match against.git\config
files on Windows.
+
Language Server configuration
+Language servers are configured separately in the table language-server
in the same file as the languages languages.toml
For example:
+[language-server.mylang-lsp]
+command = "mylang-lsp"
+args = ["--stdio"]
+config = { provideFormatter = true }
+environment = { "ENV1" = "value1", "ENV2" = "value2" }
+
+[language-server.efm-lsp-prettier]
+command = "efm-langserver"
+
+[language-server.efm-lsp-prettier.config]
+documentFormatting = true
+languages = { typescript = [ { formatCommand ="prettier --stdin-filepath ${INPUT}", formatStdin = true } ] }
+
+These are the available options for a language server.
+Key | Description |
---|---|
command | The name or path of the language server binary to execute. Binaries must be in $PATH |
args | A list of arguments to pass to the language server binary |
config | LSP initialization options |
timeout | The maximum time a request to the language server may take, in seconds. Defaults to 20 |
environment | Any environment variables that will be used when starting the language server { "KEY1" = "Value1", "KEY2" = "Value2" } |
A format
sub-table within config
can be used to pass extra formatting options to
+Document Formatting Requests.
+For example, with typescript:
[language-server.typescript-language-server]
+# pass format options according to https://github.com/typescript-language-server/typescript-language-server#workspacedidchangeconfiguration omitting the "[language].format." prefix.
+config = { format = { "semicolons" = "insert", "insertSpaceBeforeFunctionParenthesis" = true } }
+
+Configuring Language Servers for a language
+The language-servers
attribute in a language tells helix which language servers are used for this language.
They have to be defined in the [language-server]
table as described in the previous section.
Different languages can use the same language server instance, e.g. typescript-language-server
is used for javascript, jsx, tsx and typescript by default.
In case multiple language servers are specified in the language-servers
attribute of a language
,
+it's often useful to only enable/disable certain language-server features for these language servers.
As an example, efm-lsp-prettier
of the previous example is used only with a formatting command prettier
,
+so everything else should be handled by the typescript-language-server
(which is configured by default).
+The language configuration for typescript could look like this:
[[language]]
+name = "typescript"
+language-servers = [ { name = "efm-lsp-prettier", only-features = [ "format" ] }, "typescript-language-server" ]
+
+or equivalent:
+[[language]]
+name = "typescript"
+language-servers = [ { name = "typescript-language-server", except-features = [ "format" ] }, "efm-lsp-prettier" ]
+
+Each requested LSP feature is prioritized in the order of the language-servers
array.
+For example, the first goto-definition
supported language server (in this case typescript-language-server
) will be taken for the relevant LSP request (command goto_definition
).
+The features diagnostics
, code-action
, completion
, document-symbols
and workspace-symbols
are an exception to that rule, as they are working for all language servers at the same time and are merged together, if enabled for the language.
+If no except-features
or only-features
is given, all features for the language server are enabled.
+If a language server itself doesn't support a feature, the next language server array entry will be tried (and so on).
The list of supported features is:
+-
+
format
+goto-definition
+goto-declaration
+goto-type-definition
+goto-reference
+goto-implementation
+signature-help
+hover
+document-highlight
+completion
+code-action
+workspace-command
+document-symbols
+workspace-symbols
+diagnostics
+rename-symbol
+inlay-hints
+
Tree-sitter grammar configuration
+The source for a language's tree-sitter grammar is specified in a [[grammar]]
+section in languages.toml
. For example:
[[grammar]]
+name = "mylang"
+source = { git = "https://github.com/example/mylang", rev = "a250c4582510ff34767ec3b7dcdd3c24e8c8aa68" }
+
+Grammar configuration takes these keys:
+Key | Description |
---|---|
name | The name of the tree-sitter grammar |
source | The method of fetching the grammar - a table with a schema defined below |
Where source
is a table with either these keys when using a grammar from a
+git repository:
Key | Description |
---|---|
git | A git remote URL from which the grammar should be cloned |
rev | The revision (commit hash or tag) which should be fetched |
subpath | A path within the grammar directory which should be built. Some grammar repositories host multiple grammars (for example tree-sitter-typescript and tree-sitter-ocaml ) in subdirectories. This key is used to point hx --grammar build to the correct path for compilation. When omitted, the root of repository is used |
Choosing grammars
+You may use a top-level use-grammars
key to control which grammars are
+fetched and built when using hx --grammar fetch
and hx --grammar build
.
# Note: this key must come **before** the [[language]] and [[grammar]] sections
+use-grammars = { only = [ "rust", "c", "cpp" ] }
+# or
+use-grammars = { except = [ "yaml", "json" ] }
+
+When omitted, all grammars are fetched and built.
+Guides
+This section contains guides for adding new language server configurations, +tree-sitter grammars, textobject and rainbow bracket queries, and other similar items.
+Adding new languages to Helix
+In order to add a new language to Helix, you will need to follow the steps +below.
+Language configuration
+-
+
- Add a new
[[language]]
entry in thelanguages.toml
file and provide the +necessary configuration for the new language. For more information on +language configuration, refer to the +language configuration section of the documentation. +A new language server can be added by extending the[language-server]
table in the same file.
+ - If you are adding a new language or updating an existing language server
+configuration, run the command
cargo xtask docgen
to update the +Language Support documentation.
+
++๐ก If you are adding a new Language Server configuration, make sure to update +the +Language Server Wiki +with the installation instructions.
+
Grammar configuration
+-
+
- If a tree-sitter grammar is available for the new language, add a new
+
[[grammar]]
entry to thelanguages.toml
file.
+ - If you are testing the grammar locally, you can use the
source.path
key +with an absolute path to the grammar. However, before submitting a pull +request, make sure to switch to usingsource.git
.
+
Queries
+-
+
- In order to provide syntax highlighting and indentation for the new language, +you will need to add queries. +
- Create a new directory for the language with the path
+
runtime/queries/<name>/
.
+ - Refer to the +tree-sitter website +for more information on writing queries. +
- A list of highlight captures can be found on the themes page. +
++๐ก In Helix, the first matching query takes precedence when evaluating +queries, which is different from other editors such as Neovim where the last +matching query supersedes the ones before it. See +this issue +for an example.
+
Common issues
+-
+
- If you encounter errors when running Helix after switching branches, you may
+need to update the tree-sitter grammars. Run the command
hx --grammar fetch
+to fetch the grammars andhx --grammar build
to build any out-of-date +grammars.
+ - If a parser is causing a segfault, or you want to remove it, make sure to
+remove the compiled parser located at
runtime/grammars/<name>.so
.
+ - If you are attempting to add queries and Helix is unable to locate them, ensure that the environment variable
HELIX_RUNTIME
is set to the location of theruntime
folder you're developing in.
+
Adding textobject queries
+Helix supports textobjects that are language specific, such as functions, classes, etc.
+These textobjects require an accompanying tree-sitter grammar and a textobjects.scm
query file
+to work properly. Tree-sitter allows us to query the source code syntax tree
+and capture specific parts of it. The queries are written in a lisp dialect.
+More information on how to write queries can be found in the official tree-sitter
+documentation.
Query files should be placed in runtime/queries/{language}/textobjects.scm
+when contributing to Helix. Note that to test the query files locally you should put
+them under your local runtime directory (~/.config/helix/runtime
on Linux
+for example).
The following captures are recognized:
+Capture Name |
---|
function.inside |
function.around |
class.inside |
class.around |
test.inside |
test.around |
parameter.inside |
comment.inside |
comment.around |
Example query files can be found in the helix GitHub repository.
+Queries for textobject based navigation
+Tree-sitter based navigation in Helix is done using captures in the +following order:
+-
+
object.movement
+object.around
+object.inside
+
For example if a function.around
capture has been already defined for a language
+in its textobjects.scm
file, function navigation should also work automatically.
+function.movement
should be defined only if the node captured by function.around
+doesn't make sense in a navigation context.
Adding indent queries
+Helix uses tree-sitter to correctly indent new lines. This requires a tree-
+sitter grammar and an indent.scm
query file placed in runtime/queries/ {language}/indents.scm
. The indentation for a line is calculated by traversing
+the syntax tree from the lowest node at the beginning of the new line (see
+Indent queries). Each of these nodes contributes to the total
+indent when it is captured by the query (in what way depends on the name of
+the capture.
Note that it matters where these added indents begin. For example, +multiple indent level increases that start on the same line only increase +the total indent level by 1. See Capture types.
+Indent queries
+When Helix is inserting a new line through o
, O
, or <ret>
, to determine
+the indent level for the new line, the query in indents.scm
is run on the
+document. The starting position of the query is the end of the line above where
+a new line will be inserted.
For o
, the inserted line is the line below the cursor, so that starting
+position of the query is the end of the current line.
+#![allow(unused)] +fn main() { +fn need_hero(some_hero: Hero, life: Life) -> { + matches!(some_hero, Hero { // โโโโโโโโโโโโโโโโโโโฎ + strong: true,//โโฎ โ โ โ + fast: true, // โ โ โฐโโ query start โ + sure: true, // โ โฐโโโโโ cursor โโ traversal + soon: true, // โฐโโโโโโโโ new line inserted โ start node + }) && // โ +// โ โ +// โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ + some_hero > life +} +}
For O
, the newly inserted line is the current line, so the starting position
+of the query is the end of the line above the cursor.
+#![allow(unused)] +fn main() { +fn need_hero(some_hero: Hero, life: Life) -> { // โโโฎ + matches!(some_hero, Hero { // โโฎ โ โ + strong: true,// โ โญโโโโฏ โ โ + fast: true, // โ โ query start โโฏ โ + sure: true, // โฐโโโโผ cursor โโ traversal + soon: true, // โฐ new line inserted โ start node + }) && // โ + some_hero > life // โ +} // โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ +}
From this starting node, the syntax tree is traversed up until the root node. +Each indent capture is collected along the way, and then combined according to +their capture types and scopes to a final indent +level for the line.
+Capture types
+-
+
@indent
(default scopetail
): +Increase the indent level by 1. Multiple occurrences in the same line do not +stack. If there is at least one@indent
and one@outdent
capture on the +same line, the indent level isn't changed at all.
+@outdent
(default scopeall
): +Decrease the indent level by 1. The same rules as for@indent
apply.
+@indent.always
(default scopetail
): +Increase the indent level by 1. Multiple occurrences on the same line do +stack. The final indent level is@indent.always
โ@outdent.always
. If +an@indent
and an@indent.always
are on the same line, the@indent
is +ignored.
+@outdent.always
(default scopeall
): +Decrease the indent level by 1. The same rules as for@indent.always
apply.
+@align
(default scopeall
): +Align everything inside this node to some anchor. The anchor is given +by the start of the node captured by@anchor
in the same pattern. +Every pattern with an@align
should contain exactly one@anchor
. +Indent (and outdent) for nodes below (in terms of their starting line) +the@align
node is added to the indentation required for alignment.
+@extend
: +Extend the range of this node to the end of the line and to lines that are +indented more than the line that this node starts on. This is useful for +languages like Python, where for the purpose of indentation some nodes (like +functions or classes) should also contain indented lines that follow them.
+@extend.prevent-once
: +Prevents the first extension of an ancestor of this node. For example, in Python +a return expression always ends the block that it is in. Note that this only +stops the extension of the next@extend
capture. If multiple ancestors are +captured, only the extension of the innermost one is prevented. All other +ancestors are unaffected (regardless of whether the innermost ancestor would +actually have been extended).
+
@indent
/ @outdent
+Consider this example:
++#![allow(unused)] +fn main() { +fn shout(things: Vec<Thing>) { + // โ + // โโโโโโโโโโโโโโโโโโโโโโโโโฎ indent level + // @indent โโโโโโโโโโโโโโโ + // โ + let it_all = |out| { things.filter(|thing| { // โ 1 + // โ โ โ + // โโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโผโโโโโโโโโโโโโโ + // @indent @indent โ + // โ 2 + thing.can_do_with(out) // โ + })}; // โโโโโโโโโโโโโโโ + //โโโ โ 1 +} //โฐโผโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโ +// 3x @outdent +}
((block) @indent)
+["}" ")"] @outdent
+
+Note how on the second line, we have two blocks begin on the same line. In this
+case, since both captures occur on the same line, they are combined and only
+result in a net increase of 1. Also note that the closing }
s are part of the
+@indent
captures, but the 3 @outdent
s also combine into 1 and result in that
+line losing one indent level.
@extend
/ @extend.prevent-once
+For an example of where @extend
can be useful, consider Python, which is
+whitespace-sensitive.
]
+ (parenthesized_expression)
+ (function_definition)
+ (class_definition)
+] @indent
+
+
+class Hero:
+ def __init__(self, strong, fast, sure, soon):# โโโฎ
+ self.is_strong = strong # โ
+ self.is_fast = fast # โญโโโ query start โ
+ self.is_sure = sure # โ โญโ cursor โ
+ self.is_soon = soon # โ โ โ
+ # โ โ โ โ โ
+ # โ โฐโโโโโโโฏ โ โ
+ # โฐโโโโโโโโโโโโโโโโโโโโโโฏ โ
+ # โโ traversal
+ def need_hero(self, life): # โ start node
+ return ( # โ
+ self.is_strong # โ
+ and self.is_fast # โ
+ and self.is_sure # โ
+ and self.is_soon # โ
+ and self > life # โ
+ ) # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
+
+Without braces to catch the scope of the function, the smallest descendant of +the cursor on a line feed ends up being the entire inside of the class. Because +of this, it will miss the entire function node and its indent capture, leading +to an indent level one too small.
+To address this case, @extend
tells helix to "extend" the captured node's span
+to the line feed and every consecutive line that has a greater indent level than
+the line of the node.
(parenthesized_expression) @indent
+
+]
+ (function_definition)
+ (class_definition)
+] @indent @extend
+
+
+class Hero:
+ def __init__(self, strong, fast, sure, soon):# โโโฎ
+ self.is_strong = strong # โ
+ self.is_fast = fast # โญโโโ query start โโ traversal
+ self.is_sure = sure # โ โญโ cursor โ start node
+ self.is_soon = soon # โ โ โโโโโโโโโโโโโโโโโฏ
+ # โ โ โ โ
+ # โ โฐโโโโโโโฏ โ
+ # โฐโโโโโโโโโโโโโโโโโโโโโโฏ
+ def need_hero(self, life):
+ return (
+ self.is_strong
+ and self.is_fast
+ and self.is_sure
+ and self.is_soon
+ and self > life
+ )
+
+Furthermore, there are some cases where extending to everything with a greater
+indent level may not be desirable. Consider the need_hero
function above. If
+our cursor is on the last line of the returned expression.
class Hero:
+ def __init__(self, strong, fast, sure, soon):
+ self.is_strong = strong
+ self.is_fast = fast
+ self.is_sure = sure
+ self.is_soon = soon
+
+ def need_hero(self, life):
+ return (
+ self.is_strong
+ and self.is_fast
+ and self.is_sure
+ and self.is_soon
+ and self > life
+ ) # โโโโ cursor
+ #โโโโโโโโโโโ where cursor should go on new line
+
+In Python, the are a few tokens that will always end a scope, such as a return
+statement. Since the scope ends, so should the indent level. But because the
+function span is extended to every line with a greater indent level, a new line
+would just continue on the same level. And an @outdent
would not help us here
+either, since it would cause everything in the parentheses to become outdented
+as well.
To help, we need to signal an end to the extension. We can do this with
+@extend.prevent-once
.
(parenthesized_expression) @indent
+
+]
+ (function_definition)
+ (class_definition)
+] @indent @extend
+
+(return_statement) @extend.prevent-once
+
+@indent.always
/ @outdent.always
+As mentioned before, normally if there is more than one @indent
or @outdent
+capture on the same line, they are combined.
Sometimes, there are cases when you may want to ensure that every indent capture +is additive, regardless of how many occur on the same line. Consider this +example in YAML.
+ - foo: bar
+# โ โ
+# โ โฐโโโโโโโโโโโโโโโ start of map
+# โฐโโโโโโโโโโโโโโโโโ start of list element
+ baz: quux # โโโโ cursor
+ # โโโโโโโโโโโโโโ where the cursor should go on a new line
+ garply: waldo
+ - quux:
+ bar: baz
+ xyzzy: thud
+ fred: plugh
+
+In YAML, you often have lists of maps. In these cases, the syntax is such that
+the list element and the map both start on the same line. But we really do want
+to start an indentation for each of these so that subsequent keys in the map
+hang over the list and align properly. This is where @indent.always
helps.
((block_sequence_item) @item @indent.always @extend
+ (#not-one-line? @item))
+
+((block_mapping_pair
+ key: (_) @key
+ value: (_) @val
+ (#not-same-line? @key @val)
+ ) @indent.always @extend
+)
+
+Predicates
+In some cases, an S-expression cannot express exactly what pattern should be matched.
+For that, tree-sitter allows for predicates to appear anywhere within a pattern,
+similar to how #set!
declarations work:
(some_kind
+ (child_kind) @indent
+ (#predicate? arg1 arg2 ...)
+)
+
+The number of arguments depends on the predicate that's used.
+Each argument is either a capture (@name
) or a string ("some string"
).
+The following predicates are supported by tree-sitter:
-
+
-
+
+#eq?
/#not-eq?
: +The first argument (a capture) must/must not be equal to the second argument +(a capture or a string).
+ -
+
+#match?
/#not-match?
: +The first argument (a capture) must/must not match the regex given in the +second argument (a string).
+
Additionally, we support some custom predicates for indent queries:
+-
+
-
+
+#not-kind-eq?
: +The kind of the first argument (a capture) must not be equal to the second +argument (a string).
+ -
+
+#same-line?
/#not-same-line?
: +The captures given by the 2 arguments must/must not start on the same line.
+ -
+
+#one-line?
/#not-one-line?
: +The captures given by the fist argument must/must span a total of one line.
+
Scopes
+Added indents don't always apply to the whole node. For example, in most +cases when a node should be indented, we actually only want everything +except for its first line to be indented. For this, there are several +scopes (more scopes may be added in the future if required):
+-
+
tail
: +This scope applies to everything except for the first line of the +captured node.
+all
: +This scope applies to the whole captured node. This is only different from +tail
when the captured node is the first node on its line.
+
For example, imagine we have the following function
++#![allow(unused)] +fn main() { +fn aha() { // โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ + let take = "on me"; // โโโโโโโโโโโโโโโโฎ scope: โ + let take = "me on"; // โโ "tail" โโ (block) @indent + let ill = be_gone_days(1 || 2); // โ โ +} // โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโดโ "}" @outdent + // scope: "all" +}
We can write the following query with the #set!
declaration:
((block) @indent
+ (#set! "scope" "tail"))
+("}" @outdent
+ (#set! "scope" "all"))
+
+As we can see, the "tail" scope covers the node, except for the first line.
+Everything up to and including the closing brace gets an indent level of 1.
+Then, on the closing brace, we encounter an outdent with a scope of "all", which
+means the first line is included, and the indent level is cancelled out on this
+line. (Note these scopes are the defaults for @indent
and @outdent
โthey are
+written explicitly for demonstration.)
Adding Injection Queries
+Writing language injection queries allows one to highlight a specific node as a different language. +In addition to the standard language injection options used by tree-sitter, there +are a few Helix specific extensions that allow for more control.
+And example of a simple query that would highlight all strings as bash in Nix:
+((string_expression (string_fragment) @injection.content)
+ (#set! injection.language "bash"))
+
+Capture Types
+-
+
-
+
+@injection.language
(standard): +The captured node may contain the language name used to highlight the node captured by +@injection.content
.
+ -
+
+@injection.content
(standard): +Marks the content to be highlighted as the language captured with@injection.language
et al.
+ -
+
+@injection.filename
(extension): +The captured node may contain a filename with a file-extension known to Helix, +highlighting@injection.content
as that language. This uses the language extensions defined in +both the default languages.toml distributed with Helix, as well as user defined languages.
+ -
+
+@injection.shebang
(extension): +The captured node may contain a shebang used to choose a language to highlight as. This also uses +the shebangs defined in the default and userlanguages.toml
.
+
Settings
+-
+
-
+
+injection.combined
(standard): +Indicates that all the matching nodes in the tree should have their content parsed as one +nested document.
+ -
+
+injection.language
(standard): +Forces the captured content to be highlighted as the given language
+ -
+
+injection.include-children
(standard): +Indicates that the content nodeโs entire text should be re-parsed, including the text of its child +nodes. By default, child nodesโ text will be excluded from the injected document.
+ -
+
+injection.include-unnamed-children
(extension): +Same asinjection.include-children
but only for unnamed child nodes.
+
Predicates
+-
+
-
+
+#eq?
(standard): +The first argument (a capture) must be equal to the second argument +(a capture or a string).
+ -
+
+#match?
(standard): +The first argument (a capture) must match the regex given in the +second argument (a string).
+
Adding Rainbow Bracket Queries
+Helix uses rainbows.scm
tree-sitter query files to provide rainbow bracket
+functionality.
Tree-sitter queries are documented in the tree-sitter online documentation. +If you're writing queries for the first time, be sure to check out the section +on syntax highlighting queries and on query syntax.
+Rainbow queries have two captures: @rainbow.scope
and @rainbow.bracket
.
+@rainbow.scope
should capture any node that increases the nesting level
+while @rainbow.bracket
should capture any bracket nodes. Put another way:
+@rainbow.scope
switches to the next rainbow color for all nodes in the tree
+under it while @rainbow.bracket
paints captured nodes with the current
+rainbow color.
For an example, let's add rainbow queries for the tree-sitter query (TSQ)
+language itself. These queries will go into a
+runtime/queries/tsq/rainbows.scm
file in the repository root.
First we'll add the @rainbow.bracket
captures. TSQ only has parentheses and
+square brackets:
["(" ")" "[" "]"] @rainbow.bracket
+
+The ordering of the nodes within the alternation (square brackets) is not +taken into consideration.
+++Note: Why are these nodes quoted? Most syntax highlights capture text +surrounded by parentheses. These are named nodes and correspond to the +names of rules in the grammar. Brackets are usually written in tree-sitter +grammars as literal strings, for example:
++{ + // ... + arguments: seq("(", repeat($.argument), ")"), + // ... +} +
Nodes written as literal strings in tree-sitter grammars may be captured +in queries with those same literal strings.
+
Then we'll add @rainbow.scope
captures. The easiest way to do this is to
+view the grammar.js
file in the tree-sitter grammar's repository. For TSQ,
+that file is here. As we scroll down the grammar.js
, we
+see that the (alternation)
, (L36) (group)
(L57), (named_node)
(L59),
+(predicate)
(L87) and (wildcard_node)
(L97) nodes all contain literal
+parentheses or square brackets in their definitions. These nodes are all
+direct parents of brackets and happen to also be the nodes we want to change
+to the next rainbow color, so we capture them as @rainbow.scope
.
[
+ (group)
+ (named_node)
+ (wildcard_node)
+ (predicate)
+ (alternation)
+] @rainbow.scope
+
+This strategy works as a rule of thumb for most programming and configuration +languages. Markup languages can be trickier and may take additional +experimentation to find the correct nodes to use for scopes and brackets.
+The :tree-sitter-subtree
command shows the syntax tree under the primary
+selection in S-expression format and can be a useful tool for determining how
+to write a query.
Properties
+The rainbow.include-children
property may be applied to @rainbow.scope
+captures. By default, all @rainbow.bracket
captures must be direct descendant
+of a node captured with @rainbow.scope
in a syntax tree in order to be
+highlighted. The rainbow.include-children
property disables that check and
+allows @rainbow.bracket
captures to be highlighted if they are direct or
+indirect descendants of some node captured with @rainbow.scope
.
For example, this property is used in the HTML rainbow queries.
+For a document like <a>link</a>
, the syntax tree is:
(element ; <a>link</a>
+ (start_tag ; <a>
+ (tag_name)) ; a
+ (text) ; link
+ (end_tag ; </a>
+ (tag_name))) ; a
+
+If we want to highlight the <
, >
and </
nodes with rainbow colors, we
+capture them as @rainbow.bracket
:
["<" ">" "</"] @rainbow.bracket
+
+And we capture (element)
as @rainbow.scope
because (element)
nodes nest
+within each other: they increment the nesting level and switch to the next
+color in the rainbow.
(element) @rainbow.scope
+
+But this combination of @rainbow.scope
and @rainbow.bracket
will not
+highlight any nodes. <
, >
and </
are children of the (start_tag)
and
+(end_tag)
nodes. We can't capture (start_tag)
and (end_tag)
as
+@rainbow.scope
because they don't nest other elements. We can fix this case
+by removing the requirement that <
, >
and </
are direct descendants of
+(element)
using the rainbow.include-children
property.
((element) @rainbow.scope
+ (#set! rainbow.include-children))
+
+With this property set, <
, >
, and </
will highlight with rainbow colors
+even though they aren't direct descendents of the (element)
node.
rainbow.include-children
is not necessary for the vast majority of programming
+languages. It is only necessary when the node that increments the nesting level
+(changes rainbow color) is not the direct parent of the bracket node.