lib/syn/src/span/model.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 //! A scan produces marked byte ranges for one line, each with the role of the text it covers, and
2 //! whether the marks of the line were kept. A renderer needs each range as offsets into the line
3 //! and a role it can map to a color, and nothing more.
4 //!
5 //! The roles differ by language: code has keywords and types, a Lisp list has its head, TOML has
6 //! tables, Markdown has headings, and a diff has added and removed lines.
7 //!
8 //! One list of roles (*span kind*) covers every scanner, so a renderer maps each role to a style
9 //! once and every language can use it. A marked range (*span*) is a plain value of two byte offsets
10 //! and a role, with no pointer into the text. A scan of one line reports whether the storage kept
11 //! the line's marks or discarded them (*materialization*), and a line that does not fit the storage
12 //! reports that they were discarded.
13 //!
14 //! - *text limit*: the longest line, in bytes, whose marks the storage keeps
15 //! - *span storage*: the caller's buffer that receives one line's marks
16 //! - *continuation state*: what one line leaves open for the next line
17 /// The role of the text one span covers. A renderer switches on this role to pick a color or style
18 /// for each span. Thirty-one roles serve every scanner, and each scanner uses the roles that fit
19 /// its languages. The package attaches no color or style to any role.
20 pub const Kind = enum {
21 /// A reserved word of the language, such as `if`, `return` or `const`, or a CSS rule that
22 /// starts with `@`, such as `@media`.
23 keyword,
24 /// A built-in type name, such as `u8`, `int` or `String`, marked by the generic scanner.
25 type,
26 /// A name followed by an opening parenthesis, marked by the generic scanner.
27 function,
28 /// A shell, PHP or Perl variable such as `$HOME`, or a Rust lifetime such as `'a`.
29 variable,
30 /// A name after a dot in code, a CSS property name inside a rule, or a command-line option such
31 /// as `--help` in shell.
32 property,
33 /// A quoted string, including a Python or TOML triple-quoted string that runs over more than
34 /// one line.
35 string,
36 /// A numeric literal, including hexadecimal and exponent forms, and a CSS number with its unit,
37 /// such as `1rem`.
38 number,
39 /// A named constant value, such as `true`, `false`, `null` or `None`.
40 literal,
41 /// A comment: a line comment to the end of the line, or a block comment up to its closing
42 /// delimiter, which can run over more than one line.
43 comment,
44 /// A bracket, brace, comma, colon, semicolon or dot, or an angle bracket around a markup tag.
45 punctuation,
46 /// A run of operator characters, such as `=`, `+=` or `->`, marked by the generic scanner.
47 operator,
48 /// An annotation such as `@Override` in Java, C#, Kotlin, Scala or Dart, or a Python decorator.
49 decorator,
50 /// A parenthesis in Lisp or Clojure code.
51 delimiter,
52 /// The first symbol after an opening parenthesis in Lisp or Clojure code, the position of the
53 /// operator in a list.
54 head,
55 /// A key in JSON or YAML before a colon, or a key in TOML before an equals sign.
56 key,
57 /// A TOML table header, such as `[server]`.
58 table,
59 /// A TOML date or date and time.
60 date,
61 /// A YAML anchor or alias, such as `&base` or `*base`.
62 anchor,
63 /// An element name in HTML, XML, Vue or Svelte, or a YAML tag such as `!!str`.
64 tag,
65 /// An attribute name inside a markup tag.
66 attribute,
67 /// A CSS name outside the property position of a rule, which covers selectors and also property
68 /// values.
69 selector,
70 /// The text of a Markdown heading after its `#` marks.
71 heading,
72 /// A Markdown structure mark: the `#` run of a heading, a `>` quote mark, or a list bullet or
73 /// number.
74 marker,
75 /// Markdown emphasis or strong emphasis, delimiters included, such as `*word*` or `__word__`.
76 emphasis,
77 /// Markdown strikethrough between `~~` marks.
78 strike,
79 /// Markdown inline code between backticks.
80 code,
81 /// A Markdown inline link, from its `[` through the `)` that closes its address.
82 link,
83 /// A whole diff header line, such as one starting with `diff `, `index `, `--- `, `+++ ` or
84 /// `rename from `.
85 header,
86 /// A whole diff hunk line, starting with `@@`.
87 hunk,
88 /// A whole diff line starting with `+`, other than a `+++ ` header.
89 addition,
90 /// A whole diff line starting with `-`, other than a `--- ` header.
91 deletion,
92 };
93
94 /// One marked byte range of a line and its role. A renderer reads the spans of a line from
95 /// `Storage.items` and styles each byte range. The spans of one line come in order and never
96 /// overlap or have zero length. Touching spans of the same kind are merged into one. Bytes that no
97 /// span covers carry no role.
98 pub const Span = struct {
99 /// The offset of the first covered byte, counted from the start of the line passed to the scan.
100 start: usize,
101 /// The offset one past the last covered byte, so `text[span.start..span.end]` is the covered
102 /// text. The offset is always greater than `start` and at most the length of the line.
103 end: usize,
104 /// The role of the covered text.
105 kind: Kind,
106 };
107
108 /// What happened to the spans of one line. A caller checks it after each call to know whether the
109 /// storage holds the spans of the line. Both outcomes leave the continuation state exact.
110 pub const Materialization = enum {
111 /// The line fit within the text limit, and the storage holds its spans.
112 complete,
113 /// The line was longer than the text limit, and the storage holds no spans for it. The line was
114 /// still scanned in full, and the storage added one to its count of discarded lines.
115 discarded,
116 };
117
118 /// The one error that span storage raises when a line is too long to keep:
119 /// `MaterializationCapacityExceeded`. `Storage.prepare` returns it for a line that is too long, and
120 /// `highlight` and `highlightLine` catch it and return `.discarded`, so their callers never receive
121 /// it. The repository's allocation check requires a storage type of this shape to name such an
122 /// error.
123 pub const Exhaustion = error{MaterializationCapacityExceeded};