User guide / Language support

Syntax highlighting

Development reference · View source

Tree-sitter grammars are compiled into the binary: no network access, no grammar directory, and no runtime library loading. Adding a language means adding a dependency and a row to src/syntax/grammars.rs.

Language detection

Runyte checks, in order:

  1. an exact filename,
  2. a registered filename prefix,
  3. a case-insensitive extension,
  4. a bounded first-line shebang.
Language nameDetected files include
bash.bashrc, .bash_profile; sh, bash, ebuild, eclass extensions; sh, bash, dash shebangs
cmakeCMakeLists.txt
makeMakefile, makefile, GNUmakefile
lualua shebangs
dockerfileDockerfile, Containerfile, their lowercase names, dot-suffixed variants such as Dockerfile.dev, and .dockerfile / .containerfile
kotlin.kt, .kts
jsonc.jsonc, tsconfig.json, jsconfig.json, devcontainer.json, and .devcontainer.json
xml.xml, .svg, .xsd, .xsl, .xslt, .wsdl, .xaml, .csproj, .fsproj, .vbproj, .props, .targets, .resx, and .plist
hcl.hcl, .tf, and .tfvars, including terragrunt.hcl and production.auto.tfvars
ruby.rb, .rake, .gemspec, .ru, Gemfile, Rakefile, Guardfile, Vagrantfile, Brewfile, Podfile, Fastfile, Appfile, .irbrc, and .pryrc; ruby and jruby shebangs
php.php, .phtml, .php3, .php4, .php5, .php7, .php8, and .phps; php shebangs
elixir.ex and .exs, including mix.exs, config.exs, and .formatter.exs

Terraform’s .tf.json files keep JSON highlighting.

Notes by language

INI parses both ; and # comments. toggle-comments inserts ;.

JSONC is JSON with // and /* */ comments, and uses the JSON grammar. toggle-comments inserts //; in plain JSON it changes nothing.

  • Language servers see jsonc as the language, so configure one under lsp.jsonc; a server under lsp.json does not attach to JSONC files.
  • Only the exact file names in the table are JSONC. Detection sees the file name, not its directory, so .vscode/settings.json, tsconfig.base.json, and other .json files read as JSONC by their tools stay JSON.
  • Trailing commas are not part of the grammar. A file with one still highlights, but its tree has a parse error and some of its folds are missing.

Dockerfile — the language name is dockerfile, including in Markdown fences.

  • Instructions, comments, strings, build options, and ports are highlighted.
  • Shell commands and RUN heredoc bodies use Bash highlighting, within the injection size limit. Shell highlighting assumes Bash, even when a file selects another interpreter with SHELL.

Kotlin supports Kotlin 2 multi-dollar strings and guarded when branches. The pinned grammar does not yet model context receivers: it parses context(Logger) as a separate call before the function, so a function text object starts at fun and deliberately leaves out the context-receiver prefix.

XML uses its own parser, not the HTML one, including XML declarations, entities, and CDATA.

HCL highlights blocks, expressions, interpolation, template directives, and heredocs. HCL fences can use hcl or tf.

Ruby tells local bindings from method calls, including loop, exception, and pattern bindings. Class/module bodies and methods isolate their locals. Limitation: in object = Object.new; def object.greet; end, the receiver object can take function colouring, because the local-scope query covers the whole method.

PHP highlights code inside <?php or <?= tags and injects HTML into the surrounding template, including JavaScript and CSS in script/style elements.

  • Heredoc and nowdoc labels naming a bundled language, such as SQL or JSON, highlight their body in that language; unknown labels keep string highlighting. Inside an injected heredoc, the embedded language decides the colours, so interpolations such as $id may look like strings.
  • PHPDoc comments keep ordinary comment highlighting.
  • PHP snippets in Markdown fences need an opening PHP tag.

Elixir highlights offline, without an Elixir runtime or language server. An elixir Markdown fence uses the same grammar within the injection limit. EEx and HEEx templates are not registered as Elixir, and no Elixir language server is bundled or configured.

Missing structural queries. Dockerfile, XML, HCL, Ruby, PHP, and Elixir have no dedicated text-object, outline, indentation, or fold queries. In Elixir, ordinary syntax-tree selection works, but function, class, and parameter text objects, outlines, syntax indentation, and folds do not.

Large files

  • Files above 128 KB are highlighted without language injection, so embedded languages such as fenced code in Markdown are highlighted only in smaller files.
  • Markdown’s inline grammar is also an injection. Above the limit, headings, lists, quotes, and other block structure keep their colours, while emphasis, strong text, links, and inline code use the ordinary foreground. The injection query still runs over the whole document on every edit, which is why this limit remains.
  • There is no line or byte limit on highlighting itself. A slow parse delays the new tree, not the keystroke that asked for it.

Background parsing

Parsing and reparsing run on a background worker.

  • A document is readable and editable as soon as its contents load, in ordinary text colours until its first syntax tree arrives. Highlighting then appears without replacing text or moving the cursor or viewport.
  • Opening files, undo/redo, reload, and language changes all use this path.
  • If typing outruns the parser, one pending request per buffer is replaced rather than queued.
  • After the first tree, the previous tree stays in use during updates and its visible highlight spans are shifted through pending edits, so colours stay visible.
  • Completed trees are applied between frames, and only when their parse generation, language, and text revision match the live document.
  • A parse failure leaves the document editable as plain text; see :service-health.

Structural features wait for a current tree. Outline, folds, matching brackets, text objects, and structural selection cannot use stale offsets.

  • Space x commands and mm are dimmed while syntax is pending. Invoking one reports Syntax is still parsing; it is not queued.
  • Smart newline falls back to ordinary indentation and list handling.
  • Language detection, comments, editing, search, saving, and language servers do not need a syntax tree.