diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 1447b66da..965723772 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -199,6 +199,7 @@ | rst | ✓ | | | | | ruby | ✓ | ✓ | ✓ | `ruby-lsp`, `solargraph` | | rust | ✓ | ✓ | ✓ | `rust-analyzer` | +| rustfmt | ✓ | | | | | sage | ✓ | ✓ | | | | scala | ✓ | ✓ | ✓ | `metals` | | scheme | ✓ | | ✓ | | diff --git a/languages.toml b/languages.toml index 7cc373003..e286c4502 100644 --- a/languages.toml +++ b/languages.toml @@ -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 = "rustfmt" +scope = "source.rustfmt" +file-types = [] +injection-regex = "rustfmt" + +[[grammar]] +name = "rustfmt" +source = { git = "https://github.com/nik-rev/tree-sitter-rustfmt", rev = "d8e517d9aca1905261a378b25688e4500ae4dd22" } diff --git a/runtime/queries/rust/injections.scm b/runtime/queries/rust/injections.scm index 42ca12b5b..2c9ec7b1d 100644 --- a/runtime/queries/rust/injections.scm +++ b/runtime/queries/rust/injections.scm @@ -97,3 +97,104 @@ ] ) (#set! injection.language "sql")) + +; std::fmt + +; For these, only the first argument is format_args! +((macro_invocation + macro: + [ + (scoped_identifier + name: (_) @_macro_name) + (identifier) @_macro_name + ] + (token_tree . (string_literal) @injection.content)) + (#any-of? @_macro_name + ; std + "format" + "print" + "println" + "eprint" + "eprintln" + "format_args" + "todo" + "unreachable" + "unimplemented" + "compile_error" + ; log + "crit" + "trace" + "debug" + "info" + "warn" + "error" + ; anyhow + "anyhow" + "bail" + ; syn + "format_ident" + ; indoc + "formatdoc" + "printdoc" + "eprintdoc" + "writedoc") + (#set! injection.language "rustfmt") + (#set! injection.include-children)) + +; For these, only the second argument is format_args! +((macro_invocation + macro: + [ + (scoped_identifier + name: (_) @_macro_name) + (identifier) @_macro_name + ] + (token_tree . (_) . (string_literal) @injection.content)) + (#any-of? @_macro_name + ; std + "write" + "writeln" + "assert" + "debug_assert") + (#set! injection.language "rustfmt") + (#set! injection.include-children)) + +; For these, only the third argument is format_args! +((macro_invocation + macro: + [ + (scoped_identifier + name: (_) @_macro_name) + (identifier) @_macro_name + ] + (token_tree . (_) . (_) . (string_literal) @injection.content)) + (#any-of? @_macro_name + ; std + "assert_eq" + "debug_assert_eq" + "assert_ne" + "debug_assert_ne") + (#set! injection.language "rustfmt") + (#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 "rustfmt") + (#set! injection.include-children)) diff --git a/runtime/queries/rustfmt/highlights.scm b/runtime/queries/rustfmt/highlights.scm new file mode 100644 index 000000000..a4a99f999 --- /dev/null +++ b/runtime/queries/rustfmt/highlights.scm @@ -0,0 +1,29 @@ +; (format_string) @string + +(escaped) @constant.character.escape + +[ + "#" + (type) +] @special + +[ + (sign) + (fill) + (align) + (width) +] @operator + +(number) @constant.numeric + +(colon) @punctuation + +(identifier) @variable + +((identifier) @constant + (#match? @constant "^[A-Z_]+$")) + +[ + "{" + "}" +] @punctuation.special