| 🛟 Updated. 28d5985 k33g 4h ago | 1 | // Package syntax colours source code. |
| 2 | // |
| 3 | // It colours the languages every editor built on this library meets whatever it |
| 4 | // is for — TOML, Markdown, JavaScript, HTML, shell scripts, YAML, XML and |
| 5 | // Dockerfiles — and it is the place an editor registers the language it is |
| 6 | // *for*: Turbo Go registers Go, Turbo Rust registers Rust, and neither is known |
| 7 | // here. |
| 8 | // |
| 9 | // Every scanner turns its input into per-line spans the editor can draw, and |
| 10 | // every one of them is deliberately tolerant: text under the cursor is broken |
| 11 | // most of the time it is being typed, and a highlighter that gives up on |
| 12 | // invalid input is a highlighter that flickers off. |
| 13 | // |
| 14 | // Three things make up the extension point. Definition and Register say what a |
| 15 | // language is and teach it to this package; LineScanner and ScanLines are what |
| 16 | // a scanner is written against; and LineIndex is for a scanner that works in |
| 17 | // byte offsets instead, which is what a tokeniser like go/scanner gives you. |
| 18 | // |
| 19 | // A file is recognised by its extension, then by its name — which is how a |
| 20 | // Dockerfile, which has no extension, is found — and failing both by a shebang. |
| 21 | // |
| 22 | // The package depends on theme only for the vocabulary of style keys, so that a |
| 23 | // token class and the colour it resolves to are named in one place. |
| 24 | package syntax |
| 25 | |
| 26 | import "codeberg.org/turbo-editors/turbo-core/theme" |
| 27 | |
| 28 | // Class is what a run of source text is, in colouring terms. |
| 29 | // |
| 30 | // It is deliberately coarser than any one language's token set: the editor |
| 31 | // needs to tell a keyword from a string, not an ADD from a SUB. Most classes |
| 32 | // are shared across languages — a string is a string in all of them — and only |
| 33 | // the last five belong to markup, because nothing in a programming language |
| 34 | // reads as a heading. |
| 35 | // |
| 36 | // The set is fixed. A language registered from outside this package colours |
| 37 | // itself with these classes and no others, which is what lets one theme colour |
| 38 | // every language an editor built on this library will ever learn. |
| 39 | type Class uint8 |
| 40 | |
| 41 | // The classes a token can fall into. |
| 42 | const ( |
| 43 | // ClassIdentifier is the zero value, so text nothing else claims is drawn |
| 44 | // as a plain identifier. |
| 45 | ClassIdentifier Class = iota |
| 46 | ClassKeyword |
| 47 | ClassType |
| 48 | ClassBuiltin |
| 49 | ClassConstant |
| 50 | ClassFunction |
| 51 | ClassString |
| 52 | ClassChar |
| 53 | ClassNumber |
| 54 | ClassComment |
| 55 | ClassOperator |
| 56 | ClassPunctuation |
| 57 | |
| 58 | // ClassHeading is a Markdown heading, whole line included. |
| 59 | ClassHeading |
| 60 | // ClassTag is an HTML element name, and the angle brackets around it. |
| 61 | ClassTag |
| 62 | // ClassAttribute is an HTML attribute's name. |
| 63 | ClassAttribute |
| 64 | // ClassEmphasis is Markdown bold or italic text. |
| 65 | ClassEmphasis |
| 66 | // ClassLink is a Markdown link or image, both its text and its target. |
| 67 | ClassLink |
| 68 | ) |
| 69 | |
| 70 | // styleKeys maps each class onto the theme key that colours it. |
| 71 | var styleKeys = [...]string{ |
| 72 | ClassIdentifier: theme.KeySyntaxIdentifier, |
| 73 | ClassKeyword: theme.KeySyntaxKeyword, |
| 74 | ClassType: theme.KeySyntaxType, |
| 75 | ClassBuiltin: theme.KeySyntaxBuiltin, |
| 76 | ClassConstant: theme.KeySyntaxConstant, |
| 77 | ClassFunction: theme.KeySyntaxFunction, |
| 78 | ClassString: theme.KeySyntaxString, |
| 79 | ClassChar: theme.KeySyntaxChar, |
| 80 | ClassNumber: theme.KeySyntaxNumber, |
| 81 | ClassComment: theme.KeySyntaxComment, |
| 82 | ClassOperator: theme.KeySyntaxOperator, |
| 83 | ClassPunctuation: theme.KeySyntaxPunctuation, |
| 84 | ClassHeading: theme.KeySyntaxHeading, |
| 85 | ClassTag: theme.KeySyntaxTag, |
| 86 | ClassAttribute: theme.KeySyntaxAttribute, |
| 87 | ClassEmphasis: theme.KeySyntaxEmphasis, |
| 88 | ClassLink: theme.KeySyntaxLink, |
| 89 | } |
| 90 | |
| 91 | // StyleKey returns the theme key that colours this class. |
| 92 | // |
| 93 | // style := th.Style(syntax.ClassKeyword.StyleKey()) |
| 94 | func (c Class) StyleKey() string { |
| 95 | if int(c) >= len(styleKeys) { |
| 96 | return theme.KeySyntaxIdentifier |
| 97 | } |
| 98 | return styleKeys[c] |
| 99 | } |
| 100 | |
| 101 | // String returns the class name, for tests and for debugging. |
| 102 | func (c Class) String() string { |
| 103 | names := [...]string{ |
| 104 | ClassIdentifier: "identifier", |
| 105 | ClassKeyword: "keyword", |
| 106 | ClassType: "type", |
| 107 | ClassBuiltin: "builtin", |
| 108 | ClassConstant: "constant", |
| 109 | ClassFunction: "function", |
| 110 | ClassString: "string", |
| 111 | ClassChar: "char", |
| 112 | ClassNumber: "number", |
| 113 | ClassComment: "comment", |
| 114 | ClassOperator: "operator", |
| 115 | ClassPunctuation: "punctuation", |
| 116 | ClassHeading: "heading", |
| 117 | ClassTag: "tag", |
| 118 | ClassAttribute: "attribute", |
| 119 | ClassEmphasis: "emphasis", |
| 120 | ClassLink: "link", |
| 121 | } |
| 122 | if int(c) >= len(names) { |
| 123 | return "unknown" |
| 124 | } |
| 125 | return names[c] |
| 126 | } |
| 127 | |
| 128 | // Span is a run of runes on a single line that share one class. |
| 129 | // |
| 130 | // Columns are rune offsets into the line: Start is included, End excluded, |
| 131 | // matching the convention used everywhere else in the editor. |
| 132 | type Span struct { |
| 133 | Start int |
| 134 | End int |
| 135 | Class Class |
| 136 | } |