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))),
);
} else if (kwargs) {
parts.unshift($._tag_open);
// a known signature: any of the names it accepts, in any order, none twice
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($)));
}
@@ -527,7 +528,7 @@ const django = grammar({
seq(
$._bt_option,
choice(
seq(part("with"), $._translate_kwargs),
seq(part("with"), $._tag_kwargs),
part("context", $.filtered_value),
part("trimmed"),
part("asvar", field("variable", $.identifier)),
@@ -542,8 +543,13 @@ const django = grammar({
),
),
_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(
part(seq(field("variable", $.identifier), "=", $.filtered_value)),
),
@@ -674,7 +680,20 @@ const django = grammar({
optional(seq(block("else"), optional($.template))),
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. */
language_group: ($) =>
seq(
+178 -58
View File
@@ -1980,7 +1980,7 @@
},
{
"type": "SYMBOL",
"name": "_translate_kwargs"
"name": "_tag_kwargs"
}
]
},
@@ -2125,7 +2125,7 @@
]
}
},
"_translate_kwargs": {
"_tag_kwargs": {
"type": "REPEAT1",
"content": {
"type": "SEQ",
@@ -2501,6 +2501,10 @@
"value": "csp_nonce_attr"
}
},
{
"type": "SYMBOL",
"name": "_tag_open"
},
{
"type": "CHOICE",
"members": [
@@ -2523,38 +2527,42 @@
]
},
{
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "PATTERN",
"value": "[ \\t\\r\\n]+"
},
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "media"
},
{
"type": "STRING",
"value": "="
},
{
"type": "SYMBOL",
"name": "filtered_value"
}
]
}
]
},
{
"type": "BLANK"
}
]
"type": "REPEAT",
"content": {
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "PATTERN",
"value": "[ \\t\\r\\n]+"
},
{
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_kwarg_name"
},
{
"type": "STRING",
"value": "media"
},
{
"type": "STRING",
"value": "="
},
{
"type": "SYMBOL",
"name": "filtered_value"
}
]
}
]
}
]
}
},
{
"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",
"members": [
@@ -7825,25 +7961,12 @@
},
"extras": [],
"conflicts": [
[
"template"
],
[
"predicate"
],
[
"library",
"load"
],
[
"_translate_option"
],
[
"_filtered_value_spaced"
],
[
"_filter_expression_spaced"
]
["template"],
["predicate"],
["library", "load"],
["_translate_option"],
["_filtered_value_spaced"],
["_filter_expression_spaced"]
],
"precedences": [],
"externals": [
@@ -7897,10 +8020,7 @@
}
],
"inline": [],
"supertypes": [
"template_tag",
"template_block_groups"
],
"supertypes": ["template_tag", "template_block_groups"],
"reserved": {
"global": [
{
@@ -8023,4 +8143,4 @@
}
]
}
}
}
+16 -2
View File
@@ -1377,10 +1377,20 @@
"named": false
}
]
},
"variable": {
"multiple": true,
"required": false,
"types": [
{
"type": "identifier",
"named": true
}
]
}
},
"children": {
"multiple": false,
"multiple": true,
"required": true,
"types": [
{
@@ -2714,6 +2724,10 @@
"type": "on",
"named": false
},
{
"type": "only",
"named": false
},
{
"type": "openblock",
"named": false
@@ -2986,4 +3000,4 @@
"type": "}}",
"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
* translate_options. Kept out of the union below, whose members share their
* storage with each other. */
uint8_t translate_seen;
/* 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
* 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. */
Name seen_kwargs;
union {
Stack stack;
struct {
uint8_t translate_seen;
Name seen_kwargs;
Stack stack;
};
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) {
scanner->translate_seen = 0;
array_delete(&scanner->seen_kwargs);
if (scanner->has_error) {
scanner->error[0] = '\0';
scanner->has_error = false;
// the error shares storage with the stack, which is now meaningless
memset(&scanner->stack, 0, sizeof(scanner->stack));
// sets everything to 0 values: 0, '\0', false, null
memset(&scanner, 0, sizeof(scanner));
} else {
scanner->translate_seen = 0;
array_delete(&scanner->seen_kwargs);
for (unsigned i = 0; i < scanner->stack.size; ++i) {
array_delete(&array_get(&scanner->stack, i)->name);
}
+225
View File
@@ -31,3 +31,228 @@ Include With Variable
(variable_attribute
(identifier)))))
(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" %}
---