From 86f10ae24ce01df2dedda9e2ffdbb1d647cca6be Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sun, 13 Jul 2025 12:01:17 -0400 Subject: [PATCH] Add a language to fix Rust highlights in format-args macros This is a bit hacky. Injections cannot stack on each other like highlights because layers can have their own injections. So this new language `rust-format-args-macro` emulates that. It unconditionally injects `rust-format-args` into all strings. Rust injects this new language into known format-args macros like `println!`. The downside is that this can cause false-positive highlights within these macros for strings which happen to contain format-args syntax println!("Hello, {}!", "{}"); // ^ format args syntax // ^ not format args syntax, but highlighted // as if it were :( This false-positive case is expected to be rare. Injecting this fake language fixes regular non-string highlights in macro invocations: macro invocations need to inject the entire token tree and use `injection.include-children` for proper highlighting. --- book/src/generated/lang-support.md | 1 + languages.toml | 6 ++ .../rust-format-args-macro/highlights.scm | 1 + .../rust-format-args-macro/indents.scm | 1 + .../rust-format-args-macro/injections.scm | 13 ++++ .../queries/rust-format-args-macro/locals.scm | 1 + .../rust-format-args-macro/textobjects.scm | 1 + runtime/queries/rust/injections.scm | 61 +++---------------- 8 files changed, 32 insertions(+), 53 deletions(-) create mode 100644 runtime/queries/rust-format-args-macro/highlights.scm create mode 100644 runtime/queries/rust-format-args-macro/indents.scm create mode 100644 runtime/queries/rust-format-args-macro/injections.scm create mode 100644 runtime/queries/rust-format-args-macro/locals.scm create mode 100644 runtime/queries/rust-format-args-macro/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index cc9e0e62d..1c620e374 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -208,6 +208,7 @@ | ruby | ✓ | ✓ | ✓ | `ruby-lsp`, `solargraph` | | rust | ✓ | ✓ | ✓ | `rust-analyzer` | | rust-format-args | ✓ | | | | +| rust-format-args-macro | ✓ | ✓ | ✓ | | | sage | ✓ | ✓ | | | | scala | ✓ | ✓ | ✓ | `metals` | | scheme | ✓ | | ✓ | | diff --git a/languages.toml b/languages.toml index 5c37a0b7f..670904fed 100644 --- a/languages.toml +++ b/languages.toml @@ -4445,6 +4445,12 @@ injection-regex = "rust-format-args" name = "rust-format-args" source = { git = "https://github.com/nik-rev/tree-sitter-rust-format-args", rev = "84ffe550e261cf5ea40a0ec31849ba2443bae99f" } +[[language]] +name = "rust-format-args-macro" +scope = "source.rust-format-args-macro" +file-types = [] +grammar = "rust" + [[language]] name = "clarity" scope = "source.clar" diff --git a/runtime/queries/rust-format-args-macro/highlights.scm b/runtime/queries/rust-format-args-macro/highlights.scm new file mode 100644 index 000000000..ae55c7faf --- /dev/null +++ b/runtime/queries/rust-format-args-macro/highlights.scm @@ -0,0 +1 @@ +; inherits: rust diff --git a/runtime/queries/rust-format-args-macro/indents.scm b/runtime/queries/rust-format-args-macro/indents.scm new file mode 100644 index 000000000..ae55c7faf --- /dev/null +++ b/runtime/queries/rust-format-args-macro/indents.scm @@ -0,0 +1 @@ +; inherits: rust diff --git a/runtime/queries/rust-format-args-macro/injections.scm b/runtime/queries/rust-format-args-macro/injections.scm new file mode 100644 index 000000000..47db13450 --- /dev/null +++ b/runtime/queries/rust-format-args-macro/injections.scm @@ -0,0 +1,13 @@ +; inherits: rust + +; HACK: This language is the same as Rust but all strings are injected +; with rust-format-args. Rust injects this into known macros which use +; the format args syntax. This can cause false-positive highlights but +; those are expected to be rare. + +([ + (string_literal (string_content) @injection.content) + (raw_string_literal (string_content) @injection.content) + ] + (#set! injection.language "rust-format-args") + (#set! injection.include-children)) diff --git a/runtime/queries/rust-format-args-macro/locals.scm b/runtime/queries/rust-format-args-macro/locals.scm new file mode 100644 index 000000000..ae55c7faf --- /dev/null +++ b/runtime/queries/rust-format-args-macro/locals.scm @@ -0,0 +1 @@ +; inherits: rust diff --git a/runtime/queries/rust-format-args-macro/textobjects.scm b/runtime/queries/rust-format-args-macro/textobjects.scm new file mode 100644 index 000000000..ae55c7faf --- /dev/null +++ b/runtime/queries/rust-format-args-macro/textobjects.scm @@ -0,0 +1 @@ +; inherits: rust diff --git a/runtime/queries/rust/injections.scm b/runtime/queries/rust/injections.scm index ba58bf36d..af1970a93 100644 --- a/runtime/queries/rust/injections.scm +++ b/runtime/queries/rust/injections.scm @@ -103,8 +103,6 @@ ; the `format_args!` syntax. ; ; This language is injected into a hard-coded set of macros. - -; 1st argument is `format_args!` ( (macro_invocation macro: @@ -113,13 +111,11 @@ name: (_) @_macro_name) (identifier) @_macro_name ] - (token_tree . [ - (string_literal (string_content) @injection.content) - (raw_string_literal (string_content) @injection.content) - ] - ) + (token_tree) @injection.content ) (#any-of? @_macro_name + ; 1st argument is `format_args!` + ; std "print" "println" "eprint" "eprintln" "format" "format_args" "todo" "panic" @@ -140,63 +136,22 @@ "eyre" ; miette "miette" - ) - (#set! injection.language "rust-format-args") - (#set! injection.include-children) -) -; 2nd argument is `format_args!` -( - (macro_invocation - macro: - [ - (scoped_identifier - name: (_) @_macro_name) - (identifier) @_macro_name - ] - (token_tree - . (_) - . [ - (string_literal (string_content) @injection.content) - (raw_string_literal (string_content) @injection.content) - ] - ) - ) - (#any-of? @_macro_name + ; 2nd argument is `format_args!` + ; std "write" "writeln" "assert" "debug_assert" ; defmt "expect" "unwrap" ; ratatui "span" - ) - (#set! injection.language "rust-format-args") - (#set! injection.include-children) -) -; 3rd argument is `format_args!` -( - (macro_invocation - macro: - [ - (scoped_identifier - name: (_) @_macro_name) - (identifier) @_macro_name - ] - (token_tree - . (_) - . (_) - . [ - (string_literal (string_content) @injection.content) - (raw_string_literal (string_content) @injection.content) - ] - ) - ) - (#any-of? @_macro_name + ; 3rd argument is `format_args!` + ; std "assert_eq" "debug_assert_eq" "assert_ne" "debug_assert_ne" ) - (#set! injection.language "rust-format-args") + (#set! injection.language "rust-format-args-macro") (#set! injection.include-children) )