Document not found (404)
+This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +diff --git a/master/.nojekyll b/master/.nojekyll new file mode 100644 index 000000000..f17311098 --- /dev/null +++ b/master/.nojekyll @@ -0,0 +1 @@ +This file makes sure that Github Pages doesn't process mdBook's output. diff --git a/master/404.html b/master/404.html new file mode 100644 index 000000000..c80fa4b58 --- /dev/null +++ b/master/404.html @@ -0,0 +1,221 @@ + + +
+ + +This URL is invalid, sorry. Please use the navigation bar or search to continue.
+ +Requirements:
+Clone the Helix GitHub repository into a directory of your choice. The
+examples in this documentation assume installation into either ~/src/
on
+Linux and macOS, or %userprofile%\src\
on Windows.
If you are using the musl-libc
standard library instead of glibc
the following environment variable must be set during the build to ensure tree-sitter grammars can be loaded correctly:
RUSTFLAGS="-C target-feature=-crt-static"
+
+Clone the repository:
+git clone https://github.com/helix-editor/helix
+cd helix
+
+Compile from source:
+cargo install --path helix-term --locked
+
+This command will create the hx
executable and construct the tree-sitter
+grammars in the local runtime
folder.
++💡 If you do not want to fetch or build grammars, set an environment variable
+HELIX_DISABLE_AUTO_GRAMMAR_BUILD
++💡 Tree-sitter grammars can be fetched and compiled if not pre-packaged. Fetch +grammars with
+hx --grammar fetch
and compile them with +hx --grammar build
. This will install them in +theruntime
directory within the user's helix config directory (more +details below).
The runtime directory is one below the Helix source, so either export a
+HELIX_RUNTIME
environment variable to point to that directory and add it to
+your ~/.bashrc
or equivalent:
export HELIX_RUNTIME=~/src/helix/runtime
+
+Or, create a symbolic link:
+ln -Ts $PWD/runtime ~/.config/helix/runtime
+
+If the above command fails to create a symbolic link because the file exists either move ~/.config/helix/runtime
to a new location or delete it, then run the symlink command above again.
Either set the HELIX_RUNTIME
environment variable to point to the runtime files using the Windows setting (search for
+Edit environment variables for your account
) or use the setx
command in
+Cmd:
setx HELIX_RUNTIME "%userprofile%\source\repos\helix\runtime"
+
+++💡
+%userprofile%
resolves to your user directory like +C:\Users\Your-Name\
for example.
Or, create a symlink in %appdata%\helix\
that links to the source code directory:
Method | Command |
---|---|
PowerShell | New-Item -ItemType Junction -Target "runtime" -Path "$Env:AppData\helix\runtime" |
Cmd | cd %appdata%\helix mklink /D runtime "%userprofile%\src\helix\runtime" |
++💡 On Windows, creating a symbolic link may require running PowerShell or +Cmd as an administrator.
+
When Helix finds multiple runtime directories it will search through them for files in the +following order:
+runtime/
sibling directory to $CARGO_MANIFEST_DIR
directory (this is intended for
+developing and testing helix only).runtime/
subdirectory of OS-dependent helix user config directory.$HELIX_RUNTIME
HELIX_DEFAULT_RUNTIME
environment variable)runtime/
subdirectory of path to Helix executable.This order also sets the priority for selecting which file will be used if multiple runtime +directories have files with the same name.
+If you are making a package of Helix for end users, to provide a good out of
+the box experience, you should set the HELIX_DEFAULT_RUNTIME
environment
+variable at build time (before invoking cargo build
) to a directory which
+will store the final runtime files after installation. For example, say you want
+to package the runtime into /usr/lib/helix/runtime
. The rough steps a build
+script could follow are:
export HELIX_DEFAULT_RUNTIME=/usr/lib/helix/runtime
cargo build --profile opt --locked --path helix-term
cp -r runtime $BUILD_DIR/usr/lib/helix/
cp target/opt/hx $BUILD_DIR/usr/bin/hx
This way the resulting hx
binary will always look for its runtime directory in
+/usr/lib/helix/runtime
if the user has no custom runtime in ~/.config/helix
+or HELIX_RUNTIME
.
To make sure everything is set up as expected you should run the Helix health +check:
+hx --health
+
+For more information on the health check results refer to +Health check.
+If your desktop environment supports the
+XDG desktop menu
+you can configure Helix to show up in the application menu by copying the
+provided .desktop
and icon files to their correct folders:
cp contrib/Helix.desktop ~/.local/share/applications
+cp contrib/helix.png ~/.icons # or ~/.local/share/icons
+
+It is recommended to convert the links in the .desktop
file to absolute paths to avoid potential problems:
sed -i -e "s|Exec=hx %F|Exec=$(readlink -f ~/.cargo/bin/hx) %F|g" \
+ -e "s|Icon=helix|Icon=$(readlink -f ~/.icons/helix.png)|g" ~/.local/share/applications/Helix.desktop
+
+To use another terminal than the system default, you can modify the .desktop
+file. For example, to use kitty
:
sed -i "s|Exec=hx %F|Exec=kitty hx %F|g" ~/.local/share/applications/Helix.desktop
+sed -i "s|Terminal=true|Terminal=false|g" ~/.local/share/applications/Helix.desktop
+
+
+ Command mode can be activated by pressing :
. The built-in commands are:
Name | Description |
---|---|
:quit , :q | Close the current view. |
:quit! , :q! | Force close the current view, ignoring unsaved changes. |
:open , :o | Open a file from disk into the current view. |
:buffer-close , :bc , :bclose | Close the current buffer. |
:buffer-close! , :bc! , :bclose! | Close the current buffer forcefully, ignoring unsaved changes. |
:buffer-close-others , :bco , :bcloseother | Close all buffers but the currently focused one. |
:buffer-close-others! , :bco! , :bcloseother! | Force close all buffers but the currently focused one. |
:buffer-close-all , :bca , :bcloseall | Close all buffers without quitting. |
:buffer-close-all! , :bca! , :bcloseall! | Force close all buffers ignoring unsaved changes without quitting. |
:buffer-next , :bn , :bnext | Goto next buffer. |
:buffer-previous , :bp , :bprev | Goto previous buffer. |
:write , :w | Write changes to disk. Accepts an optional path (:write some/path.txt) |
:write! , :w! | Force write changes to disk creating necessary subdirectories. Accepts an optional path (:write! some/path.txt) |
:write-buffer-close , :wbc | Write changes to disk and closes the buffer. Accepts an optional path (:write-buffer-close some/path.txt) |
:write-buffer-close! , :wbc! | Force write changes to disk creating necessary subdirectories and closes the buffer. Accepts an optional path (:write-buffer-close! some/path.txt) |
:new , :n | Create a new scratch buffer. |
:format , :fmt | Format the file using the LSP formatter. |
:indent-style | Set the indentation style for editing. ('t' for tabs or 1-16 for number of spaces.) |
:line-ending | Set the document's default line ending. Options: crlf, lf. |
:earlier , :ear | Jump back to an earlier point in edit history. Accepts a number of steps or a time span. |
:later , :lat | Jump to a later point in edit history. Accepts a number of steps or a time span. |
:write-quit , :wq , :x | Write changes to disk and close the current view. Accepts an optional path (:wq some/path.txt) |
:write-quit! , :wq! , :x! | Write changes to disk and close the current view forcefully. Accepts an optional path (:wq! some/path.txt) |
:write-all , :wa | Write changes from all buffers to disk. |
:write-all! , :wa! | Forcefully write changes from all buffers to disk creating necessary subdirectories. |
:write-quit-all , :wqa , :xa | Write changes from all buffers to disk and close all views. |
:write-quit-all! , :wqa! , :xa! | Write changes from all buffers to disk and close all views forcefully (ignoring unsaved changes). |
:quit-all , :qa | Close all views. |
:quit-all! , :qa! | Force close all views ignoring unsaved changes. |
:cquit , :cq | Quit with exit code (default 1). Accepts an optional integer exit code (:cq 2). |
:cquit! , :cq! | Force quit with exit code (default 1) ignoring unsaved changes. Accepts an optional integer exit code (:cq! 2). |
:theme | Change the editor theme (show current theme if no name specified). |
:yank-join | Yank joined selections. A separator can be provided as first argument. Default value is newline. |
:clipboard-yank | Yank main selection into system clipboard. |
:clipboard-yank-join | Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline. |
:primary-clipboard-yank | Yank main selection into system primary clipboard. |
:primary-clipboard-yank-join | Yank joined selections into system primary clipboard. A separator can be provided as first argument. Default value is newline. |
:clipboard-paste-after | Paste system clipboard after selections. |
:clipboard-paste-before | Paste system clipboard before selections. |
:clipboard-paste-replace | Replace selections with content of system clipboard. |
:primary-clipboard-paste-after | Paste primary clipboard after selections. |
:primary-clipboard-paste-before | Paste primary clipboard before selections. |
:primary-clipboard-paste-replace | Replace selections with content of system primary clipboard. |
:show-clipboard-provider | Show clipboard provider name in status bar. |
:change-current-directory , :cd | Change the current working directory. |
:show-directory , :pwd | Show the current working directory. |
:encoding | Set encoding. Based on https://encoding.spec.whatwg.org . |
:character-info , :char | Get info about the character under the primary cursor. |
:reload , :rl | Discard changes and reload from the source file. |
:reload-all , :rla | Discard changes and reload all documents from the source files. |
:update , :u | Write changes only if the file has been modified. |
:lsp-workspace-command | Open workspace command picker |
:lsp-restart | Restarts the language servers used by the current doc |
:lsp-stop | Stops the language servers that are used by the current doc |
:tree-sitter-scopes | Display tree sitter scopes, primarily for theming and development. |
:tree-sitter-highlight-name | Display name of tree-sitter highlight scope under the cursor. |
:debug-start , :dbg | Start a debug session from a given template with given parameters. |
:debug-remote , :dbg-tcp | Connect to a debug adapter by TCP address and start a debugging session from a given template with given parameters. |
:debug-eval | Evaluate expression in current debug context. |
:vsplit , :vs | Open the file in a vertical split. |
:vsplit-new , :vnew | Open a scratch buffer in a vertical split. |
:hsplit , :hs , :sp | Open the file in a horizontal split. |
:hsplit-new , :hnew | Open a scratch buffer in a horizontal split. |
:tutor | Open the tutorial. |
:goto , :g | Goto line number. |
:set-language , :lang | Set the language of current buffer (show current language if no value specified). |
:set-option , :set | Set a config option at runtime. For example to disable smart case search, use :set search.smart-case false . |
:toggle-option , :toggle | Toggle a boolean config option at runtime. For example to toggle smart case search, use :toggle search.smart-case . |
:get-option , :get | Get the current value of a config option. |
:sort | Sort ranges in selection. |
:rsort | Sort ranges in selection in reverse order. |
:reflow | Hard-wrap the current selection of lines to a given width. |
:tree-sitter-subtree , :ts-subtree | Display tree sitter subtree under cursor, primarily for debugging queries. |
:config-reload | Refresh user config. |
:config-open | Open the user config.toml file. |
:config-open-workspace | Open the workspace config.toml file. |
:log-open | Open the helix log file. |
:insert-output | Run shell command, inserting output before each selection. |
:append-output | Run shell command, appending output after each selection. |
:pipe | Pipe each selection to the shell command. |
:pipe-to | Pipe each selection to the shell command, ignoring output. |
:run-shell-command , :sh | Run a shell command |
:reset-diff-change , :diffget , :diffg | Reset the diff change at the cursor position. |
:clear-register | Clear given register. If no argument is provided, clear all registers. |
:redraw | Clear and re-render the whole UI |
:move | Move the current buffer and its corresponding file to a different path |
:yank-diagnostic | Yank diagnostic(s) under primary cursor to register, or clipboard by default |
:read , :r | Load a file into buffer |
To override global configuration parameters, create a config.toml
file located in your config directory:
~/.config/helix/config.toml
%AppData%\helix\config.toml
++💡 You can easily open the config file by typing
+:config-open
within Helix normal mode.
Example config:
+theme = "onedark"
+
+[editor]
+line-number = "relative"
+mouse = false
+
+[editor.cursor-shape]
+insert = "bar"
+normal = "block"
+select = "underline"
+
+[editor.file-picker]
+hidden = false
+
+You can use a custom configuration file by specifying it with the -c
or
+--config
command line argument, for example hx -c path/to/custom-config.toml
.
+Additionally, you can reload the configuration file by sending the USR1
+signal to the Helix process on Unix operating systems, such as by using the command pkill -USR1 hx
.
Finally, you can have a config.toml
local to a project by putting it under a .helix
directory in your repository.
+Its settings will be merged with the configuration directory config.toml
and the built-in configuration.
[editor]
Section[editor.statusline]
Section[editor.lsp]
Section[editor.cursor-shape]
Section[editor.file-picker]
Section[editor.auto-pairs]
Section[editor.search]
Section[editor.whitespace]
Section[editor.indent-guides]
Section[editor.gutters]
Section
+
+[editor.soft-wrap]
Section[editor.smart-tab]
Section[editor]
SectionKey | Description | Default |
---|---|---|
scrolloff | Number of lines of padding around the edge of the screen when scrolling | 5 |
mouse | Enable mouse mode | true |
middle-click-paste | Middle click paste support | true |
scroll-lines | Number of lines to scroll per scroll wheel step | 3 |
shell | Shell to use when running external commands | Unix: ["sh", "-c"] Windows: ["cmd", "/C"] |
line-number | Line number display: absolute simply shows each line's number, while relative shows the distance from the current line. When unfocused or in insert mode, relative will still show absolute line numbers | absolute |
cursorline | Highlight all lines with a cursor | false |
cursorcolumn | Highlight all columns with a cursor | false |
gutters | Gutters to display: Available are diagnostics and diff and line-numbers and spacer , note that diagnostics also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | ["diagnostics", "spacer", "line-numbers", "spacer", "diff"] |
auto-completion | Enable automatic pop up of auto-completion | true |
auto-format | Enable automatic formatting on save | true |
idle-timeout | Time in milliseconds since last keypress before idle timers trigger. | 250 |
completion-timeout | Time in milliseconds after typing a word character before completions are shown, set to 5 for instant. | 250 |
preview-completion-insert | Whether to apply completion item instantly when selected | true |
completion-trigger-len | The min-length of word under cursor to trigger autocompletion | 2 |
completion-replace | Set to true to make completions always replace the entire word and not just the part before the cursor | false |
auto-info | Whether to display info boxes | true |
true-color | Set to true to override automatic detection of terminal truecolor support in the event of a false negative | false |
undercurl | Set to true to override automatic detection of terminal undercurl support in the event of a false negative | false |
rulers | List of column positions at which to display the rulers. Can be overridden by language specific rulers in languages.toml file | [] |
bufferline | Renders a line at the top of the editor displaying open buffers. Can be always , never or multiple (only shown if more than one buffer is in use) | never |
color-modes | Whether to color the mode indicator with different colors depending on the mode itself | false |
text-width | Maximum line length. Used for the :reflow command and soft-wrapping if soft-wrap.wrap-at-text-width is set | 80 |
workspace-lsp-roots | Directories relative to the workspace root that are treated as LSP roots. Should only be set in .helix/config.toml | [] |
default-line-ending | The line ending to use for new documents. Can be native , lf , crlf , ff , cr or nel . native uses the platform's native line ending (crlf on Windows, otherwise lf ). | native |
insert-final-newline | Whether to automatically insert a trailing line-ending on write if missing | true |
popup-border | Draw border around popup , menu , all , or none | none |
indent-heuristic | How the indentation for a newly inserted line is computed: simple just copies the indentation level from the previous line, tree-sitter computes the indentation based on the syntax tree and hybrid combines both approaches. If the chosen heuristic is not available, a different one will be used as a fallback (the fallback order being hybrid -> tree-sitter -> simple ). | hybrid |
jump-label-alphabet | The characters that are used to generate two character jump labels. Characters at the start of the alphabet are used first. | "abcdefghijklmnopqrstuvwxyz" |
[editor.statusline]
SectionAllows configuring the statusline at the bottom of the editor.
+The configuration distinguishes between three areas of the status line:
+[ ... ... LEFT ... ... | ... ... ... CENTER ... ... ... | ... ... RIGHT ... ... ]
Statusline elements can be defined as follows:
+[editor.statusline]
+left = ["mode", "spinner"]
+center = ["file-name"]
+right = ["diagnostics", "selections", "position", "file-encoding", "file-line-ending", "file-type"]
+separator = "│"
+mode.normal = "NORMAL"
+mode.insert = "INSERT"
+mode.select = "SELECT"
+
+The [editor.statusline]
key takes the following sub-keys:
Key | Description | Default |
---|---|---|
left | A list of elements aligned to the left of the statusline | ["mode", "spinner", "file-name", "read-only-indicator", "file-modification-indicator"] |
center | A list of elements aligned to the middle of the statusline | [] |
right | A list of elements aligned to the right of the statusline | ["diagnostics", "selections", "register", "position", "file-encoding"] |
separator | The character used to separate elements in the statusline | "│" |
mode.normal | The text shown in the mode element for normal mode | "NOR" |
mode.insert | The text shown in the mode element for insert mode | "INS" |
mode.select | The text shown in the mode element for select mode | "SEL" |
The following statusline elements can be configured:
+Key | Description |
---|---|
mode | The current editor mode (mode.normal /mode.insert /mode.select ) |
spinner | A progress spinner indicating LSP activity |
file-name | The path/name of the opened file |
file-absolute-path | The absolute path/name of the opened file |
file-base-name | The basename of the opened file |
file-modification-indicator | The indicator to show whether the file is modified (a [+] appears when there are unsaved changes) |
file-encoding | The encoding of the opened file if it differs from UTF-8 |
file-line-ending | The file line endings (CRLF or LF) |
read-only-indicator | An indicator that shows [readonly] when a file cannot be written |
total-line-numbers | The total line numbers of the opened file |
file-type | The type of the opened file |
diagnostics | The number of warnings and/or errors |
workspace-diagnostics | The number of warnings and/or errors on workspace |
selections | The number of active selections |
primary-selection-length | The number of characters currently in primary selection |
position | The cursor position |
position-percentage | The cursor position as a percentage of the total number of lines |
separator | The string defined in editor.statusline.separator (defaults to "│" ) |
spacer | Inserts a space between elements (multiple/contiguous spacers may be specified) |
version-control | The current branch name or detached commit hash of the opened workspace |
register | The current selected register |
[editor.lsp]
SectionKey | Description | Default |
---|---|---|
enable | Enables LSP integration. Setting to false will completely disable language servers regardless of language settings. | true |
display-messages | Display LSP progress messages below statusline1 | false |
auto-signature-help | Enable automatic popup of signature help (parameter hints) | true |
display-inlay-hints | Display inlay hints2 | false |
display-signature-help-docs | Display docs under signature help popup | true |
snippets | Enables snippet completions. Requires a server restart (:lsp-restart ) to take effect after :config-reload /:set . | true |
goto-reference-include-declaration | Include declaration in the goto references popup. | true |
By default, a progress spinner is shown in the statusline beside the file path.
+You may also have to activate them in the LSP config for them to appear, not just in Helix. Inlay hints in Helix are still being improved on and may be a little bit laggy/janky under some circumstances. Please report any bugs you see so we can fix them!
+[editor.cursor-shape]
SectionDefines the shape of cursor in each mode.
+Valid values for these options are block
, bar
, underline
, or hidden
.
++💡 Due to limitations of the terminal environment, only the primary cursor can +change shape.
+
Key | Description | Default |
---|---|---|
normal | Cursor shape in normal mode | block |
insert | Cursor shape in insert mode | block |
select | Cursor shape in select mode | block |
[editor.file-picker]
SectionSet options for file picker and global search. Ignoring a file means it is +not visible in the Helix file picker and global search.
+All git related options are only enabled in a git repository.
+Key | Description | Default |
---|---|---|
hidden | Enables ignoring hidden files | true |
follow-symlinks | Follow symlinks instead of ignoring them | true |
deduplicate-links | Ignore symlinks that point at files already shown in the picker | true |
parents | Enables reading ignore files from parent directories | true |
ignore | Enables reading .ignore files | true |
git-ignore | Enables reading .gitignore files | true |
git-global | Enables reading global .gitignore , whose path is specified in git's config: core.excludesfile option | true |
git-exclude | Enables reading .git/info/exclude files | true |
max-depth | Set with an integer value for maximum depth to recurse | Unset by default |
Ignore files can be placed locally as .ignore
or put in your home directory as ~/.ignore
. They support the usual ignore and negative ignore (unignore) rules used in .gitignore
files.
Additionally, you can use Helix-specific ignore files by creating a local .helix/ignore
file in the current workspace or a global ignore
file located in your Helix config directory:
~/.config/helix/ignore
%AppData%\helix\ignore
Example:
+# unignore in file picker and global search
+!.github/
+!.gitignore
+!.gitattributes
+
+[editor.auto-pairs]
SectionEnables automatic insertion of pairs to parentheses, brackets, etc. Can be a +simple boolean value, or a specific mapping of pairs of single characters.
+To disable auto-pairs altogether, set auto-pairs
to false
:
[editor]
+auto-pairs = false # defaults to `true`
+
+The default pairs are (){}[]''""``
, but these can be customized by
+setting auto-pairs
to a TOML table:
[editor.auto-pairs]
+'(' = ')'
+'{' = '}'
+'[' = ']'
+'"' = '"'
+'`' = '`'
+'<' = '>'
+
+Additionally, this setting can be used in a language config. Unless
+the editor setting is false
, this will override the editor config in
+documents with this language.
Example languages.toml
that adds <>
and removes ''
[[language]]
+name = "rust"
+
+[language.auto-pairs]
+'(' = ')'
+'{' = '}'
+'[' = ']'
+'"' = '"'
+'`' = '`'
+'<' = '>'
+
+[editor.auto-save]
SectionControl auto save behavior.
+Key | Description | Default |
---|---|---|
focus-lost | Enable automatic saving on the focus moving away from Helix. Requires focus event support from your terminal | false |
after-delay.enable | Enable automatic saving after auto-save.after-delay.timeout milliseconds have passed since last edit. | false |
after-delay.timeout | Time in milliseconds since last edit before auto save timer triggers. | 3000 |
[editor.search]
SectionSearch specific options.
+Key | Description | Default |
---|---|---|
smart-case | Enable smart case regex searching (case-insensitive unless pattern contains upper case characters) | true |
wrap-around | Whether the search should wrap after depleting the matches | true |
[editor.whitespace]
SectionOptions for rendering whitespace with visible characters. Use :set whitespace.render all
to temporarily enable visible whitespace.
Key | Description | Default |
---|---|---|
render | Whether to render whitespace. May either be all or none , or a table with sub-keys space , nbsp , nnbsp , tab , and newline | none |
characters | Literal characters to use when rendering whitespace. Sub-keys may be any of tab , space , nbsp , nnbsp , newline or tabpad | See example below |
Example
+[editor.whitespace]
+render = "all"
+# or control each character
+[editor.whitespace.render]
+space = "all"
+tab = "all"
+nbsp = "none"
+nnbsp = "none"
+newline = "none"
+
+[editor.whitespace.characters]
+space = "·"
+nbsp = "⍽"
+nnbsp = "␣"
+tab = "→"
+newline = "⏎"
+tabpad = "·" # Tabs will look like "→···" (depending on tab width)
+
+[editor.indent-guides]
SectionOptions for rendering vertical indent guides.
+Key | Description | Default |
---|---|---|
render | Whether to render indent guides | false |
character | Literal character to use for rendering the indent guide | │ |
skip-levels | Number of indent levels to skip | 0 |
Example:
+[editor.indent-guides]
+render = true
+character = "╎" # Some characters that work well: "▏", "┆", "┊", "⸽"
+skip-levels = 1
+
+[editor.gutters]
SectionFor simplicity, editor.gutters
accepts an array of gutter types, which will
+use default settings for all gutter components.
[editor]
+gutters = ["diff", "diagnostics", "line-numbers", "spacer"]
+
+To customize the behavior of gutters, the [editor.gutters]
section must
+be used. This section contains top level settings, as well as settings for
+specific gutter components as subsections.
Key | Description | Default |
---|---|---|
layout | A vector of gutters to display | ["diagnostics", "spacer", "line-numbers", "spacer", "diff"] |
Example:
+[editor.gutters]
+layout = ["diff", "diagnostics", "line-numbers", "spacer"]
+
+[editor.gutters.line-numbers]
SectionOptions for the line number gutter
+Key | Description | Default |
---|---|---|
min-width | The minimum number of characters to use | 3 |
Example:
+[editor.gutters.line-numbers]
+min-width = 1
+
+[editor.gutters.diagnostics]
SectionCurrently unused
+[editor.gutters.diff]
SectionThe diff
gutter option displays colored bars indicating whether a git
diff represents that a line was added, removed or changed.
+These colors are controlled by the theme attributes diff.plus
, diff.minus
and diff.delta
.
Other diff providers will eventually be supported by a future plugin system.
+There are currently no options for this section.
+[editor.gutters.spacer]
SectionCurrently unused
+[editor.soft-wrap]
SectionOptions for soft wrapping lines that exceed the view width:
+Key | Description | Default |
---|---|---|
enable | Whether soft wrapping is enabled. | false |
max-wrap | Maximum free space left at the end of the line. | 20 |
max-indent-retain | Maximum indentation to carry over when soft wrapping a line. | 40 |
wrap-indicator | Text inserted before soft wrapped lines, highlighted with ui.virtual.wrap | ↪ |
wrap-at-text-width | Soft wrap at text-width instead of using the full viewport size. | false |
Example:
+[editor.soft-wrap]
+enable = true
+max-wrap = 25 # increase value to reduce forced mid-word wrapping
+max-indent-retain = 0
+wrap-indicator = "" # set wrap-indicator to "" to hide it
+
+[editor.smart-tab]
SectionOptions for navigating and editing using tab key.
+Key | Description | Default |
---|---|---|
enable | If set to true, then when the cursor is in a position with non-whitespace to its left, instead of inserting a tab, it will run move_parent_node_end . If there is only whitespace to the left, then it inserts a tab as normal. With the default bindings, to explicitly insert a tab character, press Shift-tab. | true |
supersede-menu | Normally, when a menu is on screen, such as when auto complete is triggered, the tab key is bound to cycling through the items. This means when menus are on screen, one cannot use the tab key to trigger the smart-tab command. If this option is set to true, the smart-tab command always takes precedence, which means one cannot use the tab key to cycle through menu items. One of the other bindings must be used instead, such as arrow keys or C-n /C-p . | false |
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 Enhanced Keyboard protocol, consider setting extra keybindings:
+[keys.normal]
+tab = "move_parent_node_end"
+S-tab = "move_parent_node_start"
+
+[keys.insert]
+S-tab = "move_parent_node_start"
+
+[keys.select]
+tab = "extend_parent_node_end"
+S-tab = "extend_parent_node_start"
+
+
+ Helix's editing model is strongly inspired from Vim and Kakoune, and a notable
+difference from Vim (and the most striking similarity to Kakoune) is that Helix
+follows the selection → action
model. This means that whatever you are
+going to act on (a word, a paragraph, a line, etc.) is selected first and the
+action itself (delete, change, yank, etc.) comes second. A cursor is simply a
+single width selection.
See also Kakoune's Migrating from Vim and Helix's Migrating from Vim.
+++ +TODO: Mention textobjects, surround, registers
+
In order to add a new language to Helix, you will need to follow the steps +below.
+[[language]]
entry in the languages.toml
file and provide the
+necessary configuration for the new language. For more information on
+language configuration, refer to the
+language configuration section of the documentation.
+A new language server can be added by extending the [language-server]
table in the same file.cargo xtask docgen
to update the
+Language Support documentation.++💡 If you are adding a new Language Server configuration, make sure to update +the +Language Server Wiki +with the installation instructions.
+
[[grammar]]
entry to the languages.toml
file.source.path
key
+with an absolute path to the grammar. However, before submitting a pull
+request, make sure to switch to using source.git
.runtime/queries/<name>/
.++💡 In Helix, the first matching query takes precedence when evaluating +queries, which is different from other editors such as Neovim where the last +matching query supersedes the ones before it. See +this issue +for an example.
+
hx --grammar fetch
+to fetch the grammars and hx --grammar build
to build any out-of-date
+grammars.runtime/grammars/<name>.so
.HELIX_RUNTIME
is set to the location of the runtime
folder you're developing in.Helix uses tree-sitter to correctly indent new lines. This requires a tree-
+sitter grammar and an indent.scm
query file placed in runtime/queries/ {language}/indents.scm
. The indentation for a line is calculated by traversing
+the syntax tree from the lowest node at the beginning of the new line (see
+Indent queries). Each of these nodes contributes to the total
+indent when it is captured by the query (in what way depends on the name of
+the capture.
Note that it matters where these added indents begin. For example, +multiple indent level increases that start on the same line only increase +the total indent level by 1. See Capture types.
+By default, Helix uses the hybrid
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
+:set indent-heuristic tree-sitter
. The rest of this guide assumes that
+the tree-sitter
heuristic is used.
When Helix is inserting a new line through o
, O
, or <ret>
, to determine
+the indent level for the new line, the query in indents.scm
is run on the
+document. The starting position of the query is the end of the line above where
+a new line will be inserted.
For o
, the inserted line is the line below the cursor, so that starting
+position of the query is the end of the current line.
+#![allow(unused)] +fn main() { +fn need_hero(some_hero: Hero, life: Life) -> { + matches!(some_hero, Hero { // ←─────────────────╮ + strong: true,//←╮ ↑ ↑ │ + fast: true, // │ │ ╰── query start │ + sure: true, // │ ╰───── cursor ├─ traversal + soon: true, // ╰──────── new line inserted │ start node + }) && // │ +// ↑ │ +// ╰───────────────────────────────────────────────╯ + some_hero > life +} +}
For O
, the newly inserted line is the current line, so the starting position
+of the query is the end of the line above the cursor.
+#![allow(unused)] +fn main() { +fn need_hero(some_hero: Hero, life: Life) -> { // ←─╮ + matches!(some_hero, Hero { // ←╮ ↑ │ + strong: true,// ↑ ╭───╯ │ │ + fast: true, // │ │ query start ─╯ │ + sure: true, // ╰───┼ cursor ├─ traversal + soon: true, // ╰ new line inserted │ start node + }) && // │ + some_hero > life // │ +} // ←──────────────────────────────────────────────╯ +}
From this starting node, the syntax tree is traversed up until the root node. +Each indent capture is collected along the way, and then combined according to +their capture types and scopes to a final indent +level for the line.
+@indent
(default scope tail
):
+Increase the indent level by 1. Multiple occurrences in the same line do not
+stack. If there is at least one @indent
and one @outdent
capture on the
+same line, the indent level isn't changed at all.@outdent
(default scope all
):
+Decrease the indent level by 1. The same rules as for @indent
apply.@indent.always
(default scope tail
):
+Increase the indent level by 1. Multiple occurrences on the same line do
+stack. The final indent level is @indent.always
– @outdent.always
. If
+an @indent
and an @indent.always
are on the same line, the @indent
is
+ignored.@outdent.always
(default scope all
):
+Decrease the indent level by 1. The same rules as for @indent.always
apply.@align
(default scope all
):
+Align everything inside this node to some anchor. The anchor is given
+by the start of the node captured by @anchor
in the same pattern.
+Every pattern with an @align
should contain exactly one @anchor
.
+Indent (and outdent) for nodes below (in terms of their starting line)
+the @align
node is added to the indentation required for alignment.@extend
:
+Extend the range of this node to the end of the line and to lines that are
+indented more than the line that this node starts on. This is useful for
+languages like Python, where for the purpose of indentation some nodes (like
+functions or classes) should also contain indented lines that follow them.@extend.prevent-once
:
+Prevents the first extension of an ancestor of this node. For example, in Python
+a return expression always ends the block that it is in. Note that this only
+stops the extension of the next @extend
capture. If multiple ancestors are
+captured, only the extension of the innermost one is prevented. All other
+ancestors are unaffected (regardless of whether the innermost ancestor would
+actually have been extended).@indent
/ @outdent
Consider this example:
++#![allow(unused)] +fn main() { +fn shout(things: Vec<Thing>) { + // ↑ + // ├───────────────────────╮ indent level + // @indent ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄ + // │ + let it_all = |out| { things.filter(|thing| { // │ 1 + // ↑ ↑ │ + // ├───────────────────────┼─────┼┄┄┄┄┄┄┄┄┄┄┄┄┄┄ + // @indent @indent │ + // │ 2 + thing.can_do_with(out) // │ + })}; // ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄ + //↑↑↑ │ 1 +} //╰┼┴──────────────────────────────────────────────┴┄┄┄┄┄┄┄┄┄┄┄┄┄┄ +// 3x @outdent +}
((block) @indent)
+["}" ")"] @outdent
+
+Note how on the second line, we have two blocks begin on the same line. In this
+case, since both captures occur on the same line, they are combined and only
+result in a net increase of 1. Also note that the closing }
s are part of the
+@indent
captures, but the 3 @outdent
s also combine into 1 and result in that
+line losing one indent level.
@extend
/ @extend.prevent-once
For an example of where @extend
can be useful, consider Python, which is
+whitespace-sensitive.
]
+ (parenthesized_expression)
+ (function_definition)
+ (class_definition)
+] @indent
+
+
+class Hero:
+ def __init__(self, strong, fast, sure, soon):# ←─╮
+ self.is_strong = strong # │
+ self.is_fast = fast # ╭─── query start │
+ self.is_sure = sure # │ ╭─ cursor │
+ self.is_soon = soon # │ │ │
+ # ↑ ↑ │ │ │
+ # │ ╰──────╯ │ │
+ # ╰─────────────────────╯ │
+ # ├─ traversal
+ def need_hero(self, life): # │ start node
+ return ( # │
+ self.is_strong # │
+ and self.is_fast # │
+ and self.is_sure # │
+ and self.is_soon # │
+ and self > life # │
+ ) # ←─────────────────────────────────────────╯
+
+Without braces to catch the scope of the function, the smallest descendant of +the cursor on a line feed ends up being the entire inside of the class. Because +of this, it will miss the entire function node and its indent capture, leading +to an indent level one too small.
+To address this case, @extend
tells helix to "extend" the captured node's span
+to the line feed and every consecutive line that has a greater indent level than
+the line of the node.
(parenthesized_expression) @indent
+
+]
+ (function_definition)
+ (class_definition)
+] @indent @extend
+
+
+class Hero:
+ def __init__(self, strong, fast, sure, soon):# ←─╮
+ self.is_strong = strong # │
+ self.is_fast = fast # ╭─── query start ├─ traversal
+ self.is_sure = sure # │ ╭─ cursor │ start node
+ self.is_soon = soon # │ │ ←───────────────╯
+ # ↑ ↑ │ │
+ # │ ╰──────╯ │
+ # ╰─────────────────────╯
+ def need_hero(self, life):
+ return (
+ self.is_strong
+ and self.is_fast
+ and self.is_sure
+ and self.is_soon
+ and self > life
+ )
+
+Furthermore, there are some cases where extending to everything with a greater
+indent level may not be desirable. Consider the need_hero
function above. If
+our cursor is on the last line of the returned expression.
class Hero:
+ def __init__(self, strong, fast, sure, soon):
+ self.is_strong = strong
+ self.is_fast = fast
+ self.is_sure = sure
+ self.is_soon = soon
+
+ def need_hero(self, life):
+ return (
+ self.is_strong
+ and self.is_fast
+ and self.is_sure
+ and self.is_soon
+ and self > life
+ ) # ←─── cursor
+ #←────────── where cursor should go on new line
+
+In Python, the are a few tokens that will always end a scope, such as a return
+statement. Since the scope ends, so should the indent level. But because the
+function span is extended to every line with a greater indent level, a new line
+would just continue on the same level. And an @outdent
would not help us here
+either, since it would cause everything in the parentheses to become outdented
+as well.
To help, we need to signal an end to the extension. We can do this with
+@extend.prevent-once
.
(parenthesized_expression) @indent
+
+]
+ (function_definition)
+ (class_definition)
+] @indent @extend
+
+(return_statement) @extend.prevent-once
+
+@indent.always
/ @outdent.always
As mentioned before, normally if there is more than one @indent
or @outdent
+capture on the same line, they are combined.
Sometimes, there are cases when you may want to ensure that every indent capture +is additive, regardless of how many occur on the same line. Consider this +example in YAML.
+ - foo: bar
+# ↑ ↑
+# │ ╰─────────────── start of map
+# ╰───────────────── start of list element
+ baz: quux # ←─── cursor
+ # ←───────────── where the cursor should go on a new line
+ garply: waldo
+ - quux:
+ bar: baz
+ xyzzy: thud
+ fred: plugh
+
+In YAML, you often have lists of maps. In these cases, the syntax is such that
+the list element and the map both start on the same line. But we really do want
+to start an indentation for each of these so that subsequent keys in the map
+hang over the list and align properly. This is where @indent.always
helps.
((block_sequence_item) @item @indent.always @extend
+ (#not-one-line? @item))
+
+((block_mapping_pair
+ key: (_) @key
+ value: (_) @val
+ (#not-same-line? @key @val)
+ ) @indent.always @extend
+)
+
+In some cases, an S-expression cannot express exactly what pattern should be matched.
+For that, tree-sitter allows for predicates to appear anywhere within a pattern,
+similar to how #set!
declarations work:
(some_kind
+ (child_kind) @indent
+ (#predicate? arg1 arg2 ...)
+)
+
+The number of arguments depends on the predicate that's used.
+Each argument is either a capture (@name
) or a string ("some string"
).
+The following predicates are supported by tree-sitter:
#eq?
/#not-eq?
:
+The first argument (a capture) must/must not be equal to the second argument
+(a capture or a string).
#match?
/#not-match?
:
+The first argument (a capture) must/must not match the regex given in the
+second argument (a string).
#any-of?
/#not-any-of?
:
+The first argument (a capture) must/must not be one of the other arguments
+(strings).
Additionally, we support some custom predicates for indent queries:
+#not-kind-eq?
:
+The kind of the first argument (a capture) must not be equal to the second
+argument (a string).
#same-line?
/#not-same-line?
:
+The captures given by the 2 arguments must/must not start on the same line.
#one-line?
/#not-one-line?
:
+The captures given by the fist argument must/must span a total of one line.
Added indents don't always apply to the whole node. For example, in most +cases when a node should be indented, we actually only want everything +except for its first line to be indented. For this, there are several +scopes (more scopes may be added in the future if required):
+tail
:
+This scope applies to everything except for the first line of the
+captured node.all
:
+This scope applies to the whole captured node. This is only different from
+tail
when the captured node is the first node on its line.For example, imagine we have the following function
++#![allow(unused)] +fn main() { +fn aha() { // ←─────────────────────────────────────╮ + let take = "on me"; // ←──────────────╮ scope: │ + let take = "me on"; // ├─ "tail" ├─ (block) @indent + let ill = be_gone_days(1 || 2); // │ │ +} // ←───────────────────────────────────┴──────────┴─ "}" @outdent + // scope: "all" +}
We can write the following query with the #set!
declaration:
((block) @indent
+ (#set! "scope" "tail"))
+("}" @outdent
+ (#set! "scope" "all"))
+
+As we can see, the "tail" scope covers the node, except for the first line.
+Everything up to and including the closing brace gets an indent level of 1.
+Then, on the closing brace, we encounter an outdent with a scope of "all", which
+means the first line is included, and the indent level is cancelled out on this
+line. (Note these scopes are the defaults for @indent
and @outdent
—they are
+written explicitly for demonstration.)
This section contains guides for adding new language server configurations, +tree-sitter grammars, textobject queries, and other similar items.
+ +Writing language injection queries allows one to highlight a specific node as a different language. +In addition to the standard language injection options used by tree-sitter, there +are a few Helix specific extensions that allow for more control.
+And example of a simple query that would highlight all strings as bash in Nix:
+((string_expression (string_fragment) @injection.content)
+ (#set! injection.language "bash"))
+
+@injection.language
(standard):
+The captured node may contain the language name used to highlight the node captured by
+@injection.content
.
@injection.content
(standard):
+Marks the content to be highlighted as the language captured with @injection.language
et al.
@injection.filename
(extension):
+The captured node may contain a filename with a file-extension known to Helix,
+highlighting @injection.content
as that language. This uses the language extensions defined in
+both the default languages.toml distributed with Helix, as well as user defined languages.
@injection.shebang
(extension):
+The captured node may contain a shebang used to choose a language to highlight as. This also uses
+the shebangs defined in the default and user languages.toml
.
injection.combined
(standard):
+Indicates that all the matching nodes in the tree should have their content parsed as one
+nested document.
injection.language
(standard):
+Forces the captured content to be highlighted as the given language
injection.include-children
(standard):
+Indicates that the content node’s entire text should be re-parsed, including the text of its child
+nodes. By default, child nodes’ text will be excluded from the injected document.
injection.include-unnamed-children
(extension):
+Same as injection.include-children
but only for unnamed child nodes.
#eq?
(standard):
+The first argument (a capture) must be equal to the second argument
+(a capture or a string).
#match?
(standard):
+The first argument (a capture) must match the regex given in the
+second argument (a string).
#any-of?
(standard):
+The first argument (a capture) must be one of the other arguments (strings).
Helix supports textobjects that are language specific, such as functions, classes, etc.
+These textobjects require an accompanying tree-sitter grammar and a textobjects.scm
query file
+to work properly. Tree-sitter allows us to query the source code syntax tree
+and capture specific parts of it. The queries are written in a lisp dialect.
+More information on how to write queries can be found in the official tree-sitter
+documentation.
Query files should be placed in runtime/queries/{language}/textobjects.scm
+when contributing to Helix. Note that to test the query files locally you should put
+them under your local runtime directory (~/.config/helix/runtime
on Linux
+for example).
The following captures are recognized:
+Capture Name |
---|
function.inside |
function.around |
class.inside |
class.around |
test.inside |
test.around |
parameter.inside |
comment.inside |
comment.around |
entry.inside |
entry.around |
Example query files can be found in the helix GitHub repository.
+Tree-sitter based navigation in Helix is done using captures in the +following order:
+object.movement
object.around
object.inside
For example if a function.around
capture has been already defined for a language
+in its textobjects.scm
file, function navigation should also work automatically.
+function.movement
should be defined only if the node captured by function.around
+doesn't make sense in a navigation context.
Docs for bleeding edge master can be found at +https://docs.helix-editor.com/master.
+See the usage section for a quick overview of the editor, keymap +section for all available keybindings and the configuration section +for defining custom keybindings, setting themes, etc. +For everything else (e.g., how to install supported language servers), see the Helix Wiki.
+Refer the FAQ for common questions.
+ +