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 | ✓ | | | |
|
| rst | ✓ | | | |
|
||||||
| ruby | ✓ | ✓ | ✓ | `ruby-lsp`, `solargraph` |
|
| ruby | ✓ | ✓ | ✓ | `ruby-lsp`, `solargraph` |
|
||||||
| rust | ✓ | ✓ | ✓ | `rust-analyzer` |
|
| rust | ✓ | ✓ | ✓ | `rust-analyzer` |
|
||||||
|
| rust-format-args | ✓ | | | |
|
||||||
| sage | ✓ | ✓ | | |
|
| sage | ✓ | ✓ | | |
|
||||||
| scala | ✓ | ✓ | ✓ | `metals` |
|
| scala | ✓ | ✓ | ✓ | `metals` |
|
||||||
| scheme | ✓ | | ✓ | |
|
| scheme | ✓ | | ✓ | |
|
||||||
|
|
|
@ -734,7 +734,7 @@ async fn surround_replace_ts() -> anyhow::Result<()> {
|
||||||
const INPUT: &str = r#"\
|
const INPUT: &str = r#"\
|
||||||
fn foo() {
|
fn foo() {
|
||||||
if let Some(_) = None {
|
if let Some(_) = None {
|
||||||
todo!("f#[|o]#o)");
|
testing!("f#[|o]#o)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
@ -744,7 +744,7 @@ fn foo() {
|
||||||
r#"\
|
r#"\
|
||||||
fn foo() {
|
fn foo() {
|
||||||
if let Some(_) = None {
|
if let Some(_) = None {
|
||||||
todo!('f#[|o]#o)');
|
testing!('f#[|o]#o)');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
@ -757,7 +757,7 @@ fn foo() {
|
||||||
r#"\
|
r#"\
|
||||||
fn foo() {
|
fn foo() {
|
||||||
if let Some(_) = None [
|
if let Some(_) = None [
|
||||||
todo!("f#[|o]#o)");
|
testing!("f#[|o]#o)");
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
@ -770,7 +770,7 @@ fn foo() {
|
||||||
r#"\
|
r#"\
|
||||||
fn foo() {
|
fn foo() {
|
||||||
if let Some(_) = None {
|
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(
|
test_with_config(
|
||||||
AppBuilder::new().with_file("foo.rs", None),
|
AppBuilder::new().with_file("foo.rs", None),
|
||||||
(
|
(
|
||||||
r#"fn main() {todo!{"f#[|oo]#)"};}"#,
|
r#"fn main() {testing!{"f#[|oo]#)"};}"#,
|
||||||
"mam",
|
"mam",
|
||||||
r#"fn main() {todo!{#[|"foo)"]#};}"#,
|
r#"fn main() {testing!{#[|"foo)"]#};}"#,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -4360,3 +4360,13 @@ file-types = [ { glob = "dunst/dunstrc" } ]
|
||||||
[[grammar]]
|
[[grammar]]
|
||||||
name = "dunstrc"
|
name = "dunstrc"
|
||||||
source = { git = "https://github.com/rotmh/tree-sitter-dunstrc", rev = "9cb9d5cc51cf5e2a47bb2a0e2f2e519ff11c1431" }
|
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"))
|
(#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