ludtwig_parser/syntax/
untyped.rs

1//! This module contains all the rowan types.
2//!
3//! All GreenNodes and consequently RedNodes as well as AST Nodes
4//! contain a single [SyntaxKind] variant for each GreenNode.
5//!
6//! You can use the macro (T![])[crate::T] to quickly construct a [SyntaxKind]
7//! and for example compare it to a different one. For example
8//! ```
9//! use ludtwig_parser::syntax::untyped::SyntaxKind;
10//! use ludtwig_parser::T;
11//! assert_eq!(T!["%}"], SyntaxKind::TK_PERCENT_CURLY);
12//! ```
13//!
14//! An overview of the syntax tree concept can be found
15//! at the [crate level documentation](crate#syntax-trees).
16
17use logos::Logos;
18pub use rowan::Direction;
19// `GreenNode` is an immutable tree, which is cheap to change,
20// but doesn't contain offsets and parent pointers.
21pub use rowan::GreenNode;
22// You can construct `GreenNodes` by hand, but a builder
23// is helpful for top-down parsers: it maintains a stack
24// of currently in-progress nodes
25pub use rowan::GreenNodeBuilder;
26pub use rowan::Language;
27pub use rowan::SyntaxText;
28pub use rowan::TextLen;
29pub use rowan::TextRange;
30pub use rowan::TextSize;
31pub use rowan::WalkEvent;
32use std::fmt;
33use std::fmt::Formatter;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Logos)]
36#[allow(non_camel_case_types)]
37#[repr(u16)]
38pub enum SyntaxKind {
39    /*
40    Tokens without any meaning / semantic attached to them.
41    These are produced by the lexer and provide a small abstraction over plain text
42     */
43    #[regex(r"[ \t]+")]
44    TK_WHITESPACE = 0,
45    #[regex(r"((\n)|(\r\n))+")]
46    TK_LINE_BREAK,
47    /// a single word containing only characters, numbers or symbols
48    /// must start with an alpha or one of the special starting characters followed by a normal alpha
49    /// special case: allows a single underscore as a valid word
50    #[regex(r"([a-zA-Z]|([@\#_\$][a-zA-Z])|_)[a-zA-Z0-9_\-]*")]
51    TK_WORD,
52    /// a valid twig number
53    #[regex(r"[0-9]+(\.[0-9]+)?([Ee][\+\-][0-9]+)?")]
54    TK_NUMBER,
55    /// a html escape character like '
' or '
' or '
'
56    #[regex(r"\&(([a-zA-Z][a-zA-Z0-9]*)|(\#[0-9]+)|(\#x[0-9a-fA-F]+));")]
57    TK_HTML_ESCAPE_CHARACTER,
58    #[token(".")]
59    TK_DOT,
60    #[token("..")]
61    TK_DOUBLE_DOT,
62    #[token(",")]
63    TK_COMMA,
64    #[token(":")]
65    TK_COLON,
66    #[token(";")]
67    TK_SEMICOLON,
68    #[token("!")]
69    TK_EXCLAMATION_MARK,
70    #[token("!=")]
71    TK_EXCLAMATION_MARK_EQUALS,
72    #[token("!==")]
73    TK_EXCLAMATION_MARK_DOUBLE_EQUALS,
74    #[token("?")]
75    TK_QUESTION_MARK,
76    #[token("??")]
77    TK_DOUBLE_QUESTION_MARK,
78    #[token("%")]
79    TK_PERCENT,
80    #[token("~")]
81    TK_TILDE,
82    #[token("|")]
83    TK_SINGLE_PIPE,
84    #[token("||")]
85    TK_DOUBLE_PIPE,
86    #[token("&")]
87    TK_AMPERSAND,
88    #[token("&&")]
89    TK_DOUBLE_AMPERSAND,
90    #[token("/")]
91    TK_FORWARD_SLASH,
92    #[token("//")]
93    TK_DOUBLE_FORWARD_SLASH,
94    #[token("\\")]
95    TK_BACKWARD_SLASH,
96    #[token("(")]
97    TK_OPEN_PARENTHESIS,
98    #[token(")")]
99    TK_CLOSE_PARENTHESIS,
100    #[token("{")]
101    TK_OPEN_CURLY,
102    #[token("}")]
103    TK_CLOSE_CURLY,
104    #[token("[")]
105    TK_OPEN_SQUARE,
106    #[token("]")]
107    TK_CLOSE_SQUARE,
108    #[token("<")]
109    TK_LESS_THAN,
110    #[token("<=")]
111    TK_LESS_THAN_EQUAL,
112    #[token("<=>")]
113    TK_LESS_THAN_EQUAL_GREATER_THAN,
114    #[token("</")]
115    TK_LESS_THAN_SLASH,
116    #[token("<!")]
117    TK_LESS_THAN_EXCLAMATION_MARK,
118    #[token("DOCTYPE", ignore(ascii_case))]
119    TK_DOCTYPE,
120    #[token(">")]
121    TK_GREATER_THAN,
122    #[token(">=")]
123    TK_GREATER_THAN_EQUAL,
124    #[token("=>")]
125    TK_EQUAL_GREATER_THAN,
126    #[token("/>")]
127    TK_SLASH_GREATER_THAN,
128    #[token("<!--")]
129    TK_LESS_THAN_EXCLAMATION_MARK_MINUS_MINUS,
130    #[token("-->")]
131    TK_MINUS_MINUS_GREATER_THAN,
132    #[token("=")]
133    TK_EQUAL,
134    #[token("==")]
135    TK_DOUBLE_EQUAL,
136    #[token("===")]
137    TK_TRIPLE_EQUAL,
138    #[token("+")]
139    TK_PLUS,
140    #[token("-")]
141    TK_MINUS,
142    #[token("*")]
143    TK_STAR,
144    #[token("**")]
145    TK_DOUBLE_STAR,
146    #[token("\"")]
147    TK_DOUBLE_QUOTES,
148    #[token("'")]
149    TK_SINGLE_QUOTES,
150    #[token("`")]
151    TK_GRAVE_ACCENT_QUOTES,
152    #[token("{%")]
153    TK_CURLY_PERCENT,
154    #[token("%}")]
155    TK_PERCENT_CURLY,
156    #[token("{{")]
157    TK_OPEN_CURLY_CURLY,
158    #[token("}}")]
159    TK_CLOSE_CURLY_CURLY,
160    #[token("{#")]
161    TK_OPEN_CURLY_HASHTAG,
162    #[token("#}")]
163    TK_HASHTAG_CLOSE_CURLY,
164    #[token("#")]
165    TK_HASHTAG,
166
167    #[token("true", ignore(ascii_case))]
168    TK_TRUE,
169    #[token("false", ignore(ascii_case))]
170    TK_FALSE,
171
172    /* twig tag tokens */
173    #[token("block")]
174    TK_BLOCK,
175    #[token("endblock")]
176    TK_ENDBLOCK,
177    #[token("if")]
178    TK_IF,
179    #[token("elseif")]
180    TK_ELSE_IF,
181    #[token("else")]
182    TK_ELSE,
183    #[token("endif")]
184    TK_ENDIF,
185    #[token("apply")]
186    TK_APPLY,
187    #[token("endapply")]
188    TK_ENDAPPLY,
189    #[token("autoescape")]
190    TK_AUTOESCAPE,
191    #[token("endautoescape")]
192    TK_ENDAUTOESCAPE,
193    #[token("cache")]
194    TK_CACHE,
195    #[token("endcache")]
196    TK_ENDCACHE,
197    #[token("deprecated")]
198    TK_DEPRECATED,
199    #[token("do")]
200    TK_DO,
201    #[token("embed")]
202    TK_EMBED,
203    #[token("endembed")]
204    TK_ENDEMBED,
205    #[token("extends")]
206    TK_EXTENDS,
207    #[token("flush")]
208    TK_FLUSH,
209    #[token("for")]
210    TK_FOR,
211    #[token("endfor")]
212    TK_ENDFOR,
213    #[token("from")]
214    TK_FROM,
215    #[token("import")]
216    TK_IMPORT,
217    #[token("macro")]
218    TK_MACRO,
219    #[token("endmacro")]
220    TK_ENDMACRO,
221    #[token("sandbox")]
222    TK_SANDBOX,
223    #[token("endsandbox")]
224    TK_ENDSANDBOX,
225    #[token("set")]
226    TK_SET,
227    #[token("endset")]
228    TK_ENDSET,
229    #[token("use")]
230    TK_USE,
231    #[token("verbatim")]
232    TK_VERBATIM,
233    #[token("endverbatim")]
234    TK_ENDVERBATIM,
235    #[token("only")]
236    TK_ONLY,
237    #[token("ignore missing")]
238    TK_IGNORE_MISSING,
239    #[token("with")]
240    TK_WITH,
241    #[token("endwith")]
242    TK_ENDWITH,
243    #[token("ttl")]
244    TK_TTL,
245    #[token("tags")]
246    TK_TAGS,
247    #[token("props")]
248    TK_PROPS,
249    #[token("component")]
250    TK_COMPONENT,
251    #[token("endcomponent")]
252    TK_ENDCOMPONENT,
253    /* twig operators */
254    #[token("not")]
255    TK_NOT,
256    #[token("or")]
257    TK_OR,
258    #[token("and")]
259    TK_AND,
260    #[token("b-or")]
261    TK_BINARY_OR,
262    #[token("b-xor")]
263    TK_BINARY_XOR,
264    #[token("b-and")]
265    TK_BINARY_AND,
266    #[token("in")]
267    TK_IN,
268    #[token("matches")]
269    TK_MATCHES,
270    #[token("starts with")]
271    TK_STARTS_WITH,
272    #[token("ends with")]
273    TK_ENDS_WITH,
274    #[token("is")]
275    TK_IS,
276    /* twig tests */
277    #[token("even")]
278    TK_EVEN,
279    #[token("odd")]
280    TK_ODD,
281    #[token("defined")]
282    TK_DEFINED,
283    #[token("same as")]
284    TK_SAME_AS,
285    #[token("as")]
286    TK_AS,
287    #[token("none", ignore(ascii_case))]
288    TK_NONE,
289    #[token("null", ignore(ascii_case))]
290    TK_NULL,
291    #[token("divisible by")]
292    TK_DIVISIBLE_BY,
293    #[token("constant")]
294    TK_CONSTANT,
295    #[token("empty")]
296    TK_EMPTY,
297    #[token("iterable")]
298    TK_ITERABLE,
299    /* twig functions */
300    #[token("max")]
301    TK_MAX,
302    #[token("min")]
303    TK_MIN,
304    #[token("range")]
305    TK_RANGE,
306    #[token("cycle")]
307    TK_CYCLE,
308    #[token("random")]
309    TK_RANDOM,
310    #[token("date")]
311    TK_DATE,
312    #[token("include")]
313    TK_INCLUDE,
314    #[token("source")]
315    TK_SOURCE,
316
317    /* Drupal Trans */
318    #[token("trans")]
319    TK_TRANS,
320    #[token("endtrans")]
321    TK_ENDTRANS,
322
323    /* shopware specific */
324    #[token("sw_extends")]
325    TK_SW_EXTENDS,
326    #[token("sw_silent_feature_call")]
327    TK_SW_SILENT_FEATURE_CALL,
328    #[token("endsw_silent_feature_call")]
329    TK_ENDSW_SILENT_FEATURE_CALL,
330    #[token("sw_include")]
331    TK_SW_INCLUDE,
332    #[token("return")]
333    TK_RETURN,
334    #[token("sw_icon")]
335    TK_SW_ICON,
336    #[token("sw_thumbnails")]
337    TK_SW_THUMBNAILS,
338    #[token("style")]
339    TK_STYLE,
340
341    /* special tokens */
342    #[token("ludtwig-ignore-file", ignore(ascii_case))]
343    TK_LUDTWIG_IGNORE_FILE,
344    #[token("ludtwig-ignore", ignore(ascii_case))]
345    TK_LUDTWIG_IGNORE,
346    TK_UNKNOWN, // contains invalid / unrecognized syntax (used for error recovery).
347
348    /*
349    Composite nodes (which can have children and ast / typed counterparts)
350    These do have a meaning and are constructed by the parser
351    */
352    BODY,
353    TWIG_VAR,
354    TWIG_EXPRESSION, // covers every expression (binary / unary) or literals (where expressions are allowed)
355    TWIG_BINARY_EXPRESSION,
356    TWIG_UNARY_EXPRESSION,
357    TWIG_PARENTHESES_EXPRESSION,
358    TWIG_CONDITIONAL_EXPRESSION,
359
360    TWIG_OPERAND, // covers the operands in TWIG_ACCESSOR, TWIG_INDEX_LOOKUP, TWIG_PIPE and TWIG_FUNCTION_CALL
361    TWIG_ACCESSOR, // accessor node like 'product.price'
362    TWIG_FILTER,  // filter node like 'name|title'
363
364    TWIG_INDEX_LOOKUP, // indexer node like 'products[0]'
365    TWIG_INDEX,        // covers a array index '5' inside []
366    TWIG_INDEX_RANGE,  // covers a array index range like '0:10' inside []
367
368    TWIG_FUNCTION_CALL,
369    TWIG_ARROW_FUNCTION, // like 'i => i % 2' or '(a, b) => a >= b'
370    TWIG_ARGUMENTS,
371    TWIG_NAMED_ARGUMENT,
372
373    // twig literals
374    TWIG_LITERAL_STRING,
375    TWIG_LITERAL_STRING_INNER,
376    TWIG_LITERAL_STRING_INTERPOLATION,
377    TWIG_LITERAL_NUMBER,
378    TWIG_LITERAL_ARRAY,
379    TWIG_LITERAL_ARRAY_INNER,
380    TWIG_LITERAL_NULL,
381    TWIG_LITERAL_BOOLEAN,
382    TWIG_LITERAL_HASH,
383    TWIG_LITERAL_HASH_ITEMS,
384    TWIG_LITERAL_HASH_PAIR,
385    TWIG_LITERAL_HASH_KEY,
386    TWIG_LITERAL_HASH_VALUE,
387    TWIG_LITERAL_NAME,
388
389    // twig block like structures
390    TWIG_COMMENT,
391    // twig block
392    TWIG_BLOCK,
393    TWIG_STARTING_BLOCK,
394    TWIG_ENDING_BLOCK,
395    // twig if
396    TWIG_IF,
397    TWIG_IF_BLOCK,
398    TWIG_ELSE_IF_BLOCK,
399    TWIG_ELSE_BLOCK,
400    TWIG_ENDIF_BLOCK,
401    // twig set
402    TWIG_SET,
403    TWIG_SET_BLOCK,
404    TWIG_ENDSET_BLOCK,
405    TWIG_ASSIGNMENT,
406    // twig for
407    TWIG_FOR,
408    TWIG_FOR_BLOCK,
409    TWIG_FOR_ELSE_BLOCK,
410    TWIG_ENDFOR_BLOCK,
411    // twig extends
412    TWIG_EXTENDS,
413    // twig include
414    TWIG_INCLUDE,
415    TWIG_INCLUDE_WITH,
416    // twig use
417    TWIG_USE,
418    TWIG_OVERRIDE,
419    // twig apply
420    TWIG_APPLY,
421    TWIG_APPLY_STARTING_BLOCK,
422    TWIG_APPLY_ENDING_BLOCK,
423    // twig autoescape
424    TWIG_AUTOESCAPE,
425    TWIG_AUTOESCAPE_STARTING_BLOCK,
426    TWIG_AUTOESCAPE_ENDING_BLOCK,
427    // twig deprecated
428    TWIG_DEPRECATED,
429    // twig do
430    TWIG_DO,
431    // twig embed
432    TWIG_EMBED,
433    TWIG_EMBED_STARTING_BLOCK,
434    TWIG_EMBED_ENDING_BLOCK,
435    // twig flush
436    TWIG_FLUSH,
437    // twig from
438    TWIG_FROM, // shares TWIG_OVERRIDE with twig use tag
439    // twig import
440    TWIG_IMPORT,
441    // twig sandbox
442    TWIG_SANDBOX,
443    TWIG_SANDBOX_STARTING_BLOCK,
444    TWIG_SANDBOX_ENDING_BLOCK,
445    // twig verbatim
446    TWIG_VERBATIM,
447    TWIG_VERBATIM_STARTING_BLOCK,
448    TWIG_VERBATIM_ENDING_BLOCK,
449    // twig macro
450    TWIG_MACRO,
451    TWIG_MACRO_STARTING_BLOCK,
452    TWIG_MACRO_ENDING_BLOCK,
453    // twig with
454    TWIG_WITH,
455    TWIG_WITH_STARTING_BLOCK,
456    TWIG_WITH_ENDING_BLOCK,
457    // twig cache
458    TWIG_CACHE,
459    TWIG_CACHE_TTL,
460    TWIG_CACHE_TAGS,
461    TWIG_CACHE_STARTING_BLOCK,
462    TWIG_CACHE_ENDING_BLOCK,
463    // twig props
464    TWIG_PROPS,
465    TWIG_PROP_DECLARATION,
466    // twig component
467    TWIG_COMPONENT,
468    TWIG_COMPONENT_STARTING_BLOCK,
469    TWIG_COMPONENT_ENDING_BLOCK,
470
471    // Drupal Trans
472    TWIG_TRANS,
473    TWIG_TRANS_STARTING_BLOCK,
474    TWIG_TRANS_ENDING_BLOCK,
475
476    // shopware specific
477    SHOPWARE_TWIG_SW_EXTENDS,
478    SHOPWARE_TWIG_SW_INCLUDE,
479    SHOPWARE_SILENT_FEATURE_CALL,
480    SHOPWARE_SILENT_FEATURE_CALL_STARTING_BLOCK,
481    SHOPWARE_SILENT_FEATURE_CALL_ENDING_BLOCK,
482    SHOPWARE_RETURN,
483    SHOPWARE_ICON,
484    SHOPWARE_ICON_STYLE,
485    SHOPWARE_THUMBNAILS,
486    SHOPWARE_THUMBNAILS_WITH,
487
488    // html
489    HTML_DOCTYPE,
490    HTML_ATTRIBUTE_LIST,
491    HTML_ATTRIBUTE,
492    HTML_STRING,       // used as attribute values
493    HTML_STRING_INNER, // content inside the quotes of html attribute values
494    HTML_TEXT,         // used as plain text between html tags / twig blocks
495    HTML_RAW_TEXT, // used as raw text between "raw text" html elements like inline script and style
496    HTML_COMMENT,
497    HTML_TAG,
498    HTML_STARTING_TAG,
499    HTML_ENDING_TAG,
500
501    // special ludtwig directive
502    LUDTWIG_DIRECTIVE_FILE_IGNORE,
503    LUDTWIG_DIRECTIVE_IGNORE,
504    LUDTWIG_DIRECTIVE_RULE_LIST,
505    /*
506    Special Nodes
507     */
508    ERROR, // syntax node which wraps invalid syntax
509    /// SAFETY: this must be the last enum element for u16 conversion!
510    ROOT, // top-level node: list of elements inside the template (must be last item of enum for safety check!)
511}
512
513#[macro_export]
514macro_rules! T {
515    [ws] => { $crate::syntax::untyped::SyntaxKind::TK_WHITESPACE };
516    [lb] => { $crate::syntax::untyped::SyntaxKind::TK_LINE_BREAK };
517    [word] => { $crate::syntax::untyped::SyntaxKind::TK_WORD };
518    [number] => { $crate::syntax::untyped::SyntaxKind::TK_NUMBER };
519    [html escape character] => { $crate::syntax::untyped::SyntaxKind::TK_HTML_ESCAPE_CHARACTER };
520    [unknown] => { $crate::syntax::untyped::SyntaxKind::TK_UNKNOWN };
521    ["."] => { $crate::syntax::untyped::SyntaxKind::TK_DOT };
522    [".."] => { $crate::syntax::untyped::SyntaxKind::TK_DOUBLE_DOT };
523    [","] => { $crate::syntax::untyped::SyntaxKind::TK_COMMA };
524    [":"] => { $crate::syntax::untyped::SyntaxKind::TK_COLON };
525    [";"] => { $crate::syntax::untyped::SyntaxKind::TK_SEMICOLON };
526    ["!"] => { $crate::syntax::untyped::SyntaxKind::TK_EXCLAMATION_MARK };
527    ["!="] => { $crate::syntax::untyped::SyntaxKind::TK_EXCLAMATION_MARK_EQUALS };
528    ["!=="] => { $crate::syntax::untyped::SyntaxKind::TK_EXCLAMATION_MARK_DOUBLE_EQUALS };
529    ["?"] => { $crate::syntax::untyped::SyntaxKind::TK_QUESTION_MARK };
530    ["??"] => { $crate::syntax::untyped::SyntaxKind::TK_DOUBLE_QUESTION_MARK };
531    ["%"] => { $crate::syntax::untyped::SyntaxKind::TK_PERCENT };
532    ["~"] => { $crate::syntax::untyped::SyntaxKind::TK_TILDE };
533    ["|"] => { $crate::syntax::untyped::SyntaxKind::TK_SINGLE_PIPE };
534    ["||"] => { $crate::syntax::untyped::SyntaxKind::TK_DOUBLE_PIPE };
535    ["&"] => { $crate::syntax::untyped::SyntaxKind::TK_AMPERSAND };
536    ["&&"] => { $crate::syntax::untyped::SyntaxKind::TK_DOUBLE_AMPERSAND };
537    ["/"] => { $crate::syntax::untyped::SyntaxKind::TK_FORWARD_SLASH };
538    ["//"] => { $crate::syntax::untyped::SyntaxKind::TK_DOUBLE_FORWARD_SLASH };
539    ["\\"] => { $crate::syntax::untyped::SyntaxKind::TK_BACKWARD_SLASH };
540    ["("] => { $crate::syntax::untyped::SyntaxKind::TK_OPEN_PARENTHESIS };
541    [")"] => { $crate::syntax::untyped::SyntaxKind::TK_CLOSE_PARENTHESIS };
542    ["{"] => { $crate::syntax::untyped::SyntaxKind::TK_OPEN_CURLY };
543    ["}"] => { $crate::syntax::untyped::SyntaxKind::TK_CLOSE_CURLY };
544    ["["] => { $crate::syntax::untyped::SyntaxKind::TK_OPEN_SQUARE };
545    ["]"] => { $crate::syntax::untyped::SyntaxKind::TK_CLOSE_SQUARE };
546    ["<"] => { $crate::syntax::untyped::SyntaxKind::TK_LESS_THAN };
547    ["<="] => { $crate::syntax::untyped::SyntaxKind::TK_LESS_THAN_EQUAL };
548    ["<=>"] => { $crate::syntax::untyped::SyntaxKind::TK_LESS_THAN_EQUAL_GREATER_THAN };
549    ["</"] => { $crate::syntax::untyped::SyntaxKind::TK_LESS_THAN_SLASH };
550    ["<!"] => { $crate::syntax::untyped::SyntaxKind::TK_LESS_THAN_EXCLAMATION_MARK };
551    ["DOCTYPE"] => { $crate::syntax::untyped::SyntaxKind::TK_DOCTYPE };
552    [">"] => { $crate::syntax::untyped::SyntaxKind::TK_GREATER_THAN };
553    [">="] => { $crate::syntax::untyped::SyntaxKind::TK_GREATER_THAN_EQUAL };
554    ["=>"] => { $crate::syntax::untyped::SyntaxKind::TK_EQUAL_GREATER_THAN };
555    ["/>"] => { $crate::syntax::untyped::SyntaxKind::TK_SLASH_GREATER_THAN };
556    ["<!--"] => { $crate::syntax::untyped::SyntaxKind::TK_LESS_THAN_EXCLAMATION_MARK_MINUS_MINUS };
557    ["-->"] => { $crate::syntax::untyped::SyntaxKind::TK_MINUS_MINUS_GREATER_THAN };
558    ["="] => { $crate::syntax::untyped::SyntaxKind::TK_EQUAL };
559    ["=="] => { $crate::syntax::untyped::SyntaxKind::TK_DOUBLE_EQUAL };
560    ["==="] => { $crate::syntax::untyped::SyntaxKind::TK_TRIPLE_EQUAL };
561    ["+"] => { $crate::syntax::untyped::SyntaxKind::TK_PLUS };
562    ["-"] => { $crate::syntax::untyped::SyntaxKind::TK_MINUS };
563    ["*"] => { $crate::syntax::untyped::SyntaxKind::TK_STAR };
564    ["**"] => { $crate::syntax::untyped::SyntaxKind::TK_DOUBLE_STAR };
565    ["\""] => { $crate::syntax::untyped::SyntaxKind::TK_DOUBLE_QUOTES };
566    ["'"] => { $crate::syntax::untyped::SyntaxKind::TK_SINGLE_QUOTES };
567    ["`"] => { $crate::syntax::untyped::SyntaxKind::TK_GRAVE_ACCENT_QUOTES };
568    ["{%"] => { $crate::syntax::untyped::SyntaxKind::TK_CURLY_PERCENT };
569    ["%}"] => { $crate::syntax::untyped::SyntaxKind::TK_PERCENT_CURLY };
570    ["{{"] => { $crate::syntax::untyped::SyntaxKind::TK_OPEN_CURLY_CURLY };
571    ["}}"] => { $crate::syntax::untyped::SyntaxKind::TK_CLOSE_CURLY_CURLY };
572    ["{#"] => { $crate::syntax::untyped::SyntaxKind::TK_OPEN_CURLY_HASHTAG };
573    ["#}"] => { $crate::syntax::untyped::SyntaxKind::TK_HASHTAG_CLOSE_CURLY };
574    ["#"] => { $crate::syntax::untyped::SyntaxKind::TK_HASHTAG };
575    ["true"] => { $crate::syntax::untyped::SyntaxKind::TK_TRUE };
576    ["false"] => { $crate::syntax::untyped::SyntaxKind::TK_FALSE };
577    ["block"] => { $crate::syntax::untyped::SyntaxKind::TK_BLOCK };
578    ["endblock"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDBLOCK };
579    ["if"] => { $crate::syntax::untyped::SyntaxKind::TK_IF };
580    ["elseif"] => { $crate::syntax::untyped::SyntaxKind::TK_ELSE_IF };
581    ["else"] => { $crate::syntax::untyped::SyntaxKind::TK_ELSE };
582    ["endif"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDIF };
583    ["apply"] => { $crate::syntax::untyped::SyntaxKind::TK_APPLY };
584    ["endapply"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDAPPLY };
585    ["autoescape"] => { $crate::syntax::untyped::SyntaxKind::TK_AUTOESCAPE };
586    ["endautoescape"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDAUTOESCAPE };
587    ["cache"] => { $crate::syntax::untyped::SyntaxKind::TK_CACHE };
588    ["endcache"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDCACHE };
589    ["deprecated"] => { $crate::syntax::untyped::SyntaxKind::TK_DEPRECATED };
590    ["do"] => { $crate::syntax::untyped::SyntaxKind::TK_DO };
591    ["embed"] => { $crate::syntax::untyped::SyntaxKind::TK_EMBED };
592    ["endembed"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDEMBED };
593    ["extends"] => { $crate::syntax::untyped::SyntaxKind::TK_EXTENDS };
594    ["flush"] => { $crate::syntax::untyped::SyntaxKind::TK_FLUSH };
595    ["for"] => { $crate::syntax::untyped::SyntaxKind::TK_FOR };
596    ["endfor"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDFOR };
597    ["from"] => { $crate::syntax::untyped::SyntaxKind::TK_FROM };
598    ["import"] => { $crate::syntax::untyped::SyntaxKind::TK_IMPORT };
599    ["macro"] => { $crate::syntax::untyped::SyntaxKind::TK_MACRO };
600    ["endmacro"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDMACRO };
601    ["sandbox"] => { $crate::syntax::untyped::SyntaxKind::TK_SANDBOX };
602    ["endsandbox"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDSANDBOX };
603    ["set"] => { $crate::syntax::untyped::SyntaxKind::TK_SET };
604    ["endset"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDSET };
605    ["use"] => { $crate::syntax::untyped::SyntaxKind::TK_USE };
606    ["verbatim"] => { $crate::syntax::untyped::SyntaxKind::TK_VERBATIM };
607    ["endverbatim"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDVERBATIM };
608    ["only"] => { $crate::syntax::untyped::SyntaxKind::TK_ONLY };
609    ["ignore missing"] => { $crate::syntax::untyped::SyntaxKind::TK_IGNORE_MISSING };
610    ["with"] => { $crate::syntax::untyped::SyntaxKind::TK_WITH };
611    ["endwith"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDWITH };
612    ["ttl"] => { $crate::syntax::untyped::SyntaxKind::TK_TTL };
613    ["tags"] => { $crate::syntax::untyped::SyntaxKind::TK_TAGS };
614    ["props"] => { $crate::syntax::untyped::SyntaxKind::TK_PROPS };
615    ["component"] => { $crate::syntax::untyped::SyntaxKind::TK_COMPONENT };
616    ["endcomponent"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDCOMPONENT };
617    ["not"] => { $crate::syntax::untyped::SyntaxKind::TK_NOT };
618    ["or"] => { $crate::syntax::untyped::SyntaxKind::TK_OR };
619    ["and"] => { $crate::syntax::untyped::SyntaxKind::TK_AND };
620    ["b-or"] => { $crate::syntax::untyped::SyntaxKind::TK_BINARY_OR };
621    ["b-xor"] => { $crate::syntax::untyped::SyntaxKind::TK_BINARY_XOR };
622    ["b-and"] => { $crate::syntax::untyped::SyntaxKind::TK_BINARY_AND };
623    ["in"] => { $crate::syntax::untyped::SyntaxKind::TK_IN };
624    ["matches"] => { $crate::syntax::untyped::SyntaxKind::TK_MATCHES };
625    ["starts with"] => { $crate::syntax::untyped::SyntaxKind::TK_STARTS_WITH };
626    ["ends with"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDS_WITH };
627    ["is"] => { $crate::syntax::untyped::SyntaxKind::TK_IS };
628    ["even"] => { $crate::syntax::untyped::SyntaxKind::TK_EVEN };
629    ["odd"] => { $crate::syntax::untyped::SyntaxKind::TK_ODD };
630    ["defined"] => { $crate::syntax::untyped::SyntaxKind::TK_DEFINED };
631    ["same as"] => { $crate::syntax::untyped::SyntaxKind::TK_SAME_AS };
632    ["as"] => { $crate::syntax::untyped::SyntaxKind::TK_AS };
633    ["none"] => { $crate::syntax::untyped::SyntaxKind::TK_NONE };
634    ["null"] => { $crate::syntax::untyped::SyntaxKind::TK_NULL };
635    ["divisible by"] => { $crate::syntax::untyped::SyntaxKind::TK_DIVISIBLE_BY };
636    ["constant"] => { $crate::syntax::untyped::SyntaxKind::TK_CONSTANT };
637    ["empty"] => { $crate::syntax::untyped::SyntaxKind::TK_EMPTY };
638    ["iterable"] => { $crate::syntax::untyped::SyntaxKind::TK_ITERABLE };
639    ["max"] => { $crate::syntax::untyped::SyntaxKind::TK_MAX };
640    ["min"] => { $crate::syntax::untyped::SyntaxKind::TK_MIN };
641    ["range"] => { $crate::syntax::untyped::SyntaxKind::TK_RANGE };
642    ["cycle"] => { $crate::syntax::untyped::SyntaxKind::TK_CYCLE };
643    ["random"] => { $crate::syntax::untyped::SyntaxKind::TK_RANDOM };
644    ["date"] => { $crate::syntax::untyped::SyntaxKind::TK_DATE };
645    ["include"] => { $crate::syntax::untyped::SyntaxKind::TK_INCLUDE };
646    ["source"] => { $crate::syntax::untyped::SyntaxKind::TK_SOURCE };
647    ["trans"] => { $crate::syntax::untyped::SyntaxKind::TK_TRANS };
648    ["endtrans"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDTRANS };
649    ["sw_extends"] => { $crate::syntax::untyped::SyntaxKind::TK_SW_EXTENDS };
650    ["sw_silent_feature_call"] => { $crate::syntax::untyped::SyntaxKind::TK_SW_SILENT_FEATURE_CALL };
651    ["endsw_silent_feature_call"] => { $crate::syntax::untyped::SyntaxKind::TK_ENDSW_SILENT_FEATURE_CALL };
652    ["sw_include"] => { $crate::syntax::untyped::SyntaxKind::TK_SW_INCLUDE };
653    ["return"] => { $crate::syntax::untyped::SyntaxKind::TK_RETURN };
654    ["sw_icon"] => { $crate::syntax::untyped::SyntaxKind::TK_SW_ICON };
655    ["sw_thumbnails"] => { $crate::syntax::untyped::SyntaxKind::TK_SW_THUMBNAILS };
656    ["style"] => { $crate::syntax::untyped::SyntaxKind::TK_STYLE };
657    ["ludtwig-ignore-file"] => { $crate::syntax::untyped::SyntaxKind::TK_LUDTWIG_IGNORE_FILE };
658    ["ludtwig-ignore"] => { $crate::syntax::untyped::SyntaxKind::TK_LUDTWIG_IGNORE };
659}
660
661impl SyntaxKind {
662    #[must_use]
663    pub fn is_trivia(self) -> bool {
664        // Add comments and other non interesting things for the parser here in the future
665        matches!(self, T![ws] | T![lb])
666    }
667}
668
669#[allow(clippy::too_many_lines)]
670impl fmt::Display for SyntaxKind {
671    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
672        f.write_str(match self {
673            SyntaxKind::TK_WHITESPACE => "whitespace",
674            SyntaxKind::TK_LINE_BREAK => "line break",
675            SyntaxKind::TK_WORD => "word",
676            SyntaxKind::TK_NUMBER => "number",
677            SyntaxKind::TK_HTML_ESCAPE_CHARACTER => "html escape character",
678            SyntaxKind::TK_DOT => ".",
679            SyntaxKind::TK_DOUBLE_DOT => "..",
680            SyntaxKind::TK_COMMA => ",",
681            SyntaxKind::TK_COLON => ":",
682            SyntaxKind::TK_SEMICOLON => ";",
683            SyntaxKind::TK_EXCLAMATION_MARK => "!",
684            SyntaxKind::TK_EXCLAMATION_MARK_EQUALS => "!=",
685            SyntaxKind::TK_EXCLAMATION_MARK_DOUBLE_EQUALS => "!==",
686            SyntaxKind::TK_QUESTION_MARK => "?",
687            SyntaxKind::TK_DOUBLE_QUESTION_MARK => "??",
688            SyntaxKind::TK_PERCENT => "%",
689            SyntaxKind::TK_TILDE => "~",
690            SyntaxKind::TK_SINGLE_PIPE => "|",
691            SyntaxKind::TK_DOUBLE_PIPE => "||",
692            SyntaxKind::TK_AMPERSAND => "&",
693            SyntaxKind::TK_DOUBLE_AMPERSAND => "&&",
694            SyntaxKind::TK_FORWARD_SLASH => "/",
695            SyntaxKind::TK_DOUBLE_FORWARD_SLASH => "//",
696            SyntaxKind::TK_BACKWARD_SLASH => "\\",
697            SyntaxKind::TK_OPEN_PARENTHESIS => "(",
698            SyntaxKind::TK_CLOSE_PARENTHESIS => ")",
699            SyntaxKind::TK_OPEN_CURLY => "{",
700            SyntaxKind::TK_CLOSE_CURLY => "}",
701            SyntaxKind::TK_OPEN_SQUARE => "[",
702            SyntaxKind::TK_CLOSE_SQUARE => "]",
703            SyntaxKind::TK_LESS_THAN => "<",
704            SyntaxKind::TK_LESS_THAN_EQUAL => "<=",
705            SyntaxKind::TK_LESS_THAN_EQUAL_GREATER_THAN => "<=>",
706            SyntaxKind::TK_LESS_THAN_SLASH => "</",
707            SyntaxKind::TK_LESS_THAN_EXCLAMATION_MARK => "<!",
708            SyntaxKind::TK_DOCTYPE => "DOCTYPE",
709            SyntaxKind::TK_GREATER_THAN => ">",
710            SyntaxKind::TK_GREATER_THAN_EQUAL => ">=",
711            SyntaxKind::TK_EQUAL_GREATER_THAN => "=>",
712            SyntaxKind::TK_SLASH_GREATER_THAN => "/>",
713            SyntaxKind::TK_LESS_THAN_EXCLAMATION_MARK_MINUS_MINUS => "<!--",
714            SyntaxKind::TK_MINUS_MINUS_GREATER_THAN => "-->",
715            SyntaxKind::TK_EQUAL => "=",
716            SyntaxKind::TK_DOUBLE_EQUAL => "==",
717            SyntaxKind::TK_TRIPLE_EQUAL => "===",
718            SyntaxKind::TK_PLUS => "+",
719            SyntaxKind::TK_MINUS => "-",
720            SyntaxKind::TK_STAR => "*",
721            SyntaxKind::TK_DOUBLE_STAR => "**",
722            SyntaxKind::TK_DOUBLE_QUOTES => "\"",
723            SyntaxKind::TK_SINGLE_QUOTES => "'",
724            SyntaxKind::TK_GRAVE_ACCENT_QUOTES => "`",
725            SyntaxKind::TK_CURLY_PERCENT => "{%",
726            SyntaxKind::TK_PERCENT_CURLY => "%}",
727            SyntaxKind::TK_OPEN_CURLY_CURLY => "{{",
728            SyntaxKind::TK_CLOSE_CURLY_CURLY => "}}",
729            SyntaxKind::TK_OPEN_CURLY_HASHTAG => "{#",
730            SyntaxKind::TK_HASHTAG_CLOSE_CURLY => "#}",
731            SyntaxKind::TK_HASHTAG => "#",
732            SyntaxKind::TK_TRUE => "true",
733            SyntaxKind::TK_FALSE => "false",
734            SyntaxKind::TK_BLOCK => "block",
735            SyntaxKind::TK_ENDBLOCK => "endblock",
736            SyntaxKind::TK_IF => "if",
737            SyntaxKind::TK_ELSE_IF => "elseif",
738            SyntaxKind::TK_ELSE => "else",
739            SyntaxKind::TK_ENDIF => "endif",
740            SyntaxKind::TK_APPLY => "apply",
741            SyntaxKind::TK_ENDAPPLY => "endapply",
742            SyntaxKind::TK_AUTOESCAPE => "autoescape",
743            SyntaxKind::TK_ENDAUTOESCAPE => "endautoescape",
744            SyntaxKind::TK_CACHE => "cache",
745            SyntaxKind::TK_ENDCACHE => "endcache",
746            SyntaxKind::TK_DEPRECATED => "deprecated",
747            SyntaxKind::TK_DO => "do",
748            SyntaxKind::TK_EMBED => "embed",
749            SyntaxKind::TK_ENDEMBED => "endembed",
750            SyntaxKind::TK_EXTENDS => "extends",
751            SyntaxKind::TK_FLUSH => "flush",
752            SyntaxKind::TK_FOR => "for",
753            SyntaxKind::TK_ENDFOR => "endfor",
754            SyntaxKind::TK_FROM => "from",
755            SyntaxKind::TK_IMPORT => "import",
756            SyntaxKind::TK_MACRO => "macro",
757            SyntaxKind::TK_ENDMACRO => "endmacro",
758            SyntaxKind::TK_SANDBOX => "sandbox",
759            SyntaxKind::TK_ENDSANDBOX => "endsandbox",
760            SyntaxKind::TK_SET => "set",
761            SyntaxKind::TK_ENDSET => "endset",
762            SyntaxKind::TK_USE => "use",
763            SyntaxKind::TK_VERBATIM => "verbatim",
764            SyntaxKind::TK_ENDVERBATIM => "endverbatim",
765            SyntaxKind::TK_ONLY => "only",
766            SyntaxKind::TK_IGNORE_MISSING => "ignore missing",
767            SyntaxKind::TK_WITH => "with",
768            SyntaxKind::TK_ENDWITH => "endwith",
769            SyntaxKind::TK_TTL => "ttl",
770            SyntaxKind::TK_TAGS => "tags",
771            SyntaxKind::TK_PROPS => "props",
772            SyntaxKind::TK_COMPONENT => "component",
773            SyntaxKind::TK_ENDCOMPONENT => "endcomponent",
774            SyntaxKind::TK_NOT => "not",
775            SyntaxKind::TK_OR => "or",
776            SyntaxKind::TK_AND => "and",
777            SyntaxKind::TK_BINARY_OR => "b-or",
778            SyntaxKind::TK_BINARY_XOR => "b-xor",
779            SyntaxKind::TK_BINARY_AND => "b-and",
780            SyntaxKind::TK_IN => "in",
781            SyntaxKind::TK_MATCHES => "matches",
782            SyntaxKind::TK_STARTS_WITH => "starts with",
783            SyntaxKind::TK_ENDS_WITH => "ends with",
784            SyntaxKind::TK_IS => "is",
785            SyntaxKind::TK_EVEN => "even",
786            SyntaxKind::TK_ODD => "odd",
787            SyntaxKind::TK_DEFINED => "defined",
788            SyntaxKind::TK_SAME_AS => "same as",
789            SyntaxKind::TK_AS => "as",
790            SyntaxKind::TK_NONE => "none",
791            SyntaxKind::TK_NULL => "null",
792            SyntaxKind::TK_DIVISIBLE_BY => "divisible by",
793            SyntaxKind::TK_CONSTANT => "constant",
794            SyntaxKind::TK_EMPTY => "empty",
795            SyntaxKind::TK_ITERABLE => "iterable",
796            SyntaxKind::TK_MAX => "max",
797            SyntaxKind::TK_MIN => "min",
798            SyntaxKind::TK_RANGE => "range",
799            SyntaxKind::TK_CYCLE => "cycle",
800            SyntaxKind::TK_RANDOM => "random",
801            SyntaxKind::TK_DATE => "date",
802            SyntaxKind::TK_INCLUDE => "include",
803            SyntaxKind::TK_SOURCE => "source",
804            SyntaxKind::TK_TRANS => "trans",
805            SyntaxKind::TK_ENDTRANS => "endtrans",
806            SyntaxKind::TK_SW_EXTENDS => "sw_extends",
807            SyntaxKind::TK_SW_SILENT_FEATURE_CALL => "sw_silent_feature_call",
808            SyntaxKind::TK_ENDSW_SILENT_FEATURE_CALL => "endsw_silent_feature_call",
809            SyntaxKind::TK_SW_INCLUDE => "sw_include",
810            SyntaxKind::TK_RETURN => "return",
811            SyntaxKind::TK_SW_ICON => "sw_icon",
812            SyntaxKind::TK_SW_THUMBNAILS => "sw_thumbnails",
813            SyntaxKind::TK_STYLE => "style",
814            SyntaxKind::TK_LUDTWIG_IGNORE_FILE => "ludtwig-ignore-file",
815            SyntaxKind::TK_LUDTWIG_IGNORE => "ludtwig-ignore",
816            SyntaxKind::TK_UNKNOWN => "unknown",
817            SyntaxKind::ERROR => "error",
818            t => unreachable!("Display not implemented for {:?}", t),
819        })
820    }
821}
822
823// Some boilerplate is needed, as rowan settled on using its own
824// `struct SyntaxKind(u16)` internally, instead of accepting the
825// user's `enum SyntaxKind` as a type parameter.
826//
827// First, to easily pass the enum variants into rowan via `.into()`:
828impl From<SyntaxKind> for rowan::SyntaxKind {
829    fn from(kind: SyntaxKind) -> Self {
830        Self(kind as u16)
831    }
832}
833
834// Second, implementing the `Language` trait teaches rowan to convert between
835// these two `SyntaxKind` types, allowing for a nicer `SyntaxNode` API where
836// "kinds" are values from our `enum SyntaxKind`, instead of plain u16 values.
837#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
838pub enum TemplateLanguage {}
839impl Language for TemplateLanguage {
840    type Kind = SyntaxKind;
841    fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
842        assert!(raw.0 <= SyntaxKind::ROOT as u16);
843        unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
844    }
845    fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
846        kind.into()
847    }
848}
849
850// To work with the parse results we need a view into the
851// green tree - the Syntax tree.
852// It is also immutable, like a `GreenNode`,
853// but it contains parent pointers, offsets, and
854// has identity semantics.
855pub type SyntaxNode = rowan::SyntaxNode<TemplateLanguage>;
856pub type SyntaxToken = rowan::SyntaxToken<TemplateLanguage>;
857pub type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
858pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<TemplateLanguage>;
859pub type SyntaxElementChildren = rowan::SyntaxElementChildren<TemplateLanguage>;
860pub type Preorder = rowan::api::Preorder<TemplateLanguage>;
861pub type PreorderWithTokens = rowan::api::PreorderWithTokens<TemplateLanguage>;
862
863#[must_use]
864pub fn debug_tree(syntax_node: &SyntaxNode) -> String {
865    let formatted = format!("{syntax_node:#?}");
866    // We cut off the last byte because formatting the SyntaxNode adds on a newline at the end.
867    formatted[0..formatted.len() - 1].to_string()
868}
869
870pub trait SyntaxNodeExt {
871    fn text_range_trimmed_trivia(&self) -> TextRange;
872}
873
874impl SyntaxNodeExt for SyntaxNode {
875    /// Trims leading trivia from the original `text_range`
876    fn text_range_trimmed_trivia(&self) -> TextRange {
877        let mut range = self.text_range();
878
879        for element in self.children_with_tokens() {
880            match element {
881                SyntaxElement::Token(t) if t.kind().is_trivia() => {
882                    let new_start = range.start() + t.text_range().len();
883                    if new_start < range.end() {
884                        range = TextRange::new(new_start, range.end());
885                    }
886                }
887                _ => return range,
888            }
889        }
890
891        range
892    }
893}