mirror of https://github.com/helix-editor/helix
feat: highlight rust string interpolation macros that use `format_args!` (#13533)
Co-authored-by: Nik Revenco <154856872+NikitaRevenco@users.noreply.github.com>pull/11441/head
parent
223ceec10a
commit
1023e8f964
|
@ -199,6 +199,7 @@
|
|||
| rst | ✓ | | | |
|
||||
| ruby | ✓ | ✓ | ✓ | `ruby-lsp`, `solargraph` |
|
||||
| rust | ✓ | ✓ | ✓ | `rust-analyzer` |
|
||||
| rust-format-args | ✓ | | | |
|
||||
| sage | ✓ | ✓ | | |
|
||||
| scala | ✓ | ✓ | ✓ | `metals` |
|
||||
| scheme | ✓ | | ✓ | |
|
||||
|
|
|
@ -734,7 +734,7 @@ async fn surround_replace_ts() -> anyhow::Result<()> {
|
|||
const INPUT: &str = r#"\
|
||||
fn foo() {
|
||||
if let Some(_) = None {
|
||||
todo!("f#[|o]#o)");
|
||||
testing!("f#[|o]#o)");
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
@ -744,7 +744,7 @@ fn foo() {
|
|||
r#"\
|
||||
fn foo() {
|
||||
if let Some(_) = None {
|
||||
todo!('f#[|o]#o)');
|
||||
testing!('f#[|o]#o)');
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
@ -757,7 +757,7 @@ fn foo() {
|
|||
r#"\
|
||||
fn foo() {
|
||||
if let Some(_) = None [
|
||||
todo!("f#[|o]#o)");
|
||||
testing!("f#[|o]#o)");
|
||||
]
|
||||
}
|
||||
"#,
|
||||
|
@ -770,7 +770,7 @@ fn foo() {
|
|||
r#"\
|
||||
fn foo() {
|
||||
if let Some(_) = None {
|
||||
todo!{"f#[|o]#o)"};
|
||||
testing!{"f#[|o]#o)"};
|
||||
}
|
||||
}
|
||||
"#,
|
||||
|
|
|
@ -379,9 +379,9 @@ async fn match_around_closest_ts() -> anyhow::Result<()> {
|
|||
test_with_config(
|
||||
AppBuilder::new().with_file("foo.rs", None),
|
||||
(
|
||||
r#"fn main() {todo!{"f#[|oo]#)"};}"#,
|
||||
r#"fn main() {testing!{"f#[|oo]#)"};}"#,
|
||||
"mam",
|
||||
r#"fn main() {todo!{#[|"foo)"]#};}"#,
|
||||
r#"fn main() {testing!{#[|"foo)"]#};}"#,
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
|
|
@ -4360,3 +4360,13 @@ file-types = [ { glob = "dunst/dunstrc" } ]
|
|||
[[grammar]]
|
||||
name = "dunstrc"
|
||||
source = { git = "https://github.com/rotmh/tree-sitter-dunstrc", rev = "9cb9d5cc51cf5e2a47bb2a0e2f2e519ff11c1431" }
|
||||
|
||||
[[language]]
|
||||
name = "rust-format-args"
|
||||
scope = "source.rust-format-args"
|
||||
file-types = []
|
||||
injection-regex = "rust-format-args"
|
||||
|
||||
[[grammar]]
|
||||
name = "rust-format-args"
|
||||
source = { git = "https://github.com/nik-rev/tree-sitter-rustfmt", rev = "2ca0bdd763d0c9dbb1d0bd14aea7544cbe81309c" }
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
; regular escapes like `\n` are detected using another grammar
|
||||
; Here, we only detect `{{` and `}}` as escapes for `{` and `}`
|
||||
(escaped) @constant.character.escape
|
||||
|
||||
[
|
||||
"#"
|
||||
(type)
|
||||
] @special
|
||||
|
||||
[
|
||||
(sign)
|
||||
(fill)
|
||||
(align)
|
||||
(width)
|
||||
] @operator
|
||||
|
||||
(number) @constant.numeric
|
||||
|
||||
(colon) @punctuation
|
||||
|
||||
(identifier) @variable
|
||||
|
||||
; SCREAMING_CASE is assumed to be constant
|
||||
((identifier) @constant
|
||||
(#match? @constant "^[A-Z_]+$"))
|
||||
|
||||
[
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.special
|
|
@ -97,3 +97,127 @@
|
|||
]
|
||||
)
|
||||
(#set! injection.language "sql"))
|
||||
|
||||
; Special language `tree-sitter-rust-format-args` for Rust macros,
|
||||
; which use `format_args!` under the hood and therefore have
|
||||
; the `format_args!` syntax.
|
||||
;
|
||||
; This language is injected into a hard-coded set of macros.
|
||||
|
||||
; 1st argument is `format_args!`
|
||||
(
|
||||
(macro_invocation
|
||||
macro:
|
||||
[
|
||||
(scoped_identifier
|
||||
name: (_) @_macro_name)
|
||||
(identifier) @_macro_name
|
||||
]
|
||||
(token_tree
|
||||
. (string_literal
|
||||
(string_content) @injection.content
|
||||
)
|
||||
)
|
||||
)
|
||||
(#any-of? @_macro_name
|
||||
; std
|
||||
"print" "println" "eprint" "eprintln"
|
||||
"format" "format_args" "todo" "panic"
|
||||
"unreachable" "unimplemented" "compile_error"
|
||||
; log
|
||||
"crit" "trace" "debug" "info" "warn" "error"
|
||||
; anyhow
|
||||
"anyhow" "bail"
|
||||
; syn
|
||||
"format_ident"
|
||||
; indoc
|
||||
"formatdoc" "printdoc" "eprintdoc" "writedoc"
|
||||
; iced
|
||||
"text"
|
||||
; ratatui
|
||||
"span"
|
||||
; eyre
|
||||
"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
|
||||
)
|
||||
)
|
||||
)
|
||||
(#any-of? @_macro_name
|
||||
; 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
|
||||
)
|
||||
)
|
||||
)
|
||||
(#any-of? @_macro_name
|
||||
; std
|
||||
"assert_eq" "debug_assert_eq" "assert_ne" "debug_assert_ne"
|
||||
)
|
||||
(#set! injection.language "rust-format-args")
|
||||
(#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)
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue