Add @if/@for support in JSON/JS/CSS embedded blocks

This commit is contained in:
Michael Netshipise 2026-01-19 08:41:25 +02:00
parent b6a168a2b6
commit 7c37d0b3bc
5 changed files with 30859 additions and 19965 deletions

View File

@ -223,7 +223,7 @@ module.exports = grammar({
), ),
expression_path: ($) => expression_path: ($) =>
seq( prec.left(seq(
$.identifier, $.identifier,
repeat( repeat(
choice( choice(
@ -232,7 +232,7 @@ module.exports = grammar({
seq("(", optional($.argument_list), ")"), seq("(", optional($.argument_list), ")"),
), ),
), ),
), )),
// Control flow // Control flow
template_control_flow: ($) => template_control_flow: ($) =>
@ -590,13 +590,233 @@ module.exports = grammar({
// Embedded language blocks: @```lang ... ```@ // Embedded language blocks: @```lang ... ```@
embedded_language: ($) => embedded_language: ($) =>
seq("@```", $.language_name, optional($.embedded_content), "```@"), choice(
// JSON/Alpine blocks with structured parsing
seq("@```", choice("json", "alpine"), optional($.json_content), "```@"),
// JS blocks with structured parsing
seq("@```", choice("js", "javascript"), optional($.js_content), "```@"),
// CSS blocks with structured parsing
seq("@```", choice("css", "style"), optional($.css_content), "```@"),
// HTML blocks (less common, use simple content)
seq("@```", "html", optional($.embedded_content_simple), "```@"),
),
embedded_content: ($) => /([^`]|`[^`]|``[^`])*/, // Simple embedded content fallback
embedded_content_simple: ($) => /([^`]|`[^`]|``[^`])*/,
language_name: ($) => language_name: ($) =>
choice("html", "css", "js", "javascript", "json", "alpine", "style"), choice("html", "css", "js", "javascript", "json", "alpine", "style"),
// JSON content with template support
json_content: ($) =>
choice(
$.json_object,
$.json_array,
),
json_object: ($) =>
seq(
"{",
optional($.json_object_content),
"}",
),
json_object_content: ($) =>
seq(
$.json_member_or_control,
repeat(seq(optional(","), $.json_member_or_control)),
optional(","),
),
json_member_or_control: ($) =>
choice(
$.json_member,
$.json_control_flow,
),
json_member: ($) =>
seq(
choice($.json_key, $.template_expression),
":",
$.json_value,
),
json_key: ($) =>
choice(
$.string_literal,
$.identifier, // Allow unquoted keys for JS object shorthand
),
json_value: ($) =>
choice(
$.json_object,
$.json_array,
$.string_literal,
$.number_literal,
$.boolean_literal,
"null",
$.template_expression,
$.json_method, // For Alpine.js method shorthand
),
json_array: ($) =>
seq(
"[",
optional($.json_array_content),
"]",
),
json_array_content: ($) =>
seq(
$.json_array_element,
repeat(seq(optional(","), $.json_array_element)),
optional(","),
),
json_array_element: ($) =>
choice(
$.json_value,
$.json_control_flow,
),
// Control flow in JSON context
json_control_flow: ($) =>
choice(
$.json_if_statement,
$.json_for_loop,
),
json_if_statement: ($) =>
seq(
"@if",
$.expression,
"{",
optional($.json_if_body),
"}",
optional(seq("else", "{", optional($.json_if_body), "}")),
),
// Body of @if in JSON can contain members or array elements
json_if_body: ($) =>
seq(
$.json_if_element,
repeat(seq(optional(","), $.json_if_element)),
optional(","),
),
json_if_element: ($) =>
choice(
$.json_member,
$.json_value,
$.json_control_flow,
),
json_for_loop: ($) =>
seq(
"@for",
$.simple_pattern,
"in",
$.expression,
"{",
optional($.json_if_body),
"}",
),
// Alpine.js method shorthand: toggle() { this.open = !this.open; }
json_method: ($) =>
seq(
$.identifier,
"(",
optional($.json_method_params),
")",
"{",
optional($.js_content),
"}",
),
json_method_params: ($) =>
seq($.identifier, repeat(seq(",", $.identifier))),
// JavaScript content with template support
js_content: ($) => repeat1($.js_element),
js_element: ($) =>
choice(
$.js_control_flow,
$.template_expression,
$.js_code,
),
js_control_flow: ($) =>
choice(
$.js_if_statement,
$.js_for_loop,
),
js_if_statement: ($) =>
seq(
"@if",
$.expression,
"{",
optional($.js_content),
"}",
optional(seq("else", "{", optional($.js_content), "}")),
),
js_for_loop: ($) =>
seq(
"@for",
$.simple_pattern,
"in",
$.expression,
"{",
optional($.js_content),
"}",
),
// Raw JS code (anything that's not @expression or @control_flow)
js_code: ($) => token(prec(-1, /[^@`]+/)),
// CSS content with template support
css_content: ($) => repeat1($.css_element),
css_element: ($) =>
choice(
$.css_control_flow,
$.template_expression,
$.css_code,
),
css_control_flow: ($) =>
choice(
$.css_if_statement,
$.css_for_loop,
),
css_if_statement: ($) =>
seq(
"@if",
$.expression,
"{",
optional($.css_content),
"}",
optional(seq("else", "{", optional($.css_content), "}")),
),
css_for_loop: ($) =>
seq(
"@for",
$.simple_pattern,
"in",
$.expression,
"{",
optional($.css_content),
"}",
),
// Raw CSS code
css_code: ($) => token(prec(-1, /[^@`]+/)),
// Escape sequence for literal @ // Escape sequence for literal @
escape_at: ($) => "@@", escape_at: ($) => "@@",

View File

@ -97,8 +97,14 @@
(escape_sequence) @escape (escape_sequence) @escape
(escape_at) @escape (escape_at) @escape
; Embedded ; Embedded language names
(language_name) @label "json" @label
"alpine" @label
"js" @label
"javascript" @label
"css" @label
"style" @label
"html" @label
; Variables (fallback) ; Variables (fallback)
(identifier) @variable (identifier) @variable

File diff suppressed because it is too large Load Diff

View File

@ -399,6 +399,110 @@
] ]
} }
}, },
{
"type": "css_code",
"named": true,
"fields": {}
},
{
"type": "css_content",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "css_element",
"named": true
}
]
}
},
{
"type": "css_control_flow",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "css_for_loop",
"named": true
},
{
"type": "css_if_statement",
"named": true
}
]
}
},
{
"type": "css_element",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "css_code",
"named": true
},
{
"type": "css_control_flow",
"named": true
},
{
"type": "template_expression",
"named": true
}
]
}
},
{
"type": "css_for_loop",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "css_content",
"named": true
},
{
"type": "expression",
"named": true
},
{
"type": "simple_pattern",
"named": true
}
]
}
},
{
"type": "css_if_statement",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "css_content",
"named": true
},
{
"type": "expression",
"named": true
}
]
}
},
{ {
"type": "default_value", "type": "default_value",
"named": true, "named": true,
@ -457,15 +561,23 @@
"named": true, "named": true,
"fields": {}, "fields": {},
"children": { "children": {
"multiple": true, "multiple": false,
"required": true, "required": false,
"types": [ "types": [
{ {
"type": "embedded_content", "type": "css_content",
"named": true "named": true
}, },
{ {
"type": "language_name", "type": "embedded_content_simple",
"named": true
},
{
"type": "js_content",
"named": true
},
{
"type": "json_content",
"named": true "named": true
} }
] ]
@ -902,10 +1014,444 @@
"fields": {} "fields": {}
}, },
{ {
"type": "language_name", "type": "js_code",
"named": true, "named": true,
"fields": {} "fields": {}
}, },
{
"type": "js_content",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "js_element",
"named": true
}
]
}
},
{
"type": "js_control_flow",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "js_for_loop",
"named": true
},
{
"type": "js_if_statement",
"named": true
}
]
}
},
{
"type": "js_element",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "js_code",
"named": true
},
{
"type": "js_control_flow",
"named": true
},
{
"type": "template_expression",
"named": true
}
]
}
},
{
"type": "js_for_loop",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "expression",
"named": true
},
{
"type": "js_content",
"named": true
},
{
"type": "simple_pattern",
"named": true
}
]
}
},
{
"type": "js_if_statement",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "expression",
"named": true
},
{
"type": "js_content",
"named": true
}
]
}
},
{
"type": "json_array",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": false,
"types": [
{
"type": "json_array_content",
"named": true
}
]
}
},
{
"type": "json_array_content",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "json_array_element",
"named": true
}
]
}
},
{
"type": "json_array_element",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "json_control_flow",
"named": true
},
{
"type": "json_value",
"named": true
}
]
}
},
{
"type": "json_content",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "json_array",
"named": true
},
{
"type": "json_object",
"named": true
}
]
}
},
{
"type": "json_control_flow",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "json_for_loop",
"named": true
},
{
"type": "json_if_statement",
"named": true
}
]
}
},
{
"type": "json_for_loop",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "expression",
"named": true
},
{
"type": "json_if_body",
"named": true
},
{
"type": "simple_pattern",
"named": true
}
]
}
},
{
"type": "json_if_body",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "json_if_element",
"named": true
}
]
}
},
{
"type": "json_if_element",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "json_control_flow",
"named": true
},
{
"type": "json_member",
"named": true
},
{
"type": "json_value",
"named": true
}
]
}
},
{
"type": "json_if_statement",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "expression",
"named": true
},
{
"type": "json_if_body",
"named": true
}
]
}
},
{
"type": "json_key",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "identifier",
"named": true
},
{
"type": "string_literal",
"named": true
}
]
}
},
{
"type": "json_member",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "json_key",
"named": true
},
{
"type": "json_value",
"named": true
},
{
"type": "template_expression",
"named": true
}
]
}
},
{
"type": "json_member_or_control",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": true,
"types": [
{
"type": "json_control_flow",
"named": true
},
{
"type": "json_member",
"named": true
}
]
}
},
{
"type": "json_method",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "identifier",
"named": true
},
{
"type": "js_content",
"named": true
},
{
"type": "json_method_params",
"named": true
}
]
}
},
{
"type": "json_method_params",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "identifier",
"named": true
}
]
}
},
{
"type": "json_object",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": false,
"types": [
{
"type": "json_object_content",
"named": true
}
]
}
},
{
"type": "json_object_content",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "json_member_or_control",
"named": true
}
]
}
},
{
"type": "json_value",
"named": true,
"fields": {},
"children": {
"multiple": false,
"required": false,
"types": [
{
"type": "boolean_literal",
"named": true
},
{
"type": "json_array",
"named": true
},
{
"type": "json_method",
"named": true
},
{
"type": "json_object",
"named": true
},
{
"type": "number_literal",
"named": true
},
{
"type": "string_literal",
"named": true
},
{
"type": "template_expression",
"named": true
}
]
}
},
{ {
"type": "let_statement", "type": "let_statement",
"named": true, "named": true,
@ -2013,7 +2559,7 @@
"named": false "named": false
}, },
{ {
"type": "embedded_content", "type": "embedded_content_simple",
"named": true "named": true
}, },
{ {
@ -2104,6 +2650,10 @@
"type": "mut", "type": "mut",
"named": false "named": false
}, },
{
"type": "null",
"named": false
},
{ {
"type": "safe", "type": "safe",
"named": false "named": false

48898
src/parser.c

File diff suppressed because it is too large Load Diff