From fd11bd26cbeaac027e4025acd0ac9b1c20bcc49d Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Fri, 11 Jul 2025 03:01:59 +0100 Subject: [PATCH 01/10] queries: improve `@function` highlights when passed as arguments --- runtime/queries/rust/highlights.scm | 138 ++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/runtime/queries/rust/highlights.scm b/runtime/queries/rust/highlights.scm index 981896f94..69bffa843 100644 --- a/runtime/queries/rust/highlights.scm +++ b/runtime/queries/rust/highlights.scm @@ -305,6 +305,144 @@ ; Functions ; ------- +; Special handling for point-free functions passed to some "well-known" +; methods, that are known to take a closure as the first argument +; +; For example, these are @function: +; +; - `foo` in `a.map(foo)` +; - `bar` in `a.map(foo::bar)` +; - `baz` in `a.map(foo::bar::baz)` +(call_expression + function: (field_expression + value: _ + field: (field_identifier) @_method_name) + arguments: [ + ; handles `a.map(foo)` + (arguments "(" + . + (identifier) @function ")") + ; `a.map(foo::bar)`, `a.map(foo::bar::baz)`... (unlimited path segments) + (arguments "(" + . + (scoped_identifier + path: _ + name: (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" + + ; 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 + . + _ + (identifier) @function ")") + ; `a.map_or_else(..., foo::bar)`, `a.map(..., foo::bar::baz)`... (unlimited path segments) + (arguments "(" + ; first argument is ignored + . + _ + (scoped_identifier + path: _ + name: (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) From 42db29a32d7f44edc73966e2a42aaab98d46996f Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Fri, 11 Jul 2025 03:21:45 +0100 Subject: [PATCH 02/10] fix: highlight `_` in `[_]` as `@comment.unused` --- runtime/queries/rust/highlights.scm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runtime/queries/rust/highlights.scm b/runtime/queries/rust/highlights.scm index 69bffa843..4134e681a 100644 --- a/runtime/queries/rust/highlights.scm +++ b/runtime/queries/rust/highlights.scm @@ -88,9 +88,14 @@ ((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 From b5a07f9974806b91b34650af23e81aedf495c794 Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Sat, 12 Jul 2025 14:11:22 +0100 Subject: [PATCH 03/10] queries: `bar` in `foo::bar` is always a function when passed as arg --- runtime/queries/rust/highlights.scm | 54 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/runtime/queries/rust/highlights.scm b/runtime/queries/rust/highlights.scm index 4134e681a..65f7fb1e5 100644 --- a/runtime/queries/rust/highlights.scm +++ b/runtime/queries/rust/highlights.scm @@ -310,30 +310,37 @@ ; Functions ; ------- -; Special handling for point-free functions passed to some "well-known" -; methods, that are known to take a closure as the first argument +; 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, these are @function: -; -; - `foo` in `a.map(foo)` -; - `bar` in `a.map(foo::bar)` -; - `baz` in `a.map(foo::bar::baz)` +; For example, `foo` in `a.map(foo)` is a @function (call_expression function: (field_expression value: _ field: (field_identifier) @_method_name) - arguments: [ - ; handles `a.map(foo)` + arguments: + ; first argument is `@function` (arguments "(" . - (identifier) @function ")") - ; `a.map(foo::bar)`, `a.map(foo::bar::baz)`... (unlimited path segments) - (arguments "(" - . - (scoped_identifier - path: _ - name: (identifier) @function) ")") - ] + (identifier) @function) (#any-of? @_method_name ; methods on `core::iter::Iterator` "map" "map_while" "filter_map" "flat_map" "map_windows" @@ -411,22 +418,15 @@ function: (field_expression value: _ field: (field_identifier) @_method_name) - arguments: [ + arguments: ; handles `a.map_or_else(..., foo)` (arguments "(" ; first argument is ignored . _ + . + ; second argument is @function (identifier) @function ")") - ; `a.map_or_else(..., foo::bar)`, `a.map(..., foo::bar::baz)`... (unlimited path segments) - (arguments "(" - ; first argument is ignored - . - _ - (scoped_identifier - path: _ - name: (identifier) @function) ")") - ] (#any-of? @_method_name ; methods on `Option` "map_or_else" "zip_with" From 9de1c7213bdaeffcf8d0081c5fd626e0d7be36e6 Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Sat, 12 Jul 2025 14:18:12 +0100 Subject: [PATCH 04/10] queries: `bar` in `let bar = || 4` is a function --- runtime/queries/rust/highlights.scm | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/runtime/queries/rust/highlights.scm b/runtime/queries/rust/highlights.scm index 65f7fb1e5..5fd0756ae 100644 --- a/runtime/queries/rust/highlights.scm +++ b/runtime/queries/rust/highlights.scm @@ -310,6 +310,13 @@ ; Functions ; ------- +; In here, `bar` is a function, as it is equal to a closure: +; +; let bar = || 4; +(let_declaration + pattern: (identifier) @function + value: (closure_expression)) + ; highlight `baz` in `any_function(foo::bar::baz)` as function ; This generically works for an unlimited number of path segments: ; @@ -322,10 +329,10 @@ ; that position, however you cannot pass modules as arguments (call_expression function: _ - arguments: (arguments "(" + arguments: (arguments (scoped_identifier path: _ - name: (identifier) @function) ")")) + 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" @@ -338,7 +345,7 @@ field: (field_identifier) @_method_name) arguments: ; first argument is `@function` - (arguments "(" + (arguments . (identifier) @function) (#any-of? @_method_name @@ -420,13 +427,13 @@ field: (field_identifier) @_method_name) arguments: ; handles `a.map_or_else(..., foo)` - (arguments "(" + (arguments ; first argument is ignored . _ . ; second argument is @function - (identifier) @function ")") + (identifier) @function) (#any-of? @_method_name ; methods on `Option` "map_or_else" "zip_with" From 36c4fa6ab4894fce3926db40436b2e06bac16c4b Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Sat, 12 Jul 2025 14:24:38 +0100 Subject: [PATCH 05/10] fix: `map_or_else` has both arguments as closures --- runtime/queries/rust/highlights.scm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/queries/rust/highlights.scm b/runtime/queries/rust/highlights.scm index 5fd0756ae..de59c9321 100644 --- a/runtime/queries/rust/highlights.scm +++ b/runtime/queries/rust/highlights.scm @@ -363,6 +363,8 @@ ; 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" @@ -430,9 +432,9 @@ (arguments ; first argument is ignored . - _ - . ; second argument is @function + (_) + . (identifier) @function) (#any-of? @_method_name ; methods on `Option` From 6ddadd3fa702bd7f4077abeef0a02a10fc381f45 Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Sat, 12 Jul 2025 14:35:13 +0100 Subject: [PATCH 06/10] queries: highlight local closures --- runtime/queries/rust/locals.scm | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/runtime/queries/rust/locals.scm b/runtime/queries/rust/locals.scm index 7958ef04d..6f8e98f04 100644 --- a/runtime/queries/rust/locals.scm +++ b/runtime/queries/rust/locals.scm @@ -21,3 +21,18 @@ ; References (identifier) @local.reference + +; In here, `bar` is a function, as it is equal to a closure: +; +; let bar = || 4; +; +; After this, we know that `bar` must be a function: +; +; let a = bar; +; ^^^ function +; +; let a = f(bar) +; ^^^ function +(let_declaration + pattern: (identifier) @local.definition.function + value: (closure_expression)) From db15411f635b85fefb240073e92cebf8e52f4e88 Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Sat, 12 Jul 2025 15:09:41 +0100 Subject: [PATCH 07/10] queries: highlight more patterns in parameters --- runtime/queries/rust/highlights.scm | 5 ----- runtime/queries/rust/locals.scm | 26 +++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/runtime/queries/rust/highlights.scm b/runtime/queries/rust/highlights.scm index de59c9321..babc1ce7d 100644 --- a/runtime/queries/rust/highlights.scm +++ b/runtime/queries/rust/highlights.scm @@ -206,11 +206,6 @@ value: (identifier)? @variable field: (field_identifier) @variable.other.member)) -(parameter - pattern: (identifier) @variable.parameter) -(closure_parameters - (identifier) @variable.parameter) - ; ------- ; Keywords ; ------- diff --git a/runtime/queries/rust/locals.scm b/runtime/queries/rust/locals.scm index 6f8e98f04..0267a55dd 100644 --- a/runtime/queries/rust/locals.scm +++ b/runtime/queries/rust/locals.scm @@ -15,9 +15,29 @@ ; Definitions (parameter - pattern: (identifier) @local.definition.variable.parameter) - -(closure_parameters (identifier) @local.definition.variable.parameter) + pattern: [ + ; `foo` in `fn x(foo: !) {}` + (identifier) @local.definition.variable.parameter @variable.parameter + ; `foo` and `bar` in `fn x((foo, bar): !) {}` + (tuple_pattern (identifier)* (identifier) @local.definition.variable.parameter @variable.parameter) + ; `foo` and `bar` in `fn x(Struct { foo, bar }: !) {}` + (struct_pattern + (field_pattern)* + (field_pattern + name: (shorthand_field_identifier) @local.definition.variable.parameter @variable.parameter) + ) + ; `foo` and `bar` in `fn x(TupleStruct(foo, bar): !) {}` + (tuple_struct_pattern + type: _ + (identifier)* + (identifier) @local.definition.variable.parameter @variable.parameter + ) + ; `foo` and `bar` in `fn x([foo, bar]: !) {}` + (slice_pattern + (identifier)* + (identifier) @local.definition.variable.parameter @variable.parameter + ) + ]) ; References (identifier) @local.reference From df1ea8cba3caebe9690cb6fd80d075988e53f91e Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Sat, 12 Jul 2025 15:38:50 +0100 Subject: [PATCH 08/10] queries: do not highlight `bar` in `bar = || ()` as closure --- runtime/queries/rust/highlights.scm | 7 ------- runtime/queries/rust/locals.scm | 15 --------------- 2 files changed, 22 deletions(-) diff --git a/runtime/queries/rust/highlights.scm b/runtime/queries/rust/highlights.scm index babc1ce7d..5e3471b36 100644 --- a/runtime/queries/rust/highlights.scm +++ b/runtime/queries/rust/highlights.scm @@ -305,13 +305,6 @@ ; Functions ; ------- -; In here, `bar` is a function, as it is equal to a closure: -; -; let bar = || 4; -(let_declaration - pattern: (identifier) @function - value: (closure_expression)) - ; highlight `baz` in `any_function(foo::bar::baz)` as function ; This generically works for an unlimited number of path segments: ; diff --git a/runtime/queries/rust/locals.scm b/runtime/queries/rust/locals.scm index 0267a55dd..3262f9473 100644 --- a/runtime/queries/rust/locals.scm +++ b/runtime/queries/rust/locals.scm @@ -41,18 +41,3 @@ ; References (identifier) @local.reference - -; In here, `bar` is a function, as it is equal to a closure: -; -; let bar = || 4; -; -; After this, we know that `bar` must be a function: -; -; let a = bar; -; ^^^ function -; -; let a = f(bar) -; ^^^ function -(let_declaration - pattern: (identifier) @local.definition.function - value: (closure_expression)) From e610e7dd80a8cc1a8afb8ba06adf067471f148d7 Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Mon, 14 Jul 2025 02:02:10 +0100 Subject: [PATCH 09/10] queries: Highlight interpolations in the `rsx!` macro --- runtime/queries/rust/injections.scm | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/runtime/queries/rust/injections.scm b/runtime/queries/rust/injections.scm index af1970a93..22b1ad673 100644 --- a/runtime/queries/rust/injections.scm +++ b/runtime/queries/rust/injections.scm @@ -150,31 +150,11 @@ ; 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) ) - -; Dioxus' "rsx!" macro relies heavily on string interpolation as well. The strings can be nested very deeply -( - (macro_invocation - macro: [ - (scoped_identifier - name: (_) @_macro_name) - (identifier) @_macro_name - ] - ; TODO: This only captures 1 level of string literals. But in dioxus you can have - ; nested string literals. For instance: - ; - ; rsx! { "{hello} world" }: - ; -> (token_tree (string_literal)) - ; rsx! { div { "{hello} world" } } - ; -> (token_tree (token_tree (string_literal))) - ; rsx! { div { div { "{hello} world" } } } - ; -> (token_tree (token_tree (token_tree (string_literal)))) - (token_tree (string_literal) @injection.content) - ) - (#eq? @_macro_name "rsx") - (#set! injection.language "rust-format-args") - (#set! injection.include-children) -) From a2f219968a8f6b9ef193eb76a92ed913929d91e6 Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Mon, 14 Jul 2025 02:28:50 +0100 Subject: [PATCH 10/10] queries: Highlight mutable variables in Rust --- book/src/themes.md | 1 + runtime/queries/rust/highlights.scm | 2 -- runtime/queries/rust/locals.scm | 16 +++++++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/book/src/themes.md b/book/src/themes.md index 3ef064d6d..faac0841f 100644 --- a/book/src/themes.md +++ b/book/src/themes.md @@ -179,6 +179,7 @@ We use a similar set of scopes as - `variable` - Variables - `builtin` - Reserved language variables (`self`, `this`, `super`, etc.) - `parameter` - Function parameters + - `mutable` - Mutable variables (e.g. marked with `mut` in Rust) - `other` - `member` - Fields of composite data types (e.g. structs, unions) - `private` - Private fields that use a unique syntax (currently just ECMAScript-based languages) diff --git a/runtime/queries/rust/highlights.scm b/runtime/queries/rust/highlights.scm index 5e3471b36..4e7d79f20 100644 --- a/runtime/queries/rust/highlights.scm +++ b/runtime/queries/rust/highlights.scm @@ -289,8 +289,6 @@ "dyn" ] @keyword.storage.modifier -; TODO: variable.mut to highlight mutable identifiers via locals.scm - ; --- ; Remaining Paths ; --- diff --git a/runtime/queries/rust/locals.scm b/runtime/queries/rust/locals.scm index 3262f9473..1aca98ff8 100644 --- a/runtime/queries/rust/locals.scm +++ b/runtime/queries/rust/locals.scm @@ -12,7 +12,7 @@ (block) ] @local.scope -; Definitions +; Parameters (parameter pattern: [ @@ -39,5 +39,19 @@ ) ]) +; Mutable variables + +[ + (let_declaration + (mutable_specifier) + pattern: (identifier) @local.definition.variable.mutable @variable.mutable) + (parameter + (mutable_specifier) + pattern: (identifier) @local.definition.variable.mutable @variable.mutable) + (mut_pattern + (mutable_specifier) + (identifier) @local.definition.variable.mutable @variable.mutable) +] + ; References (identifier) @local.reference