; ------- ; 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_parameters (type_identifier) @type.parameter) (constrained_type_parameter left: (type_identifier) @type.parameter) (optional_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 ; ------- (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)) ; ------- ; Keywords ; ------- (for_expression "for" @keyword.control.repeat) (gen_block "gen" @keyword.control) "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) ((generic_type type: (type_identifier) @keyword) (#eq? @keyword "use")) [ (crate) (super) "as" "pub" "mod" "extern" "impl" "where" "trait" "for" "default" "async" ] @keyword [ "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))) ; 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 ; ; For example, `foo` in `a.map(foo)` is a @function (call_expression function: (field_expression value: _ field: (field_identifier) @_method_name) arguments: ; first argument is `@function` (arguments . (identifier) @function) (#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" "map_or_else" ; NOTE: both arguments are closures, so it is here and also in the block to ; highlight the 2nd argument ; 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) arguments: ; handles `a.map_or_else(..., foo)` (arguments ; first argument is ignored . ; second argument is @function (_) . (identifier) @function) (#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")) (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"))