feat(highlight): migrate to tree-house with Helix queries

Replace tree-sitter-highlight dep with tree-house crate from Helix
editor. Add ropey dependency required by tree-house.

Reorganize query files from Helix's runtime/queries/ for all 14
supported languages:
- bash, c, css, go, html, javascript, json, markdown, nix, python,
  rust, toml, typescript, yaml
- Include ecma, _javascript, _typescript base dirs for JS/TS inheritance
- Copy highlights.scm, injections.scm, locals.scm where available

This commit establishes the foundation; highlight.rs implementation
will follow in subsequent commits.
This commit is contained in:
Timothy DeHerrera
2026-02-04 16:49:04 -07:00
parent 98977136c8
commit 136be19533
43 changed files with 3310 additions and 85 deletions

514
queries/rust/highlights.scm Normal file
View File

@@ -0,0 +1,514 @@
; -------
; Basic identifiers
; -------
; We do not style ? as an operator on purpose as it allows styling ? differently, as many highlighters do. @operator.special might have been a better scope, but @special is already documented so the change would break themes (including the intent of the default theme)
"?" @special
(type_identifier) @type
(identifier) @variable
(field_identifier) @variable.other.member
; -------
; Operators
; -------
[
"*"
"'"
"->"
"=>"
"<="
"="
"=="
"!"
"!="
"%"
"%="
"&"
"&="
"&&"
"|"
"|="
"||"
"^"
"^="
"*"
"*="
"-"
"-="
"+"
"+="
"/"
"/="
">"
"<"
">="
">>"
"<<"
">>="
"<<="
"@"
".."
"..="
"'"
] @operator
; -------
; Paths
; -------
(use_declaration
argument: (identifier) @namespace)
(use_wildcard
(identifier) @namespace)
(extern_crate_declaration
name: (identifier) @namespace
alias: (identifier)? @namespace)
(mod_item
name: (identifier) @namespace)
(scoped_use_list
path: (identifier)? @namespace)
(use_list
(identifier) @namespace)
(use_as_clause
path: (identifier)? @namespace
alias: (identifier) @namespace)
; -------
; Types
; -------
(type_parameter
name: (type_identifier) @type.parameter)
((type_arguments (type_identifier) @constant)
(#match? @constant "^[A-Z_]+$"))
(type_arguments (type_identifier) @type)
; `_` in `(_, _)`
(tuple_struct_pattern "_" @comment.unused)
; `_` in `Vec<_>`
((type_arguments (type_identifier) @comment.unused)
(#eq? @comment.unused "_"))
; `_` in `Rc<[_]>`
((array_type (type_identifier) @comment.unused)
(#eq? @comment.unused "_"))
; ---
; Primitives
; ---
(escape_sequence) @constant.character.escape
(primitive_type) @type.builtin
(boolean_literal) @constant.builtin.boolean
(integer_literal) @constant.numeric.integer
(float_literal) @constant.numeric.float
(char_literal) @constant.character
[
(string_literal)
(raw_string_literal)
] @string
; -------
; Comments
; -------
(shebang) @comment
(line_comment) @comment.line
(block_comment) @comment.block
; Doc Comments
(line_comment
(outer_doc_comment_marker "/" @comment.line.documentation)
(doc_comment)) @comment.line.documentation
(line_comment
(inner_doc_comment_marker "!" @comment.line.documentation)
(doc_comment)) @comment.line.documentation
(block_comment
(outer_doc_comment_marker) @comment.block.documentation
(doc_comment) "*/" @comment.block.documentation) @comment.block.documentation
(block_comment
(inner_doc_comment_marker) @comment.block.documentation
(doc_comment) "*/" @comment.block.documentation) @comment.block.documentation
; ---
; Extraneous
; ---
(self) @variable.builtin
(field_initializer
(field_identifier) @variable.other.member)
(shorthand_field_initializer
(identifier) @variable.other.member)
(shorthand_field_identifier) @variable.other.member
(lifetime
"'" @label
(identifier) @label)
(label
"'" @label
(identifier) @label)
; ---
; Punctuation
; ---
[
"::"
"."
";"
","
":"
] @punctuation.delimiter
[
"("
")"
"["
"]"
"{"
"}"
"#"
] @punctuation.bracket
(type_arguments
[
"<"
">"
] @punctuation.bracket)
(type_parameters
[
"<"
">"
] @punctuation.bracket)
(for_lifetimes ["<" ">"] @punctuation.bracket)
(closure_parameters
"|" @punctuation.bracket)
(bracketed_type ["<" ">"] @punctuation.bracket)
; ---
; Variables
; ---
(let_declaration
pattern: [
((identifier) @variable)
((tuple_pattern
(identifier) @variable))
])
; It needs to be anonymous to not conflict with `call_expression` further below.
(_
value: (field_expression
value: (identifier)? @variable
field: (field_identifier) @variable.other.member))
(parameter
pattern: (identifier) @variable.parameter)
(closure_parameters
(identifier) @variable.parameter)
; -------
; Keywords
; -------
"in" @keyword.control
[
"match"
"if"
"else"
"try"
] @keyword.control.conditional
[
"while"
"loop"
] @keyword.control.repeat
[
"break"
"continue"
"return"
"await"
"yield"
] @keyword.control.return
"use" @keyword.control.import
(mod_item "mod" @keyword.control.import !body)
(use_as_clause "as" @keyword.control.import)
(type_cast_expression "as" @keyword.operator)
[
(crate)
(super)
"as"
"pub"
"mod"
"extern"
"impl"
"where"
"trait"
"for"
"default"
"async"
] @keyword
(for_expression
"for" @keyword.control.repeat)
(gen_block "gen" @keyword.control)
[
"struct"
"enum"
"union"
"type"
] @keyword.storage.type
"let" @keyword.storage
"fn" @keyword.function
"unsafe" @keyword.special
"macro_rules!" @function.macro
(mutable_specifier) @keyword.storage.modifier.mut
(reference_type "&" @keyword.storage.modifier.ref)
(self_parameter "&" @keyword.storage.modifier.ref)
[
"static"
"const"
"raw"
"ref"
"move"
"dyn"
] @keyword.storage.modifier
; TODO: variable.mut to highlight mutable identifiers via locals.scm
; ---
; Remaining Paths
; ---
(scoped_identifier
path: (identifier)? @namespace
name: (identifier) @namespace)
(scoped_type_identifier
path: (identifier) @namespace)
; -------
; Functions
; -------
; highlight `baz` in `any_function(foo::bar::baz)` as function
; This generically works for an unlimited number of path segments:
;
; - `f(foo::bar)`
; - `f(foo::bar::baz)`
; - `f(foo::bar::baz::quux)`
;
; We know that in the above examples, the last component of each path is a function
; as the only other valid thing (following Rust naming conventions) would be a module at
; that position, however you cannot pass modules as arguments
(call_expression
function: _
arguments: (arguments
(scoped_identifier
path: _
name: (identifier) @function)))
(call_expression
function: [
((identifier) @function)
(scoped_identifier
name: (identifier) @function)
(field_expression
field: (field_identifier) @function)
])
(generic_function
function: [
((identifier) @function)
(scoped_identifier
name: (identifier) @function)
(field_expression
field: (field_identifier) @function.method)
])
(function_item
name: (identifier) @function)
(function_signature_item
name: (identifier) @function)
; -------
; Guess Other Types
; -------
; Other PascalCase identifiers are assumed to be structs.
((identifier) @type
(#match? @type "^[A-Z]"))
(never_type "!" @type)
((identifier) @constant
(#match? @constant "^[A-Z][A-Z\\d_]*$"))
; ---
; PascalCase identifiers in call_expressions (e.g. `Ok()`)
; are assumed to be enum constructors.
; ---
(call_expression
function: [
((identifier) @constructor
(#match? @constructor "^[A-Z]"))
(scoped_identifier
name: ((identifier) @constructor
(#match? @constructor "^[A-Z]")))
])
; ---
; PascalCase identifiers under a path which is also PascalCase
; are assumed to be constructors if they have methods or fields.
; ---
(field_expression
value: (scoped_identifier
path: [
(identifier) @type
(scoped_identifier
name: (identifier) @type)
]
name: (identifier) @constructor
(#match? @type "^[A-Z]")
(#match? @constructor "^[A-Z]")))
(enum_variant (identifier) @type.enum.variant)
; -------
; Constructors
; -------
; TODO: this is largely guesswork, remove it once we get actual info from locals.scm or r-a
(struct_expression
name: (type_identifier) @constructor)
(tuple_struct_pattern
type: [
(identifier) @constructor
(scoped_identifier
name: (identifier) @constructor)
])
(struct_pattern
type: [
((type_identifier) @constructor)
(scoped_type_identifier
name: (type_identifier) @constructor)
])
(match_pattern
((identifier) @constructor) (#match? @constructor "^[A-Z]"))
(or_pattern
((identifier) @constructor)
((identifier) @constructor)
(#match? @constructor "^[A-Z]"))
; ---
; Macros
; ---
(attribute
(identifier) @function.macro)
(inner_attribute_item "!" @punctuation)
(attribute
[
(identifier) @function.macro
(scoped_identifier
name: (identifier) @function.macro)
]
(token_tree (identifier) @function.macro)?)
(inner_attribute_item) @attribute
(macro_definition
name: (identifier) @function.macro)
(macro_invocation
macro: [
((identifier) @function.macro)
(scoped_identifier
name: (identifier) @function.macro)
]
"!" @function.macro)
(metavariable) @variable.parameter
(fragment_specifier) @type
(attribute
(identifier) @special
arguments: (token_tree (identifier) @type)
(#eq? @special "derive")
)
(token_repetition_pattern) @punctuation.delimiter
(token_repetition_pattern [")" "(" "$"] @punctuation.special)
(token_repetition_pattern "?" @operator)
; ---
; Prelude
; ---
((identifier) @type.enum.variant.builtin
(#any-of? @type.enum.variant.builtin "Some" "None" "Ok" "Err"))
(call_expression
(identifier) @function.builtin
(#any-of? @function.builtin
"drop"
"size_of"
"size_of_val"
"align_of"
"align_of_val"))
((type_identifier) @type.builtin
(#any-of?
@type.builtin
"Send"
"Sized"
"Sync"
"Unpin"
"Drop"
"Fn"
"FnMut"
"FnOnce"
"AsMut"
"AsRef"
"From"
"Into"
"DoubleEndedIterator"
"ExactSizeIterator"
"Extend"
"IntoIterator"
"Iterator"
"Option"
"Result"
"Clone"
"Copy"
"Debug"
"Default"
"Eq"
"Hash"
"Ord"
"PartialEq"
"PartialOrd"
"ToOwned"
"Box"
"String"
"ToString"
"Vec"
"FromIterator"
"TryFrom"
"TryInto"))

217
queries/rust/injections.scm Normal file
View File

@@ -0,0 +1,217 @@
([(line_comment !doc) (block_comment !doc)] @injection.content
(#set! injection.language "comment"))
((doc_comment) @injection.content
(#set! injection.language "markdown-rustdoc")
(#set! injection.combined))
((macro_invocation
(token_tree) @injection.content)
(#set! injection.language "rust")
(#set! injection.include-children))
((macro_rule
(token_tree) @injection.content)
(#set! injection.language "rust")
(#set! injection.include-children))
((macro_invocation
macro:
[
(scoped_identifier
name: (_) @_macro_name)
(identifier) @_macro_name
]
(token_tree) @injection.content)
(#eq? @_macro_name "html")
(#set! injection.language "html")
(#set! injection.include-children))
((macro_invocation
macro:
[
(scoped_identifier
name: (_) @_macro_name)
(identifier) @_macro_name
]
(token_tree) @injection.content)
(#eq? @_macro_name "slint")
(#set! injection.language "slint")
(#set! injection.include-children))
((macro_invocation
macro:
[
(scoped_identifier name: (_) @_macro_name)
(identifier) @_macro_name
]
(token_tree
(token_tree . "{" "}" .) @injection.content))
(#eq? @_macro_name "json")
(#set! injection.language "json")
(#set! injection.include-children))
(call_expression
function: (scoped_identifier
path: (identifier) @_regex (#any-of? @_regex "Regex" "RegexBuilder")
name: (identifier) @_new (#eq? @_new "new"))
arguments:
(arguments
[
(string_literal (string_content) @injection.content)
(raw_string_literal (string_content) @injection.content)
])
(#set! injection.language "regex"))
(call_expression
function: (scoped_identifier
path: (scoped_identifier (identifier) @_regex (#any-of? @_regex "Regex" "RegexBuilder") .)
name: (identifier) @_new (#eq? @_new "new"))
arguments:
(arguments
[
(string_literal (string_content) @injection.content)
(raw_string_literal (string_content) @injection.content)
])
(#set! injection.language "regex"))
; Highlight SQL in `sqlx::query!()`, `sqlx::query_scalar!()`, and `sqlx::query_scalar_unchecked!()`
(macro_invocation
macro: (scoped_identifier
path: (identifier) @_sqlx (#eq? @_sqlx "sqlx")
name: (identifier) @_query (#match? @_query "^query(_scalar|_scalar_unchecked)?$"))
(token_tree
; Only the first argument is SQL
.
[
(string_literal (string_content) @injection.content)
(raw_string_literal (string_content) @injection.content)
]
)
(#set! injection.language "sql"))
; Highlight SQL in `sqlx::query_as!()` and `sqlx::query_as_unchecked!()`
(macro_invocation
macro: (scoped_identifier
path: (identifier) @_sqlx (#eq? @_sqlx "sqlx")
name: (identifier) @_query_as (#match? @_query_as "^query_as(_unchecked)?$"))
(token_tree
; Only the second argument is SQL
.
; Allow anything as the first argument in case the user has lower case type
; names for some reason
(_)
[
(string_literal (string_content) @injection.content)
(raw_string_literal (string_content) @injection.content)
]
)
(#set! injection.language "sql"))
; Highlight SQL in `sqlx::query*` and `sqlx::raw_sql` functions
(call_expression
function: (scoped_identifier
path: (identifier) @_sqlx
(#eq? @_sqlx "sqlx")
name: (identifier) @_query_function)
(#match? @_query_function "^query.*|raw_sql$")
arguments: (arguments
.
[
(string_literal
(string_content) @injection.content)
(raw_string_literal
(string_content) @injection.content)
])
(#set! injection.language "sql"))
; Special language `tree-sitter-rust-format-args` for Rust macros,
; which use `format_args!` under the hood and therefore have
; the `format_args!` syntax.
;
; This language is injected into a hard-coded set of macros.
(
(macro_invocation
macro:
[
(scoped_identifier
name: (_) @_macro_name)
(identifier) @_macro_name
]
(token_tree) @injection.content
)
(#any-of? @_macro_name
; 1st argument is `format_args!`
; std
"print" "println" "eprint" "eprintln"
"format" "format_args" "todo" "panic"
"unreachable" "unimplemented" "compile_error"
; log
"crit" "trace" "debug" "info" "warn" "error"
; anyhow
"anyhow" "bail"
; syn
"format_ident"
; indoc
"formatdoc" "printdoc" "eprintdoc" "writedoc"
; iced
"text"
; ratatui
"span"
; eyre
"eyre"
; miette
"miette"
; 2nd argument is `format_args!`
; std
"write" "writeln" "assert" "debug_assert"
; defmt
"expect" "unwrap"
; ratatui
"span"
; 3rd argument is `format_args!`
; std
"assert_eq" "debug_assert_eq" "assert_ne" "debug_assert_ne"
; Dioxus's rsx! macro accepts string interpolation in all
; strings, across the entire token tree
"rsx"
)
(#set! injection.language "rust-format-args-macro")
(#set! injection.include-children)
)
; for some queries (e.g. when you have generic table names) you need to wrap it in `AssertSqlSafe`
; after `format!` so it can overwrite `format!` formatting correctly.
(call_expression
function: [
(scoped_identifier
path: (identifier) @_sqlx
(#eq? @_sqlx "sqlx")
name: (identifier) @_AssertSqlSafe)
(identifier) @_AssertSqlSafe
]
(#eq? @_AssertSqlSafe "AssertSqlSafe")
arguments: (arguments
[
(string_literal
(string_content) @injection.content)
(raw_string_literal
(string_content) @injection.content)
(macro_invocation
macro: (identifier) @_format
(#eq? @_format "format")
(token_tree
[
(string_literal
(string_content) @injection.content)
(raw_string_literal
(string_content) @injection.content)
]))
])
(#set! injection.language "sql"))

28
queries/rust/locals.scm Normal file
View File

@@ -0,0 +1,28 @@
; Scopes
[
(function_item)
(struct_item)
(enum_item)
(union_item)
(type_item)
(trait_item)
(impl_item)
(closure_expression)
(block)
] @local.scope
; Definitions
(function_item
(parameters
(parameter
pattern: (identifier) @local.definition.variable.parameter)))
(closure_parameters (identifier) @local.definition.variable.parameter)
; References
(identifier) @local.reference
; lifetimes / labels
(lifetime (identifier) @label)
(label (identifier) @label)