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