Installation
We provide pre-built binaries on the GitHub Releases page.
OSX
A Homebrew tap is available:
brew tap helix-editor/helix
brew install helix
Linux
NixOS
A flake containing the package is available in the project root. The flake can also be used to spin up a reproducible development shell for working on Helix.
Arch Linux
Releases are available in the community
repository.
Packages are also available on AUR:
Build from source
git clone --recurse-submodules --shallow-submodules -j8 https://github.com/helix-editor/helix
cd helix
cargo install --path helix-term
This will install the hx
binary to $HOME/.cargo/bin
.
Helix also needs it's runtime files so make sure to copy/symlink the runtime/
directory into the
config directory (for example ~/.config/helix/runtime
on Linux/macOS). This location can be overriden
via the HELIX_RUNTIME
environment variable.
Usage
(Currently not fully documented, see the keymappings list for more.)
See tutor.txt (accessible via hx --tutor
or :tutor
) for a vimtutor-like introduction.
Registers
Vim-like registers can be used to yank and store text to be pasted later. Usage is similar, with "
being used to select a register:
"ay
- Yank the current selection to registera
."op
- Paste the text in registero
after the selection.
If there is a selected register 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.
Special Registers
Register character | Contains |
---|---|
/ | Last search |
: | Last executed command |
" | Last yanked text |
There is no special register for copying to system clipboard, instead special commands and keybindings are provided. See the keymap for the specifics.
Surround
Functionality similar to vim-surround is built into helix. The keymappings have been inspired from vim-sandwich:
ms
- Add surround charactersmr
- Replace surround charactersmd
- Delete surround characters
ms
acts on a selection, so select the text first and use ms<char>
. mr
and md
work
on the closest pairs found and selections are not required; use counts to act in outer pairs.
It can also act on multiple seletions (yay!). For example, to change every occurance of (use)
to [use]
:
%
to select the whole files
to split the selections on a search term- Input
use
and hit Enter mr([
to replace the parens with square brackets
Multiple characters are currently not supported, but planned.
Textobjects
Currently supported: word
, surround
, function
, class
, parameter
.
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 |
( , [ , ' , etc | Specified surround pairs |
f | Function |
c | Class |
p | Parameter |
Note: f
, c
, 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 !
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 the 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.
TODO: Mention texobjects, 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
Editor
[editor]
section of the config.
Key | Description | Default |
---|---|---|
scrolloff | Number of lines of padding around the edge of the screen when scrolling. | 3 |
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 , relative ) | absolute |
smart-case | Enable smart case regex searching (case insensitive unless pattern contains upper case characters) | true |
auto-pairs | Enable automatic insertion of pairs to parenthese, brackets, etc. | true |
auto-completion | Enable automatic pop up of auto-completion. | true |
idle-timeout | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. | 400 |
completion-trigger-len | The min-length of word under cursor to trigger autocompletion | 2 |
auto-info | Whether to display infoboxes | true |
LSP
To display all language server messages in the status line add the following to your config.toml
:
[lsp]
display-messages = true
Themes
First you'll need to place selected themes in your themes
directory (i.e ~/.config/helix/themes
), the directory might have to be created beforehand.
To use a custom theme add theme = <name>
to your config.toml
or override it during runtime using :theme <name>
.
The default theme.toml can be found here, and user submitted themes here.
Creating a theme
First create a file with the name of your theme as file name (i.e mytheme.toml
) and place it in your themes
directory (i.e ~/.config/helix/themes
).
Each line in the theme file is specified as below:
key = { fg = "#ffffff", bg = "#000000", modifiers = ["bold", "italic"] }
where key
represents what you want to style, fg
specifies the foreground color, bg
the background color, and modifiers
is a list of style modifiers. bg
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"
Color palettes
It's recommended define a palette of named colors, and refer to them from 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"
Remember that the [palette]
table includes all keys after its header,
so you should define the palette after 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 |
---|
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 modifiers.
Less common modifiers might not be supported by your terminal emulator.
Modifier |
---|
bold |
dim |
italic |
underlined |
slow_blink |
rapid_blink |
reversed |
hidden |
crossed_out |
Scopes
The following is a list of scopes available to use for styling.
Syntax highlighting
These keys match tree-sitter scopes.
For a given highlight produced, styling will be determined based on the longest matching theme key. For example, the highlight function.builtin.static
would match the key function.builtin
rather than function
.
We use a similar set of scopes as SublimeText. See also TextMate scopes.
-
type
- Typesbuiltin
- Primitive types provided by the language (int
,usize
)
-
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 expressionsspecial
path
url
symbol
- Erlang/Elixir atoms, Ruby symbols, Clojure keywords
-
comment
- Code commentsline
- Single line comments (//
)block
- Block comments (e.g. (/* */
)documentation
- Documentation comments (e.g.///
in Rust)
-
variable
- Variablesbuiltin
- Reserved language variables (self
,this
,super
, etc)parameter
- Function parametersother
member
- Fields of composite data types (e.g. structs, unions)
function
(TODO: ?)
-
label
-
punctuation
delimiter
- Commas, colonsbracket
- Parentheses, angle brackets, etc.
-
keyword
control
conditional
-if
,else
repeat
-for
,while
,loop
import
-import
,export
- (TODO: return?)
directive
- Preprocessor directives (#if
in C)function
-fn
,func
-
operator
-||
,+=
,>
,or
-
function
builtin
method
macro
special
(preprocesor in C)
-
tag
- Tags (e.g.<body>
in HTML) -
namespace
Interface
These scopes are used for theming the editor interface.
Key | Notes |
---|---|
ui.background | |
ui.cursor | |
ui.cursor.insert | |
ui.cursor.select | |
ui.cursor.match | Matching bracket etc. |
ui.cursor.primary | Cursor with primary selection |
ui.linenr | |
ui.linenr.selected | |
ui.statusline | Statusline |
ui.statusline.inactive | Statusline (unfocused document) |
ui.popup | |
ui.window | |
ui.help | |
ui.text | |
ui.text.focus | |
ui.info | |
ui.info.text | |
ui.menu | |
ui.menu.selected | |
ui.selection | For selections in the editing area |
ui.selection.primary | |
warning | Diagnostics warning (gutter) |
error | Diagnostics error (gutter) |
info | Diagnostics info (gutter) |
hint | Diagnostics hint (gutter) |
diagnostic | For text in editing area |
Keymap
Normal mode
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_line_down |
k /Up | Move up | move_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 |
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 |
PageUp | Move page up | page_up |
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 |
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 |
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 | prepend_to_line |
A | Insert at the end of the line | append_to_line |
o | Open new line below selection | open_below |
O | Open new line above selection | open_above |
. | Repeat last change | N/A |
u | Undo change | undo |
U | Redo change | redo |
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 | format_selections |
d | Delete selection | delete_selection |
c | Change selection (delete and enter insert mode) | change_selection |
Shell
Key | Description | Command |
---|---|---|
| | Pipe each selection through shell command, replacing with output | shell_pipe |
A-| | Pipe each selection into shell command, ignoring output | shell_pipe_to |
! | Run shell command, inserting output before each selection | shell_insert_output |
A-! | Run shell command, appending output after each selection | shell_append_output |
Selection manipulation
Key | Description | Command |
---|---|---|
s | Select all regex matches inside selections | select_regex |
S | Split selection into subselections on regex matches | split_selection |
Alt-s | Split selection on newlines | split_selection_on_newline |
; | Collapse selection onto a single cursor | collapse_selection |
Alt-; | Flip selection cursor and anchor | flip_selections |
, | 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 |
X | Extend selection to line bounds (line-wise selection) | extend_to_line_bounds |
Expand selection to parent syntax node TODO: pick a key | expand_selection | |
J | Join lines inside selection | join_selections |
K | Keep selections matching the regex | keep_selections |
$ | Pipe each selection into shell command, keep selections where command returned 0 | shell_keep_pipe |
Ctrl-c | Comment/uncomment the selections | toggle_comments |
Search
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.
View mode
View mode is intended for scrolling and manipulating the view without changing the selection. The "sticky" variant of this mode is persistent; use the Escape key to return to normal mode after usage (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 | Scroll the view downwards | scroll_down |
k | Scroll the view upwards | scroll_up |
f | Move page down | page_down |
b | Move page up | page_up |
d | Move half page down | half_page_down |
u | Move half page up | half_page_up |
Goto mode
Jumps to various locations.
NOTE: Some of these features are only available with the LSP present.
Key | Description | Command |
---|---|---|
g | Go to the start of the file | goto_file_start |
e | Go to the end of the file | goto_last_line |
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 |
m | Go to the middle of the screen | goto_window_middle |
b | Go to the bottom of the screen | goto_window_bottom |
d | Go to definition | goto_definition |
y | Go to type definition | goto_type_definition |
r | Go to references | goto_reference |
i | Go to implementation | goto_implementation |
a | Go to the last accessed/alternate file | goto_last_accessed_file |
n | Go to next buffer | goto_next_buffer |
p | Go to previous buffer | goto_previous_buffer |
Match mode
Enter this mode using m
from normal mode. See the relavant section
in Usage for an explanation about surround
and textobject usage.
Key | Description | Command |
---|---|---|
m | Goto matching bracket | 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
This layer is similar to vim keybindings as kakoune does not support window.
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 |
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 |
Space mode
This layer is a kludge of mappings, mostly pickers.
Key | Description | Command |
---|---|---|
k | Show documentation for the item under the cursor | hover |
f | Open file picker | file_picker |
b | Open buffer picker | buffer_picker |
s | Open symbol picker (current document) | symbol_picker |
a | Apply code action | code_action |
' | 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 | Join and yank selections to clipboard | yank_joined_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 |
NOTE: Global search display results in a fuzzy picker, use
space + '
to bring it back up after opening a file.
Unimpaired
Mappings in the style of vim-unimpaired.
Key | Description | Command |
---|---|---|
[d | Go to previous diagnostic | goto_prev_diag |
]d | Go to next diagnostic | goto_next_diag |
[D | Go to first diagnostic in document | goto_first_diag |
]D | Go to last diagnostic in document | goto_last_diag |
[space | Add newline above | add_newline_above |
]space | Add newline below | add_newline_below |
Insert Mode
Key | Description | Command |
---|---|---|
Escape | Switch to normal mode | normal_mode |
Ctrl-x | Autocomplete | completion |
Ctrl-w | Delete previous word | delete_word_backward |
Select / extend mode
I'm still pondering whether to keep this mode or not. It changes movement commands (including goto) to extend the existing selection instead of replacing it.
NOTE: It's a bit confusing at the moment because extend hasn't been implemented for all movement commands yet.
Picker
Keys to use within picker. Remapping currently not supported.
Key | Description |
---|---|
Up , Ctrl-k , Ctrl-p | Previous entry |
Down , Ctrl-j , Ctrl-n | Next entry |
Ctrl-space | Filter options |
Enter | Open selected |
Ctrl-s | Open horizontally |
Ctrl-v | Open vertically |
Escape , Ctrl-c | Close picker |
Prompt
Keys to use within prompt, Remapping currently not supported.
| Key | Description |
| ----- | ------------- |
| Escape
, Ctrl-c
| Close prompt |
| Alt-b
, Alt-Left
| Backward a word |
| Ctrl-b
, Left
| Backward a char |
| Alt-f
, Alt-Right
| Forward a word |
| Ctrl-f
, Right
| Forward a char |
| Ctrl-e
, End
| move prompt end |
| Ctrl-a
, Home
| move prompt start |
| Ctrl-w
| delete previous word |
| Ctrl-k
| delete to end of line |
| backspace
| delete previous 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 |
| Tab
| slect next completion item |
| BackTab
| slect previous completion item |
| Enter
| Open selected |
Key Remapping
One-way key remapping is temporarily supported via a simple TOML configuration file. (More powerful solutions such as rebinding via commands will be available in the future).
To remap keys, write a config.toml
file in your helix
configuration
directory (default ~/.config/helix
in Linux systems) with a structure like
this:
# At most one section each of 'keys.normal', 'keys.insert' and 'keys.select'
[keys.normal]
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 Control-Shift-Escape to extend_line
g = { a = "code_action" } # Maps `ga` to show possible code actions
[keys.insert]
"A-x" = "normal_mode" # Maps Alt-X to enter normal mode
j = { k = "normal_mode" } # Maps `jk` to exit insert mode
Control, 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" |
< | "lt" |
> | "gt" |
+ | "plus" |
- | "minus" |
; | "semicolon" |
% | "percent" |
Left | "left" |
Right | "right" |
Up | "up" |
Home | "home" |
End | "end" |
Page | "pageup" |
Page | "pagedown" |
Tab | "tab" |
Back | "backtab" |
Delete | "del" |
Insert | "ins" |
Null | "null" |
Escape | "esc" |
Keys can be disabled by binding them to the no_op
command.
Commands can be found in the source code at helix-term/src/commands.rs
Hooks
Languages
Language-specific settings and settings for particular language servers can be configured in a languages.toml
file placed in your configuration directory. Helix actually uses two languages.toml
files, the first one is in the main helix repository; it contains the default settings for each language and is included in the helix binary at compile time. Users who want to see the available settings and options can either reference the helix repo's languages.toml
file, or consult the table in the adding languages section.
Changes made to the languages.toml
file in a user's configuration directory are merged with helix's defaults on start-up, such that a user's settings will take precedence over defaults in the event of a collision. For example, the default languages.toml
sets rust's auto-format
to true
. If a user wants to disable auto-format, they can change the languages.toml
in their configuration directory to make the rust entry read like the example below; the new key/value pair auto-format = false
will override the default when the two sets of settings are merged on start-up:
# in <config_dir>/helix/languages.toml
[[language]]
name = "rust"
auto-format = false
Guides
This section contains guides for adding new language server configurations, tree-sitter grammers, textobject queries, etc.
Adding languages
Submodules
To add a new langauge, you should first add a tree-sitter submodule. To do this, you can run the command
git submodule add -f <repository> helix-syntax/languages/tree-sitter-<name>
For example, to add tree-sitter-ocaml you would run
git submodule add -f https://github.com/tree-sitter/tree-sitter-ocaml helix-syntax/languages/tree-sitter-ocaml
Make sure the submodule is shallow by doing
git config -f .gitmodules submodule.helix-syntax/languages/tree-sitter-<name>.shallow true
or you can manually add shallow = true
to .gitmodules
.
languages.toml
Next, you need to add the language to the languages.toml
found in the root of
the repository; this languages.toml
file is included at compilation time, and
is distinct from the language.toml
file in the user's configuration
directory.
These are the available keys and descriptions for the file.
Key | Description |
---|---|
name | The name of the language |
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"] |
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 |
comment-token | The token to use as a comment-token |
indent | The indent to use. Has sub keys tab-width and unit |
config | Language server configuration |
Queries
For a language to have syntax-highlighting and indentation among other things, you have to add queries. Add a directory for your language with the path runtime/queries/<name>/
. The tree-sitter website gives more info on how to write queries.
Common Issues
-
If you get errors when building after switching branches, you may have to remove or update tree-sitter submodules. You can update submodules by running
git submodule sync; git submodule update --init
-
Make sure to not use the
--remote
flag. To remove submodules look inside the.gitmodules
and remove directories that are not present inside of it. -
If a parser is segfaulting or you want to remove the parser, make sure to remove the submodule and the compiled parser in
runtime/grammar/<name>.so
-
The indents query is
indents.toml
, notindents.scm
. See this issue for more information.
Adding Textobject Queries
Textobjects that are language specific (like functions, classes, etc)
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. 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 |
parameter.inside |
Example query files can be found in the helix GitHub repository.