<labelid="sidebar-toggle"class="icon-button"for="sidebar-toggle-anchor"title="Toggle Table of Contents"aria-label="Toggle Table of Contents"aria-controls="sidebar">
<inputtype="search"id="searchbar"name="searchbar"placeholder="Search this book ..."aria-controls="searchresults-outer"aria-describedby="searchresults-header">
<p>Download pre-built binaries from the <ahref="https://github.com/helix-editor/helix/releases">GitHub Releases page</a>.
The tarball contents include an <code>hx</code> binary and a <code>runtime</code> directory.
To set up Helix:</p>
<ol>
<li>Add the <code>hx</code> binary to your system's <code>$PATH</code> to allow it to be used from the command line.</li>
<li>Copy the <code>runtime</code> directory to a location that <code>hx</code> searches for runtime files. A typical location on Linux/macOS is <code>~/.config/helix/runtime</code>.</li>
</ol>
<p>To see the runtime directories that <code>hx</code> searches, run <code>hx --health</code>. If necessary, you can override the default runtime location by setting the <code>HELIX_RUNTIME</code> environment variable.</p>
<p>This will install Helix as both <code>/snap/bin/helix</code> and <code>/snap/bin/hx</code>, so make sure <code>/snap/bin</code> is in your <code>PATH</code>.</p>
<p>Install Helix using the Linux <ahref="https://appimage.org/">AppImage</a> format.
Download the official Helix AppImage from the <ahref="https://github.com/helix-editor/helix/releases/latest">latest releases</a> page.</p>
<pre><codeclass="language-sh">chmod +x helix-*.AppImage # change permission for executable mode
./helix-*.AppImage # run helix
</code></pre>
<p>You can optionally <ahref="./building-from-source.html#configure-the-desktop-shortcut">add the <code>.desktop</code> file</a>. Helix must be installed in <code>PATH</code> with the name <code>hx</code>. For example:</p>
<p>Install on Windows using <ahref="https://learn.microsoft.com/en-us/windows/package-manager/winget/">Winget</a>, <ahref="https://scoop.sh/">Scoop</a>, <ahref="https://chocolatey.org/">Chocolatey</a>
<p>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 <ahref="https://www.microsoft.com/p/app-installer/9nblggh4nns1#activetab=pivot:overviewtab">App Installer from the Microsoft Store</a>. If it's already installed, make sure it is updated with the latest version.</p>
<li>The <ahref="https://git-scm.com/">Git version control system</a></li>
<li>A C++14 compatible compiler to build the tree-sitter grammars, for example GCC or Clang</li>
</ul>
<p>If you are using the <code>musl-libc</code> standard library instead of <code>glibc</code> the following environment variable must be set during the build to ensure tree-sitter grammars can be loaded correctly:</p>
<p>Helix is a modal editor, meaning it has different modes for different tasks. The main modes are:</p>
<ul>
<li><ahref="./keymap.html#normal-mode">Normal mode</a>: For navigation and editing commands. This is the default mode.</li>
<li><ahref="./keymap.html#insert-mode">Insert mode</a>: For typing text directly into the document. Access by typing <code>i</code> in normal mode.</li>
<li><ahref="./keymap.html#select--extend-mode">Select/extend mode</a>: For making selections and performing operations on them. Access by typing <code>v</code> in normal mode.</li>
<p>Buffers are in-memory representations of files. You can have multiple buffers open at once. Use <ahref="./pickers.html">pickers</a> or commands like <code>:buffer-next</code> and <code>:buffer-previous</code> to open buffers or switch between them.</p>
<p>Inspired by <ahref="http://kakoune.org/">Kakoune</a>, Helix follows the <code>selection → action</code> 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.</p>
<p>Also inspired by Kakoune, multiple selections are a core mode of interaction in Helix. For example, the standard way of replacing multiple instances of a word is to first select all instances (so there is one selection per instance) and then use the change action (<code>c</code>) to edit them all at the same time.</p>
<p>Motions are commands that move the cursor or modify selections. They're used for navigation and text manipulation. Examples include <code>w</code> to move to the next word, or <code>f</code> to find a character. See the <ahref="./keymap.html#movement">Movement</a> section of the keymap for more motions.</p>
<p>Helix allows you to create your own named registers for storing text, for
example:</p>
<ul>
<li><code>"ay</code> - Yank the current selection to register <code>a</code>.</li>
<li><code>"op</code> - Paste the text in register <code>o</code> after the selection.</li>
</ul>
<p>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:</p>
<ul>
<li><code>"hc</code> - Store the selection in register <code>h</code> and then change it (delete and enter insert mode).</li>
<li><code>"md</code> - Store the selection in register <code>m</code> and delete it.</li>
<tr><td><code>_</code></td><td>No values are returned</td><td>All values are discarded</td></tr>
<tr><td><code>#</code></td><td>Selection indices (first selection is <code>1</code>, second is <code>2</code>, etc.)</td><td>This register is not writable</td></tr>
<tr><td><code>.</code></td><td>Contents of the current selections</td><td>This register is not writable</td></tr>
<tr><td><code>%</code></td><td>Name of the current file</td><td>This register is not writable</td></tr>
<tr><td><code>+</code></td><td>Reads from the system clipboard</td><td>Joins and yanks to the system clipboard</td></tr>
<tr><td><code>*</code></td><td>Reads from the primary clipboard</td><td>Joins and yanks to the primary clipboard</td></tr>
</tbody></table>
</div>
<p>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.</p>
<tr><td><code>ms<char></code> (after selecting text)</td><td>Add surround characters to selection</td></tr>
<tr><td><code>mr<char_to_replace><new_char></code></td><td>Replace the closest surround characters</td></tr>
<tr><td><code>md<char_to_delete></code></td><td>Delete the closest surround characters</td></tr>
</tbody></table>
</div>
<p>You can use counts to act on outer pairs.</p>
<p>Surround can also act on multiple selections. For example, to change every occurrence of <code>(use)</code> to <code>[use]</code>:</p>
<ol>
<li><code>%</code> to select the whole file</li>
<li><code>s</code> to split the selections on a search term</li>
<li>Input <code>use</code> and hit Enter</li>
<li><code>mr([</code> to replace the parentheses with square brackets</li>
</ol>
<p>Multiple characters are currently not supported, but planned for future release.</p>
<divstyle="break-before: page; page-break-before: always;"></div><h2id="selecting-and-manipulating-text-with-textobjects"><aclass="header"href="#selecting-and-manipulating-text-with-textobjects">Selecting and manipulating text with textobjects</a></h2>
<p>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
<p>💡 <code>f</code>, <code>t</code>, etc. need a tree-sitter grammar active for the current
document and a special tree-sitter query file to work properly. <ahref="./lang-support.html">Only
some grammars</a> currently have the query file implemented.
Contributions are welcome!</p>
</blockquote>
<h2id="navigating-using-tree-sitter-textobjects"><aclass="header"href="#navigating-using-tree-sitter-textobjects">Navigating using tree-sitter textobjects</a></h2>
<p>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 <code>]f</code>, to move to previous
<p>For the full reference see the <ahref="./keymap.html#unimpaired">unimpaired</a> section of the key bind
documentation.</p>
<blockquote>
<p>💡 This feature relies on tree-sitter textobjects
and requires the corresponding query file to work properly.</p>
</blockquote>
<divstyle="break-before: page; page-break-before: always;"></div><h2id="moving-the-selection-with-syntax-aware-motions"><aclass="header"href="#moving-the-selection-with-syntax-aware-motions">Moving the selection with syntax-aware motions</a></h2>
<p><code>Alt-p</code>, <code>Alt-o</code>, <code>Alt-i</code>, and <code>Alt-n</code> (or <code>Alt</code> and arrow keys) allow you to move the
selection according to its location in the syntax tree. For example, many languages have the
<p>Helix has a variety of pickers, which are interactive windows used to select various kinds of items. These include a file picker, global search picker, and more. Most pickers are accessed via keybindings in <ahref="./keymap.html#space-mode">space mode</a>. Pickers have their own <ahref="./keymap.html#picker">keymap</a> for navigation.</p>
<p>Most pickers perform fuzzy matching using <ahref="https://github.com/junegunn/fzf?tab=readme-ov-file#search-syntax">fzf syntax</a>. Two exceptions are the global search picker, which uses regex, and the workspace symbol picker, which passes search terms to the language server. Note that OR operations (<code>|</code>) are not currently supported.</p>
<p>If a picker shows multiple columns, you may apply the filter to a specific column by prefixing the column name with <code>%</code>. Column names can be shortened to any prefix, so <code>%p</code>, <code>%pa</code> or <code>%pat</code> all mean the same as <code>%path</code>. For example, a query of <code>helix %p .toml !lang</code> in the global search picker searches for the term "helix" within files with paths ending in ".toml" but not including "lang".</p>
<p>You can insert the contents of a <ahref="./registers.html">register</a> using <code>Ctrl-r</code> followed by a register name. For example, one could insert the currently selected text using <code>Ctrl-r</code>-<code>.</code>, or the directory of the current file using <code>Ctrl-r</code>-<code>%</code> followed by <code>Ctrl-w</code> to remove the last path section. The global search picker will use the contents of the <ahref="./registers.html#default-registers">search register</a> if you press <code>Enter</code> without typing a filter. For example, pressing <code>*</code>-<code>Space-/</code>-<code>Enter</code> will start a global search for the currently selected text.</p>
<p>💡 Mappings marked (<strong>LSP</strong>) require an active language server for the file.</p>
</blockquote>
<blockquote>
<p>💡 Mappings marked (<strong>TS</strong>) require a tree-sitter grammar for the file type.</p>
</blockquote>
<blockquote>
<p>⚠️ Some terminals' default key mappings conflict with Helix's. If any of the mappings described on this page do not work as expected, check your terminal's mappings to ensure they do not conflict. See the <ahref="https://github.com/helix-editor/helix/wiki/Terminal-Support">wiki</a> for known conflicts.</p>
<tr><td><code>G</code></td><td>Go to line number <code><n></code></td><td><code>goto_line</code></td></tr>
<tr><td><code>Alt-.</code></td><td>Repeat last motion (<code>f</code>, <code>t</code>, <code>m</code>, <code>[</code> or <code>]</code>)</td><td><code>repeat_last_motion</code></td></tr>
<tr><td><code>Home</code></td><td>Move to the start of the line</td><td><code>goto_line_start</code></td></tr>
<tr><td><code>End</code></td><td>Move to the end of the line</td><td><code>goto_line_end</code></td></tr>
<tr><td><code>Alt-d</code></td><td>Delete selection, without yanking</td><td><code>delete_selection_noyank</code></td></tr>
<tr><td><code>c</code></td><td>Change selection (delete and enter insert mode)</td><td><code>change_selection</code></td></tr>
<tr><td><code>Alt-c</code></td><td>Change selection (delete and enter insert mode, without yanking)</td><td><code>change_selection_noyank</code></td></tr>
<tr><td><code>Ctrl-a</code></td><td>Increment object (number) under cursor</td><td><code>increment</code></td></tr>
<tr><td><code>Ctrl-x</code></td><td>Decrement object (number) under cursor</td><td><code>decrement</code></td></tr>
<tr><td><code>Q</code></td><td>Start/stop macro recording to the selected register (experimental)</td><td><code>record_macro</code></td></tr>
<tr><td><code>q</code></td><td>Play back a recorded macro from the selected register (experimental)</td><td><code>replay_macro</code></td></tr>
<tr><td><code>|</code></td><td>Pipe each selection through shell command, replacing with output</td><td><code>shell_pipe</code></td></tr>
<tr><td><code>Alt-|</code></td><td>Pipe each selection into shell command, ignoring output</td><td><code>shell_pipe_to</code></td></tr>
<tr><td><code>!</code></td><td>Run shell command, inserting output before each selection</td><td><code>shell_insert_output</code></td></tr>
<tr><td><code>Alt-!</code></td><td>Run shell command, appending output after each selection</td><td><code>shell_append_output</code></td></tr>
<tr><td><code>$</code></td><td>Pipe each selection into shell command, keep selections where command returned 0</td><td><code>shell_keep_pipe</code></td></tr>
<tr><td><code>Alt-J</code></td><td>Join lines inside selection and select the inserted space</td><td><code>join_selections_space</code></td></tr>
<tr><td><code>K</code></td><td>Keep selections matching the regex</td><td><code>keep_selections</code></td></tr>
<tr><td><code>Alt-K</code></td><td>Remove selections matching the regex</td><td><code>remove_selections</code></td></tr>
<tr><td><code>Ctrl-c</code></td><td>Comment/uncomment the selections</td><td><code>toggle_comments</code></td></tr>
<tr><td><code>Alt-o</code>, <code>Alt-up</code></td><td>Expand selection to parent syntax node (<strong>TS</strong>)</td><td><code>expand_selection</code></td></tr>
<tr><td><code>Alt-i</code>, <code>Alt-down</code></td><td>Shrink syntax tree object selection (<strong>TS</strong>)</td><td><code>shrink_selection</code></td></tr>
<tr><td><code>Alt-p</code>, <code>Alt-left</code></td><td>Select previous sibling node in syntax tree (<strong>TS</strong>)</td><td><code>select_prev_sibling</code></td></tr>
<tr><td><code>Alt-n</code>, <code>Alt-right</code></td><td>Select next sibling node in syntax tree (<strong>TS</strong>)</td><td><code>select_next_sibling</code></td></tr>
<tr><td><code>Alt-a</code></td><td>Select all sibling nodes in syntax tree (<strong>TS</strong>)</td><td><code>select_all_siblings</code></td></tr>
<tr><td><code>Alt-I</code>, <code>Alt-Shift-down</code></td><td>Select all children nodes in syntax tree (<strong>TS</strong>)</td><td><code>select_all_children</code></td></tr>
<tr><td><code>Alt-e</code></td><td>Move to end of parent node in syntax tree (<strong>TS</strong>)</td><td><code>move_parent_node_end</code></td></tr>
<tr><td><code>Alt-b</code></td><td>Move to start of parent node in syntax tree (<strong>TS</strong>)</td><td><code>move_parent_node_start</code></td></tr>
<tr><td><code>*</code></td><td>Use current selection as the search pattern, automatically wrapping with <code>\b</code> on word boundaries</td><td><code>search_selection_detect_word_boundaries</code></td></tr>
<tr><td><code>Alt-*</code></td><td>Use current selection as the search pattern</td><td><code>search_selection</code></td></tr>
<tr><td><code>g</code></td><td>Go to line number <code><n></code> else start of file</td><td><code>goto_file_start</code></td></tr>
<tr><td><code>e</code></td><td>Go to the end of the file</td><td><code>goto_last_line</code></td></tr>
<tr><td><code>f</code></td><td>Go to files in the selections</td><td><code>goto_file</code></td></tr>
<tr><td><code>h</code></td><td>Go to the start of the line</td><td><code>goto_line_start</code></td></tr>
<tr><td><code>l</code></td><td>Go to the end of the line</td><td><code>goto_line_end</code></td></tr>
<tr><td><code>s</code></td><td>Go to first non-whitespace character of the line</td><td><code>goto_first_nonwhitespace</code></td></tr>
<tr><td><code>t</code></td><td>Go to the top of the screen</td><td><code>goto_window_top</code></td></tr>
<tr><td><code>c</code></td><td>Go to the middle of the screen</td><td><code>goto_window_center</code></td></tr>
<tr><td><code>b</code></td><td>Go to the bottom of the screen</td><td><code>goto_window_bottom</code></td></tr>
<tr><td><code>d</code></td><td>Go to definition (<strong>LSP</strong>)</td><td><code>goto_definition</code></td></tr>
<tr><td><code>y</code></td><td>Go to type definition (<strong>LSP</strong>)</td><td><code>goto_type_definition</code></td></tr>
<tr><td><code>r</code></td><td>Go to references (<strong>LSP</strong>)</td><td><code>goto_reference</code></td></tr>
<tr><td><code>i</code></td><td>Go to implementation (<strong>LSP</strong>)</td><td><code>goto_implementation</code></td></tr>
<tr><td><code>a</code></td><td>Go to the last accessed/alternate file</td><td><code>goto_last_accessed_file</code></td></tr>
<tr><td><code>m</code></td><td>Go to the last modified/alternate file</td><td><code>goto_last_modified_file</code></td></tr>
<tr><td><code>n</code></td><td>Go to next buffer</td><td><code>goto_next_buffer</code></td></tr>
<tr><td><code>p</code></td><td>Go to previous buffer</td><td><code>goto_previous_buffer</code></td></tr>
<tr><td><code>.</code></td><td>Go to last modification in current file</td><td><code>goto_last_modification</code></td></tr>
<tr><td><code>j</code></td><td>Move down textual (instead of visual) line</td><td><code>move_line_down</code></td></tr>
<tr><td><code>k</code></td><td>Move up textual (instead of visual) line</td><td><code>move_line_up</code></td></tr>
<tr><td><code>w</code></td><td>Show labels at each word and select the word that belongs to the entered labels</td><td><code>goto_word</code></td></tr>
<p>Accessed by typing <code>m</code> in <ahref="keymap.html#normal-mode">normal mode</a>.</p>
<p>Please refer to the relevant sections for detailed explanations about <ahref="./surround.html">surround</a> and <ahref="./textobjects.html">textobjects</a>.</p>
<tr><td><code>s</code><code><char></code></td><td>Surround current selection with <code><char></code></td><td><code>surround_add</code></td></tr>
<tr><td><code>r</code><code><from><to></code></td><td>Replace surround character <code><from></code> with <code><to></code></td><td><code>surround_replace</code></td></tr>
<tr><td><code>d</code><code><char></code></td><td>Delete surround character <code><char></code></td><td><code>surround_delete</code></td></tr>
<tr><td><code>a</code><code><object></code></td><td>Select around textobject</td><td><code>select_textobject_around</code></td></tr>
<tr><td><code>k</code></td><td>Show documentation for item under cursor in a <ahref="keymap.html#popup">popup</a> (<strong>LSP</strong>)</td><td><code>hover</code></td></tr>
<tr><td><code>s</code></td><td>Open document symbol picker (<strong>LSP</strong>)</td><td><code>symbol_picker</code></td></tr>
<tr><td><code>S</code></td><td>Open workspace symbol picker (<strong>LSP</strong>)</td><td><code>workspace_symbol_picker</code></td></tr>
<p>The command line is used for executing <ahref="./commands.html#typable-commands">typable commands</a> like <code>:write</code> or <code>:quit</code>. Press <code>:</code> to activate the command line.</p>
<p>Typable commands optionally accept arguments. <code>:write</code> for example accepts an optional path to write the file contents. The command line also supports a quoting syntax for arguments, flags to modify command behaviors, and <em>expansions</em> - a way to insert values from the editor. Most commands support these features but some have custom parsing rules (see the <ahref="command-line.html#exceptions">exceptions</a> below).</p>
<p>By default, command arguments are split on tabs and space characters. <code>:open README.md CHANGELOG.md</code> for example should open two files, <code>README.md</code> and <code>CHANGELOG.md</code>. Arguments that contain spaces can be surrounded in single quotes (<code>'</code>) or backticks (<code>`</code>) to prevent the space from separating the argument, like <code>:open 'a b.txt'</code>.</p>
<p>Double quotes may be used the same way, but double quotes <em>expand</em> their inner content. <code>:echo "%{cursor_line}"</code> for example may print <code>1</code> because of the expansion for the <code>cursor_line</code> variable. <code>:echo '%{cursor_line}'</code> though prints <code>%{cursor_line}</code> literally: content within single quotes or backticks is interpreted as-is.</p>
<p>On Unix systems the backslash character may be used to escape certain characters depending on where it is used. Within an argument which isn't surround in quotes, the backslash can be used to escape the space or tab characters: <code>:open a\ b.txt</code> is equivalent to <code>:open 'a b.txt'</code>. The backslash may also be used to escape quote characters (<code>'</code>, <code>`</code>, <code>"</code>) or the percent token (<code>%</code>) when used at the beginning of an argument. <code>:echo \%%sh{foo}</code> for example prints <code>%sh{foo}</code> instead of invoking a <code>foo</code> shell command and <code>:echo \"quote</code> prints <code>"quote</code>. The backslash character is treated literally in any other situation on Unix systems and always on Windows: <code>:echo \n</code> always prints <code>\n</code>.</p>
<p>Command flags are optional switches that can be used to alter the behavior of a command. For example the <code>:sort</code> command accepts an optional <code>--reverse</code> (or <code>-r</code> for short) flag which causes the sort command to reverse the sorting direction. Typing the <code>-</code> character shows completions for the current command's flags, if any.</p>
<p>The <code>--</code> flag specifies the end of flags. All arguments after <code>--</code> are treated as positional arguments: <code>:open -- -a.txt</code> opens a file called <code>-a.txt</code>.</p>
<p>Expansions are patterns that Helix recognizes and replaces within the command line. Helix recognizes anything starting with a percent token (<code>%</code>) as an expansion, for example <code>%sh{echo hi!}</code>. Expansions are particularly useful when used in commands like <code>:echo</code> or <code>:noop</code> for executing simple scripts. For example:</p>
<pre><codeclass="language-toml">[keys.normal]
# Print the current line's git blame information to the statusline.
<p>Expansions take the form <code>%[<kind>]<open><contents><close></code>. In <code>%sh{echo hi!}</code>, for example, the kind is <code>sh</code> - the shell expansion - and the contents are "echo hi!", with <code>{</code> and <code>}</code> acting as opening and closing delimiters. The following open/close characters are recognized as expansion delimiter pairs: <code>(</code>/<code>)</code>, <code>[</code>/<code>]</code>, <code>{</code>/<code>}</code> and <code><</code>/<code>></code>. Plus the single characters <code>'</code>, <code>"</code> or <code>|</code> may be used instead: <code>%{cursor_line}</code> is equivalent to <code>%<cursor_line></code>, <code>%[cursor_line]</code> or <code>%|cursor_line|</code>.</p>
<p>To escape a percent character instead of treating it as an expansion, use two percent characters consecutively. To execute a shell command like <code>date -u +'%Y-%m-%d'</code>, double the percent characters: <code>:echo %sh{date -u +'%%Y-%%m-%%d'}</code>.</p>
<p>When no <code><kind></code> is provided, Helix will expand a <strong>variable</strong>. For example <code>%{cursor_line}</code> can be used as in argument to insert the line number. <code>:echo %{cursor_line}</code> for instance may print <code>1</code> to the statusline.</p>
<tr><td><code>cursor_line</code></td><td>The line number of the primary cursor in the currently focused document, starting at 1.</td></tr>
<tr><td><code>cursor_column</code></td><td>The column number of the primary cursor in the currently focused document, starting at 1. This is counted as the number of grapheme clusters from the start of the line rather than bytes or codepoints.</td></tr>
<tr><td><code>buffer_name</code></td><td>The relative path of the currently focused document. <code>[scratch]</code> is expanded instead for scratch buffers.</td></tr>
<tr><td><code>line_ending</code></td><td>A string containing the line ending of the currently focused document. For example on Unix systems this is usually a line-feed character (<code>\n</code>) but on Windows systems this may be a carriage-return plus a line-feed (<code>\r\n</code>). The line ending kind of the currently focused document can be inspected with the <code>:line-ending</code> command.</td></tr>
</tbody></table>
</div>
<p>Aside from editor variables, the following expansions may be used:</p>
<ul>
<li>Unicode <code>%u{..}</code>. The contents may contain up to six hexadecimal numbers corresponding to a Unicode codepoint value. For example <code>:echo %u{25CF}</code> prints <code>●</code> to the statusline.</li>
<li>Shell <code>%sh{..}</code>. The contents are passed to the configured shell command. For example <code>:echo %sh{echo "20 * 5" | bc}</code> may print <code>100</code> on the statusline on when using a shell with <code>echo</code> and the <code>bc</code> calculator installed. Shell expansions are evaluated recursively. <code>%sh{echo '%{buffer_name}:%{cursor_line}'}</code> for example executes a command like <code>echo 'README.md:1'</code>: the variables within the <code>%sh{..}</code> expansion are evaluated before executing the shell command.</li>
</ul>
<p>As mentioned above, double quotes can be used to surround arguments containing spaces but also support expansions within the quoted content unlike singe quotes or backticks. For example <code>:echo "circle: %u{25CF}"</code> prints <code>circle: ●</code> to the statusline while <code>:echo 'circle: %u{25CF}'</code> prints <code>circle: %u{25CF}</code>.</p>
<p>Note that expansions are only evaluated once the Enter key is pressed in command mode.</p>
<p>The following commands support expansions but otherwise pass the given argument directly to the shell program without interpreting quotes:</p>
<ul>
<li><code>:insert-output</code></li>
<li><code>:append-output</code></li>
<li><code>:pipe</code></li>
<li><code>:pipe-to</code></li>
<li><code>:run-shell-command</code></li>
</ul>
<p>For example executing <code>:sh echo "%{buffer_name}:%{cursor_column}"</code> would pass text like <code>echo "README.md:1"</code> as an argument to the shell program: the expansions are evaluated but not the quotes. As mentioned above, percent characters can be used in shell commands by doubling the percent character. To insert the output of a command like <code>date -u +'%Y-%m-%d'</code> use <code>:insert-output date -u +'%%Y-%%m-%%d'</code>.</p>
<p>The <code>:set-option</code> and <code>:toggle-option</code> commands use regular parsing for the first argument - the config option name - and parse the rest depending on the config option's type. <code>:set-option</code> interprets the second argument as a string for string config options and parses everything else as JSON.</p>
<p><code>:toggle-option</code>'s behavior depends on the JSON type of the config option supplied as the first argument:</p>
<ul>
<li>Booleans: only the config option name should be provided. For example <code>:toggle-option auto-format</code> will flip the <code>auto-format</code> option.</li>
<li>Strings: the rest of the command line is parsed with regular quoting rules. For example <code>:toggle-option indent-heuristic hybrid tree-sitter simple</code> cycles through "hybrid", "tree-sitter" and "simple" values on each invocation of the command.</li>
<li>Numbers, arrays and objects: the rest of the command line is parsed as a stream of JSON values. For example <code>:toggle-option rulers [81] [51, 73]</code> cycles through <code>[81]</code> and <code>[51, 73]</code>.</li>
</ul>
<p>When providing multiple values to <code>:toggle-option</code> there should be no duplicates. <code>:toggle-option indent-heuristic hybrid simple tree-sitter simple</code> for example would only toggle between "hybrid" and "tree-sitter" values.</p>
<p><code>:lsp-workspace-command</code> works similarly to <code>:toggle-option</code>. The first argument (if present) is parsed according to normal rules. The rest of the line is parsed as JSON values. Unlike <code>:toggle-option</code>, string arguments for a command must be quoted. For example <code>:lsp-workspace-command lsp.Command "foo" "bar"</code>.</p>
<p>Typable commands are used from command mode and may take arguments. Command mode can be activated by pressing <code>:</code>. The built-in typable commands are:</p>
<tr><td><code>:quit</code>, <code>:q</code></td><td>Close the current view.</td></tr>
<tr><td><code>:quit!</code>, <code>:q!</code></td><td>Force close the current view, ignoring unsaved changes.</td></tr>
<tr><td><code>:open</code>, <code>:o</code>, <code>:edit</code>, <code>:e</code></td><td>Open a file from disk into the current view.</td></tr>
<tr><td><code>:buffer-close</code>, <code>:bc</code>, <code>:bclose</code></td><td>Close the current buffer.</td></tr>
<tr><td><code>:buffer-close!</code>, <code>:bc!</code>, <code>:bclose!</code></td><td>Close the current buffer forcefully, ignoring unsaved changes.</td></tr>
<tr><td><code>:buffer-close-others</code>, <code>:bco</code>, <code>:bcloseother</code></td><td>Close all buffers but the currently focused one.</td></tr>
<tr><td><code>:buffer-close-others!</code>, <code>:bco!</code>, <code>:bcloseother!</code></td><td>Force close all buffers but the currently focused one.</td></tr>
<tr><td><code>:buffer-close-all</code>, <code>:bca</code>, <code>:bcloseall</code></td><td>Close all buffers without quitting.</td></tr>
<tr><td><code>:buffer-close-all!</code>, <code>:bca!</code>, <code>:bcloseall!</code></td><td>Force close all buffers ignoring unsaved changes without quitting.</td></tr>
<tr><td><code>:buffer-next</code>, <code>:bn</code>, <code>:bnext</code></td><td>Goto next buffer.</td></tr>
<tr><td><code>:write</code>, <code>:w</code></td><td>Write changes to disk. Accepts an optional path (:write some/path.txt)</td></tr>
<tr><td><code>:write!</code>, <code>:w!</code></td><td>Force write changes to disk creating necessary subdirectories. Accepts an optional path (:write! some/path.txt)</td></tr>
<tr><td><code>:write-buffer-close</code>, <code>:wbc</code></td><td>Write changes to disk and closes the buffer. Accepts an optional path (:write-buffer-close some/path.txt)</td></tr>
<tr><td><code>:write-buffer-close!</code>, <code>:wbc!</code></td><td>Force write changes to disk creating necessary subdirectories and closes the buffer. Accepts an optional path (:write-buffer-close! some/path.txt)</td></tr>
<tr><td><code>:new</code>, <code>:n</code></td><td>Create a new scratch buffer.</td></tr>
<tr><td><code>:format</code>, <code>:fmt</code></td><td>Format the file using an external formatter or language server.</td></tr>
<tr><td><code>:indent-style</code></td><td>Set the indentation style for editing. ('t' for tabs or 1-16 for number of spaces.)</td></tr>
<tr><td><code>:line-ending</code></td><td>Set the document's default line ending. Options: crlf, lf.</td></tr>
<tr><td><code>:earlier</code>, <code>:ear</code></td><td>Jump back to an earlier point in edit history. Accepts a number of steps or a time span.</td></tr>
<tr><td><code>:later</code>, <code>:lat</code></td><td>Jump to a later point in edit history. Accepts a number of steps or a time span.</td></tr>
<tr><td><code>:write-quit</code>, <code>:wq</code>, <code>:x</code></td><td>Write changes to disk and close the current view. Accepts an optional path (:wq some/path.txt)</td></tr>
<tr><td><code>:write-quit!</code>, <code>:wq!</code>, <code>:x!</code></td><td>Write changes to disk and close the current view forcefully. Accepts an optional path (:wq! some/path.txt)</td></tr>
<tr><td><code>:write-all</code>, <code>:wa</code></td><td>Write changes from all buffers to disk.</td></tr>
<tr><td><code>:write-all!</code>, <code>:wa!</code></td><td>Forcefully write changes from all buffers to disk creating necessary subdirectories.</td></tr>
<tr><td><code>:write-quit-all</code>, <code>:wqa</code>, <code>:xa</code></td><td>Write changes from all buffers to disk and close all views.</td></tr>
<tr><td><code>:write-quit-all!</code>, <code>:wqa!</code>, <code>:xa!</code></td><td>Write changes from all buffers to disk and close all views forcefully (ignoring unsaved changes).</td></tr>
<tr><td><code>:quit-all</code>, <code>:qa</code></td><td>Close all views.</td></tr>
<tr><td><code>:quit-all!</code>, <code>:qa!</code></td><td>Force close all views ignoring unsaved changes.</td></tr>
<tr><td><code>:cquit</code>, <code>:cq</code></td><td>Quit with exit code (default 1). Accepts an optional integer exit code (:cq 2).</td></tr>
<tr><td><code>:cquit!</code>, <code>:cq!</code></td><td>Force quit with exit code (default 1) ignoring unsaved changes. Accepts an optional integer exit code (:cq! 2).</td></tr>
<tr><td><code>:theme</code></td><td>Change the editor theme (show current theme if no name specified).</td></tr>
<tr><td><code>:yank-join</code></td><td>Yank joined selections. A separator can be provided as first argument. Default value is newline.</td></tr>
<tr><td><code>:clipboard-yank</code></td><td>Yank main selection into system clipboard.</td></tr>
<tr><td><code>:clipboard-yank-join</code></td><td>Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline.</td></tr>
<tr><td><code>:primary-clipboard-yank</code></td><td>Yank main selection into system primary clipboard.</td></tr>
<tr><td><code>:primary-clipboard-yank-join</code></td><td>Yank joined selections into system primary clipboard. A separator can be provided as first argument. Default value is newline.</td></tr>
<tr><td><code>:clipboard-paste-after</code></td><td>Paste system clipboard after selections.</td></tr>
<tr><td><code>:clipboard-paste-before</code></td><td>Paste system clipboard before selections.</td></tr>
<tr><td><code>:clipboard-paste-replace</code></td><td>Replace selections with content of system clipboard.</td></tr>
<tr><td><code>:primary-clipboard-paste-after</code></td><td>Paste primary clipboard after selections.</td></tr>
<tr><td><code>:primary-clipboard-paste-before</code></td><td>Paste primary clipboard before selections.</td></tr>
<tr><td><code>:primary-clipboard-paste-replace</code></td><td>Replace selections with content of system primary clipboard.</td></tr>
<tr><td><code>:show-clipboard-provider</code></td><td>Show clipboard provider name in status bar.</td></tr>
<tr><td><code>:change-current-directory</code>, <code>:cd</code></td><td>Change the current working directory.</td></tr>
<tr><td><code>:show-directory</code>, <code>:pwd</code></td><td>Show the current working directory.</td></tr>
<tr><td><code>:encoding</code></td><td>Set encoding. Based on <code>https://encoding.spec.whatwg.org</code>.</td></tr>
<tr><td><code>:character-info</code>, <code>:char</code></td><td>Get info about the character under the primary cursor.</td></tr>
<tr><td><code>:reload</code>, <code>:rl</code></td><td>Discard changes and reload from the source file.</td></tr>
<tr><td><code>:reload-all</code>, <code>:rla</code></td><td>Discard changes and reload all documents from the source files.</td></tr>
<tr><td><code>:update</code>, <code>:u</code></td><td>Write changes only if the file has been modified.</td></tr>
<tr><td><code>:lsp-restart</code></td><td>Restarts the given language servers, or all language servers that are used by the current file if no arguments are supplied</td></tr>
<tr><td><code>:lsp-stop</code></td><td>Stops the given language servers, or all language servers that are used by the current file if no arguments are supplied</td></tr>
<tr><td><code>:tree-sitter-scopes</code></td><td>Display tree sitter scopes, primarily for theming and development.</td></tr>
<tr><td><code>:tree-sitter-highlight-name</code></td><td>Display name of tree-sitter highlight scope under the cursor.</td></tr>
<tr><td><code>:debug-start</code>, <code>:dbg</code></td><td>Start a debug session from a given template with given parameters.</td></tr>
<tr><td><code>:debug-remote</code>, <code>:dbg-tcp</code></td><td>Connect to a debug adapter by TCP address and start a debugging session from a given template with given parameters.</td></tr>
<tr><td><code>:debug-eval</code></td><td>Evaluate expression in current debug context.</td></tr>
<tr><td><code>:vsplit</code>, <code>:vs</code></td><td>Open the file in a vertical split.</td></tr>
<tr><td><code>:vsplit-new</code>, <code>:vnew</code></td><td>Open a scratch buffer in a vertical split.</td></tr>
<tr><td><code>:hsplit</code>, <code>:hs</code>, <code>:sp</code></td><td>Open the file in a horizontal split.</td></tr>
<tr><td><code>:hsplit-new</code>, <code>:hnew</code></td><td>Open a scratch buffer in a horizontal split.</td></tr>
<tr><td><code>:tutor</code></td><td>Open the tutorial.</td></tr>
<tr><td><code>:goto</code>, <code>:g</code></td><td>Goto line number.</td></tr>
<tr><td><code>:set-language</code>, <code>:lang</code></td><td>Set the language of current buffer (show current language if no value specified).</td></tr>
<tr><td><code>:set-option</code>, <code>:set</code></td><td>Set a config option at runtime.<br>For example to disable smart case search, use <code>:set search.smart-case false</code>.</td></tr>
<tr><td><code>:toggle-option</code>, <code>:toggle</code></td><td>Toggle a config option at runtime.<br>For example to toggle smart case search, use <code>:toggle search.smart-case</code>.</td></tr>
<tr><td><code>:get-option</code>, <code>:get</code></td><td>Get the current value of a config option.</td></tr>
<tr><td><code>:sort</code></td><td>Sort ranges in selection.</td></tr>
<tr><td><code>:reflow</code></td><td>Hard-wrap the current selection of lines to a given width.</td></tr>
<tr><td><code>:tree-sitter-subtree</code>, <code>:ts-subtree</code></td><td>Display the smallest tree-sitter subtree that spans the primary selection, primarily for debugging queries.</td></tr>
<tr><td><code>:config-reload</code></td><td>Refresh user config.</td></tr>
<tr><td><code>:config-open</code></td><td>Open the user config.toml file.</td></tr>
<tr><td><code>:config-open-workspace</code></td><td>Open the workspace config.toml file.</td></tr>
<tr><td><code>:log-open</code></td><td>Open the helix log file.</td></tr>
<tr><td><code>:insert-output</code></td><td>Run shell command, inserting output before each selection.</td></tr>
<tr><td><code>:append-output</code></td><td>Run shell command, appending output after each selection.</td></tr>
<tr><td><code>:pipe</code></td><td>Pipe each selection to the shell command.</td></tr>
<tr><td><code>:pipe-to</code></td><td>Pipe each selection to the shell command, ignoring output.</td></tr>
<tr><td><code>:run-shell-command</code>, <code>:sh</code></td><td>Run a shell command</td></tr>
<tr><td><code>:reset-diff-change</code>, <code>:diffget</code>, <code>:diffg</code></td><td>Reset the diff change at the cursor position.</td></tr>
<tr><td><code>:clear-register</code></td><td>Clear given register. If no argument is provided, clear all registers.</td></tr>
<tr><td><code>:redraw</code></td><td>Clear and re-render the whole UI</td></tr>
<tr><td><code>:move</code>, <code>:mv</code></td><td>Move the current buffer and its corresponding file to a different path</td></tr>
<tr><td><code>:yank-diagnostic</code></td><td>Yank diagnostic(s) under primary cursor to register, or clipboard by default</td></tr>
<tr><td><code>:read</code>, <code>:r</code></td><td>Load a file into buffer</td></tr>
<p>Static commands take no arguments and can be bound to keys. Static commands can also be executed from the command picker (<code><space>?</code>). The built-in static commands are:</p>
<tr><td><code>extend_search_next</code></td><td>Add next search match to selection</td><td>select: <code>n</code></td></tr>
<tr><td><code>extend_search_prev</code></td><td>Add previous search match to selection</td><td>select: <code>N</code></td></tr>
<tr><td><code>search_selection</code></td><td>Use current selection as search pattern</td><td>normal: <code><A-*></code>, select: <code><A-*></code></td></tr>
<tr><td><code>search_selection_detect_word_boundaries</code></td><td>Use current selection as the search pattern, automatically wrapping with <code>\b</code> on word boundaries</td><td>normal: <code>*</code>, select: <code>*</code></td></tr>
<tr><td><code>make_search_word_bounded</code></td><td>Modify current search to make it word bounded</td><td></td></tr>
<tr><td><code>global_search</code></td><td>Global search in workspace folder</td><td>normal: <code><space>/</code>, select: <code><space>/</code></td></tr>
<tr><td><code>extend_line</code></td><td>Select current line, if already selected, extend to another line based on the anchor</td><td></td></tr>
<tr><td><code>extend_line_below</code></td><td>Select current line, if already selected, extend to next line</td><td>normal: <code>x</code>, select: <code>x</code></td></tr>
<tr><td><code>extend_line_above</code></td><td>Select current line, if already selected, extend to previous line</td><td></td></tr>
<tr><td><code>select_line_above</code></td><td>Select current line, if already selected, extend or shrink line above based on the anchor</td><td></td></tr>
<tr><td><code>select_line_below</code></td><td>Select current line, if already selected, extend or shrink line below based on the anchor</td><td></td></tr>
<tr><td><code>extend_to_line_bounds</code></td><td>Extend selection to line bounds</td><td>normal: <code>X</code>, select: <code>X</code></td></tr>
<tr><td><code>shrink_to_line_bounds</code></td><td>Shrink selection to line bounds</td><td>normal: <code><A-x></code>, select: <code><A-x></code></td></tr>
<tr><td><code>change_selection_noyank</code></td><td>Change selection without yanking</td><td>normal: <code><A-c></code>, select: <code><A-c></code></td></tr>
<tr><td><code>collapse_selection</code></td><td>Collapse selection into single cursor</td><td>normal: <code>;</code>, select: <code>;</code></td></tr>
<tr><td><code>flip_selections</code></td><td>Flip selection cursor and anchor</td><td>normal: <code><A-;></code>, select: <code><A-;></code></td></tr>
<tr><td><code>ensure_selections_forward</code></td><td>Ensure all selections face forward</td><td>normal: <code><A-:></code>, select: <code><A-:></code></td></tr>
<tr><td><code>insert_mode</code></td><td>Insert before selection</td><td>normal: <code>i</code>, select: <code>i</code></td></tr>
<tr><td><code>append_mode</code></td><td>Append after selection</td><td>normal: <code>a</code>, select: <code>a</code></td></tr>
<tr><td><code>file_picker_in_current_buffer_directory</code></td><td>Open file picker at current buffer's directory</td><td></td></tr>
<tr><td><code>file_picker_in_current_directory</code></td><td>Open file picker at current working directory</td><td>normal: <code><space>F</code>, select: <code><space>F</code></td></tr>
<tr><td><code>file_explorer</code></td><td>Open file explorer in workspace root</td><td>normal: <code><space>e</code>, select: <code><space>e</code></td></tr>
<tr><td><code>file_explorer_in_current_buffer_directory</code></td><td>Open file explorer at current buffer's directory</td><td>normal: <code><space>E</code>, select: <code><space>E</code></td></tr>
<tr><td><code>file_explorer_in_current_directory</code></td><td>Open file explorer at current working directory</td><td></td></tr>
<tr><td><code>smart_tab</code></td><td>Insert tab if all cursors have all whitespace to their left; otherwise, run a separate command.</td><td>insert: <code><tab></code></td></tr>
<tr><td><code>yank_to_clipboard</code></td><td>Yank selections to clipboard</td><td>normal: <code><space>y</code>, select: <code><space>y</code></td></tr>
<tr><td><code>yank_to_primary_clipboard</code></td><td>Yank selections to primary clipboard</td><td></td></tr>
<tr><td><code>yank_joined</code></td><td>Join and yank selections</td><td></td></tr>
<tr><td><code>yank_joined_to_clipboard</code></td><td>Join and yank selections to clipboard</td><td></td></tr>
<tr><td><code>yank_main_selection_to_clipboard</code></td><td>Yank main selection to clipboard</td><td>normal: <code><space>Y</code>, select: <code><space>Y</code></td></tr>
<tr><td><code>yank_joined_to_primary_clipboard</code></td><td>Join and yank selections to primary clipboard</td><td></td></tr>
<tr><td><code>yank_main_selection_to_primary_clipboard</code></td><td>Yank main selection to primary clipboard</td><td></td></tr>
<tr><td><code>replace_with_yanked</code></td><td>Replace with yanked text</td><td>normal: <code>R</code>, select: <code>R</code></td></tr>
<tr><td><code>replace_selections_with_clipboard</code></td><td>Replace selections by clipboard content</td><td>normal: <code><space>R</code>, select: <code><space>R</code></td></tr>
<tr><td><code>replace_selections_with_primary_clipboard</code></td><td>Replace selections by primary clipboard</td><td></td></tr>
<tr><td><code>paste_after</code></td><td>Paste after selection</td><td>normal: <code>p</code>, select: <code>p</code></td></tr>
<tr><td><code>paste_before</code></td><td>Paste before selection</td><td>normal: <code>P</code>, select: <code>P</code></td></tr>
<tr><td><code>paste_clipboard_after</code></td><td>Paste clipboard after selections</td><td>normal: <code><space>p</code>, select: <code><space>p</code></td></tr>
<tr><td><code>paste_clipboard_before</code></td><td>Paste clipboard before selections</td><td>normal: <code><space>P</code>, select: <code><space>P</code></td></tr>
<tr><td><code>paste_primary_clipboard_after</code></td><td>Paste primary clipboard after selections</td><td></td></tr>
<tr><td><code>paste_primary_clipboard_before</code></td><td>Paste primary clipboard before selections</td><td></td></tr>
<tr><td><code>hover</code></td><td>Show docs for item under cursor</td><td>normal: <code><space>k</code>, select: <code><space>k</code></td></tr>
<tr><td><code>select_next_sibling</code></td><td>Select next sibling in the syntax tree</td><td>normal: <code><A-n></code>, <code><A-right></code>, select: <code><A-n></code>, <code><A-right></code></td></tr>
<tr><td><code>select_prev_sibling</code></td><td>Select previous sibling the in syntax tree</td><td>normal: <code><A-p></code>, <code><A-left></code>, select: <code><A-p></code>, <code><A-left></code></td></tr>
<tr><td><code>select_all_siblings</code></td><td>Select all siblings of the current node</td><td>normal: <code><A-a></code>, select: <code><A-a></code></td></tr>
<tr><td><code>select_all_children</code></td><td>Select all children of the current node</td><td>normal: <code><A-I></code>, <code><S-A-down></code>, select: <code><A-I></code>, <code><S-A-down></code></td></tr>
<tr><td><code>jump_forward</code></td><td>Jump forward on jumplist</td><td>normal: <code><C-i></code>, <code><tab></code>, select: <code><C-i></code>, <code><tab></code></td></tr>
<tr><td><code>jump_backward</code></td><td>Jump backward on jumplist</td><td>normal: <code><C-o></code>, select: <code><C-o></code></td></tr>
<tr><td><code>save_selection</code></td><td>Save current selection to jumplist</td><td>normal: <code><C-s></code>, select: <code><C-s></code></td></tr>
<tr><td><code>jump_view_right</code></td><td>Jump to right split</td><td>normal: <code><C-w>l</code>, <code><space>wl</code>, <code><C-w><C-l></code>, <code><C-w><right></code>, <code><space>w<C-l></code>, <code><space>w<right></code>, select: <code><C-w>l</code>, <code><space>wl</code>, <code><C-w><C-l></code>, <code><C-w><right></code>, <code><space>w<C-l></code>, <code><space>w<right></code></td></tr>
<tr><td><code>jump_view_left</code></td><td>Jump to left split</td><td>normal: <code><C-w>h</code>, <code><space>wh</code>, <code><C-w><C-h></code>, <code><C-w><left></code>, <code><space>w<C-h></code>, <code><space>w<left></code>, select: <code><C-w>h</code>, <code><space>wh</code>, <code><C-w><C-h></code>, <code><C-w><left></code>, <code><space>w<C-h></code>, <code><space>w<left></code></td></tr>
<tr><td><code>swap_view_right</code></td><td>Swap with right split</td><td>normal: <code><C-w>L</code>, <code><space>wL</code>, select: <code><C-w>L</code>, <code><space>wL</code></td></tr>
<tr><td><code>swap_view_left</code></td><td>Swap with left split</td><td>normal: <code><C-w>H</code>, <code><space>wH</code>, select: <code><C-w>H</code>, <code><space>wH</code></td></tr>
<tr><td><code>swap_view_up</code></td><td>Swap with split above</td><td>normal: <code><C-w>K</code>, <code><space>wK</code>, select: <code><C-w>K</code>, <code><space>wK</code></td></tr>
<tr><td><code>swap_view_down</code></td><td>Swap with split below</td><td>normal: <code><C-w>J</code>, <code><space>wJ</code>, select: <code><C-w>J</code>, <code><space>wJ</code></td></tr>
<tr><td><code>dap_edit_condition</code></td><td>Edit breakpoint condition on current line</td><td>normal: <code><space>G<C-c></code>, select: <code><space>G<C-c></code></td></tr>
<tr><td><code>dap_edit_log</code></td><td>Edit breakpoint log message on current line</td><td>normal: <code><space>G<C-l></code>, select: <code><space>G<C-l></code></td></tr>
<tr><td><code>dap_switch_thread</code></td><td>Switch current thread</td><td>normal: <code><space>Gst</code>, select: <code><space>Gst</code></td></tr>
</div><divstyle="break-before: page; page-break-before: always;"></div><h1id="migrating-from-vim"><aclass="header"href="#migrating-from-vim">Migrating from Vim</a></h1>
<p>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 <code>selection → action</code> 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.</p>
<p>See also Kakoune's <ahref="https://github.com/mawww/kakoune/wiki/Migrating-from-Vim">Migrating from Vim</a> and Helix's <ahref="https://github.com/helix-editor/helix/wiki/Migrating-from-Vim">Migrating from Vim</a>.</p>
<p>💡 You can easily open the config file by typing <code>:config-open</code> within Helix normal mode.</p>
</blockquote>
<p>Example config:</p>
<pre><codeclass="language-toml">theme = "onedark"
[editor]
line-number = "relative"
mouse = false
[editor.cursor-shape]
insert = "bar"
normal = "block"
select = "underline"
[editor.file-picker]
hidden = false
</code></pre>
<p>You can use a custom configuration file by specifying it with the <code>-c</code> or
<code>--config</code> command line argument, for example <code>hx -c path/to/custom-config.toml</code>.
You can reload the config file by issuing the <code>:config-reload</code> command. Alternatively, on Unix operating systems, you can reload it by sending the USR1
signal to the Helix process, such as by using the command <code>pkill -USR1 hx</code>.</p>
<p>Finally, you can have a <code>config.toml</code> local to a project by putting it under a <code>.helix</code> directory in your repository.
Its settings will be merged with the configuration directory <code>config.toml</code> and the built-in configuration.</p>
<tr><td><code>scroll-lines</code></td><td>Number of lines to scroll per scroll wheel step</td><td><code>3</code></td></tr>
<tr><td><code>shell</code></td><td>Shell to use when running external commands</td><td>Unix: <code>["sh", "-c"]</code><br/>Windows: <code>["cmd", "/C"]</code></td></tr>
<tr><td><code>line-number</code></td><td>Line number display: <code>absolute</code> simply shows each line's number, while <code>relative</code> shows the distance from the current line. When unfocused or in insert mode, <code>relative</code> will still show absolute line numbers</td><td><code>absolute</code></td></tr>
<tr><td><code>cursorline</code></td><td>Highlight all lines with a cursor</td><td><code>false</code></td></tr>
<tr><td><code>cursorcolumn</code></td><td>Highlight all columns with a cursor</td><td><code>false</code></td></tr>
<tr><td><code>continue-comments</code></td><td>if helix should automatically add a line comment token if you create a new line inside a comment.</td><td><code>true</code></td></tr>
<tr><td><code>gutters</code></td><td>Gutters to display: Available are <code>diagnostics</code> and <code>diff</code> and <code>line-numbers</code> and <code>spacer</code>, note that <code>diagnostics</code> also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty</td><td><code>["diagnostics", "spacer", "line-numbers", "spacer", "diff"]</code></td></tr>
<tr><td><code>auto-completion</code></td><td>Enable automatic pop up of auto-completion</td><td><code>true</code></td></tr>
<tr><td><code>path-completion</code></td><td>Enable filepath completion. Show files and directories if an existing path at the cursor was recognized, either absolute or relative to the current opened document or current working directory (if the buffer is not yet saved). Defaults to true.</td><td><code>true</code></td></tr>
<tr><td><code>auto-format</code></td><td>Enable automatic formatting on save</td><td><code>true</code></td></tr>
<tr><td><code>idle-timeout</code></td><td>Time in milliseconds since last keypress before idle timers trigger.</td><td><code>250</code></td></tr>
<tr><td><code>completion-timeout</code></td><td>Time in milliseconds after typing a word character before completions are shown, set to 5 for instant.</td><td><code>250</code></td></tr>
<tr><td><code>preview-completion-insert</code></td><td>Whether to apply completion item instantly when selected</td><td><code>true</code></td></tr>
<tr><td><code>completion-trigger-len</code></td><td>The min-length of word under cursor to trigger autocompletion</td><td><code>2</code></td></tr>
<tr><td><code>completion-replace</code></td><td>Set to <code>true</code> to make completions always replace the entire word and not just the part before the cursor</td><td><code>false</code></td></tr>
<tr><td><code>auto-info</code></td><td>Whether to display info boxes</td><td><code>true</code></td></tr>
<tr><td><code>true-color</code></td><td>Set to <code>true</code> to override automatic detection of terminal truecolor support in the event of a false negative</td><td><code>false</code></td></tr>
<tr><td><code>undercurl</code></td><td>Set to <code>true</code> to override automatic detection of terminal undercurl support in the event of a false negative</td><td><code>false</code></td></tr>
<tr><td><code>rulers</code></td><td>List of column positions at which to display the rulers. Can be overridden by language specific <code>rulers</code> in <code>languages.toml</code> file</td><td><code>[]</code></td></tr>
<tr><td><code>bufferline</code></td><td>Renders a line at the top of the editor displaying open buffers. Can be <code>always</code>, <code>never</code> or <code>multiple</code> (only shown if more than one buffer is in use)</td><td><code>never</code></td></tr>
<tr><td><code>color-modes</code></td><td>Whether to color the mode indicator with different colors depending on the mode itself</td><td><code>false</code></td></tr>
<tr><td><code>text-width</code></td><td>Maximum line length. Used for the <code>:reflow</code> command and soft-wrapping if <code>soft-wrap.wrap-at-text-width</code> is set</td><td><code>80</code></td></tr>
<tr><td><code>workspace-lsp-roots</code></td><td>Directories relative to the workspace root that are treated as LSP roots. Should only be set in <code>.helix/config.toml</code></td><td><code>[]</code></td></tr>
<tr><td><code>default-line-ending</code></td><td>The line ending to use for new documents. Can be <code>native</code>, <code>lf</code>, <code>crlf</code>, <code>ff</code>, <code>cr</code> or <code>nel</code>. <code>native</code> uses the platform's native line ending (<code>crlf</code> on Windows, otherwise <code>lf</code>).</td><td><code>native</code></td></tr>
<tr><td><code>insert-final-newline</code></td><td>Whether to automatically insert a trailing line-ending on write if missing</td><td><code>true</code></td></tr>
<tr><td><code>popup-border</code></td><td>Draw border around <code>popup</code>, <code>menu</code>, <code>all</code>, or <code>none</code></td><td><code>none</code></td></tr>
<tr><td><code>indent-heuristic</code></td><td>How the indentation for a newly inserted line is computed: <code>simple</code> just copies the indentation level from the previous line, <code>tree-sitter</code> computes the indentation based on the syntax tree and <code>hybrid</code> combines both approaches. If the chosen heuristic is not available, a different one will be used as a fallback (the fallback order being <code>hybrid</code> -><code>tree-sitter</code> -><code>simple</code>).</td><td><code>hybrid</code></td></tr>
<tr><td><code>jump-label-alphabet</code></td><td>The characters that are used to generate two character jump labels. Characters at the start of the alphabet are used first.</td><td><code>"abcdefghijklmnopqrstuvwxyz"</code></td></tr>
<tr><td><code>end-of-line-diagnostics</code></td><td>Minimum severity of diagnostics to render at the end of the line. Set to <code>disable</code> to disable entirely. Refer to the setting about <code>inline-diagnostics</code> for more details</td><td>"disable"</td></tr>
<tr><td><code>clipboard-provider</code></td><td>Which API to use for clipboard interaction. One of <code>pasteboard</code> (MacOS), <code>wayland</code>, <code>x-clip</code>, <code>x-sel</code>, <code>win-32-yank</code>, <code>termux</code>, <code>tmux</code>, <code>windows</code>, <code>termcode</code>, <code>none</code>, or a custom command set.</td><td>Platform and environment specific.</td></tr>
<tr><td><code>left</code></td><td>A list of elements aligned to the left of the statusline</td><td><code>["mode", "spinner", "file-name", "read-only-indicator", "file-modification-indicator"]</code></td></tr>
<tr><td><code>center</code></td><td>A list of elements aligned to the middle of the statusline</td><td><code>[]</code></td></tr>
<tr><td><code>right</code></td><td>A list of elements aligned to the right of the statusline</td><td><code>["diagnostics", "selections", "register", "position", "file-encoding"]</code></td></tr>
<tr><td><code>separator</code></td><td>The character used to separate elements in the statusline</td><td><code>"│"</code></td></tr>
<tr><td><code>mode.normal</code></td><td>The text shown in the <code>mode</code> element for normal mode</td><td><code>"NOR"</code></td></tr>
<tr><td><code>mode.insert</code></td><td>The text shown in the <code>mode</code> element for insert mode</td><td><code>"INS"</code></td></tr>
<tr><td><code>mode.select</code></td><td>The text shown in the <code>mode</code> element for select mode</td><td><code>"SEL"</code></td></tr>
</tbody></table>
</div>
<p>The following statusline elements can be configured:</p>
<tr><td><code>file-name</code></td><td>The path/name of the opened file</td></tr>
<tr><td><code>file-absolute-path</code></td><td>The absolute path/name of the opened file</td></tr>
<tr><td><code>file-base-name</code></td><td>The basename of the opened file</td></tr>
<tr><td><code>file-modification-indicator</code></td><td>The indicator to show whether the file is modified (a <code>[+]</code> appears when there are unsaved changes)</td></tr>
<tr><td><code>file-encoding</code></td><td>The encoding of the opened file if it differs from UTF-8</td></tr>
<tr><td><code>file-line-ending</code></td><td>The file line endings (CRLF or LF)</td></tr>
<tr><td><code>read-only-indicator</code></td><td>An indicator that shows <code>[readonly]</code> when a file cannot be written</td></tr>
<tr><td><code>total-line-numbers</code></td><td>The total line numbers of the opened file</td></tr>
<tr><td><code>file-type</code></td><td>The type of the opened file</td></tr>
<tr><td><code>diagnostics</code></td><td>The number of warnings and/or errors</td></tr>
<tr><td><code>workspace-diagnostics</code></td><td>The number of warnings and/or errors on workspace</td></tr>
<tr><td><code>selections</code></td><td>The number of active selections</td></tr>
<tr><td><code>primary-selection-length</code></td><td>The number of characters currently in primary selection</td></tr>
<tr><td><code>enable</code></td><td>Enables LSP integration. Setting to false will completely disable language servers regardless of language settings.</td><td><code>true</code></td></tr>
<tr><td><code>display-signature-help-docs</code></td><td>Display docs under signature help popup</td><td><code>true</code></td></tr>
<tr><td><code>snippets</code></td><td>Enables snippet completions. Requires a server restart (<code>:lsp-restart</code>) to take effect after <code>:config-reload</code>/<code>:set</code>.</td><td><code>true</code></td></tr>
<tr><td><code>goto-reference-include-declaration</code></td><td>Include declaration in the goto references popup.</td><td><code>true</code></td></tr>
<p>You may also have to activate them in the language server 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!</p>
<tr><td><code>git-global</code></td><td>Enables reading global <code>.gitignore</code>, whose path is specified in git's config: <code>core.excludesfile</code> option</td><td><code>true</code></td></tr>
<tr><td><code>max-depth</code></td><td>Set with an integer value for maximum depth to recurse</td><td>Unset by default</td></tr>
</tbody></table>
</div>
<p>Ignore files can be placed locally as <code>.ignore</code> or put in your home directory as <code>~/.ignore</code>. They support the usual ignore and negative ignore (unignore) rules used in <code>.gitignore</code> files.</p>
<p>Additionally, you can use Helix-specific ignore files by creating a local <code>.helix/ignore</code> file in the current workspace or a global <code>ignore</code> file located in your Helix config directory:</p>
<ul>
<li>Linux and Mac: <code>~/.config/helix/ignore</code></li>
<tr><td><code>focus-lost</code></td><td>Enable automatic saving on the focus moving away from Helix. Requires <ahref="https://github.com/helix-editor/helix/wiki/Terminal-Support">focus event support</a> from your terminal</td><td><code>false</code></td></tr>
<tr><td><code>after-delay.enable</code></td><td>Enable automatic saving after <code>auto-save.after-delay.timeout</code> milliseconds have passed since last edit.</td><td><code>false</code></td></tr>
<tr><td><code>after-delay.timeout</code></td><td>Time in milliseconds since last edit before auto save timer triggers.</td><td><code>3000</code></td></tr>
<tr><td><code>render</code></td><td>Whether to render whitespace. May either be <code>all</code> or <code>none</code>, or a table with sub-keys <code>space</code>, <code>nbsp</code>, <code>nnbsp</code>, <code>tab</code>, and <code>newline</code></td><td><code>none</code></td></tr>
<tr><td><code>characters</code></td><td>Literal characters to use when rendering whitespace. Sub-keys may be any of <code>tab</code>, <code>space</code>, <code>nbsp</code>, <code>nnbsp</code>, <code>newline</code> or <code>tabpad</code></td><td>See example below</td></tr>
<tr><td><code>layout</code></td><td>A vector of gutters to display</td><td><code>["diagnostics", "spacer", "line-numbers", "spacer", "diff"]</code></td></tr>
<p>The <code>diff</code> gutter option displays colored bars indicating whether a <code>git</code> diff represents that a line was added, removed or changed.
These colors are controlled by the theme attributes <code>diff.plus</code>, <code>diff.minus</code> and <code>diff.delta</code>.</p>
<p>Other diff providers will eventually be supported by a future plugin system.</p>
<p>There are currently no options for this section.</p>
<tr><td><code>enable</code></td><td>Whether soft wrapping is enabled.</td><td><code>false</code></td></tr>
<tr><td><code>max-wrap</code></td><td>Maximum free space left at the end of the line.</td><td><code>20</code></td></tr>
<tr><td><code>max-indent-retain</code></td><td>Maximum indentation to carry over when soft wrapping a line.</td><td><code>40</code></td></tr>
<tr><td><code>wrap-indicator</code></td><td>Text inserted before soft wrapped lines, highlighted with <code>ui.virtual.wrap</code></td><td><code>↪ </code></td></tr>
<tr><td><code>wrap-at-text-width</code></td><td>Soft wrap at <code>text-width</code> instead of using the full viewport size.</td><td><code>false</code></td></tr>
<tr><td><code>enable</code></td><td>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 <code>move_parent_node_end</code>. 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.</td><td><code>true</code></td></tr>
<tr><td><code>supersede-menu</code></td><td>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 <code>smart-tab</code> command. If this option is set to true, the <code>smart-tab</code> 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 <code>C-n</code>/<code>C-p</code>.</td><td><code>false</code></td></tr>
</tbody></table>
</div>
<p>Due to lack of support for S-tab in some terminals, the default keybindings don't fully embrace smart-tab editing experience. If you enjoy smart-tab navigation and a terminal that supports the <ahref="https://github.com/helix-editor/helix/wiki/Terminal-Support#enhanced-keyboard-protocol">Enhanced Keyboard protocol</a>, consider setting extra keybindings:</p>
<tr><td><code>cursor-line</code></td><td>The minimum severity that a diagnostic must have to be shown inline on the line that contains the primary cursor. Set to <code>disable</code> to not show any diagnostics inline. This option does not have any effect when in insert-mode and will only take effect 350ms after moving the cursor to a different line.</td><td><code>"disable"</code></td></tr>
<tr><td><code>other-lines</code></td><td>The minimum severity that a diagnostic must have to be shown inline on a line that does not contain the cursor-line. Set to <code>disable</code> to not show any diagnostics inline.</td><td><code>"disable"</code></td></tr>
<tr><td><code>prefix-len</code></td><td>How many horizontal bars <code>─</code> are rendered before the diagnostic text.</td><td><code>1</code></td></tr>
<tr><td><code>max-wrap</code></td><td>Equivalent of the <code>editor.soft-wrap.max-wrap</code> option for diagnostics.</td><td><code>20</code></td></tr>
<tr><td><code>max-diagnostics</code></td><td>Maximum number of diagnostics to render inline for a given line</td><td><code>10</code></td></tr>
</tbody></table>
</div>
<p>The allowed values for <code>cursor-line</code> and <code>other-lines</code> are: <code>error</code>, <code>warning</code>, <code>info</code>, <code>hint</code>.</p>
<p>The (first) diagnostic with the highest severity that is not shown inline is rendered at the end of the line (as long as its severity is higher than the <code>end-of-line-diagnostics</code> config option):</p>
<pre><code>fn main() {
let baz = 1;
let foo = bar; a local variable with a similar name exists: baz
└─ no such value in this scope
}
</code></pre>
<p>The new diagnostic rendering is not yet enabled by default. As soon as end of line or inline diagnostics are enabled the old diagnostics rendering is automatically disabled. The recommended default setting are:</p>
<pre><codeclass="language-toml">[editor]
end-of-line-diagnostics = "hint"
[editor.inline-diagnostics]
cursor-line = "warning" # show warnings and errors on the cursorline inline
<p>To use a theme add <code>theme = "<name>"</code> to the top of your <ahref="./configuration.html"><code>config.toml</code></a> file, or select it during runtime using <code>:theme <name></code>.</p>
<h2id="creating-a-theme"><aclass="header"href="#creating-a-theme">Creating a theme</a></h2>
<p>Create a file with the name of your theme as the file name (i.e <code>mytheme.toml</code>) and place it in your <code>themes</code> directory (i.e <code>~/.config/helix/themes</code> or <code>%AppData%\helix\themes</code> on Windows). The directory might have to be created beforehand.</p>
<blockquote>
<p>💡 The names "default" and "base16_default" are reserved for built-in themes
and cannot be overridden by user-defined themes.</p>
<p>Where <code>key</code> represents what you want to style, <code>fg</code> specifies the foreground color, <code>bg</code> the background color, <code>underline</code> the underline <code>style</code>/<code>color</code>, and <code>modifiers</code> is a list of style modifiers. <code>bg</code>, <code>underline</code> and <code>modifiers</code> can be omitted to defer to the defaults.</p>
<p>To specify only the foreground color:</p>
<pre><codeclass="language-toml">key = "#ffffff"
</code></pre>
<p>If the key contains a dot <code>'.'</code>, it must be quoted to prevent it being parsed as a <ahref="https://toml.io/en/v1.0.0#keys">dotted key</a>.</p>
<p>These keys match <ahref="https://tree-sitter.github.io/tree-sitter/3-syntax-highlighting.html#highlights">tree-sitter scopes</a>.</p>
<p>When determining styling for a highlight, the longest matching theme key will be used. For example, if the highlight is <code>function.builtin.static</code>, the key <code>function.builtin</code> will be used instead of <code>function</code>.</p>
<p>We use a similar set of scopes as
<ahref="https://www.sublimetext.com/docs/scope_naming.html">Sublime Text</a>. See also
<tr><td><code>ui.statusline.normal</code></td><td>Statusline mode during normal mode (<ahref="./configuration.html#editor-section">only if <code>editor.color-modes</code> is enabled</a>)</td></tr>
<tr><td><code>ui.statusline.insert</code></td><td>Statusline mode during insert mode (<ahref="./configuration.html#editor-section">only if <code>editor.color-modes</code> is enabled</a>)</td></tr>
<tr><td><code>ui.statusline.select</code></td><td>Statusline mode during select mode (<ahref="./configuration.html#editor-section">only if <code>editor.color-modes</code> is enabled</a>)</td></tr>
<tr><td><code>ui.statusline.separator</code></td><td>Separator character in statusline</td></tr>
<tr><td><code>ui.bufferline</code></td><td>Style for the buffer line</td></tr>
<tr><td><code>ui.bufferline.active</code></td><td>Style for the active buffer in buffer line</td></tr>
<tr><td><code>ui.bufferline.background</code></td><td>Style for bufferline background</td></tr>
<tr><td><code>ui.popup</code></td><td>Documentation popups (e.g. Space + k)</td></tr>
<tr><td><code>ui.popup.info</code></td><td>Prompt for multiple key options</td></tr>
<tr><td><code>ui.picker.header</code></td><td>Header row area in pickers with multiple columns</td></tr>
<tr><td><code>ui.picker.header.column</code></td><td>Column names in pickers with multiple columns</td></tr>
<tr><td><code>ui.picker.header.column.active</code></td><td>The column name in pickers with multiple columns where the cursor is entering into.</td></tr>
<tr><td><code>ui.help</code></td><td>Description box for commands</td></tr>
<tr><td><code>ui.text</code></td><td>Default text style, command prompts, popup text, etc.</td></tr>
<tr><td><code>ui.text.focus</code></td><td>The currently selected line in the picker</td></tr>
<tr><td><code>ui.text.inactive</code></td><td>Same as <code>ui.text</code> but when the text is inactive (e.g. suggestions)</td></tr>
<tr><td><code>ui.text.info</code></td><td>The key: command text in <code>ui.popup.info</code> boxes</td></tr>
<tr><td><code>ui.text.directory</code></td><td>Directory names in prompt completion</td></tr>
<tr><td><code>ui.virtual.ruler</code></td><td>Ruler columns (see the <ahref="./configuration.html#editor-section"><code>editor.rulers</code> config</a>)</td></tr>
<tr><td><code>ui.virtual.inlay-hint</code></td><td>Default style for inlay hints of all kinds</td></tr>
<tr><td><code>ui.virtual.inlay-hint.parameter</code></td><td>Style for inlay hints of kind <code>parameter</code> (language servers are not required to set a kind)</td></tr>
<tr><td><code>ui.virtual.inlay-hint.type</code></td><td>Style for inlay hints of kind <code>type</code> (language servers are not required to set a kind)</td></tr>
<tr><td><code>ui.virtual.wrap</code></td><td>Soft-wrap indicator (see the <ahref="./configuration.html#editor-section"><code>editor.soft-wrap</code> config</a>)</td></tr>
<tr><td><code>ui.virtual.jump-label</code></td><td>Style for virtual jump labels</td></tr>
<tr><td><code>ui.menu</code></td><td>Code and command completion menus</td></tr>
<tr><td><code>ui.highlight</code></td><td>Highlighted lines in the picker preview</td></tr>
<tr><td><code>ui.highlight.frameline</code></td><td>Line at which debugging execution is paused at</td></tr>
<tr><td><code>ui.cursorline.primary</code></td><td>The line of the primary cursor (<ahref="./configuration.html#editor-section">if cursorline is enabled</a>)</td></tr>
<tr><td><code>ui.cursorline.secondary</code></td><td>The lines of any other cursors (<ahref="./configuration.html#editor-section">if cursorline is enabled</a>)</td></tr>
<tr><td><code>ui.cursorcolumn.primary</code></td><td>The column of the primary cursor (<ahref="./configuration.html#editor-section">if cursorcolumn is enabled</a>)</td></tr>
<tr><td><code>ui.cursorcolumn.secondary</code></td><td>The columns of any other cursors (<ahref="./configuration.html#editor-section">if cursorcolumn is enabled</a>)</td></tr>
<p>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).</p>
<p>There are three kinds of commands that can be used in keymaps:</p>
<ul>
<li>Static commands: commands like <code>move_char_right</code> which are usually bound to
keys and used for movement and editing. A list of static commands is
available in the <ahref="./keymap.html">Keymap</a> documentation and in the source code
in <ahref="https://github.com/helix-editor/helix/blob/master/helix-term/src/commands.rs"><code>helix-term/src/commands.rs</code></a>
at the invocation of <code>static_commands!</code> macro.</li>
<li>Typable commands: commands that can be executed from command mode (<code>:</code>), for
example <code>:write!</code>. See the <ahref="./commands.html">Commands</a> documentation for a
list of available typeable commands or the <code>TypableCommandList</code> declaration in
the source code at <ahref="https://github.com/helix-editor/helix/blob/master/helix-term/src/commands/typed.rs"><code>helix-term/src/commands/typed.rs</code></a>.</li>
<li>Macros: sequences of keys that are executed in order. These keybindings
start with <code>@</code> and then list any number of keys to be executed. For example
<code>@miw</code> can be used to select the surrounding word. For now, macro keybindings
are not allowed in keybinding sequences due to limitations in the way that
command sequences are executed. Modifier keys (e.g. Alt+o) can be used
like <code>"<A-o>"</code>, e.g. <code>"@miw<A-o>"</code></li>
</ul>
<p>To remap keys, create a <code>config.toml</code> file in your <code>helix</code> configuration
directory (default <code>~/.config/helix</code> on Linux systems) with a structure like
this:</p>
<blockquote>
<p>💡 To set a modifier + key as a keymap, type <code>A-X = ...</code> or <code>C-X = ...</code> for Alt + X or Ctrl + X. Combine with Shift using a dash, e.g. <code>C-S-esc</code>.
Within macros, wrap them in <code><></code>, e.g. <code><A-X></code> and <code><C-X></code> to distinguish from the <code>A</code> or <code>C</code> keys.</p>
</blockquote>
<pre><codeclass="language-toml"># 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
"A-x" = "@x<A-d>" # Maps Alt-x to a macro selecting the whole line and deleting it without yanking it
[keys.insert]
"A-x" = "normal_mode" # Maps Alt-X to enter normal mode
j = { k = "normal_mode" } # Maps `jk` to exit insert mode
<p>Keys can be disabled by binding them to the <code>no_op</code> command.</p>
<p>All other keys such as <code>?</code>, <code>!</code>, <code>-</code> etc. can be used literally:</p>
<pre><codeclass="language-toml">[keys.normal]
"?" = ":write"
"!" = ":write"
"-" = ":write"
</code></pre>
<p>Note: <code>-</code> can't be used when combined with a modifier, for example <code>Alt</code> + <code>-</code> should be written as <code>A-minus</code>. <code>A--</code> is not accepted.</p>
<tr><td><code>name</code></td><td>The name of the language</td></tr>
<tr><td><code>language-id</code></td><td>The language-id for language servers, checkout the table at <ahref="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem">TextDocumentItem</a> for the right id</td></tr>
<tr><td><code>scope</code></td><td>A string like <code>source.js</code> that identifies the language. Currently, we strive to match the scope names used by popular TextMate grammars and by the Linguist library. Usually <code>source.<name></code> or <code>text.<name></code> in case of markup languages</td></tr>
<tr><td><code>injection-regex</code></td><td>regex pattern that will be tested against a language name in order to determine whether this language should be used for a potential <ahref="https://tree-sitter.github.io/tree-sitter/3-syntax-highlighting.html#language-injection">language injection</a> site.</td></tr>
<tr><td><code>file-types</code></td><td>The filetypes of the language, for example <code>["yml", "yaml"]</code>. See the file-type detection section below.</td></tr>
<tr><td><code>shebangs</code></td><td>The interpreters from the shebang line, for example <code>["sh", "bash"]</code></td></tr>
<tr><td><code>roots</code></td><td>A set of marker files to look for when trying to find the workspace root. For example <code>Cargo.lock</code>, <code>yarn.lock</code></td></tr>
<tr><td><code>auto-format</code></td><td>Whether to autoformat this language when saving</td></tr>
<tr><td><code>diagnostic-severity</code></td><td>Minimal severity of diagnostic for it to be displayed. (Allowed values: <code>error</code>, <code>warning</code>, <code>info</code>, <code>hint</code>)</td></tr>
<tr><td><code>comment-tokens</code></td><td>The tokens to use as a comment token, either a single token <code>"//"</code> or an array <code>["//", "///", "//!"]</code> (the first token will be used for commenting). Also configurable as <code>comment-token</code> for backwards compatibility</td></tr>
<tr><td><code>block-comment-tokens</code></td><td>The start and end tokens for a multiline comment either an array or single table of <code>{ start = "/*", end = "*/"}</code>. The first set of tokens will be used for commenting, any pairs in the array can be uncommented</td></tr>
<tr><td><code>indent</code></td><td>The indent to use. Has sub keys <code>unit</code> (the text inserted into the document when indenting; usually set to N spaces or <code>"\t"</code> for tabs) and <code>tab-width</code> (the number of spaces rendered for a tab)</td></tr>
<tr><td><code>language-servers</code></td><td>The Language Servers used for this language. See below for more information in the section <ahref="languages.html#configuring-language-servers-for-a-language">Configuring Language Servers for a language</a></td></tr>
<tr><td><code>grammar</code></td><td>The tree-sitter grammar to use (defaults to the value of <code>name</code>)</td></tr>
<tr><td><code>formatter</code></td><td>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</td></tr>
<tr><td><code>text-width</code></td><td>Maximum line length. Used for the <code>:reflow</code> command and soft-wrapping if <code>soft-wrap.wrap-at-text-width</code> is set, defaults to <code>editor.text-width</code></td></tr>
<tr><td><code>path-completion</code></td><td>Overrides the <code>editor.path-completion</code> config key for the language.</td></tr>
<tr><td><code>workspace-lsp-roots</code></td><td>Directories relative to the workspace root that are treated as LSP roots. Should only be set in <code>.helix/config.toml</code>. Overwrites the setting of the same name in <code>config.toml</code> if set.</td></tr>
<tr><td><code>persistent-diagnostic-sources</code></td><td>An array of LSP diagnostic sources assumed unchanged when the language server resends the same set of diagnostics. Helix can track the position for these diagnostics internally instead. Useful for diagnostics that are recomputed on save.</td></tr>
</tbody></table>
</div>
<h3id="file-type-detection-and-the-file-types-key"><aclass="header"href="#file-type-detection-and-the-file-types-key">File-type detection and the <code>file-types</code> key</a></h3>
<p>Helix determines which language configuration to use based on the <code>file-types</code> key
from the above section. <code>file-types</code> is a list of strings or tables, for
<p>When determining a language configuration to use, Helix searches the file-types
with the following priorities:</p>
<ol>
<li>Glob: values in <code>glob</code> tables are checked against the full path of the given
file. Globs are standard Unix-style path globs (e.g. the kind you use in Shell)
and can be used to match paths for a specific prefix, suffix, directory, etc.
In the above example, the <code>{ glob = "Makefile" }</code> config would match files
with the name <code>Makefile</code>, the <code>{ glob = ".git/config" }</code> config would match
<code>config</code> files in <code>.git</code> directories, and the <code>{ glob = ".github/workflows/*.yaml" }</code>
config would match any <code>yaml</code> files in <code>.github/workflow</code> directories. Note
that globs should always use the Unix path separator <code>/</code> even on Windows systems;
the matcher will automatically take the machine-specific separators into account.
If the glob isn't an absolute path or doesn't already start with a glob prefix,
<code>*/</code> will automatically be added to ensure it matches for any subdirectory.</li>
<li>Extension: if there are no glob matches, any <code>file-types</code> string that matches
the file extension of a given file wins. In the example above, the <code>"toml"</code>
config matches files like <code>Cargo.toml</code> or <code>languages.toml</code>.</li>
</ol>
<h2id="language-server-configuration"><aclass="header"href="#language-server-configuration">Language Server configuration</a></h2>
<p>Language servers are configured separately in the table <code>language-server</code> in the same file as the languages <code>languages.toml</code></p>
<tr><td><code>command</code></td><td>The name or path of the language server binary to execute. Binaries must be in <code>$PATH</code></td></tr>
<tr><td><code>args</code></td><td>A list of arguments to pass to the language server binary</td></tr>
<tr><td><code>config</code></td><td>Language server initialization options</td></tr>
<tr><td><code>timeout</code></td><td>The maximum time a request to the language server may take, in seconds. Defaults to <code>20</code></td></tr>
<tr><td><code>environment</code></td><td>Any environment variables that will be used when starting the language server <code>{ "KEY1" = "Value1", "KEY2" = "Value2" }</code></td></tr>
<tr><td><code>required-root-patterns</code></td><td>A list of <code>glob</code> patterns to look for in the working directory. The language server is started if at least one of them is found.</td></tr>
</tbody></table>
</div>
<p>A <code>format</code> sub-table within <code>config</code> can be used to pass extra formatting options to
# pass format options according to https://github.com/typescript-language-server/typescript-language-server#workspacedidchangeconfiguration omitting the "[language].format." prefix.
<h3id="configuring-language-servers-for-a-language"><aclass="header"href="#configuring-language-servers-for-a-language">Configuring Language Servers for a language</a></h3>
<p>The <code>language-servers</code> attribute in a language tells helix which language servers are used for this language.</p>
<p>They have to be defined in the <code>[language-server]</code> table as described in the previous section.</p>
<p>Different languages can use the same language server instance, e.g. <code>typescript-language-server</code> is used for javascript, jsx, tsx and typescript by default.</p>
<p>The definition order of language servers affects the order in the results list of code action menu.</p>
<p>In case multiple language servers are specified in the <code>language-servers</code> attribute of a <code>language</code>,
it's often useful to only enable/disable certain language-server features for these language servers.</p>
<p>As an example, <code>efm-lsp-prettier</code> of the previous example is used only with a formatting command <code>prettier</code>,
so everything else should be handled by the <code>typescript-language-server</code> (which is configured by default).
The language configuration for typescript could look like this:</p>
<p>Each requested LSP feature is prioritized in the order of the <code>language-servers</code> array.
For example, the first <code>goto-definition</code> supported language server (in this case <code>typescript-language-server</code>) will be taken for the relevant LSP request (command <code>goto_definition</code>).
The features <code>diagnostics</code>, <code>code-action</code>, <code>completion</code>, <code>document-symbols</code> and <code>workspace-symbols</code> 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 <code>except-features</code> or <code>only-features</code> 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).</p>
<tr><td><code>git</code></td><td>A git remote URL from which the grammar should be cloned</td></tr>
<tr><td><code>rev</code></td><td>The revision (commit hash or tag) which should be fetched</td></tr>
<tr><td><code>subpath</code></td><td>A path within the grammar directory which should be built. Some grammar repositories host multiple grammars (for example <code>tree-sitter-typescript</code> and <code>tree-sitter-ocaml</code>) in subdirectories. This key is used to point <code>hx --grammar build</code> to the correct path for compilation. When omitted, the root of repository is used</td></tr>
<p>This section contains guides for adding new language server configurations,
tree-sitter grammars, textobject queries, and other similar items.</p>
<divstyle="break-before: page; page-break-before: always;"></div><h2id="adding-new-languages-to-helix"><aclass="header"href="#adding-new-languages-to-helix">Adding new languages to Helix</a></h2>
<p>In order to add a new language to Helix, you will need to follow the steps
<li>If you encounter errors when running Helix after switching branches, you may
need to update the tree-sitter grammars. Run the command <code>hx --grammar fetch</code>
to fetch the grammars and <code>hx --grammar build</code> to build any out-of-date
grammars.</li>
<li>If a parser is causing a segfault, or you want to remove it, make sure to
remove the compiled parser located at <code>runtime/grammars/<name>.so</code>.</li>
<li>If you are attempting to add queries and Helix is unable to locate them, ensure that the environment variable <code>HELIX_RUNTIME</code> is set to the location of the <code>runtime</code> folder you're developing in.</li>
<p>Helix supports textobjects that are language specific, such as functions, classes, etc.
These textobjects require an accompanying tree-sitter grammar and a <code>textobjects.scm</code> 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 <ahref="https://tree-sitter.github.io/tree-sitter/using-parsers/queries/1-syntax.html">official tree-sitter
documentation</a>.</p>
<p>Query files should be placed in <code>runtime/queries/{language}/textobjects.scm</code>
when contributing to Helix. Note that to test the query files locally you should put
them under your local runtime directory (<code>~/.config/helix/runtime</code> on Linux
for example).</p>
<p>The following <ahref="https://tree-sitter.github.io/tree-sitter/using-parsers/queries/2-operators.html#capturing-nodes">captures</a> are recognized:</p>
<p><ahref="https://github.com/search?q=repo%3Ahelix-editor%2Fhelix+path%3A%2A%2A/textobjects.scm&type=Code&ref=advsearch&l=&l=">Example query files</a> can be found in the helix GitHub repository.</p>
<h2id="queries-for-textobject-based-navigation"><aclass="header"href="#queries-for-textobject-based-navigation">Queries for textobject based navigation</a></h2>
<p>Tree-sitter based navigation in Helix is done using captures in the
following order:</p>
<ul>
<li><code>object.movement</code></li>
<li><code>object.around</code></li>
<li><code>object.inside</code></li>
</ul>
<p>For example if a <code>function.around</code> capture has been already defined for a language
in its <code>textobjects.scm</code> file, function navigation should also work automatically.
<code>function.movement</code> should be defined only if the node captured by <code>function.around</code>
<p>Helix uses tree-sitter to correctly indent new lines. This requires a tree-
sitter grammar and an <code>indent.scm</code> query file placed in <code>runtime/queries/ {language}/indents.scm</code>. The indentation for a line is calculated by traversing
the syntax tree from the lowest node at the beginning of the new line (see
<ahref="guides/indent.html#indent-queries">Indent queries</a>). 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.</p>
<p>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 <ahref="guides/indent.html#capture-types">Capture types</a>.</p>
<p>By default, Helix uses the <code>hybrid</code> indentation heuristic. This means that
indent queries are not used to compute the expected absolute indentation of a
line but rather the expected difference in indentation between the new and an
already existing line. This difference is then added to the actual indentation
of the already existing line. Since this makes errors in the indent queries
harder to find, it is recommended to disable it when testing via
<code>:set indent-heuristic tree-sitter</code>. The rest of this guide assumes that
the <code>tree-sitter</code> heuristic is used.</p>
<p>Writing language injection queries allows one to highlight a specific node as a different language.
In addition to the <ahref="https://tree-sitter.github.io/tree-sitter/3-syntax-highlighting.html#language-injection">standard</a> language injection options used by tree-sitter, there
are a few Helix specific extensions that allow for more control.</p>
<p>And example of a simple query that would highlight all strings as bash in Nix:</p>