2021-07-09 00:11:20 +08:00
; -------
2025-02-03 08:17:10 +08:00
; Basic identifiers
2021-07-09 00:11:20 +08:00
; -------
2021-04-08 22:18:25 +08:00
2025-02-03 08:17:10 +08:00
; 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 )
2021-07-09 00:11:20 +08:00
; -------
; Types
; -------
2021-05-16 17:58:27 +08:00
2023-11-04 05:21:01 +08:00
( type_parameters
( type_identifier ) @type . parameter )
( constrained_type_parameter
left: ( type_identifier ) @type . parameter )
( optional_type_parameter
name: ( type_identifier ) @type . parameter )
2025-02-03 08:24:13 +08:00
( ( type_arguments ( type_identifier ) @constant )
( # match? @constant "^[A-Z_]+$" ) )
2025-02-07 22:51:22 +08:00
( type_arguments ( type_identifier ) @type )
2025-07-11 10:21:45 +08:00
; `_` in `(_, _)`
2025-02-07 22:51:22 +08:00
( tuple_struct_pattern "_" @comment . unused )
2025-07-11 10:21:45 +08:00
; `_` in `Vec<_>`
2025-02-03 08:24:13 +08:00
( ( type_arguments ( type_identifier ) @comment . unused )
( # eq? @comment . unused "_" ) )
2025-07-11 10:21:45 +08:00
; `_` in `Rc<[_]>`
( ( array_type ( type_identifier ) @comment . unused )
( # eq? @comment . unused "_" ) )
2023-11-04 05:21:01 +08:00
2021-07-09 00:11:20 +08:00
; ---
; Primitives
; ---
2021-05-16 17:58:27 +08:00
2021-11-03 11:00:52 +08:00
( escape_sequence ) @constant . character . escape
2021-07-09 00:11:20 +08:00
( primitive_type ) @type . builtin
2021-09-02 13:58:57 +08:00
( boolean_literal ) @constant . builtin . boolean
2021-11-03 11:00:52 +08:00
( integer_literal ) @constant . numeric . integer
( float_literal ) @constant . numeric . float
( char_literal ) @constant . character
2021-07-09 00:11:20 +08:00
[
( string_literal )
( raw_string_literal )
] @string
2025-06-01 03:37:25 +08:00
; -------
; Comments
; -------
( 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
2021-04-08 22:18:25 +08:00
2021-07-09 00:11:20 +08:00
; ---
; Extraneous
; ---
2021-05-16 17:58:27 +08:00
2021-07-09 00:11:20 +08:00
( self ) @variable . builtin
2021-04-08 22:18:25 +08:00
2021-07-09 00:11:20 +08:00
( field_initializer
2021-11-03 11:00:52 +08:00
( field_identifier ) @variable . other . member )
2021-08-19 00:02:15 +08:00
( shorthand_field_initializer
2021-11-03 11:00:52 +08:00
( identifier ) @variable . other . member )
( shorthand_field_identifier ) @variable . other . member
2021-04-08 22:18:25 +08:00
2021-07-09 00:11:20 +08:00
( lifetime
"'" @label
( identifier ) @label )
2024-04-16 00:07:15 +08:00
( label
2022-12-22 07:10:45 +08:00
"'" @label
( identifier ) @label )
2021-04-08 22:18:25 +08:00
2021-07-09 00:11:20 +08:00
; ---
; Punctuation
; ---
2021-04-08 22:18:25 +08:00
2021-07-09 00:11:20 +08:00
[
"::"
"."
";"
2022-06-29 09:10:19 +08:00
","
2025-01-06 00:51:33 +08:00
":"
2021-07-09 00:11:20 +08:00
] @punctuation . delimiter
2021-05-16 17:58:27 +08:00
2021-07-09 00:11:20 +08:00
[
"("
")"
"["
"]"
2021-08-19 00:02:15 +08:00
"{"
"}"
2022-08-18 05:20:44 +08:00
"#"
2021-07-09 00:11:20 +08:00
] @punctuation . bracket
( type_arguments
[
"<"
">"
] @punctuation . bracket )
( type_parameters
[
"<"
">"
] @punctuation . bracket )
2025-02-07 22:51:22 +08:00
( for_lifetimes [ "<" ">" ] @punctuation . bracket )
2022-08-12 08:00:03 +08:00
( closure_parameters
"|" @punctuation . bracket )
2025-02-07 22:51:22 +08:00
( bracketed_type [ "<" ">" ] @punctuation . bracket )
2021-05-16 17:58:27 +08:00
2021-07-09 00:11:20 +08:00
; ---
2021-11-03 11:00:52 +08:00
; Variables
2021-07-09 00:11:20 +08:00
; ---
2021-04-08 22:18:25 +08:00
2021-11-03 11:00:52 +08:00
( 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 ) )
2021-07-09 00:11:20 +08:00
; -------
; Keywords
; -------
2021-04-08 22:18:25 +08:00
2021-07-09 00:11:20 +08:00
( for_expression
2022-10-29 23:36:26 +08:00
"for" @keyword . control . repeat )
2025-01-24 03:11:07 +08:00
( gen_block "gen" @keyword . control )
2022-10-29 23:36:26 +08:00
"in" @keyword . control
[
"match"
"if"
"else"
2024-04-30 12:13:27 +08:00
"try"
2022-10-29 23:36:26 +08:00
] @keyword . control . conditional
2021-07-09 00:11:20 +08:00
[
"while"
"loop"
2022-10-29 23:36:26 +08:00
] @keyword . control . repeat
[
2021-07-09 00:11:20 +08:00
"break"
"continue"
"return"
"await"
2025-01-24 03:11:07 +08:00
"yield"
2022-10-29 23:36:26 +08:00
] @keyword . control . return
2021-07-09 00:11:20 +08:00
2021-12-23 11:10:24 +08:00
"use" @keyword . control . import
( mod_item "mod" @keyword . control . import !body )
( use_as_clause "as" @keyword . control . import )
( type_cast_expression "as" @keyword . operator )
2025-01-24 03:11:07 +08:00
( ( generic_type
type: ( type_identifier ) @keyword )
( # eq? @keyword "use" ) )
2021-07-09 00:11:20 +08:00
[
( crate )
( super )
"as"
"pub"
"mod"
"extern"
"impl"
"where"
"trait"
"for"
"default"
"async"
] @keyword
2021-04-08 22:18:25 +08:00
2022-10-29 23:36:26 +08:00
[
"struct"
"enum"
"union"
"type"
] @keyword . storage . type
"let" @keyword . storage
2022-06-29 09:10:19 +08:00
"fn" @keyword . function
2022-12-22 07:10:45 +08:00
"unsafe" @keyword . special
"macro_rules!" @function . macro
2022-06-29 09:10:19 +08:00
2022-06-15 01:35:36 +08:00
( mutable_specifier ) @keyword . storage . modifier . mut
( reference_type "&" @keyword . storage . modifier . ref )
( self_parameter "&" @keyword . storage . modifier . ref )
[
"static"
"const"
2025-01-24 03:11:07 +08:00
"raw"
2022-06-15 01:35:36 +08:00
"ref"
"move"
"dyn"
] @keyword . storage . modifier
2021-04-08 22:18:25 +08:00
2025-02-07 22:51:22 +08:00
; ---
; Remaining Paths
; ---
2024-01-02 23:38:13 +08:00
2025-02-07 22:51:22 +08:00
( scoped_identifier
path: ( identifier ) ? @namespace
name: ( identifier ) @namespace )
( scoped_type_identifier
path: ( identifier ) @namespace )
2024-01-02 23:38:13 +08:00
2025-02-03 08:17:10 +08:00
; -------
; Functions
; -------
2025-07-12 21:11:22 +08:00
; 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: _
2025-07-12 21:18:12 +08:00
arguments: ( arguments
2025-07-12 21:11:22 +08:00
( scoped_identifier
path: _
2025-07-12 21:18:12 +08:00
name: ( identifier ) @function ) ) )
2025-07-12 21:11:22 +08:00
; Special handling for point-free functions that are not part of a path
; but are just passed as variables to some "well-known"
; methods, which are known to take a closure as the first argument
2025-07-11 10:01:59 +08:00
;
2025-07-12 21:11:22 +08:00
; For example, `foo` in `a.map(foo)` is a @function
2025-07-11 10:01:59 +08:00
( call_expression
function: ( field_expression
value: _
field: ( field_identifier ) @_method_name )
2025-07-12 21:11:22 +08:00
arguments:
; first argument is `@function`
2025-07-12 21:18:12 +08:00
( arguments
2025-07-11 10:01:59 +08:00
.
2025-07-12 21:11:22 +08:00
( identifier ) @function )
2025-07-11 10:01:59 +08:00
( # any-of? @_method_name
; methods on `core::iter::Iterator`
"map" "map_while" "filter_map" "flat_map" "map_windows"
"try_for_each" "for_each"
"is_sorted_by" "is_sorted_by_key"
"all" "any" "reduce" "try_reduce"
"find" "find_map" "try_find" "position" "rposition"
"max_by" "max_by_key" "min_by" "min_by_key"
"filter" "inspect" "intersperse_with"
"partition" "partition_in_place" "is_partitioned"
"skip_while" "take_while"
; methods on `Option`
"and_then" "is_some_and" "is_none_or" "unwrap_or_else"
"ok_or_else" "or_else" "get_or_insert_with" "take_if"
2025-07-12 21:24:38 +08:00
"map_or_else" ; NOTE: both arguments are closures, so it is here and also in the block to
; highlight the 2nd argument
2025-07-11 10:01:59 +08:00
; methods on `Result
"map_err" "inspect_err"
; methods on `Entry`
"or_insert_with" "or_insert_with_key" "and_modify"
; method on `bool
"then"
; method on `Vec`
"chunk_by_mut" "split" "split_mut" "split_inclusive" "split_inclusive_mut"
"rsplit" "rsplit_mut" "binary_search_by"
"sort_unstable_by" "sort_unstable_by_key" "partition_dedup_by"
"partition_dedup_by_key" "fill_with" "partition_point" "sort_by"
"sort_by_key"
; methods on `HashMap`
"extract_if" "retain"
; methods on `itertools::Itertools`
"batching" "chunk_by" "group_by" "map_ok"
"filter_ok" "filter_map_ok" "process_results"
"kmerge_by" "coalesce" "dedup_by" "dedup_by_with_count"
"duplicates_by" "unique_by" "peeking_take_while"
"take_while_ref" "take_while_inclusive" "positions"
"update" "find_position" "find_or_last" "find_or_first"
"fold1" "tree_reduce" "tree_fold1" "partition_map"
"into_group_map_by" "into_grouping_map_by"
"min_set_by" "min_set_by_key" "max_set_by" "max_set_by_key"
"minmax_by_key" "minmax_by" "position_max_by_key"
"position_max_by" "position_min_by_key" "position_min_by"
"position_minmax_by_key" "position_minmax_by"
"sorted_unstable_by" "sorted_unstable_by_key" "sorted_by"
"sorted_by_key" "sorted_by_cached_key"
; method on `core::iter::Peekable`
"next_if"
; methods on `rayon::ParallelIterator`
;
; note: some of these methods are also
; present in the 2nd argument highlights, because
; those methods take a closure both as a 1st and 2nd arg
"for_each_init" "try_for_each_init" "map_init"
"update"
"flat_map_iter" "reduce_with" "try_reduce"
"try_reduce_with" "fold_with" "try_fold_with"
"find_any" "find_first" "find_last" "find_map_any"
"find_map_first" "find_map_last"
"take_any_while" "skip_any_while"
; method on `tap::Pipe`
"pipe" "pipe_ref" "pipe_ref_mut" "pipe_borrow" "pipe_deref_mut"
"pipe_borrow_mut" "pipe_as_ref" "pipe_as_mut" "pipe_deref" ) )
; Here, we do something similar to the above except for
; methods that take a closure as a 2nd argument instead of the first
( call_expression
function: ( field_expression
value: _
field: ( field_identifier ) @_method_name )
2025-07-12 21:11:22 +08:00
arguments:
2025-07-11 10:01:59 +08:00
; handles `a.map_or_else(..., foo)`
2025-07-12 21:18:12 +08:00
( arguments
2025-07-11 10:01:59 +08:00
; first argument is ignored
.
2025-07-12 21:11:22 +08:00
; second argument is @function
2025-07-12 21:24:38 +08:00
( _ )
.
2025-07-12 21:18:12 +08:00
( identifier ) @function )
2025-07-11 10:01:59 +08:00
( # any-of? @_method_name
; methods on `Option`
"map_or_else" "zip_with"
; methods on `Iterator`
"try_fold" "scan" "fold" "cmp_by" "partial_cmp_by" "eq_by"
; methods on `rayon::ParallelIterator`
"for_each_with" "for_each_init" "try_for_each_with" "try_for_each_init"
"map_with" "map_init" "try_reduce" "fold_with" "try_fold_with"
; methods on `Vec`
"splitn" "splitn_mut" "rsplitn" "rsplitn_mut" "split_once"
"rsplit_once" "binary_search_by_key" "select_nth_unstable_by"
"select_nth_unstable_by_key"
; methods on `Itertools`
"k_largest_by" "k_largest_by_key" "k_largest_relaxed_by"
"k_largest_relaxed_by_key"
"k_smallest_by" "k_smallest_by_key" "k_smallest_relaxed_by" "k_smallest_relaxed_by_key"
"fold_while" "fold_ok" "fold_options" "merge_by" "merge_join_by" "pad_using" "format_with" ) )
2025-02-03 08:17:10 +08:00
( 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 )
2021-07-09 00:11:20 +08:00
; -------
; Guess Other Types
; -------
2025-02-03 08:17:10 +08:00
; Other PascalCase identifiers are assumed to be structs.
( ( identifier ) @type
( # match? @type "^[A-Z]" ) )
( never_type "!" @type )
2021-04-08 22:18:25 +08:00
2021-07-09 00:11:20 +08:00
( ( identifier ) @constant
2022-09-22 06:55:28 +08:00
( # match? @constant "^[A-Z][A-Z\\d_]*$" ) )
2021-07-09 00:11:20 +08:00
; ---
; PascalCase identifiers in call_expressions (e.g. `Ok()`)
; are assumed to be enum constructors.
; ---
( call_expression
function: [
2024-01-02 23:38:13 +08:00
( ( identifier ) @constructor
( # match? @constructor "^[A-Z]" ) )
2021-07-09 00:11:20 +08:00
( scoped_identifier
2024-01-02 23:38:13 +08:00
name: ( ( identifier ) @constructor
( # match? @constructor "^[A-Z]" ) ) )
2021-07-09 00:11:20 +08:00
] )
; ---
2024-01-02 23:38:13 +08:00
; PascalCase identifiers under a path which is also PascalCase
; are assumed to be constructors if they have methods or fields.
2021-07-09 00:11:20 +08:00
; ---
2024-01-02 23:38:13 +08:00
( field_expression
value: ( scoped_identifier
path: [
( identifier ) @type
( scoped_identifier
name: ( identifier ) @type )
]
name: ( identifier ) @constructor
( # match? @type "^[A-Z]" )
( # match? @constructor "^[A-Z]" ) ) )
2021-04-08 22:18:25 +08:00
2025-02-07 22:51:22 +08:00
( 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]" ) )
2021-07-09 00:11:20 +08:00
; ---
; Macros
; ---
2022-12-12 16:49:57 +08:00
2022-11-12 06:51:58 +08:00
( attribute
2021-12-20 10:47:40 +08:00
( identifier ) @function . macro )
2025-02-07 22:51:22 +08:00
( inner_attribute_item "!" @punctuation )
2022-11-12 06:51:58 +08:00
( attribute
2022-11-09 03:41:09 +08:00
[
( identifier ) @function . macro
( scoped_identifier
name: ( identifier ) @function . macro )
]
2022-09-07 11:45:51 +08:00
( token_tree ( identifier ) @function . macro ) ? )
2021-12-20 10:47:40 +08:00
2021-04-08 22:18:25 +08:00
( inner_attribute_item ) @attribute
2021-07-09 00:11:20 +08:00
( macro_definition
name: ( identifier ) @function . macro )
( macro_invocation
macro: [
( ( identifier ) @function . macro )
( scoped_identifier
name: ( identifier ) @function . macro )
]
"!" @function . macro )
( metavariable ) @variable . parameter
2021-12-20 10:47:40 +08:00
( fragment_specifier ) @type
2025-02-07 22:51:22 +08:00
( attribute
( identifier ) @special
arguments: ( token_tree ( identifier ) @type )
( # eq? @special "derive" )
)
2025-05-31 22:43:19 +08:00
( token_repetition_pattern ) @punctuation . delimiter
2025-02-18 02:54:49 +08:00
( token_repetition_pattern [ ")" "(" "$" ] @punctuation . special )
( token_repetition_pattern "?" @operator )
2025-02-07 22:51:22 +08:00
; ---
; 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" ) )