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.
pull/13947/head
Michael Davis 2025-07-13 12:01:17 -04:00
parent d2f37b1559
commit 86f10ae24c
No known key found for this signature in database
8 changed files with 32 additions and 53 deletions

View File

@ -208,6 +208,7 @@
| ruby | ✓ | ✓ | ✓ | `ruby-lsp`, `solargraph` | | ruby | ✓ | ✓ | ✓ | `ruby-lsp`, `solargraph` |
| rust | ✓ | ✓ | ✓ | `rust-analyzer` | | rust | ✓ | ✓ | ✓ | `rust-analyzer` |
| rust-format-args | ✓ | | | | | rust-format-args | ✓ | | | |
| rust-format-args-macro | ✓ | ✓ | ✓ | |
| sage | ✓ | ✓ | | | | sage | ✓ | ✓ | | |
| scala | ✓ | ✓ | ✓ | `metals` | | scala | ✓ | ✓ | ✓ | `metals` |
| scheme | ✓ | | ✓ | | | scheme | ✓ | | ✓ | |

View File

@ -4445,6 +4445,12 @@ injection-regex = "rust-format-args"
name = "rust-format-args" name = "rust-format-args"
source = { git = "https://github.com/nik-rev/tree-sitter-rust-format-args", rev = "84ffe550e261cf5ea40a0ec31849ba2443bae99f" } 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]] [[language]]
name = "clarity" name = "clarity"
scope = "source.clar" scope = "source.clar"

View File

@ -0,0 +1 @@
; inherits: rust

View File

@ -0,0 +1 @@
; inherits: rust

View File

@ -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))

View File

@ -0,0 +1 @@
; inherits: rust

View File

@ -0,0 +1 @@
; inherits: rust

View File

@ -103,8 +103,6 @@
; the `format_args!` syntax. ; the `format_args!` syntax.
; ;
; This language is injected into a hard-coded set of macros. ; This language is injected into a hard-coded set of macros.
; 1st argument is `format_args!`
( (
(macro_invocation (macro_invocation
macro: macro:
@ -113,13 +111,11 @@
name: (_) @_macro_name) name: (_) @_macro_name)
(identifier) @_macro_name (identifier) @_macro_name
] ]
(token_tree . [ (token_tree) @injection.content
(string_literal (string_content) @injection.content)
(raw_string_literal (string_content) @injection.content)
]
)
) )
(#any-of? @_macro_name (#any-of? @_macro_name
; 1st argument is `format_args!`
; std ; std
"print" "println" "eprint" "eprintln" "print" "println" "eprint" "eprintln"
"format" "format_args" "todo" "panic" "format" "format_args" "todo" "panic"
@ -140,63 +136,22 @@
"eyre" "eyre"
; miette ; miette
"miette" "miette"
)
(#set! injection.language "rust-format-args")
(#set! injection.include-children)
)
; 2nd argument is `format_args!` ; 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
; std ; std
"write" "writeln" "assert" "debug_assert" "write" "writeln" "assert" "debug_assert"
; defmt ; defmt
"expect" "unwrap" "expect" "unwrap"
; ratatui ; ratatui
"span" "span"
)
(#set! injection.language "rust-format-args")
(#set! injection.include-children)
)
; 3rd argument is `format_args!` ; 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
; std ; std
"assert_eq" "debug_assert_eq" "assert_ne" "debug_assert_ne" "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) (#set! injection.include-children)
) )