Use standard injection.language and injection.content captures (#22268)

Closes #9656. Continuation of #9654, but with the addition of backwards
compatibility for the existing captures.

Release Notes:

- Improved Tree-sitter support with added compatibility for standard
injections captures

---------

Co-authored-by: Finn Evers <finn.evers@outlook.de>
This commit is contained in:
uncenter 2025-01-07 13:17:49 -05:00 committed by GitHub
parent f3e75d8ff6
commit d58f006498
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 181 additions and 159 deletions

View file

@ -5962,8 +5962,8 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
.with_injection_query(
r#"
(script_element
(raw_text) @content
(#set! "language" "javascript"))
(raw_text) @injection.content
(#set! injection.language "javascript"))
"#,
)
.unwrap(),
@ -9068,8 +9068,8 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
.with_injection_query(
r#"
(script_element
(raw_text) @content
(#set! "language" "javascript"))
(raw_text) @injection.content
(#set! injection.language "javascript"))
"#,
)
.unwrap(),

View file

@ -3115,8 +3115,8 @@ fn html_lang() -> Language {
.with_injection_query(
r#"
(script_element
(raw_text) @content
(#set! "language" "javascript"))
(raw_text) @injection.content
(#set! injection.language "javascript"))
"#,
)
.unwrap()
@ -3138,15 +3138,15 @@ fn erb_lang() -> Language {
.with_injection_query(
r#"
(
(code) @content
(#set! "language" "ruby")
(#set! "combined")
(code) @injection.content
(#set! injection.language "ruby")
(#set! injection.combined)
)
(
(content) @content
(#set! "language" "html")
(#set! "combined")
(content) @injection.content
(#set! injection.language "html")
(#set! injection.combined)
)
"#,
)
@ -3278,11 +3278,11 @@ pub fn markdown_lang() -> Language {
r#"
(fenced_code_block
(info_string
(language) @language)
(code_fence_content) @content)
(language) @injection.language)
(code_fence_content) @injection.content)
((inline) @content
(#set! "language" "markdown-inline"))
((inline) @injection.content
(#set! injection.language "markdown-inline"))
"#,
)
.unwrap()

View file

@ -1273,23 +1273,45 @@ impl Language {
.ok_or_else(|| anyhow!("cannot mutate grammar"))?;
let query = Query::new(&grammar.ts_language, source)?;
let mut language_capture_ix = None;
let mut injection_language_capture_ix = None;
let mut content_capture_ix = None;
let mut injection_content_capture_ix = None;
get_capture_indices(
&query,
&mut [
("language", &mut language_capture_ix),
("injection.language", &mut injection_language_capture_ix),
("content", &mut content_capture_ix),
("injection.content", &mut injection_content_capture_ix),
],
);
language_capture_ix = match (language_capture_ix, injection_language_capture_ix) {
(None, Some(ix)) => Some(ix),
(Some(_), Some(_)) => {
return Err(anyhow!(
"both language and injection.language captures are present"
));
}
_ => language_capture_ix,
};
content_capture_ix = match (content_capture_ix, injection_content_capture_ix) {
(None, Some(ix)) => Some(ix),
(Some(_), Some(_)) => {
return Err(anyhow!(
"both content and injection.content captures are present"
));
}
_ => content_capture_ix,
};
let patterns = (0..query.pattern_count())
.map(|ix| {
let mut config = InjectionPatternConfig::default();
for setting in query.property_settings(ix) {
match setting.key.as_ref() {
"language" => {
"language" | "injection.language" => {
config.language.clone_from(&setting.value);
}
"combined" => {
"combined" | "injection.combined" => {
config.combined = true;
}
_ => {}

View file

@ -1193,15 +1193,15 @@ fn erb_lang() -> Language {
.with_injection_query(
r#"
(
(code) @content
(#set! "language" "ruby")
(#set! "combined")
(code) @injection.content
(#set! injection.language "ruby")
(#set! injection.combined)
)
(
(content) @content
(#set! "language" "html")
(#set! "combined")
(content) @injection.content
(#set! injection.language "html")
(#set! injection.combined)
)
"#,
)
@ -1230,8 +1230,8 @@ fn rust_lang() -> Language {
.with_injection_query(
r#"
(macro_invocation
(token_tree) @content
(#set! "language" "rust"))
(token_tree) @injection.content
(#set! injection.language "rust"))
"#,
)
.unwrap()
@ -1277,13 +1277,13 @@ fn heex_lang() -> Language {
(partial_expression_value)
(expression_value)
(ending_expression_value)
] @content)
(#set! language "elixir")
(#set! combined)
] @injection.content)
(#set! injection.language "elixir")
(#set! injection.combined)
)
((expression (expression_value) @content)
(#set! language "elixir"))
((expression (expression_value) @injection.content)
(#set! injection.language "elixir"))
"#,
)
.unwrap()

View file

@ -1,7 +1,7 @@
(preproc_def
value: (preproc_arg) @content
(#set! "language" "c"))
value: (preproc_arg) @injection.content
(#set! injection.language "c"))
(preproc_function_def
value: (preproc_arg) @content
(#set! "language" "c"))
value: (preproc_arg) @injection.content
(#set! injection.language "c"))

View file

@ -1,11 +1,11 @@
(preproc_def
value: (preproc_arg) @content
(#set! "language" "c++"))
value: (preproc_arg) @injection.content
(#set! injection.language "c++"))
(preproc_function_def
value: (preproc_arg) @content
(#set! "language" "c++"))
value: (preproc_arg) @injection.content
(#set! injection.language "c++"))
(raw_string_literal
delimiter: (raw_string_delimiter) @language
(raw_string_content) @content)
delimiter: (raw_string_delimiter) @injection.language
(raw_string_content) @injection.content)

View file

@ -9,5 +9,5 @@
[
(raw_string_literal)
(interpreted_string_literal)
] @content
(#set! "language" "regex")))
] @injection.content
(#set! injection.language "regex")))

View file

@ -1,60 +1,60 @@
(((comment) @_jsdoc_comment
(#match? @_jsdoc_comment "(?s)^/[*][*][^*].*[*]/$")) @content
(#set! "language" "jsdoc"))
(#match? @_jsdoc_comment "(?s)^/[*][*][^*].*[*]/$")) @injection.content
(#set! injection.language "jsdoc"))
((regex) @content
(#set! "language" "regex"))
((regex) @injection.content
(#set! injection.language "regex"))
(call_expression
function: (identifier) @_name (#eq? @_name "css")
arguments: (template_string (string_fragment) @content
(#set! "language" "css"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "css"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "html")
arguments: (template_string) @content
(#set! "language" "html")
arguments: (template_string) @injection.content
(#set! injection.language "html")
)
(call_expression
function: (identifier) @_name (#eq? @_name "js")
arguments: (template_string (string_fragment) @content
(#set! "language" "javascript"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "javascript"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "json")
arguments: (template_string (string_fragment) @content
(#set! "language" "json"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "json"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "sql")
arguments: (template_string (string_fragment) @content
(#set! "language" "sql"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "sql"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "ts")
arguments: (template_string (string_fragment) @content
(#set! "language" "typescript"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "typescript"))
)
(call_expression
function: (identifier) @_name (#match? @_name "^ya?ml$")
arguments: (template_string (string_fragment) @content
(#set! "language" "yaml"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "yaml"))
)
(call_expression
function: (identifier) @_name (#match? @_name "^g(raph)?ql$")
arguments: (template_string (string_fragment) @content
(#set! "language" "graphql"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "graphql"))
)
(call_expression
function: (identifier) @_name (#match? @_name "^g(raph)?ql$")
arguments: (arguments (template_string (string_fragment) @content
(#set! "language" "graphql")))
arguments: (arguments (template_string (string_fragment) @injection.content
(#set! injection.language "graphql")))
)

View file

@ -1,14 +1,14 @@
(fenced_code_block
(info_string
(language) @language)
(code_fence_content) @content)
(language) @injection.language)
(code_fence_content) @injection.content)
((inline) @content
(#set! "language" "markdown-inline"))
((inline) @injection.content
(#set! injection.language "markdown-inline"))
((html_block) @content
(#set! "language" "html"))
((html_block) @injection.content
(#set! injection.language "html"))
((minus_metadata) @content (#set! "language" "yaml"))
((minus_metadata) @injection.content (#set! injection.language "yaml"))
((plus_metadata) @content (#set! "language" "toml"))
((plus_metadata) @injection.content (#set! injection.language "toml"))

View file

@ -1,7 +1,7 @@
(macro_invocation
(token_tree) @content
(#set! "language" "rust"))
(token_tree) @injection.content
(#set! injection.language "rust"))
(macro_rule
(token_tree) @content
(#set! "language" "rust"))
(token_tree) @injection.content
(#set! injection.language "rust"))

View file

@ -1,60 +1,60 @@
(((comment) @_jsdoc_comment
(#match? @_jsdoc_comment "(?s)^/[*][*][^*].*[*]/$")) @content
(#set! "language" "jsdoc"))
(#match? @_jsdoc_comment "(?s)^/[*][*][^*].*[*]/$")) @injection.content
(#set! injection.language "jsdoc"))
((regex) @content
(#set! "language" "regex"))
((regex) @injection.content
(#set! injection.language "regex"))
(call_expression
function: (identifier) @_name (#eq? @_name "css")
arguments: (template_string (string_fragment) @content
(#set! "language" "css"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "css"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "html")
arguments: (template_string (string_fragment) @content
(#set! "language" "html"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "html"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "js")
arguments: (template_string (string_fragment) @content
(#set! "language" "javascript"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "javascript"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "json")
arguments: (template_string (string_fragment) @content
(#set! "language" "json"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "json"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "sql")
arguments: (template_string (string_fragment) @content
(#set! "language" "sql"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "sql"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "ts")
arguments: (template_string (string_fragment) @content
(#set! "language" "typescript"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "typescript"))
)
(call_expression
function: (identifier) @_name (#match? @_name "^ya?ml$")
arguments: (template_string (string_fragment) @content
(#set! "language" "yaml"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "yaml"))
)
(call_expression
function: (identifier) @_name (#match? @_name "^g(raph)?ql$")
arguments: (template_string (string_fragment) @content
(#set! "language" "graphql"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "graphql"))
)
(call_expression
function: (identifier) @_name (#match? @_name "^g(raph)?ql$")
arguments: (arguments (template_string (string_fragment) @content
(#set! "language" "graphql")))
arguments: (arguments (template_string (string_fragment) @injection.content
(#set! injection.language "graphql")))
)

View file

@ -1,64 +1,64 @@
(((comment) @_jsdoc_comment
(#match? @_jsdoc_comment "(?s)^/[*][*][^*].*[*]/$")) @content
(#set! "language" "jsdoc"))
(#match? @_jsdoc_comment "(?s)^/[*][*][^*].*[*]/$")) @injection.content
(#set! injection.language "jsdoc"))
(((comment) @reference
(#match? @reference "^///\\s+<reference\\s+types=\"\\S+\"\\s*/>\\s*$")) @content
(#set! "language" "html"))
(#match? @reference "^///\\s+<reference\\s+types=\"\\S+\"\\s*/>\\s*$")) @injection.content
(#set! injection.language "html"))
((regex) @content
(#set! "language" "regex"))
((regex) @injection.content
(#set! injection.language "regex"))
(call_expression
function: (identifier) @_name (#eq? @_name "css")
arguments: (template_string (string_fragment) @content
(#set! "language" "css"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "css"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "html")
arguments: (template_string) @content
(#set! "language" "html")
arguments: (template_string) @injection.content
(#set! injection.language "html")
)
(call_expression
function: (identifier) @_name (#eq? @_name "js")
arguments: (template_string (string_fragment) @content
(#set! "language" "javascript"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "javascript"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "json")
arguments: (template_string (string_fragment) @content
(#set! "language" "json"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "json"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "sql")
arguments: (template_string (string_fragment) @content
(#set! "language" "sql"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "sql"))
)
(call_expression
function: (identifier) @_name (#eq? @_name "ts")
arguments: (template_string (string_fragment) @content
(#set! "language" "typescript"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "typescript"))
)
(call_expression
function: (identifier) @_name (#match? @_name "^ya?ml$")
arguments: (template_string (string_fragment) @content
(#set! "language" "yaml"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "yaml"))
)
(call_expression
function: (identifier) @_name (#match? @_name "^g(raph)?ql$")
arguments: (template_string (string_fragment) @content
(#set! "language" "graphql"))
arguments: (template_string (string_fragment) @injection.content
(#set! injection.language "graphql"))
)
(call_expression
function: (identifier) @_name (#match? @_name "^g(raph)?ql$")
arguments: (arguments (template_string (string_fragment) @content
(#set! "language" "graphql")))
arguments: (arguments (template_string (string_fragment) @injection.content
(#set! injection.language "graphql")))
)

View file

@ -6043,8 +6043,8 @@ mod tests {
.with_injection_query(
r#"
(macro_invocation
(token_tree) @content
(#set! "language" "rust"))
(token_tree) @injection.content
(#set! injection.language "rust"))
"#,
)
.unwrap()

View file

@ -203,19 +203,19 @@ Here's an example from an `injections.scm` file for Markdown:
```scheme
(fenced_code_block
(info_string
(language) @language)
(code_fence_content) @content)
(language) @injection.language)
(code_fence_content) @injection.content)
((inline) @content
(#set! "language" "markdown-inline"))
(#set! injection.language "markdown-inline"))
```
This query identifies fenced code blocks, capturing the language specified in the info string and the content within the block. It also captures inline content and sets its language to "markdown-inline".
| Capture | Description |
| --------- | ---------------------------------------------------------- |
| @language | Captures the language identifier for a code block |
| @content | Captures the content to be treated as a different language |
| ------------------- | ---------------------------------------------------------- |
| @injection.language | Captures the language identifier for a code block |
| @injection.content | Captures the content to be treated as a different language |
Note that we couldn't use JSON as an example here because it doesn't support language injections.

View file

@ -2,6 +2,6 @@
((sigil
(sigil_name) @_sigil_name
(quoted_content) @content)
(quoted_content) @injection.content)
(#eq? @_sigil_name "H")
(#set! language "heex"))
(#set! injection.language "heex"))

View file

@ -4,10 +4,10 @@
(partial_expression_value)
(expression_value)
(ending_expression_value)
] @content)
(#set! language "elixir")
(#set! combined)
] @injection.content)
(#set! injection.language "elixir")
(#set! injection.combined)
)
((expression (expression_value) @content)
(#set! language "elixir"))
((expression (expression_value) @injection.content)
(#set! injection.language "elixir"))

View file

@ -1,7 +1,7 @@
(script_element
(raw_text) @content
(#set! "language" "javascript"))
(raw_text) @injection.content
(#set! injection.language "javascript"))
(style_element
(raw_text) @content
(#set! "language" "css"))
(raw_text) @injection.content
(#set! injection.language "css"))

View file

@ -1,9 +1,9 @@
((text) @content
(#set! "language" "html")
(#set! "combined"))
((text) @injection.content
(#set! injection.language "html")
(#set! injection.combined))
((comment) @content
(#match? @content "^/\\*\\*[^*]")
(#set! "language" "phpdoc"))
((comment) @injection.content
(#match? @injection.content "^/\\*\\*[^*]")
(#set! injection.language "phpdoc"))
((heredoc_body) (heredoc_end) @language) @content
((heredoc_body) (heredoc_end) @injection.language) @injection.content

View file

@ -1,6 +1,6 @@
; https://github.com/nvim-treesitter/nvim-treesitter/blob/ce4adf11cfe36fc5b0e5bcdce0c7c6e8fbc9798a/queries/hcl/injections.scm
(heredoc_template
(template_literal) @content
(heredoc_identifier) @language
(#downcase! @language))
(template_literal) @injection.content
(heredoc_identifier) @injection.language
(#downcase! @injection.language))

View file

@ -1,9 +1,9 @@
; https://github.com/nvim-treesitter/nvim-treesitter/blob/ce4adf11cfe36fc5b0e5bcdce0c7c6e8fbc9798a/queries/hcl/injections.scm
(heredoc_template
(template_literal) @content
(heredoc_identifier) @language
(#downcase! @language))
(template_literal) @injection.content
(heredoc_identifier) @injection.language
(#downcase! @injection.language))
; https://github.com/nvim-treesitter/nvim-treesitter/blob/ce4adf11cfe36fc5b0e5bcdce0c7c6e8fbc9798a/queries/terraform/injections.scm
; inherits: hcl

View file

@ -1,9 +1,9 @@
; https://github.com/nvim-treesitter/nvim-treesitter/blob/ce4adf11cfe36fc5b0e5bcdce0c7c6e8fbc9798a/queries/hcl/injections.scm
(heredoc_template
(template_literal) @content
(heredoc_identifier) @language
(#downcase! @language))
(template_literal) @injection.content
(heredoc_identifier) @injection.language
(#downcase! @injection.language))
; https://github.com/nvim-treesitter/nvim-treesitter/blob/ce4adf11cfe36fc5b0e5bcdce0c7c6e8fbc9798a/queries/terraform/injections.scm
; inherits: hcl