Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccb80972b6 | ||
|
|
fa0d880e68 | ||
|
|
831acd3516 | ||
|
|
f17fdd2d74 |
No files matched your search
@@ -197,13 +197,46 @@ group can hold it — `{% endif %}` with nothing open, `{% else %}` in a `{% for
|
||||
and error recovery placed it badly: every tag starts with `{%` and the read token, which the parser has
|
||||
already accepted when the name turns out to be wrong, so recovery rewinds to just after them and folds the
|
||||
stray tag into whichever tag comes next, which then holds the `ERROR`. `unexpected_tag` takes any of
|
||||
`NAMES_INSIDE_A_TAG_GROUP` with loose arguments, so there is nothing to recover; `prec.dynamic(-1)` keeps it
|
||||
`NAMES_INSIDE_A_TAG_GROUP` with loose arguments, so there is nothing to recover; `prec.dynamic(-2)`, or `-4`, keeps it
|
||||
from displacing a tag the group does take, and `queries/errors.scm` reports it as
|
||||
`@error.unexpected_tag`. It added 31 states and no `conflicts` entry.
|
||||
|
||||
**Words a tag does not take are an `unexpected_argument` inside it.** Every close utility accepts one
|
||||
before its scanner token, so `{% else x %}` stays the `else` of its group, `{% endif junk %}` still closes
|
||||
its `if` (it used to leave the whole file an `ERROR`), and `{% static "a.css" "b.css" %}` is a `static_tag`.
|
||||
The node is a run of hidden `_unexpected_word`s, and three details make it work:
|
||||
|
||||
- **Lexical precedence -1.** A loose word is `token(prec(-1, /[^\s%]+/))`. Tree-sitter weighs lexical
|
||||
precedence before match length, so a token an argument really takes wins wherever one is valid
|
||||
(`b|upper` stays a filtered value) and a loose word only matches where nothing else can.
|
||||
- **Identifiers are words too.** Wherever a keyword is valid — after `{% for a in b`, where `reversed` may
|
||||
follow, or after `as`, `only`, `noop`, `silent` — tree-sitter lexes every word as an `identifier` to
|
||||
check for the keyword, and the loose-word token never gets a turn. So a word may also be an
|
||||
`identifier`, aliased to an anonymous `unexpected_word` so `queries/locals.scm` does not count it as a
|
||||
reference, with whatever touches it (`a=2`, `x.y|upper`, `"z"|lower:"q"`) as an immediate tail.
|
||||
- **-1 of dynamic precedence per word**, not per node, so a reading that flags more words than it must
|
||||
always loses: `{% cycle a b as x silent zqx %}` flags `zqx`, not everything after `a`. `unexpected_tag`
|
||||
costs -2 or -4 so that a misused tag its group does take beats reading it as a stray.
|
||||
|
||||
A tag with a known keyword signature (`simpleTag` given names, so far only `csp_nonce_attr`) needs one more
|
||||
piece: the scanner's `_kwarg_name` accepts any name followed by `=` and commits the parser to a keyword
|
||||
argument before the name is checked, so `{% csp_nonce_attr other="b" %}` would be an `ERROR`. `simpleTag`
|
||||
offers `_unexpected_keyword` — an identifier and its tail, aliased to `unexpected_argument` — beside the
|
||||
names it knows.
|
||||
|
||||
A separator after any argument can now also start stray words, so this needs `conflicts` entries for
|
||||
`unexpected_argument`, `predicate`, `_translate_option`, and `_unexpected_word` with `variable_attribute`,
|
||||
`resetcycle_tag` and `library`. It took the table from 3,363 states to 4,913 and the Wasm build from 251KB to
|
||||
323KB. Two consequences: a repeat the scanner refuses — `{% with a=1 a=2 %}`, `{% blocktrans trimmed trimmed %}`
|
||||
— is reported as an unexpected argument rather than an `ERROR`; and a stray **reserved** word is still an
|
||||
`ERROR`, since `not`, `if`, `in`, `is`, `as`, `for` and `from` can never be an identifier:
|
||||
`{% trans "hi" as x as y %}` errors on the second `as`. A tag missing a required argument, such as
|
||||
`{% with a = 1 %}`, is an `ERROR` too; `unexpected_argument` only takes words after a tag that is otherwise
|
||||
complete.
|
||||
|
||||
Where several tags share a name the group holds only once — `{% if %}{% else %}{% else %}{% else %}` — reading
|
||||
any of them as the stray parses, so the ties are broken by dynamic precedence: `unexpected_tag` is -1 when
|
||||
`_group_held` confirms the innermost group already holds the name and -2 otherwise, so each tag flagged
|
||||
any of them as the stray parses, so the ties are broken by dynamic precedence: `unexpected_tag` is -2 when
|
||||
`_group_held` confirms the innermost group already holds the name and -4 otherwise, so each tag flagged
|
||||
before its time costs one more, and the first still opens the block however many follow.
|
||||
|
||||
**Builtin names must stay keyword-extractable, or the fallbacks swallow them.** `word: $.identifier` turns
|
||||
@@ -249,7 +282,7 @@ README nothing checks at all. The library-contents differential below, run again
|
||||
|
||||
### External scanner (`src/scanner.c`)
|
||||
|
||||
Eighteen external tokens, for the constraints a context-free grammar cannot express:
|
||||
Nineteen external tokens, for the constraints a context-free grammar cannot express:
|
||||
|
||||
- `push_block`/`push_partial`/`push_verbatim` and the matching `pop_*` — the names in
|
||||
`{% block a %}…{% endblock a %}`. A push token reads the name into `pending_name`, which the group takes
|
||||
@@ -305,6 +338,12 @@ Eighteen external tokens, for the constraints a context-free grammar cannot expr
|
||||
`{% if %}…{% else %}{% if %}…{% else %}`, because the outer group, having held an `else`, claimed the
|
||||
inner one's.
|
||||
|
||||
- `_text_brace` — a single `{` that is text although the content regex cannot tell: before `{%` or `{#`,
|
||||
and at the end of input. Django splits a template with `({%.*?%}|{{.*?}}|{#.*?#})`, so a `{` is text
|
||||
unless one of those starts at it; `{{{` is not a text brace, since `{{` starts a variable at its first
|
||||
brace. `content` is a right-associative run of `_text` and `_text_brace`, so it stays one node. Inside a
|
||||
group's body the missing-tag check reads past `{` first, so it scans the brace itself
|
||||
(`scan_text_brace_rest`) rather than give up the position.
|
||||
- `matcher_error` — used by no rule; returned only if the scanner reaches an error state.
|
||||
|
||||
`check_space()` defines whitespace for the scanner and **must stay in sync with `_sep`**, because the
|
||||
@@ -368,7 +407,8 @@ A branch that is also a `@local.scope` confines its bindings whichever way the b
|
||||
`context.push()` as the loop body, so `empty_block` is a scope alongside `for_block`.
|
||||
|
||||
`queries/errors.scm` collects what an editor should report: `(ERROR)` as `@error.syntax`, `(MISSING)` as
|
||||
`@error.missing`, and every missing-tag marker as `@error.missing_tag`. A tree holding only markers has
|
||||
`@error.missing`, every missing-tag marker as `@error.missing_tag`, and `unexpected_tag` and
|
||||
`unexpected_argument` as `@error.unexpected_tag` and `@error.unexpected_argument`. A tree holding only markers has
|
||||
no `has_error`, so a tool that checks that flag alone misses them. Adding a tag group means adding its
|
||||
marker there, since no supertype matches them all.
|
||||
|
||||
@@ -423,8 +463,8 @@ unavoidable limitation, or a case where this grammar is the more permissive one
|
||||
binds an `identifier`, because the keyword is not valid where a binding name is — but `{{ True.x }}` is
|
||||
an error here and an ordinary two-part lookup in Django, which renders `string_if_invalid`.
|
||||
- A `with` binding written twice (`{% with a=1 a=2 %}`, and the same in `{% include %}` and
|
||||
`{% blocktranslate %}`) is an error here and not in Django, where `token_kwargs` builds a dict and the
|
||||
later value wins. It has no use, so it is treated as the mistake it almost certainly is.
|
||||
`{% blocktranslate %}`) is reported here, as an `unexpected_argument`, and not in Django, where
|
||||
`token_kwargs` builds a dict and the later value wins. It has no use, so it is treated as the mistake it almost certainly is.
|
||||
- `{% csp_nonce_attr "a" media="b" %}` is accepted, matching Django, which only rejects it at render time.
|
||||
- A misspelled tag or filter cannot be caught: `{{ x|lenght }}` is indistinguishable from a filter some
|
||||
library registered. Cross-referencing names against `{% load %}` needs the project's Python, so it
|
||||
@@ -437,7 +477,7 @@ unavoidable limitation, or a case where this grammar is the more permissive one
|
||||
redefines `length` with a different arity gets a false error here.
|
||||
- Django's `static` tags ignore what follows their argument: `StaticNode.handle_token` looks for `as` two
|
||||
bits from the end, so `{% static "a" junk %}` and `{% static "a" as u v %}` are accepted there and
|
||||
rejected here, and `PrefixNode.handle_token` raises `IndexError` rather than `TemplateSyntaxError` on
|
||||
reported here as an `unexpected_argument`, and `PrefixNode.handle_token` raises `IndexError` rather than `TemplateSyntaxError` on
|
||||
`{% get_static_prefix as %}`.
|
||||
- Django's `{% cache %}` takes its fragment name as the raw word in that position and finds the cache to
|
||||
use by testing whether the last word starts with `using=`, and only when something precedes it. So
|
||||
|
||||
@@ -69,7 +69,8 @@ Five of the files in `queries/` are query files an editor loads:
|
||||
it. A marker is placed only where the scanner can tell: at the end of input, before an end tag that
|
||||
belongs to an enclosing group, or before an `{% endblock %}` naming a block further out.
|
||||
A group's tag written where no open group can hold it, such as `{% else %}` inside a `{% for %}`, is an
|
||||
`unexpected_tag`, captured as `@error.unexpected_tag`.
|
||||
`unexpected_tag`, captured as `@error.unexpected_tag`. Words a tag does not take, such as the `x` in
|
||||
`{% else x %}`, are an `unexpected_argument` inside that tag, captured as `@error.unexpected_argument`.
|
||||
|
||||
The other three are data for a tool rather than queries an editor runs. Each captures the two halves of a
|
||||
relation a tree-sitter query cannot express, and the tool does the join:
|
||||
@@ -224,7 +225,9 @@ Seven things to know:
|
||||
`conflicts` entry, as every block in the base grammar does. The generator offers an associativity
|
||||
instead; taking it silently discards the parse in which the body continues.
|
||||
- **End tags are not reserved**, a stray `{% endmap %}` parses as a `custom_tag` rather than an error — `reserved` takes no
|
||||
`previous`, so the `tag_name` list cannot be added to a name at a time. Instead you may drop the `custom_tag` rule to avoid parsing end tags as custom tags.
|
||||
`previous`, so the `tag_name` list cannot be added to a name at a time. Instead you may drop the `custom_tag` rule to avoid parsing end tags as custom tags. While the fallback
|
||||
is there, stray words in one of your end tags (`{% endloop extra %}`) read as a `custom_tag` too, which
|
||||
leaves your group with a missing-tag marker at the end of input instead of an `unexpected_argument`.
|
||||
- **Your groups get missing-tag markers.** The scanner knows no group by name — a group is closed by `end`
|
||||
followed by its opening tag's name — so the `alias($._missing_tag, $.missing_endloop_tag)` choice
|
||||
above is all it takes: an unclosed `{% loop x %}`, or one left open inside an `{% if %}` that
|
||||
|
||||
+110
-19
@@ -281,7 +281,14 @@ function simpleTag(name, args = true, kwargs = true) {
|
||||
const named = Object.entries(kwargs).map(([name, value]) =>
|
||||
part(seq(sym("_kwarg_name"), name, "=", value)),
|
||||
);
|
||||
parts.push(repeat(choice(...named)));
|
||||
// a name the signature does not take is flagged in place
|
||||
const unexpected = part(
|
||||
seq(
|
||||
sym("_kwarg_name"),
|
||||
alias(sym("_unexpected_keyword"), sym("unexpected_argument")),
|
||||
),
|
||||
);
|
||||
parts.push(repeat(choice(...named, unexpected)));
|
||||
}
|
||||
return tag(name, ...parts, optional(asVariable));
|
||||
}
|
||||
@@ -514,6 +521,12 @@ const django = grammar({
|
||||
[$.timezone_block],
|
||||
[$.with_block],
|
||||
[$.binaryOperator],
|
||||
[$.predicate],
|
||||
[$._translate_option],
|
||||
[$.unexpected_argument],
|
||||
[$.variable_attribute, $._unexpected_word],
|
||||
[$.resetcycle_tag, $._unexpected_word],
|
||||
[$.library, $._unexpected_word],
|
||||
[$.library, $.load_tag],
|
||||
[$._filtered_value_spaced],
|
||||
[$._filter_expression_spaced],
|
||||
@@ -552,6 +565,7 @@ const django = grammar({
|
||||
$._group_repeat,
|
||||
$._group_close,
|
||||
$._group_held,
|
||||
$._text_brace,
|
||||
],
|
||||
reserved: {
|
||||
global: ($) => ["not", "if", "in", "is", "as", "for", "from"],
|
||||
@@ -561,16 +575,43 @@ const django = grammar({
|
||||
template: ($) => repeat1(choice($.template_node, $.content)),
|
||||
// the tags of a group; see startTag
|
||||
simpleTagOpen: ($) => seq("{%", $._group_open_tag_read, optional(SEP)),
|
||||
simpleTagClose: ($) => seq(optional(SEP), "%}"),
|
||||
simpleTagClose: ($) =>
|
||||
seq(optional($.unexpected_argument), optional(SEP), "%}"),
|
||||
startTagOpen: ($) => seq("{%", $._group_open_tag_read, optional(SEP)),
|
||||
startTagClose: ($) => seq($._group_open_tag_push, optional(SEP), "%}"),
|
||||
startTagClose: ($) =>
|
||||
seq(
|
||||
optional($.unexpected_argument),
|
||||
$._group_open_tag_push,
|
||||
optional(SEP),
|
||||
"%}",
|
||||
),
|
||||
followTagOpen: ($) => seq("{%", $._group_open_tag_read, optional(SEP)),
|
||||
followTagClose: ($) => seq($._group_follow, optional(SEP), "%}"),
|
||||
followTagClose: ($) =>
|
||||
seq(
|
||||
optional($.unexpected_argument),
|
||||
$._group_follow,
|
||||
optional(SEP),
|
||||
"%}",
|
||||
),
|
||||
repeatTagOpen: ($) => seq("{%", $._group_open_tag_read, optional(SEP)),
|
||||
repeatTagClose: ($) => seq($._group_repeat, optional(SEP), "%}"),
|
||||
repeatTagClose: ($) =>
|
||||
seq(
|
||||
optional($.unexpected_argument),
|
||||
$._group_repeat,
|
||||
optional(SEP),
|
||||
"%}",
|
||||
),
|
||||
endTagOpen: ($) => seq("{%", $._group_open_tag_read, optional(SEP)),
|
||||
endTagClose: ($) => seq($._group_close, optional(SEP), "%}"),
|
||||
content: ($) => /(?:[^\{]|\{[^\{#%}])+/,
|
||||
endTagClose: ($) =>
|
||||
seq(optional($.unexpected_argument), $._group_close, optional(SEP), "%}"),
|
||||
content: ($) => prec.right(repeat1(choice($._text, $._text_brace))),
|
||||
/**
|
||||
* Template text up to a "{{", "{%" or "{#". A "{" that is text only because
|
||||
* of what comes after the next character, as in "a{{% if x %}" or a "{" at
|
||||
* the end of input, is _text_brace from the scanner; see
|
||||
* scan_text_brace_rest.
|
||||
*/
|
||||
_text: ($) => /(?:[^\{]|\{[^\{#%])+/,
|
||||
// the separator between the parts of a tag; see SEP
|
||||
_sep: ($) => /[ \t\r\n]+/,
|
||||
template_node: ($) =>
|
||||
@@ -1165,30 +1206,80 @@ const django = grammar({
|
||||
* second and a third "else", the later ones are the strays. Reading the
|
||||
* first as the stray instead parses just as well, so _group_held, which the
|
||||
* scanner returns only for a name the innermost group already holds, puts
|
||||
* the later reading at -1 against -2 for each tag flagged before its time.
|
||||
* the later reading at -2 against -4 for each tag flagged before its time. Both
|
||||
* cost more than an unexpected_argument, at -1, so a tag its group does take
|
||||
* but with a word it does not is read as that tag rather than as a stray.
|
||||
*/
|
||||
unexpected_tag: ($) =>
|
||||
choice(
|
||||
prec.dynamic(
|
||||
-1,
|
||||
seq(
|
||||
$.simpleTagOpen,
|
||||
field("tag_name", choice(...NAMES_INSIDE_A_TAG_GROUP)),
|
||||
repeat(seq(SEP, /[^\s%]+/)),
|
||||
$._group_held,
|
||||
$.simpleTagClose,
|
||||
),
|
||||
),
|
||||
prec.dynamic(
|
||||
-2,
|
||||
seq(
|
||||
$.simpleTagOpen,
|
||||
field("tag_name", choice(...NAMES_INSIDE_A_TAG_GROUP)),
|
||||
repeat(seq(SEP, /[^\s%]+/)),
|
||||
optional($.unexpected_argument),
|
||||
$._group_held,
|
||||
optional(SEP),
|
||||
"%}",
|
||||
),
|
||||
),
|
||||
prec.dynamic(
|
||||
-4,
|
||||
seq(
|
||||
$.simpleTagOpen,
|
||||
field("tag_name", choice(...NAMES_INSIDE_A_TAG_GROUP)),
|
||||
$.simpleTagClose,
|
||||
),
|
||||
),
|
||||
),
|
||||
/**
|
||||
* Words a tag does not take, kept inside the tag they were written in. Every
|
||||
* close utility accepts them before its "%}", so a misused tag keeps its own
|
||||
* node, "{% else x %}" is still the else of its group, and error recovery
|
||||
* has nothing to repair. queries/errors.scm reports it.
|
||||
*/
|
||||
unexpected_argument: ($) => repeat1($._unexpected_word),
|
||||
/**
|
||||
* One word of an unexpected_argument, costing -1 of dynamic precedence of
|
||||
* its own, so that a reading flagging more words than it must always loses:
|
||||
* "{% cycle a b as x silent zqx %}" flags zqx, not everything after a.
|
||||
*
|
||||
* The word is a token of lexical precedence -1, so that a token an argument
|
||||
* really takes wins wherever one is valid, even over a longer match:
|
||||
* "b|upper" is still a filtered value. Where a keyword is valid, as after
|
||||
* "{% for a in b" where "reversed" may follow, tree-sitter lexes every word
|
||||
* as an identifier to check for the keyword, so an identifier is a word
|
||||
* here too, followed by whatever touches it ("a=2", "x.y|upper"). It is
|
||||
* aliased so that queries/locals.scm does not take it for a reference.
|
||||
*/
|
||||
_unexpected_word: ($) =>
|
||||
prec.dynamic(
|
||||
-1,
|
||||
seq(
|
||||
SEP,
|
||||
choice(
|
||||
token(prec(-1, /[^\s%]+/)),
|
||||
seq(
|
||||
alias($.identifier, "unexpected_word"),
|
||||
optional(token.immediate(prec(-1, /[^\s%]+/))),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
/**
|
||||
* A "name=value" argument whose name a tag with a known signature does not
|
||||
* take. The scanner's _kwarg_name reads any name followed by "=", which
|
||||
* commits the parser to a keyword argument before the name is known, so
|
||||
* the name the signature lacks is taken here rather than after the tag.
|
||||
*/
|
||||
_unexpected_keyword: ($) =>
|
||||
prec.dynamic(
|
||||
-1,
|
||||
seq(
|
||||
alias($.identifier, "unexpected_word"),
|
||||
token.immediate(prec(-1, /[^\s%]+/)),
|
||||
),
|
||||
),
|
||||
url_tag: ($) =>
|
||||
tag(
|
||||
"url",
|
||||
|
||||
@@ -41,3 +41,5 @@
|
||||
(missing_plural_tag)] @error.missing_tag
|
||||
|
||||
(unexpected_tag) @error.unexpected_tag
|
||||
|
||||
(unexpected_argument) @error.unexpected_argument
|
||||
+363
-141
@@ -47,6 +47,18 @@
|
||||
"simpleTagClose": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "unexpected_argument"
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
@@ -93,6 +105,18 @@
|
||||
"startTagClose": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "unexpected_argument"
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_group_open_tag_push"
|
||||
@@ -143,6 +167,18 @@
|
||||
"followTagClose": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "unexpected_argument"
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_group_follow"
|
||||
@@ -193,6 +229,18 @@
|
||||
"repeatTagClose": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "unexpected_argument"
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_group_repeat"
|
||||
@@ -243,6 +291,18 @@
|
||||
"endTagClose": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "unexpected_argument"
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_group_close"
|
||||
@@ -266,8 +326,28 @@
|
||||
]
|
||||
},
|
||||
"content": {
|
||||
"type": "PREC_RIGHT",
|
||||
"value": 0,
|
||||
"content": {
|
||||
"type": "REPEAT1",
|
||||
"content": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_text"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_text_brace"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"_text": {
|
||||
"type": "PATTERN",
|
||||
"value": "(?:[^\\{]|\\{[^\\{#%}])+"
|
||||
"value": "(?:[^\\{]|\\{[^\\{#%])+"
|
||||
},
|
||||
"_sep": {
|
||||
"type": "PATTERN",
|
||||
@@ -2833,6 +2913,33 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_sep"
|
||||
},
|
||||
{
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_kwarg_name"
|
||||
},
|
||||
{
|
||||
"type": "ALIAS",
|
||||
"content": {
|
||||
"type": "SYMBOL",
|
||||
"name": "_unexpected_keyword"
|
||||
},
|
||||
"named": true,
|
||||
"value": "unexpected_argument"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7062,140 +7169,6 @@
|
||||
"unexpected_tag": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "PREC_DYNAMIC",
|
||||
"value": -1,
|
||||
"content": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "simpleTagOpen"
|
||||
},
|
||||
{
|
||||
"type": "FIELD",
|
||||
"name": "tag_name",
|
||||
"content": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "elif"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "else"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "empty"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endautoescape"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endblock"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endblocktrans"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endblocktranslate"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endcache"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endcomment"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endfilter"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endfor"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endif"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endifchanged"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endlanguage"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endlocalize"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endlocaltime"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endpartialdef"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endspaceless"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endtimezone"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endverbatim"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endwith"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "plural"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "REPEAT",
|
||||
"content": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_sep"
|
||||
},
|
||||
{
|
||||
"type": "PATTERN",
|
||||
"value": "[^\\s%]+"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_group_held"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "simpleTagClose"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "PREC_DYNAMIC",
|
||||
"value": -2,
|
||||
@@ -7304,17 +7277,143 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "REPEAT",
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "unexpected_argument"
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_group_held"
|
||||
},
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_sep"
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "%}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "PREC_DYNAMIC",
|
||||
"value": -4,
|
||||
"content": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "simpleTagOpen"
|
||||
},
|
||||
{
|
||||
"type": "FIELD",
|
||||
"name": "tag_name",
|
||||
"content": {
|
||||
"type": "SEQ",
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_sep"
|
||||
"type": "STRING",
|
||||
"value": "elif"
|
||||
},
|
||||
{
|
||||
"type": "PATTERN",
|
||||
"value": "[^\\s%]+"
|
||||
"type": "STRING",
|
||||
"value": "else"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "empty"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endautoescape"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endblock"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endblocktrans"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endblocktranslate"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endcache"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endcomment"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endfilter"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endfor"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endif"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endifchanged"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endlanguage"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endlocalize"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endlocaltime"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endpartialdef"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endspaceless"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endtimezone"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endverbatim"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "endwith"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "plural"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7328,6 +7427,104 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"unexpected_argument": {
|
||||
"type": "REPEAT1",
|
||||
"content": {
|
||||
"type": "SYMBOL",
|
||||
"name": "_unexpected_word"
|
||||
}
|
||||
},
|
||||
"_unexpected_word": {
|
||||
"type": "PREC_DYNAMIC",
|
||||
"value": -1,
|
||||
"content": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_sep"
|
||||
},
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "TOKEN",
|
||||
"content": {
|
||||
"type": "PREC",
|
||||
"value": -1,
|
||||
"content": {
|
||||
"type": "PATTERN",
|
||||
"value": "[^\\s%]+"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "ALIAS",
|
||||
"content": {
|
||||
"type": "SYMBOL",
|
||||
"name": "identifier"
|
||||
},
|
||||
"named": false,
|
||||
"value": "unexpected_word"
|
||||
},
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "IMMEDIATE_TOKEN",
|
||||
"content": {
|
||||
"type": "PREC",
|
||||
"value": -1,
|
||||
"content": {
|
||||
"type": "PATTERN",
|
||||
"value": "[^\\s%]+"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"_unexpected_keyword": {
|
||||
"type": "PREC_DYNAMIC",
|
||||
"value": -1,
|
||||
"content": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "ALIAS",
|
||||
"content": {
|
||||
"type": "SYMBOL",
|
||||
"name": "identifier"
|
||||
},
|
||||
"named": false,
|
||||
"value": "unexpected_word"
|
||||
},
|
||||
{
|
||||
"type": "IMMEDIATE_TOKEN",
|
||||
"content": {
|
||||
"type": "PREC",
|
||||
"value": -1,
|
||||
"content": {
|
||||
"type": "PATTERN",
|
||||
"value": "[^\\s%]+"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"url_tag": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
@@ -7821,6 +8018,27 @@
|
||||
[
|
||||
"binaryOperator"
|
||||
],
|
||||
[
|
||||
"predicate"
|
||||
],
|
||||
[
|
||||
"_translate_option"
|
||||
],
|
||||
[
|
||||
"unexpected_argument"
|
||||
],
|
||||
[
|
||||
"variable_attribute",
|
||||
"_unexpected_word"
|
||||
],
|
||||
[
|
||||
"resetcycle_tag",
|
||||
"_unexpected_word"
|
||||
],
|
||||
[
|
||||
"library",
|
||||
"_unexpected_word"
|
||||
],
|
||||
[
|
||||
"library",
|
||||
"load_tag"
|
||||
@@ -7909,6 +8127,10 @@
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_group_held"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "_text_brace"
|
||||
}
|
||||
],
|
||||
"inline": [
|
||||
|
||||
+508
-24
File diff suppressed because it is too large.
Load diff
+52563
-33491
File diff suppressed because it is too large.
Load diff
@@ -57,6 +57,9 @@ enum TokenType {
|
||||
* the innermost group already holds a tag by that name, which makes this the
|
||||
* likelier stray of the two */
|
||||
GroupHeld,
|
||||
/* a "{" that is text although another "{" follows it, because the second
|
||||
* one opens a tag or a comment, or although nothing follows it at all */
|
||||
TextBrace,
|
||||
};
|
||||
|
||||
/* The options blocktranslate takes, in the order of the bits recording them.
|
||||
@@ -773,8 +776,33 @@ typedef enum {
|
||||
/* no marker, but input was read to decide that, so nothing else may be
|
||||
* scanned from here */
|
||||
NotMissingAfterReading,
|
||||
/* no marker, but the "{" read to decide that was text, and was scanned */
|
||||
TextBraceScanned,
|
||||
} MissingResult;
|
||||
|
||||
/* Scans TextBrace once its "{" has been read. Django's lexer takes a template
|
||||
* apart with ({%.*?%}|{{.*?}}|{#.*?#}), so a "{" is text unless one of those
|
||||
* starts at it: before "{%" or "{#" the tag or comment starts one character
|
||||
* later, and at the end of input nothing starts at all. The content regex
|
||||
* cannot look that far ahead, so these are scanned here; "{{{" is not one of
|
||||
* them, since "{{" does start a variable at its first brace. */
|
||||
static bool scan_text_brace_rest(TSLexer *const lexer) {
|
||||
lexer->mark_end(lexer);
|
||||
if (lexer->eof(lexer)) {
|
||||
lexer->result_symbol = TextBrace;
|
||||
return true;
|
||||
}
|
||||
if (lexer->lookahead != '{') {
|
||||
return false;
|
||||
}
|
||||
lexer->advance(lexer, false);
|
||||
if (lexer->lookahead == '%' || lexer->lookahead == '#') {
|
||||
lexer->result_symbol = TextBrace;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Scans the zero-width marker for the innermost group's missing tag. The
|
||||
* marker is valid only where that group could close, which is after a body,
|
||||
* where nothing else of this scanner is valid but the raw-text tokens, and
|
||||
@@ -809,6 +837,10 @@ static MissingResult scan_missing_tag(
|
||||
}
|
||||
lexer->advance(lexer, false);
|
||||
if (lexer->lookahead != '%') {
|
||||
// not a tag, but the "{" may be text the grammar still has to take
|
||||
if (valid_symbols[TextBrace] && scan_text_brace_rest(lexer)) {
|
||||
return TextBraceScanned;
|
||||
}
|
||||
return NotMissingAfterReading;
|
||||
}
|
||||
lexer->advance(lexer, false);
|
||||
@@ -950,6 +982,7 @@ bool tree_sitter_django_external_scanner_scan(
|
||||
if (valid_symbols[MissingTag]) {
|
||||
switch (scan_missing_tag(scanner, lexer, valid_symbols)) {
|
||||
case Missing:
|
||||
case TextBraceScanned:
|
||||
return true;
|
||||
case NotMissingAfterReading:
|
||||
return false;
|
||||
@@ -957,6 +990,10 @@ bool tree_sitter_django_external_scanner_scan(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid_symbols[TextBrace] && lexer->lookahead == '{') {
|
||||
lexer->advance(lexer, false);
|
||||
return scan_text_brace_rest(lexer);
|
||||
}
|
||||
if (valid_symbols[GroupOpenTagRead]) {
|
||||
scan_group_open_tag_read(scanner, lexer);
|
||||
return true;
|
||||
|
||||
+129
-10
@@ -380,13 +380,29 @@ Blocktrans With Plural And No Count
|
||||
|
||||
==================
|
||||
Blocktrans Counting Two Bindings
|
||||
:error
|
||||
==================
|
||||
|
||||
{% blocktrans count n=x c=d %}one{% plural %}many{% endblocktrans %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(blocktranslate_group
|
||||
(blocktrans_block
|
||||
(blocktrans_tag
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(variable_attribute
|
||||
(identifier))))
|
||||
(unexpected_argument))
|
||||
(content)
|
||||
(plural_block
|
||||
(plural_tag)
|
||||
(content)))
|
||||
(endblocktrans_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Blocktrans With Nothing To Bind
|
||||
@@ -400,13 +416,21 @@ Blocktrans With Nothing To Bind
|
||||
|
||||
==================
|
||||
Blocktrans With An Unknown Option
|
||||
:error
|
||||
==================
|
||||
|
||||
{% blocktrans bogus %}hi{% endblocktrans %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(blocktranslate_group
|
||||
(blocktrans_block
|
||||
(blocktrans_tag
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
(endblocktrans_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Blocktrans With Asvar And No Name
|
||||
@@ -478,73 +502,155 @@ Blocktrans With Two Plurals
|
||||
|
||||
==================
|
||||
Blocktrans With An Argument On Plural
|
||||
:error
|
||||
==================
|
||||
|
||||
{% blocktrans count n=x %}one{% plural extra %}many{% endblocktrans %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(blocktranslate_group
|
||||
(blocktrans_block
|
||||
(blocktrans_tag
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(variable_attribute
|
||||
(identifier)))))
|
||||
(content)
|
||||
(plural_block
|
||||
(plural_tag
|
||||
(unexpected_argument))
|
||||
(content)))
|
||||
(endblocktrans_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Blocktrans With Trimmed Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% blocktrans trimmed trimmed %}hi{% endblocktrans %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(blocktranslate_group
|
||||
(blocktrans_block
|
||||
(blocktrans_tag
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
(endblocktrans_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Blocktrans With With Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% blocktrans with a=b with c=d %}hi{% endblocktrans %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(blocktranslate_group
|
||||
(blocktrans_block
|
||||
(blocktrans_tag
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(variable_attribute
|
||||
(identifier))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
(endblocktrans_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Blocktrans With Context Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% blocktrans context "a" context "b" %}hi{% endblocktrans %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(blocktranslate_group
|
||||
(blocktrans_block
|
||||
(blocktrans_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
(endblocktrans_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Blocktrans With Asvar Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% blocktrans asvar x asvar y %}hi{% endblocktrans %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(blocktranslate_group
|
||||
(blocktrans_block
|
||||
(blocktrans_tag
|
||||
(identifier)
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
(endblocktrans_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Blocktrans With Count Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% blocktrans count n=x count m=y %}a{% plural %}b{% endblocktrans %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(blocktranslate_group
|
||||
(blocktrans_block
|
||||
(blocktrans_tag
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(variable_attribute
|
||||
(identifier))))
|
||||
(unexpected_argument))
|
||||
(content)
|
||||
(plural_block
|
||||
(plural_tag)
|
||||
(content)))
|
||||
(endblocktrans_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Blocktranslate With Trimmed Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% blocktranslate trimmed trimmed %}hi{% endblocktranslate %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(blocktranslate_group
|
||||
(blocktranslate_block
|
||||
(blocktranslate_tag
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
(endblocktranslate_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Blocktrans Repeating An Option Of The Tag Before It
|
||||
@@ -570,10 +676,23 @@ Blocktrans Repeating An Option Of The Tag Before It
|
||||
|
||||
==================
|
||||
Blocktrans With A Binding Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% blocktrans with a=1 a=2 %}x{% endblocktrans %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(blocktranslate_group
|
||||
(blocktrans_block
|
||||
(blocktrans_tag
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
(endblocktrans_tag))
|
||||
(content))
|
||||
+20
-1
@@ -221,13 +221,31 @@ Cache Without A Fragment Name
|
||||
|
||||
==================
|
||||
Cache With Anything After Using
|
||||
:error
|
||||
==================
|
||||
|
||||
{% cache 500 sidebar using="c" extra %}x{% endcache %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(cache_group
|
||||
(cache_block
|
||||
(cache_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endcache_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Cache With An Empty Using
|
||||
@@ -257,3 +275,4 @@ Cache With Using As Its Fragment Name
|
||||
{% cache 500 using="c" %}x{% endcache %}
|
||||
|
||||
---
|
||||
|
||||
@@ -48,43 +48,68 @@ Csp Nonce Attr With Media Keyword
|
||||
|
||||
==================
|
||||
Csp Nonce Attr Takes One Argument
|
||||
:error
|
||||
==================
|
||||
|
||||
{% csp_nonce_attr "a" "b" %}
|
||||
|
||||
---
|
||||
|
||||
(ERROR)
|
||||
(template
|
||||
(content)
|
||||
(csp_nonce_attr_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Csp Nonce Attr Rejects Unknown Keyword
|
||||
:error
|
||||
==================
|
||||
|
||||
{% csp_nonce_attr other="b" %}
|
||||
|
||||
---
|
||||
|
||||
(ERROR)
|
||||
(template
|
||||
(content)
|
||||
(csp_nonce_attr_tag
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Csp Nonce Attr Rejects Repeated Keyword
|
||||
:error
|
||||
==================
|
||||
|
||||
{% csp_nonce_attr media=1 media=2 %}
|
||||
|
||||
---
|
||||
|
||||
(ERROR)
|
||||
|
||||
(template
|
||||
(content)
|
||||
(csp_nonce_attr_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Csp Nonce Attr With Media Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% csp_nonce_attr media="a" media="b" %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(csp_nonce_attr_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
+11
-1
@@ -294,13 +294,23 @@ A Name Reserved For A Tag Is Still An Argument
|
||||
|
||||
==================
|
||||
Custom Tag With A Keyword Argument Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% mytag a=1 a=2 %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(custom_tag
|
||||
(identifier)
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Custom Tag With Keyword Arguments And As
|
||||
|
||||
+31
-2
@@ -486,13 +486,27 @@ For Reversed With A Tag In Its Body
|
||||
|
||||
==================
|
||||
For With Reversed Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% for x in y reversed reversed %}a{% endfor %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(for_group
|
||||
(for_block
|
||||
(for_tag
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(variable_attribute
|
||||
(identifier))))
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endfor_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
For With Reversed Before In
|
||||
@@ -503,6 +517,7 @@ For With Reversed Before In
|
||||
|
||||
---
|
||||
|
||||
|
||||
==================
|
||||
For Over A Variable Named Reversed
|
||||
==================
|
||||
@@ -587,10 +602,24 @@ For With Reversed As Its Whole Sequence
|
||||
|
||||
==================
|
||||
For With Reversed Three Times
|
||||
:error
|
||||
==================
|
||||
|
||||
{% for x in reversed reversed reversed %}a{% endfor %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(for_group
|
||||
(for_block
|
||||
(for_tag
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(variable_attribute
|
||||
(identifier))))
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endfor_tag))
|
||||
(content))
|
||||
+33
-3
@@ -180,13 +180,21 @@ Trans Without A Message
|
||||
|
||||
==================
|
||||
Trans With An Option Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% trans "hi" noop noop %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(translate_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Trans With As Twice
|
||||
@@ -200,13 +208,21 @@ Trans With As Twice
|
||||
|
||||
==================
|
||||
Trans With An Unknown Option
|
||||
:error
|
||||
==================
|
||||
|
||||
{% trans "hi" bogus %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(translate_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Trans With Context And No Value
|
||||
@@ -400,13 +416,26 @@ Language Without An Argument
|
||||
|
||||
==================
|
||||
Language With Two Arguments
|
||||
:error
|
||||
==================
|
||||
|
||||
{% language "a" "b" %}x{% endlanguage %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(language_group
|
||||
(language_block
|
||||
(language_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endlanguage_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Language Name Filter
|
||||
@@ -493,6 +522,7 @@ Language Name Filter With An Argument
|
||||
|
||||
---
|
||||
|
||||
|
||||
==================
|
||||
Translate With A Context Named After Noop
|
||||
:error
|
||||
|
||||
@@ -219,8 +219,11 @@ b
|
||||
(ifchanged_block
|
||||
(ifchanged_tag)
|
||||
(template
|
||||
(content)
|
||||
(unexpected_tag)
|
||||
(content)))
|
||||
(else_block
|
||||
(else_tag
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endifchanged_tag))
|
||||
(content))
|
||||
+56
-5
@@ -163,13 +163,26 @@ Include With Only Before With
|
||||
|
||||
==================
|
||||
Include With A Binding Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% include "t.html" with a=1 a=2 %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(include_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Include Without A Name
|
||||
@@ -193,33 +206,62 @@ Include With Nothing To Bind
|
||||
|
||||
==================
|
||||
Include With With Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% include "t.html" with a=1 with b=2 %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(include_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Include With Only Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% include "t.html" only only %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(include_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Include With An Unknown Option
|
||||
:error
|
||||
==================
|
||||
|
||||
{% include "t.html" bogus %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(include_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Include With A Legacy Binding
|
||||
@@ -233,9 +275,18 @@ Include With A Legacy Binding
|
||||
|
||||
==================
|
||||
Include With A Second Name
|
||||
:error
|
||||
==================
|
||||
|
||||
{% include "t.html" "u.html" %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(include_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
+21
-3
@@ -95,23 +95,41 @@ Localize Nested
|
||||
|
||||
==================
|
||||
Localize With Another Argument
|
||||
:error
|
||||
==================
|
||||
|
||||
{% localize maybe %}x{% endlocalize %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(localize_group
|
||||
(localize_block
|
||||
(localize_tag
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endlocalize_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Localize With Two Arguments
|
||||
:error
|
||||
==================
|
||||
|
||||
{% localize on off %}x{% endlocalize %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(localize_group
|
||||
(localize_block
|
||||
(localize_tag
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endlocalize_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Localize Unclosed
|
||||
@@ -130,7 +148,6 @@ Localize Unclosed
|
||||
(content)))
|
||||
(missing_endlocalize_tag)))
|
||||
|
||||
|
||||
==================
|
||||
Localize Filter
|
||||
==================
|
||||
@@ -187,3 +204,4 @@ Unlocalize Filter With An Argument
|
||||
{{ x|unlocalize:1 }}
|
||||
|
||||
---
|
||||
|
||||
@@ -950,7 +950,8 @@ Endblock Naming No Open Block
|
||||
(block_tag
|
||||
(push_block))
|
||||
(template
|
||||
(unexpected_tag)
|
||||
(unexpected_tag
|
||||
(unexpected_argument))
|
||||
(content)))
|
||||
(missing_endblock_tag))))
|
||||
(missing_endblock_tag)))
|
||||
|
||||
@@ -100,19 +100,43 @@ Querystring With Two Keys
|
||||
|
||||
==================
|
||||
Querystring With A Key Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% querystring page=2 page=3 %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(querystring_tag
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Querystring With A Key Repeated Later
|
||||
:error
|
||||
==================
|
||||
|
||||
{% querystring a=1 b=2 a=3 %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(querystring_tag
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
+24
-3
@@ -81,23 +81,40 @@ Static Without A Path
|
||||
|
||||
==================
|
||||
Static With A Second Path
|
||||
:error
|
||||
==================
|
||||
|
||||
{% static "a.css" "b.css" %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(static_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Static With Anything After As
|
||||
:error
|
||||
==================
|
||||
|
||||
{% static "a.css" as url junk %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(static_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(identifier)
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Get Static Prefix
|
||||
@@ -142,13 +159,17 @@ Get Media Prefix With As
|
||||
|
||||
==================
|
||||
Get Static Prefix Without As
|
||||
:error
|
||||
==================
|
||||
|
||||
{% get_static_prefix prefix %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(get_static_prefix_tag
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Get Static Prefix Without A Variable
|
||||
|
||||
@@ -211,6 +211,7 @@ Template Variable Literal Adjacent To Name
|
||||
---
|
||||
|
||||
|
||||
|
||||
==================
|
||||
Template Variable With Escaped String
|
||||
==================
|
||||
@@ -272,6 +273,7 @@ Template Variable Attribute Beginning With Underscore
|
||||
---
|
||||
|
||||
|
||||
|
||||
==================
|
||||
Template Variable Translated String
|
||||
==================
|
||||
@@ -320,6 +322,7 @@ Template Variable Translated String With Spaces
|
||||
---
|
||||
|
||||
|
||||
|
||||
==================
|
||||
Template Variable Spanning Lines
|
||||
==================
|
||||
@@ -462,6 +465,7 @@ Template Variable Attribute Of A Literal
|
||||
---
|
||||
|
||||
|
||||
|
||||
==================
|
||||
Template Variable Trailing Dot With An Exponent
|
||||
==================
|
||||
@@ -489,6 +493,7 @@ Template Variable Trailing Dot
|
||||
---
|
||||
|
||||
|
||||
|
||||
==================
|
||||
Template Variable Signed Trailing Dot
|
||||
:error
|
||||
@@ -498,3 +503,108 @@ Template Variable Signed Trailing Dot
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
==================
|
||||
Braces That Open Nothing Are Text
|
||||
==================
|
||||
|
||||
{% if x %}Hello {} world{% endif %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(if_group
|
||||
(if_block
|
||||
(if_tag
|
||||
(predicate
|
||||
(filtered_value
|
||||
(value
|
||||
(variable_attribute
|
||||
(identifier))))))
|
||||
(template
|
||||
(content)))
|
||||
(endif_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
A Brace Before A Tag Is Text
|
||||
==================
|
||||
|
||||
Hello {{% if x %}a{% endif %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(if_group
|
||||
(if_block
|
||||
(if_tag
|
||||
(predicate
|
||||
(filtered_value
|
||||
(value
|
||||
(variable_attribute
|
||||
(identifier))))))
|
||||
(template
|
||||
(content)))
|
||||
(endif_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
A Brace Before A Comment Is Text
|
||||
==================
|
||||
|
||||
a{{# c #}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(template_comment)
|
||||
(content))
|
||||
|
||||
==================
|
||||
A Brace At The End Is Text
|
||||
==================
|
||||
|
||||
a{
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content))
|
||||
|
||||
==================
|
||||
A Brace Before The End Tag Of A Group Is Text
|
||||
==================
|
||||
|
||||
{% if x %}Hello {{% endif %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(if_group
|
||||
(if_block
|
||||
(if_tag
|
||||
(predicate
|
||||
(filtered_value
|
||||
(value
|
||||
(variable_attribute
|
||||
(identifier))))))
|
||||
(template
|
||||
(content)))
|
||||
(endif_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Three Braces Open A Variable
|
||||
:error
|
||||
==================
|
||||
|
||||
a{{{ x }}
|
||||
|
||||
---
|
||||
|
||||
|
||||
+41
-4
@@ -54,23 +54,41 @@ Localtime Off
|
||||
|
||||
==================
|
||||
Localtime With Another Argument
|
||||
:error
|
||||
==================
|
||||
|
||||
{% localtime maybe %}x{% endlocaltime %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(localtime_group
|
||||
(localtime_block
|
||||
(localtime_tag
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endlocaltime_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Localtime With Two Arguments
|
||||
:error
|
||||
==================
|
||||
|
||||
{% localtime on off %}x{% endlocaltime %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(localtime_group
|
||||
(localtime_block
|
||||
(localtime_tag
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endlocaltime_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Timezone
|
||||
@@ -152,13 +170,26 @@ Timezone Without An Argument
|
||||
|
||||
==================
|
||||
Timezone With Two Arguments
|
||||
:error
|
||||
==================
|
||||
|
||||
{% timezone "a" "b" %}x{% endtimezone %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(timezone_group
|
||||
(timezone_block
|
||||
(timezone_tag
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(string))))
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endtimezone_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Get Current Timezone
|
||||
@@ -186,13 +217,18 @@ Get Current Timezone Without As
|
||||
|
||||
==================
|
||||
Get Current Timezone With Anything After As
|
||||
:error
|
||||
==================
|
||||
|
||||
{% get_current_timezone as tz junk %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(get_current_timezone_tag
|
||||
(identifier)
|
||||
(unexpected_argument))
|
||||
(content))
|
||||
|
||||
==================
|
||||
Localtime Filter
|
||||
@@ -272,3 +308,4 @@ Localtime Filter With An Argument
|
||||
{{ x|localtime:1 }}
|
||||
|
||||
---
|
||||
|
||||
+35
-2
@@ -68,23 +68,56 @@ With Multiple Variables
|
||||
|
||||
==================
|
||||
With A Binding Twice
|
||||
:error
|
||||
==================
|
||||
|
||||
{% with a=1 a=2 %}x{% endwith %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(with_group
|
||||
(with_block
|
||||
(with_tag
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endwith_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
With A Binding Repeated Later
|
||||
:error
|
||||
==================
|
||||
|
||||
{% with a=1 b=2 a=3 %}x{% endwith %}
|
||||
|
||||
---
|
||||
|
||||
(template
|
||||
(content)
|
||||
(with_group
|
||||
(with_block
|
||||
(with_tag
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(identifier)
|
||||
(filtered_value
|
||||
(value
|
||||
(literal
|
||||
(number))))
|
||||
(unexpected_argument))
|
||||
(template
|
||||
(content)))
|
||||
(endwith_tag))
|
||||
(content))
|
||||
|
||||
==================
|
||||
With Repeating A Binding Of The Tag Before It
|
||||
|
||||
Reference in new issue
Block a user