Compare commits

..
2 Commits
Author SHA1 Message Date
danhanson 6105fd9002 replace arrangements with _kwarg_name for simpleTag kwargs 2026-09-07 20:01:51 -05:00
danhanson 0caa8c104b fix include 2026-09-07 19:45:24 -05:00
6 changed files with 56880 additions and 56111 deletions

No files matched your search

+25 -6
View File
@@ -144,11 +144,12 @@ function simpleTag($, tag, args = true, kwargs = true) {
repeat(part(seq($._kwarg_name, $.identifier, "=", $.filtered_value))), repeat(part(seq($._kwarg_name, $.identifier, "=", $.filtered_value))),
); );
} else if (kwargs) { } else if (kwargs) {
parts.unshift($._tag_open);
// a known signature: any of the names it accepts, in any order, none twice // a known signature: any of the names it accepts, in any order, none twice
const named = Object.entries(kwargs).map(([name, value]) => const named = Object.entries(kwargs).map(([name, value]) =>
part(seq(name, "=", value)), part(seq($._kwarg_name, name, "=", value)),
); );
parts.push(optional(arrangements(named))); parts.push(repeat(choice(...named)));
} }
return block(tag, ...parts, optional(asVariable($))); return block(tag, ...parts, optional(asVariable($)));
} }
@@ -527,7 +528,7 @@ const django = grammar({
seq( seq(
$._bt_option, $._bt_option,
choice( choice(
seq(part("with"), $._translate_kwargs), seq(part("with"), $._tag_kwargs),
part("context", $.filtered_value), part("context", $.filtered_value),
part("trimmed"), part("trimmed"),
part("asvar", field("variable", $.identifier)), part("asvar", field("variable", $.identifier)),
@@ -542,8 +543,13 @@ const django = grammar({
), ),
), ),
_translate_body: ($) => repeat1(choice($.content, $.template_variable)), _translate_body: ($) => repeat1(choice($.content, $.template_variable)),
/** The "with" option of blocktranslate takes at least one of these. */ /**
_translate_kwargs: ($) => * The "with" option of blocktranslate and of include takes at least one of
* these. Django lets a name repeat here — token_kwargs builds a dict and
* the later value wins — so unlike a simple_tag's arguments they are not
* guarded.
*/
_tag_kwargs: ($) =>
repeat1( repeat1(
part(seq(field("variable", $.identifier), "=", $.filtered_value)), part(seq(field("variable", $.identifier), "=", $.filtered_value)),
), ),
@@ -674,7 +680,20 @@ const django = grammar({
optional(seq(block("else"), optional($.template))), optional(seq(block("else"), optional($.template))),
block("endifchanged"), block("endifchanged"),
), ),
include: ($) => block("include", part($.filtered_value)), /**
* do_include takes "with" and "only" in either order and refuses one it has
* already been given, and "with" needs at least one argument. It calls
* token_kwargs with support_legacy=False, so "{% include "t" with a as b %}"
* is an error here as it is there, unlike the same clause in "{% with %}".
*/
include: ($) =>
block(
"include",
part($.filtered_value),
optional(
arrangements([seq(part("with"), $._tag_kwargs), part("only")]),
),
),
/** django.templatetags.i18n. The language tag takes the one argument. */ /** django.templatetags.i18n. The language tag takes the one argument. */
language_group: ($) => language_group: ($) =>
seq( seq(
+178 -58
View File
@@ -1980,7 +1980,7 @@
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "_translate_kwargs" "name": "_tag_kwargs"
} }
] ]
}, },
@@ -2125,7 +2125,7 @@
] ]
} }
}, },
"_translate_kwargs": { "_tag_kwargs": {
"type": "REPEAT1", "type": "REPEAT1",
"content": { "content": {
"type": "SEQ", "type": "SEQ",
@@ -2501,6 +2501,10 @@
"value": "csp_nonce_attr" "value": "csp_nonce_attr"
} }
}, },
{
"type": "SYMBOL",
"name": "_tag_open"
},
{ {
"type": "CHOICE", "type": "CHOICE",
"members": [ "members": [
@@ -2523,38 +2527,42 @@
] ]
}, },
{ {
"type": "CHOICE", "type": "REPEAT",
"members": [ "content": {
{ "type": "CHOICE",
"type": "SEQ", "members": [
"members": [ {
{ "type": "SEQ",
"type": "PATTERN", "members": [
"value": "[ \\t\\r\\n]+" {
}, "type": "PATTERN",
{ "value": "[ \\t\\r\\n]+"
"type": "SEQ", },
"members": [ {
{ "type": "SEQ",
"type": "STRING", "members": [
"value": "media" {
}, "type": "SYMBOL",
{ "name": "_kwarg_name"
"type": "STRING", },
"value": "=" {
}, "type": "STRING",
{ "value": "media"
"type": "SYMBOL", },
"name": "filtered_value" {
} "type": "STRING",
] "value": "="
} },
] {
}, "type": "SYMBOL",
{ "name": "filtered_value"
"type": "BLANK" }
} ]
] }
]
}
]
}
}, },
{ {
"type": "CHOICE", "type": "CHOICE",
@@ -4695,6 +4703,134 @@
} }
] ]
}, },
{
"type": "CHOICE",
"members": [
{
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "PATTERN",
"value": "[ \\t\\r\\n]+"
},
{
"type": "STRING",
"value": "with"
}
]
},
{
"type": "SYMBOL",
"name": "_tag_kwargs"
}
]
},
{
"type": "SEQ",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "PATTERN",
"value": "[ \\t\\r\\n]+"
},
{
"type": "STRING",
"value": "with"
}
]
},
{
"type": "SYMBOL",
"name": "_tag_kwargs"
}
]
},
{
"type": "SEQ",
"members": [
{
"type": "PATTERN",
"value": "[ \\t\\r\\n]+"
},
{
"type": "STRING",
"value": "only"
}
]
}
]
},
{
"type": "SEQ",
"members": [
{
"type": "PATTERN",
"value": "[ \\t\\r\\n]+"
},
{
"type": "STRING",
"value": "only"
}
]
},
{
"type": "SEQ",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "PATTERN",
"value": "[ \\t\\r\\n]+"
},
{
"type": "STRING",
"value": "only"
}
]
},
{
"type": "SEQ",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "PATTERN",
"value": "[ \\t\\r\\n]+"
},
{
"type": "STRING",
"value": "with"
}
]
},
{
"type": "SYMBOL",
"name": "_tag_kwargs"
}
]
}
]
}
]
},
{
"type": "BLANK"
}
]
},
{ {
"type": "CHOICE", "type": "CHOICE",
"members": [ "members": [
@@ -7825,25 +7961,12 @@
}, },
"extras": [], "extras": [],
"conflicts": [ "conflicts": [
[ ["template"],
"template" ["predicate"],
], ["library", "load"],
[ ["_translate_option"],
"predicate" ["_filtered_value_spaced"],
], ["_filter_expression_spaced"]
[
"library",
"load"
],
[
"_translate_option"
],
[
"_filtered_value_spaced"
],
[
"_filter_expression_spaced"
]
], ],
"precedences": [], "precedences": [],
"externals": [ "externals": [
@@ -7897,10 +8020,7 @@
} }
], ],
"inline": [], "inline": [],
"supertypes": [ "supertypes": ["template_tag", "template_block_groups"],
"template_tag",
"template_block_groups"
],
"reserved": { "reserved": {
"global": [ "global": [
{ {
@@ -8023,4 +8143,4 @@
} }
] ]
} }
} }
+16 -2
View File
@@ -1377,10 +1377,20 @@
"named": false "named": false
} }
] ]
},
"variable": {
"multiple": true,
"required": false,
"types": [
{
"type": "identifier",
"named": true
}
]
} }
}, },
"children": { "children": {
"multiple": false, "multiple": true,
"required": true, "required": true,
"types": [ "types": [
{ {
@@ -2714,6 +2724,10 @@
"type": "on", "type": "on",
"named": false "named": false
}, },
{
"type": "only",
"named": false
},
{ {
"type": "openblock", "type": "openblock",
"named": false "named": false
@@ -2986,4 +3000,4 @@
"type": "}}", "type": "}}",
"named": false "named": false
} }
] ]
+56427 -56036
View File
File diff suppressed because it is too large. Load diff
+9 -9
View File
@@ -68,14 +68,16 @@ struct Scanner {
/* one bit per option of the blocktranslate tag being read, by the order of /* one bit per option of the blocktranslate tag being read, by the order of
* translate_options. Kept out of the union below, whose members share their * translate_options. Kept out of the union below, whose members share their
* storage with each other. */ * storage with each other. */
uint8_t translate_seen;
/* the "name=value" argument names the tag being read has been given, each /* the "name=value" argument names the tag being read has been given, each
* followed by NAME_SEP. parse_bits refuses a repeated keyword argument, and * followed by NAME_SEP. parse_bits refuses a repeated keyword argument, and
* the set of names is open, so this remembers the names rather than a mask * the set of names is open, so this remembers the names rather than a mask
* of known ones. Kept out of the union, as translate_seen is. */ * of known ones. Kept out of the union, as translate_seen is. */
Name seen_kwargs;
union { union {
Stack stack; struct {
uint8_t translate_seen;
Name seen_kwargs;
Stack stack;
};
char error [ERROR_SIZE]; char error [ERROR_SIZE];
}; };
}; };
@@ -89,14 +91,12 @@ static enum TokenType pop_token_for_kind(TagKind kind) {
} }
static void reset_scanner(struct Scanner *const scanner) { static void reset_scanner(struct Scanner *const scanner) {
scanner->translate_seen = 0;
array_delete(&scanner->seen_kwargs);
if (scanner->has_error) { if (scanner->has_error) {
scanner->error[0] = '\0'; // sets everything to 0 values: 0, '\0', false, null
scanner->has_error = false; memset(&scanner, 0, sizeof(scanner));
// the error shares storage with the stack, which is now meaningless
memset(&scanner->stack, 0, sizeof(scanner->stack));
} else { } else {
scanner->translate_seen = 0;
array_delete(&scanner->seen_kwargs);
for (unsigned i = 0; i < scanner->stack.size; ++i) { for (unsigned i = 0; i < scanner->stack.size; ++i) {
array_delete(&array_get(&scanner->stack, i)->name); array_delete(&array_get(&scanner->stack, i)->name);
} }
+225
View File
@@ -31,3 +31,228 @@ Include With Variable
(variable_attribute (variable_attribute
(identifier))))) (identifier)))))
(content)) (content))
==================
Include With A Filtered Name
==================
{% include "t.html"|upper %}
---
(template
(content)
(include
(filtered_value
(value
(literal
(string)))
(filter_expression
(filter))))
(content))
==================
Include Only
==================
{% include "t.html" only %}
---
(template
(content)
(include
(filtered_value
(value
(literal
(string)))))
(content))
==================
Include With Context
==================
{% include "t.html" with a=1 %}
---
(template
(content)
(include
(filtered_value
(value
(literal
(string))))
variable: (identifier)
(filtered_value
(value
(literal
(number)))))
(content))
==================
Include With Several Bindings
==================
{% include "t.html" with a=1 b=2 %}
---
(template
(content)
(include
(filtered_value
(value
(literal
(string))))
variable: (identifier)
(filtered_value
(value
(literal
(number))))
variable: (identifier)
(filtered_value
(value
(literal
(number)))))
(content))
==================
Include With Context And Only
==================
{% include "t.html" with a=1 only %}
---
(template
(content)
(include
(filtered_value
(value
(literal
(string))))
variable: (identifier)
(filtered_value
(value
(literal
(number)))))
(content))
==================
Include With Only Before With
==================
{% include "t.html" only with a=1 %}
---
(template
(content)
(include
(filtered_value
(value
(literal
(string))))
variable: (identifier)
(filtered_value
(value
(literal
(number)))))
(content))
==================
Include With A Repeated Binding
==================
{% include "t.html" with a=1 a=2 %}
---
(template
(content)
(include
(filtered_value
(value
(literal
(string))))
variable: (identifier)
(filtered_value
(value
(literal
(number))))
variable: (identifier)
(filtered_value
(value
(literal
(number)))))
(content))
==================
Include Without A Name
:error
==================
{% include %}
---
==================
Include With Nothing To Bind
:error
==================
{% include "t.html" with %}
---
==================
Include With With Twice
:error
==================
{% include "t.html" with a=1 with b=2 %}
---
==================
Include With Only Twice
:error
==================
{% include "t.html" only only %}
---
==================
Include With An Unknown Option
:error
==================
{% include "t.html" bogus %}
---
==================
Include With A Legacy Binding
:error
==================
{% include "t.html" with a as b %}
---
==================
Include With A Second Name
:error
==================
{% include "t.html" "u.html" %}
---