Skip to main content

typst_syntax/
kind.rs

1use crate::SyntaxMode;
2
3/// A syntactical building block of a Typst file.
4///
5/// Can be created by the lexer or by the parser.
6#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
7#[repr(u8)]
8pub enum SyntaxKind {
9    /// The end of token stream.
10    End,
11    /// An invalid sequence of characters.
12    Error,
13
14    /// A shebang: `#! ...`
15    Shebang,
16    /// A line comment: `// ...`.
17    LineComment,
18    /// A block comment: `/* ... */`.
19    BlockComment,
20
21    /// The contents of a file or content block.
22    Markup,
23    /// Plain text without markup.
24    Text,
25    /// Whitespace. Contains at most one newline in markup, as more indicate a
26    /// paragraph break.
27    Space,
28    /// A forced line break: `\`.
29    Linebreak,
30    /// A paragraph break, indicated by one or multiple blank lines.
31    Parbreak,
32    /// An escape sequence: `\#`, `\u{1F5FA}`.
33    Escape,
34    /// A shorthand for a unicode codepoint. For example, `~` for non-breaking
35    /// space or `-?` for a soft hyphen.
36    Shorthand,
37    /// A smart quote: `'` or `"`.
38    SmartQuote,
39    /// Strong content: `*Strong*`.
40    Strong,
41    /// Emphasized content: `_Emphasized_`.
42    Emph,
43    /// Raw text with optional syntax highlighting: `` `...` ``.
44    Raw,
45    /// A language tag at the start of raw text: ``typ ``.
46    RawLang,
47    /// A raw delimiter consisting of 1 or 3+ backticks: `` ` ``.
48    RawDelim,
49    /// A sequence of whitespace to ignore in a raw text: `    `.
50    RawTrimmed,
51    /// A hyperlink: `https://typst.org`.
52    Link,
53    /// A label: `<intro>`.
54    Label,
55    /// A reference: `@target`, `@target[..]`.
56    Ref,
57    /// Introduces a reference: `@target`.
58    RefMarker,
59    /// A section heading: `= Introduction`.
60    Heading,
61    /// Introduces a section heading: `=`, `==`, ...
62    HeadingMarker,
63    /// An item in a bullet list: `- ...`.
64    ListItem,
65    /// Introduces a list item: `-`.
66    ListMarker,
67    /// An item in an enumeration (numbered list): `+ ...` or `1. ...`.
68    EnumItem,
69    /// Introduces an enumeration item: `+`, `1.`.
70    EnumMarker,
71    /// An item in a term list: `/ Term: Details`.
72    TermItem,
73    /// Introduces a term item: `/`.
74    TermMarker,
75
76    /// A mathematical equation: `$x$`, `$ x^2 $`.
77    Equation,
78    /// The contents of a mathematical equation: `x^2 + 1`.
79    Math,
80    /// A lone text fragment in math: `x`, `25`, `3.1415`, `=`, `|`, `[`.
81    MathText,
82    /// An identifier in math: `pi`.
83    MathIdent,
84    /// A field access in math: `arrow.r.long.double.bar`.
85    MathFieldAccess,
86    /// A shorthand for a unicode codepoint in math: `a <= b`.
87    MathShorthand,
88    /// An alignment point in math: `&`.
89    MathAlignPoint,
90    /// A function call in math: `mat(delim: "[", a, b; ..#($c$,), d)`.
91    MathCall,
92    /// Function arguments in math: `(delim: "[", a, b; ..#($c$,), d)`.
93    MathArgs,
94    /// Matched delimiters in math: `[x + y]`.
95    MathDelimited,
96    /// A base with optional attachments in math: `a_1^2`.
97    MathAttach,
98    /// Grouped primes in math: `a'''`.
99    MathPrimes,
100    /// A fraction in math: `x/2`.
101    MathFrac,
102    /// A root in math: `√x`, `∛x` or `∜x`.
103    MathRoot,
104
105    /// A hash that switches into code mode: `#`.
106    Hash,
107    /// A left curly brace, starting a code block: `{`.
108    LeftBrace,
109    /// A right curly brace, terminating a code block: `}`.
110    RightBrace,
111    /// A left square bracket, starting a content block: `[`.
112    LeftBracket,
113    /// A right square bracket, terminating a content block: `]`.
114    RightBracket,
115    /// A left round parenthesis, starting a grouped expression, collection,
116    /// argument or parameter list: `(`.
117    LeftParen,
118    /// A right round parenthesis, terminating a grouped expression, collection,
119    /// argument or parameter list: `)`.
120    RightParen,
121    /// A comma separator in a sequence: `,`.
122    Comma,
123    /// A semicolon terminating an expression: `;`.
124    Semicolon,
125    /// A colon between name/key and value in a dictionary, argument or
126    /// parameter list, or between the term and body of a term list term: `:`.
127    Colon,
128    /// The strong text toggle, multiplication operator, and wildcard import
129    /// symbol: `*`.
130    Star,
131    /// Toggles emphasized text and indicates a subscript in math: `_`.
132    Underscore,
133    /// Starts and ends a mathematical equation: `$`.
134    Dollar,
135    /// The unary plus and binary addition operator: `+`.
136    Plus,
137    /// The unary negation and binary subtraction operator: `-`.
138    Minus,
139    /// The division operator and fraction operator in math: `/`.
140    Slash,
141    /// The superscript operator in math: `^`.
142    Hat,
143    /// The field access and method call operator: `.`.
144    Dot,
145    /// The assignment operator: `=`.
146    Eq,
147    /// The equality operator: `==`.
148    EqEq,
149    /// The inequality operator: `!=`.
150    ExclEq,
151    /// The less-than operator: `<`.
152    Lt,
153    /// The less-than or equal operator: `<=`.
154    LtEq,
155    /// The greater-than operator: `>`.
156    Gt,
157    /// The greater-than or equal operator: `>=`.
158    GtEq,
159    /// The add-assign operator: `+=`.
160    PlusEq,
161    /// The subtract-assign operator: `-=`.
162    HyphEq,
163    /// The multiply-assign operator: `*=`.
164    StarEq,
165    /// The divide-assign operator: `/=`.
166    SlashEq,
167    /// Indicates a spread or sink: `..`.
168    Dots,
169    /// An arrow between a closure's parameters and body: `=>`.
170    Arrow,
171    /// A root: `√`, `∛` or `∜`.
172    Root,
173    /// An exclamation mark; groups with directly preceding text in math: `!`.
174    Bang,
175
176    /// The `not` operator.
177    Not,
178    /// The `and` operator.
179    And,
180    /// The `or` operator.
181    Or,
182    /// The `none` literal.
183    None,
184    /// The `auto` literal.
185    Auto,
186    /// The `let` keyword.
187    Let,
188    /// The `set` keyword.
189    Set,
190    /// The `show` keyword.
191    Show,
192    /// The `context` keyword.
193    Context,
194    /// The `if` keyword.
195    If,
196    /// The `else` keyword.
197    Else,
198    /// The `for` keyword.
199    For,
200    /// The `in` keyword.
201    In,
202    /// The `while` keyword.
203    While,
204    /// The `break` keyword.
205    Break,
206    /// The `continue` keyword.
207    Continue,
208    /// The `return` keyword.
209    Return,
210    /// The `import` keyword.
211    Import,
212    /// The `include` keyword.
213    Include,
214    /// The `as` keyword.
215    As,
216
217    /// The contents of a code block.
218    Code,
219    /// An identifier: `it`.
220    Ident,
221    /// A boolean: `true`, `false`.
222    Bool,
223    /// An integer: `120`.
224    Int,
225    /// A floating-point number: `1.2`, `10e-4`.
226    Float,
227    /// A numeric value with a unit: `12pt`, `3cm`, `2em`, `90deg`, `50%`.
228    Numeric,
229    /// A quoted string: `"..."`.
230    Str,
231    /// A code block: `{ let x = 1; x + 2 }`.
232    CodeBlock,
233    /// A content block: `[*Hi* there!]`.
234    ContentBlock,
235    /// A grouped expression: `(1 + 2)`.
236    Parenthesized,
237    /// An array: `(1, "hi", 12cm)`.
238    Array,
239    /// A dictionary: `(thickness: 3pt, dash: "solid")`.
240    Dict,
241    /// A named pair: `thickness: 3pt`.
242    Named,
243    /// A keyed pair: `"spacy key": true`.
244    Keyed,
245    /// A unary operation: `-x`.
246    Unary,
247    /// A binary operation: `a + b`.
248    Binary,
249    /// A field access: `properties.age`.
250    FieldAccess,
251    /// An invocation of a function or method: `f(x, y)`.
252    FuncCall,
253    /// A function call's argument list: `(12pt, y)`.
254    Args,
255    /// Spread arguments or an argument sink: `..x`.
256    Spread,
257    /// A closure: `(x, y) => z`.
258    Closure,
259    /// A closure's parameters: `(x, y)`.
260    Params,
261    /// A let binding: `let x = 1`.
262    LetBinding,
263    /// A set rule: `set text(...)`.
264    SetRule,
265    /// A show rule: `show heading: it => emph(it.body)`.
266    ShowRule,
267    /// A contextual expression: `context text.lang`.
268    Contextual,
269    /// An if-else conditional: `if x { y } else { z }`.
270    Conditional,
271    /// A while loop: `while x { y }`.
272    WhileLoop,
273    /// A for loop: `for x in y { z }`.
274    ForLoop,
275    /// A module import: `import "utils.typ": a, b, c`.
276    ModuleImport,
277    /// Items to import from a module: `a, b, c`.
278    ImportItems,
279    /// A path to an imported name from a submodule: `a.b.c`.
280    ImportItemPath,
281    /// A renamed import item: `a as d`.
282    RenamedImportItem,
283    /// A module include: `include "chapter1.typ"`.
284    ModuleInclude,
285    /// A break from a loop: `break`.
286    LoopBreak,
287    /// A continue in a loop: `continue`.
288    LoopContinue,
289    /// A return from a function: `return`, `return x + 1`.
290    FuncReturn,
291    /// A destructuring pattern: `(x, _, ..y)`.
292    Destructuring,
293    /// A destructuring assignment expression: `(x, y) = (1, 2)`.
294    DestructAssignment,
295}
296
297impl SyntaxKind {
298    /// Is this a bracket, brace, or parenthesis?
299    pub fn is_grouping(self) -> bool {
300        matches!(
301            self,
302            Self::LeftBracket
303                | Self::LeftBrace
304                | Self::LeftParen
305                | Self::RightBracket
306                | Self::RightBrace
307                | Self::RightParen
308        )
309    }
310
311    /// Does this node terminate a preceding expression?
312    pub fn is_terminator(self) -> bool {
313        matches!(
314            self,
315            Self::End
316                | Self::Semicolon
317                | Self::RightBrace
318                | Self::RightParen
319                | Self::RightBracket
320        )
321    }
322
323    /// Is this a code or content block.
324    pub fn is_block(self) -> bool {
325        matches!(self, Self::CodeBlock | Self::ContentBlock)
326    }
327
328    /// Does this node need termination through a semicolon or linebreak?
329    pub fn is_stmt(self) -> bool {
330        matches!(
331            self,
332            Self::LetBinding
333                | Self::SetRule
334                | Self::ShowRule
335                | Self::ModuleImport
336                | Self::ModuleInclude
337        )
338    }
339
340    /// Is this node is a keyword.
341    pub fn is_keyword(self) -> bool {
342        matches!(
343            self,
344            Self::Not
345                | Self::And
346                | Self::Or
347                | Self::None
348                | Self::Auto
349                | Self::Let
350                | Self::Set
351                | Self::Show
352                | Self::Context
353                | Self::If
354                | Self::Else
355                | Self::For
356                | Self::In
357                | Self::While
358                | Self::Break
359                | Self::Continue
360                | Self::Return
361                | Self::Import
362                | Self::Include
363                | Self::As
364        )
365    }
366
367    /// Whether this kind of node is automatically skipped by the parser in
368    /// code and math mode.
369    pub fn is_trivia(self) -> bool {
370        matches!(
371            self,
372            Self::Shebang
373                | Self::LineComment
374                | Self::BlockComment
375                | Self::Space
376                | Self::Parbreak
377        )
378    }
379
380    /// Whether this is an error.
381    pub const fn is_error(self) -> bool {
382        matches!(self, Self::Error)
383    }
384
385    /// A human-readable name for the kind.
386    pub fn name(self) -> &'static str {
387        match self {
388            Self::End => "end of tokens",
389            Self::Error => "syntax error",
390            Self::Shebang => "shebang",
391            Self::LineComment => "line comment",
392            Self::BlockComment => "block comment",
393            Self::Markup => "markup",
394            Self::Text => "text",
395            Self::Space => "space",
396            Self::Linebreak => "line break",
397            Self::Parbreak => "paragraph break",
398            Self::Escape => "escape sequence",
399            Self::Shorthand => "shorthand",
400            Self::SmartQuote => "smart quote",
401            Self::Strong => "strong content",
402            Self::Emph => "emphasized content",
403            Self::Raw => "raw block",
404            Self::RawLang => "raw language tag",
405            Self::RawTrimmed => "raw trimmed",
406            Self::RawDelim => "raw delimiter",
407            Self::Link => "link",
408            Self::Label => "label",
409            Self::Ref => "reference",
410            Self::RefMarker => "reference marker",
411            Self::Heading => "heading",
412            Self::HeadingMarker => "heading marker",
413            Self::ListItem => "list item",
414            Self::ListMarker => "list marker",
415            Self::EnumItem => "enum item",
416            Self::EnumMarker => "enum marker",
417            Self::TermItem => "term list item",
418            Self::TermMarker => "term marker",
419            Self::Equation => "equation",
420            Self::Math => "math",
421            Self::MathText => "math text",
422            Self::MathIdent => "math identifier",
423            Self::MathFieldAccess => "math field access",
424            Self::MathShorthand => "math shorthand",
425            Self::MathAlignPoint => "math alignment point",
426            Self::MathCall => "math function call",
427            Self::MathArgs => "math call arguments",
428            Self::MathDelimited => "delimited math",
429            Self::MathAttach => "math attachments",
430            Self::MathFrac => "math fraction",
431            Self::MathRoot => "math root",
432            Self::MathPrimes => "math primes",
433            Self::Hash => "hash",
434            Self::LeftBrace => "opening brace",
435            Self::RightBrace => "closing brace",
436            Self::LeftBracket => "opening bracket",
437            Self::RightBracket => "closing bracket",
438            Self::LeftParen => "opening paren",
439            Self::RightParen => "closing paren",
440            Self::Comma => "comma",
441            Self::Semicolon => "semicolon",
442            Self::Colon => "colon",
443            Self::Star => "star",
444            Self::Underscore => "underscore",
445            Self::Dollar => "dollar sign",
446            Self::Plus => "plus",
447            Self::Minus => "minus",
448            Self::Slash => "slash",
449            Self::Hat => "hat",
450            Self::Dot => "dot",
451            Self::Eq => "equals sign",
452            Self::EqEq => "equality operator",
453            Self::ExclEq => "inequality operator",
454            Self::Lt => "less-than operator",
455            Self::LtEq => "less-than or equal operator",
456            Self::Gt => "greater-than operator",
457            Self::GtEq => "greater-than or equal operator",
458            Self::PlusEq => "add-assign operator",
459            Self::HyphEq => "subtract-assign operator",
460            Self::StarEq => "multiply-assign operator",
461            Self::SlashEq => "divide-assign operator",
462            Self::Dots => "dots",
463            Self::Arrow => "arrow",
464            Self::Root => "root",
465            Self::Bang => "exclamation mark",
466            Self::Not => "operator `not`",
467            Self::And => "operator `and`",
468            Self::Or => "operator `or`",
469            Self::None => "`none`",
470            Self::Auto => "`auto`",
471            Self::Let => "keyword `let`",
472            Self::Set => "keyword `set`",
473            Self::Show => "keyword `show`",
474            Self::Context => "keyword `context`",
475            Self::If => "keyword `if`",
476            Self::Else => "keyword `else`",
477            Self::For => "keyword `for`",
478            Self::In => "keyword `in`",
479            Self::While => "keyword `while`",
480            Self::Break => "keyword `break`",
481            Self::Continue => "keyword `continue`",
482            Self::Return => "keyword `return`",
483            Self::Import => "keyword `import`",
484            Self::Include => "keyword `include`",
485            Self::As => "keyword `as`",
486            Self::Code => "code",
487            Self::Ident => "identifier",
488            Self::Bool => "boolean",
489            Self::Int => "integer",
490            Self::Float => "float",
491            Self::Numeric => "numeric value",
492            Self::Str => "string",
493            Self::CodeBlock => "code block",
494            Self::ContentBlock => "content block",
495            Self::Parenthesized => "group",
496            Self::Array => "array",
497            Self::Dict => "dictionary",
498            Self::Named => "named pair",
499            Self::Keyed => "keyed pair",
500            Self::Unary => "unary expression",
501            Self::Binary => "binary expression",
502            Self::FieldAccess => "field access",
503            Self::FuncCall => "function call",
504            Self::Args => "call arguments",
505            Self::Spread => "spread",
506            Self::Closure => "closure",
507            Self::Params => "closure parameters",
508            Self::LetBinding => "`let` expression",
509            Self::SetRule => "`set` expression",
510            Self::ShowRule => "`show` expression",
511            Self::Contextual => "`context` expression",
512            Self::Conditional => "`if` expression",
513            Self::WhileLoop => "while-loop expression",
514            Self::ForLoop => "for-loop expression",
515            Self::ModuleImport => "`import` expression",
516            Self::ImportItems => "import items",
517            Self::ImportItemPath => "imported item path",
518            Self::RenamedImportItem => "renamed import item",
519            Self::ModuleInclude => "`include` expression",
520            Self::LoopBreak => "`break` expression",
521            Self::LoopContinue => "`continue` expression",
522            Self::FuncReturn => "`return` expression",
523            Self::Destructuring => "destructuring pattern",
524            Self::DestructAssignment => "destructuring assignment expression",
525        }
526    }
527}
528
529/// How to determine the [`SyntaxMode`] we will be in when immediately after a
530/// node of this kind.
531#[derive(Debug, Clone, Copy, PartialEq, Eq)]
532pub(crate) enum ModeAfter {
533    /// The mode is known!
534    Known(SyntaxMode),
535    /// The syntax mode after this kind is based on its parent.
536    Parent,
537    /// We treat comments and the bodies of raw text as not producing any syntax
538    /// mode.
539    None,
540    /// Text under `Raw` is `None`, otherwise it is markup.
541    Text,
542    /// After the opening raw delimiter is `None`, but after the closing one is
543    /// the same as the parent `Raw`.
544    RawDelim,
545    /// Dollar signs in an equation. After the opening dollar sign is math mode,
546    /// after the closing dollar sign is the same as the parent `Equation`.
547    Dollar,
548    /// Spaces are usually only based on their parent, but an edge case with
549    /// `Equation` requires special handling.
550    ///
551    /// In equations, unlike in code and content blocks, the spaces at the edges
552    /// are not included in the `Math` wrapper kind, and require special
553    /// handling to still return as math mode.
554    Space,
555    /// Kinds that can be embedded in markup or math with a hash, but can also
556    /// be used without a hash in other contexts.
557    ///
558    /// As an example, the mode after `<label>` in these will be:
559    /// - Code in `#let x = <label>`
560    /// - Markup in `<label>`
561    /// - Code in `#<label>`
562    Embeddable,
563}
564
565impl SyntaxKind {
566    /// How to determine the [`SyntaxMode`] we will be in when immediately after
567    /// a node of this kind.
568    ///
569    /// The high-level interface for this is [`crate::LinkedNode::mode_after`].
570    pub(crate) fn mode_after(self) -> ModeAfter {
571        use ModeAfter::*;
572        use SyntaxMode::{Code, Markup, Math};
573
574        // For the kinds which are not `Known`, the possible modes are listed in
575        // a comment with the parent kinds that can cause that mode, although
576        // `expr` and `part` are used as shorthands when the list of parent
577        // kinds would be too long (otherwise, the list of kinds should be
578        // exhaustive). `expr` is a kind which can be a top-level expression in
579        // its mode, and `part` is a kind which is just a part of an expression.
580        match self {
581            Self::End => None,     // none
582            Self::Error => Parent, // code/math/markup
583
584            Self::Shebang => None,      // none
585            Self::LineComment => None,  // none
586            Self::BlockComment => None, // none
587
588            Self::Markup => Known(Markup),
589            Self::Text => Text,        // none: Raw | markup: expr
590            Self::Space => Space,      // code/math: part | markup: expr/part
591            Self::Linebreak => Parent, // math/markup: expr
592            Self::Parbreak => Known(Markup),
593            Self::Escape => Parent, // math/markup: expr
594            Self::Shorthand => Known(Markup),
595            Self::SmartQuote => Known(Markup),
596            Self::Strong => Known(Markup),
597            Self::Emph => Known(Markup),
598            Self::Raw => Embeddable,    // code/markup: expr
599            Self::RawLang => None,      // none
600            Self::RawDelim => RawDelim, // none: opening backticks | code/markup: Raw
601            Self::RawTrimmed => None,   // none
602            Self::Link => Known(Markup),
603            Self::Label => Embeddable, // code/markup: expr
604            Self::Ref => Known(Markup),
605            Self::RefMarker => Known(Markup),
606            Self::Heading => Known(Markup),
607            Self::HeadingMarker => Known(Markup),
608            Self::ListItem => Known(Markup),
609            Self::ListMarker => Known(Markup),
610            Self::EnumItem => Known(Markup),
611            Self::EnumMarker => Known(Markup),
612            Self::TermItem => Known(Markup),
613            Self::TermMarker => Known(Markup),
614
615            Self::Equation => Embeddable, // code/markup: expr
616            Self::Math => Known(Math),
617            Self::MathText => Known(Math),
618            Self::MathIdent => Known(Math),
619            Self::MathFieldAccess => Known(Math),
620            Self::MathShorthand => Known(Math),
621            Self::MathAlignPoint => Known(Math),
622            Self::MathCall => Known(Math),
623            Self::MathArgs => Known(Math),
624            Self::MathDelimited => Known(Math),
625            Self::MathAttach => Known(Math),
626            Self::MathPrimes => Known(Math),
627            Self::MathFrac => Known(Math),
628            Self::MathRoot => Known(Math),
629
630            Self::Hash => Known(Code),
631            Self::LeftBrace => Known(Code),
632            Self::RightBrace => Known(Code),
633            Self::LeftBracket => Known(Markup),
634            Self::RightBracket => Parent, // code/markup: ContentBlock
635            Self::LeftParen => Parent,    // code: part | math: MathArgs/MathDelimited
636            Self::RightParen => Parent,   // code: part | math: MathArgs/MathDelimited
637            Self::Comma => Parent,        // code: part | math: MathArgs
638            Self::Semicolon => Parent,    // code: CodeBlock | math/markup: after embedded
639            Self::Colon => Parent,        // code: part | math: Named | markup: TermItem
640            Self::Star => Parent,         // code: Binary/ModuleImport | markup: Strong
641            Self::Underscore => Parent,   // code: part | math: MathAttach | markup: Emph
642            Self::Dollar => Dollar,       // code/markup: Equation | math: opening dollar
643            Self::Plus => Known(Code),
644            Self::Minus => Known(Code),
645            Self::Slash => Parent, // code: Binary | math: MathFrac
646            Self::Hat => Known(Math),
647            Self::Dot => Parent, // code: part | math: MathFieldAccess
648            Self::Eq => Known(Code),
649            Self::EqEq => Known(Code),
650            Self::ExclEq => Known(Code),
651            Self::Lt => Known(Code),
652            Self::LtEq => Known(Code),
653            Self::Gt => Known(Code),
654            Self::GtEq => Known(Code),
655            Self::PlusEq => Known(Code),
656            Self::HyphEq => Known(Code),
657            Self::StarEq => Known(Code),
658            Self::SlashEq => Known(Code),
659            Self::Dots => Parent, // code/math: Spread
660            Self::Arrow => Known(Code),
661            Self::Root => Known(Math),
662            Self::Bang => Known(Math),
663
664            Self::Not => Known(Code),
665            Self::And => Known(Code),
666            Self::Or => Known(Code),
667            Self::None => Known(Code),
668            Self::Auto => Known(Code),
669            Self::Let => Known(Code),
670            Self::Set => Known(Code),
671            Self::Show => Known(Code),
672            Self::Context => Known(Code),
673            Self::If => Known(Code),
674            Self::Else => Known(Code),
675            Self::For => Known(Code),
676            Self::In => Known(Code),
677            Self::While => Known(Code),
678            Self::Break => Known(Code),
679            Self::Continue => Known(Code),
680            Self::Return => Known(Code),
681            Self::Import => Known(Code),
682            Self::Include => Known(Code),
683            Self::As => Known(Code),
684
685            Self::Code => Known(Code),
686            Self::Ident => Embeddable, // code: expr/part | math: Named
687            Self::Bool => Known(Code),
688            Self::Int => Known(Code),
689            Self::Float => Known(Code),
690            Self::Numeric => Known(Code),
691            Self::Str => Embeddable, // code/math: expr
692            Self::CodeBlock => Known(Code),
693            Self::ContentBlock => Embeddable, // code: expr | markup: Ref
694            Self::Parenthesized => Known(Code),
695            Self::Array => Known(Code),
696            Self::Dict => Known(Code),
697            Self::Named => Parent, // code: part | math: MathArgs
698            Self::Keyed => Known(Code),
699            Self::Unary => Known(Code),
700            Self::Binary => Known(Code),
701            Self::FieldAccess => Known(Code),
702            Self::FuncCall => Known(Code),
703            Self::Args => Known(Code),
704            Self::Spread => Parent, // code: part | math: MathArgs
705            Self::Closure => Known(Code),
706            Self::Params => Known(Code),
707            Self::LetBinding => Known(Code),
708            Self::SetRule => Known(Code),
709            Self::ShowRule => Known(Code),
710            Self::Contextual => Known(Code),
711            Self::Conditional => Known(Code),
712            Self::WhileLoop => Known(Code),
713            Self::ForLoop => Known(Code),
714            Self::ModuleImport => Known(Code),
715            Self::ImportItems => Known(Code),
716            Self::ImportItemPath => Known(Code),
717            Self::RenamedImportItem => Known(Code),
718            Self::ModuleInclude => Known(Code),
719            Self::LoopBreak => Known(Code),
720            Self::LoopContinue => Known(Code),
721            Self::FuncReturn => Known(Code),
722            Self::Destructuring => Known(Code),
723            Self::DestructAssignment => Known(Code),
724        }
725    }
726}
727
728#[cfg(test)]
729mod test {
730    use super::*;
731    use crate::{LinkedNode, Side, Source};
732
733    #[track_caller]
734    fn test_mode(
735        text: &str,
736        cursors: impl IntoIterator<Item = usize>,
737        expected: Option<SyntaxMode>,
738    ) {
739        let source = Source::detached(text);
740        let root = LinkedNode::new(source.root());
741        for cursor in cursors {
742            let leaf = root.leaf_at(cursor, Side::After).unwrap();
743            let c = source.text()[cursor..].chars().next().unwrap();
744            assert_eq!(leaf.mode_after(), expected, "different at '{c}' index {cursor}");
745        }
746    }
747
748    #[test]
749    fn test_mode_after() {
750        use SyntaxMode::{Code, Markup, Math};
751
752        // Trivia
753        test_mode("#! typ", [0, 1, 2, 3], None);
754        test_mode("#! typ\n", [6], Some(Markup));
755        test_mode("// xxx", [0, 1, 2, 3], None);
756        test_mode("\n// xxx\n", [0, 7], Some(Markup));
757        test_mode("/* xxx */", [0, 1, 2, 3, 4, 7, 8], None);
758
759        // Syntax errors
760        test_mode("*/", [0, 1], Some(Markup));
761        test_mode("#{*/}", [2, 3], Some(Code));
762        test_mode("$*/$", [1, 2], Some(Math));
763
764        // Markup
765        test_mode("https://typst.org", [0], Some(Markup));
766        test_mode("a\\bcd", [0, 1, 2, 3], Some(Markup));
767        test_mode("a   c\n\n d\\\nef", [1, 5, 9], Some(Markup));
768        test_mode("\\u{41}", [0, 1, 2, 3, 5], Some(Markup));
769        test_mode("\"abc\"", [0, 1, 4], Some(Markup));
770        test_mode("a-?b", [1, 2], Some(Markup));
771        test_mode("_*abcd*_", [0, 1, 6, 7], Some(Markup));
772        test_mode("<label>", [0, 1], Some(Markup));
773        test_mode("@label[x @y]", [0, 1, 6, 7, 8, 9, 11], Some(Markup));
774        test_mode("= marker", [0, 1, 2], Some(Markup));
775        test_mode("- marker", [0, 1, 2], Some(Markup));
776        test_mode("+ marker", [0, 1, 2], Some(Markup));
777        test_mode("/ marker: y", [0, 1, 2, 8, 9, 10], Some(Markup));
778
779        // Basic code
780        test_mode("#{x;1}", [0, 1, 2, 3, 4], Some(Code));
781        test_mode("#(x)", [1, 2, 3], Some(Code));
782        test_mode("#(1,2,)", [1, 2, 3, 5, 6], Some(Code));
783        test_mode("#(a:1,\"b\":2)", [1, 2, 3, 4, 5, 6, 7, 8, 9, 11], Some(Code));
784        test_mode("#{-x}", [2], Some(Code));
785        test_mode("#{a / b}", [4], Some(Code));
786        test_mode("#a.b", [1, 2, 3], Some(Code));
787        test_mode("#$$.at()", [2, 3], Some(Code));
788        test_mode("#[].at()", [2, 3], Some(Code));
789        test_mode("#f(x, ..y)", [2, 4, 6], Some(Code));
790        test_mode("#{(x) => {}}", [3, 6], Some(Code));
791        test_mode("#let x = 1", [1, 7], Some(Code));
792        test_mode("#let x;", [6], Some(Markup)); // After semicolon is the parent
793        test_mode("#set text()", [1, 4], Some(Code));
794        test_mode("#show text : it => it", [1, 11], Some(Code));
795        test_mode("#context 1", [1, 8], Some(Code));
796        test_mode("#while true {break;continue;}", [1, 13, 19], Some(Code));
797        test_mode("#for a in b {}", [1, 7], Some(Code));
798        test_mode("#if true {} else {}", [1, 12], Some(Code));
799        test_mode("#import \"lib.typ\" : a, b as d, e.f", [2, 8, 21, 25, 32], Some(Code));
800        test_mode("#include \"lib.typ\"", [1], Some(Code));
801        test_mode("#let f() = { return 1 }", [13], Some(Code));
802        test_mode("#{(x, _, ..y) = (1, 2, ..z)}", [2, 14], Some(Code));
803        test_mode("= #1.1", [2, 3], Some(Code));
804
805        // Math
806        test_mode("$$", [0], Some(Math)); // Opening dollar is math
807        test_mode("$$", [1], Some(Markup)); // Closing dollar is parent
808        test_mode("#$$", [2], Some(Code)); // Closing dollar is parent
809        test_mode("$ a b $", [1, 3, 5], Some(Math)); // Just the spaces
810        test_mode("$\na\nb\n$", [1, 3, 5], Some(Math)); // Just the newlines
811        test_mode("$arrow$", [1], Some(Math));
812        test_mode("$123.32$", [1, 2, 4, 5], Some(Math));
813        test_mode("$+12 * y!", [1, 5, 8], Some(Math));
814        test_mode("$1/2$", [2], Some(Math));
815        test_mode("$f''$", [2], Some(Math));
816        test_mode("$f_(x)^y$", [2, 3, 6, 7], Some(Math));
817        test_mode("$a>=b$", [2], Some(Math));
818        test_mode("$√x$", [1, 4], Some(Math));
819        test_mode("$&x$", [1], Some(Math));
820        test_mode("$\\#\\u{41}$", [1, 2, 3, 4, 5, 6], Some(Math));
821        test_mode("$ff(x, sin(y), abs(z))$", [3, 4, 5, 7, 10, 15, 18], Some(Math));
822        test_mode("$ff(..args, named: key)$", [4, 6, 16, 17], Some(Math));
823        test_mode("$arrow.r$", [6], Some(Math));
824
825        // Raw text
826        test_mode("`r`", [0, 1], None);
827        test_mode("`r`", [2], Some(Markup));
828        test_mode("` \n r\n `", [1, 2, 3, 4, 5, 6], None);
829        test_mode("#`r`", [1, 2], None);
830        test_mode("#`r`", [3], Some(Code));
831        test_mode("```l r\n```", [7], Some(Markup));
832        test_mode("```l r\n```", [0, 3, 4, 5, 6], None);
833        test_mode("#```l r\n```", [8], Some(Code));
834        test_mode("#```l r\n```", [1, 4, 5, 6, 7], None);
835
836        // Edge cases with embedded code expressions
837        test_mode("#<l>", [1, 2, 3], Some(Code));
838        test_mode("$#pa$", [2, 3], Some(Code));
839        test_mode("$#{x}$", [2], Some(Code));
840        test_mode("$#[x]$", [1, 4], Some(Code));
841        test_mode("$#[x]$", [2, 3], Some(Markup));
842        test_mode("$#f(x, ..args, named: key)$", [3, 4, 5, 7, 9, 19, 20], Some(Code));
843        test_mode("$#context 1$", [9, 10], Some(Code));
844        test_mode("$#context $", [9], Some(Code));
845        test_mode("$#std.align$", [2, 5, 6], Some(Code));
846        test_mode("$ff(named: #ident)$", [12], Some(Code));
847        test_mode("$ #$x$; $", [0, 1, 3, 4, 6, 7], Some(Math));
848        test_mode("$ #$x$; $", [8], Some(Markup));
849        test_mode("$ #$x$; $", [2, 5], Some(Code));
850        test_mode("#[$x$]", [2, 3], Some(Math));
851        test_mode("#[$x$]", [1, 4], Some(Markup));
852    }
853}