turbo-editors/turbo-moonbitpublic Fork 0
v1.0.2
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-moonbit.git
git clone ssh://git@rickub.com/turbo-editors/turbo-moonbit.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

languages.md · 296 lines · 19.4 KBmarkdown Blame HistoryRaw
📦 Turbo MoonBit cc1f595 k33g 14h ago1# Reference: languages coloured
2
3> Neutral description of which files Turbo MoonBit colours, how it decides, and what each scanner recognises.
4
5## Recognition
6
7A file's **extension** decides whenever it is one of these:
8
9| Extension | Language |
10| --- | --- |
11| `.mbt`, `.mbti`, `.mbtx` | MoonBit |
12| `.toml` | TOML |
13| `.yaml`, `.yml` | YAML |
14| `.md`, `.markdown` | Markdown |
15| `.js`, `.mjs`, `.cjs` | JavaScript |
16| `.html`, `.htm` | HTML |
17| `.xml`, `.xsd`, `.xsl`, `.xslt`, `.svg`, `.plist`, `.csproj`, `.pom` | XML |
18| `.sh`, `.bash`, `.zsh` | Shell |
19| `.dockerfile`, `.containerfile` | Dockerfile |
20
21Extensions are matched case-insensitively, and only the last one counts: `README.mbt.md` is Markdown, and `main.mbt.backup` is not MoonBit.
22
23A file whose extension decides nothing is looked up by **name** next. Only files that carry no useful extension need this:
24
25| Name | Language |
26| --- | --- |
27| `Dockerfile`, `Containerfile` | Dockerfile |
28
29A name matches on the whole of it or on the part before the first dot, ignoring case — so `Dockerfile`, `dockerfile` and `Dockerfile.dev` are all recognised, while `Dockerfile.md` is Markdown, because the extension is consulted first.
30
31`moon.mod`, `moon.pkg` and `moon.work` are **not** in that table. They are MoonBit's own configuration DSL rather than MoonBit, and their legacy JSON forms — `moon.mod.json`, `moon.pkg.json` — are not JSON that this editor colours either. All five open in plain text.
32
33A file that neither table claims is read by its **first line**. A shebang naming a shell — `sh`, `bash`, `zsh`, `dash` or `ksh` — makes it a shell script, and the interpreter is recognised as a path element or as the argument to `env`. That is what colours a script in a `bin` directory, a git hook, or `configure`.
34
35**No shebang makes a file MoonBit.** The language has no interpreter line: a file opening with `#!` would lex as an attribute named `!` and fail. A file with no extension is not MoonBit, and claiming otherwise would take a shell script away from the scanner that can actually colour it.
36
37| First line | Result |
38| --- | --- |
39| `#!/bin/sh` | Shell |
40| `#!/usr/bin/env bash` | Shell |
41| `#!/usr/bin/env -S bash -e` | Shell |
42| `#!/usr/bin/env moon` | Not coloured |
43| `#!/usr/bin/env node` | Not coloured |
44| Anything not starting `#!` | Not coloured |
45
46The order is fixed — extension, then name, then first line — and the first to decide wins.
47
48Everything else is shown in plain text. That is not an error — opening a PNG in the editor is not a mistake, it is just not coloured.
49
50## Classes
51
52Every scanner produces the same vocabulary of classes, and each maps to one theme key.
53
54| Class | Theme key | Produced by |
55| --- | --- | --- |
56| `identifier` | `syntax.identifier` | MoonBit, TOML, JavaScript, shell, YAML, Dockerfile |
57| `keyword` | `syntax.keyword` | MoonBit, JavaScript, shell, HTML (doctype), XML, Dockerfile |
58| `type` | `syntax.type` | MoonBit (every capitalised name, and package qualifiers), TOML (table headers), YAML (tags) |
59| `builtin` | `syntax.builtin` | MoonBit (the prelude), JavaScript, shell (builtins and expansions), YAML (anchors and aliases), Dockerfile (variables) |
60| `constant` | `syntax.constant` | MoonBit, TOML, JavaScript, shell, YAML, HTML and XML (entities) |
61| `function` | `syntax.function` | MoonBit, JavaScript, shell (the command) |
62| `string` | `syntax.string` | all |
63| `char` | `syntax.char` | MoonBit (`'c'` and `b'c'`) |
64| `number` | `syntax.number` | MoonBit, TOML, JavaScript, shell, YAML, Dockerfile |
65| `comment` | `syntax.comment` | MoonBit, TOML, JavaScript, shell, HTML, YAML, XML, Dockerfile |
66| `operator` | `syntax.operator` | MoonBit, TOML, JavaScript, shell, HTML, YAML (block scalar headers), XML, Dockerfile |
67| `punctuation` | `syntax.punctuation` | MoonBit, TOML, JavaScript, shell, Markdown, YAML, Dockerfile |
68| `heading` | `syntax.heading` | Markdown |
69| `tag` | `syntax.tag` | HTML, XML |
70| `attribute` | `syntax.attribute` | MoonBit (attributes and labelled arguments), HTML, XML, Dockerfile (flags) |
71| `emphasis` | `syntax.emphasis` | Markdown |
72| `link` | `syntax.link` | Markdown |
73
74In `turbo-classic` alone, `syntax.attribute` and `syntax.identifier` are both plain yellow, so a MoonBit attribute or label is not told apart from an ordinary name in that one theme. The other seven give them different colours. See [how to write your own theme](../how-to/write-a-theme.md) if you want to change it.
75
76## MoonBit
77
78Hand-written, in `internal/moonbitlang`. **Nothing crosses a line break**, and that is a property of the language rather than a simplification: MoonBit has no block comment, a newline before a closing quote is an *unterminated literal* error, a multi-line string is a run of self-contained `#|` or `$|` lines, and an attribute is explicitly one line. So a stray quote colours to the end of its line and the next line is code again.
79
80| Recognised | As |
81| --- | --- |
82| `and`, `as`, `async`, `break`, `catch`, `const`, `continue`, `declare`, `defer`, `derive`, `else`, `enum`, `enumview`, `extend`, `extenum`, `extern`, `fn`, `for`, `guard`, `if`, `impl`, `import`, `in`, `is`, `let`, `letrec`, `lexscan`, `loop`, `match`, `mut`, `nobreak`, `nocancel`, `noraise`, `package`, `priv`, `proof_assert`, `proof_let`, `pub`, `raise`, `readonly`, `return`, `struct`, `suberror`, `test`, `throw`, `trait`, `try`, `type`, `using`, `where`, `while`, `with` | keyword |
83| `try!` and `guard!`, mark included | keyword |
84| `true`, `false`, `None`, `Some`, `Ok`, `Err` | constant |
85| any name starting with an ASCII capital — `Int`, `StringBuilder`, `Shape`, `Circle` | type |
86| `println`, `abort`, `panic`, `fail`, `ignore`, `inspect`, `debug`, `repr`, `hash`, `compare`, `null`, `assert_eq`, `assert_not_eq`, `assert_true`, `assert_false`, `debug_assert`, `debug_inspect`, `json_inspect`, `physical_equal` | builtin |
87| any other lower-case name immediately before `(` | function |
88| `"…"`, `b"…"`, `re"…"` | string |
89| `'c'`, `b'c'` | char |
90| `#\|` and `$\|` | the two-rune prefix as punctuation, the rest of the line as string |
91| `42`, `1_000`, `0xFF_FF`, `0o17`, `0b1010`, `1.5`, `1.`, `1.5e-3`, `0x1.8p3F`, `42U`, `42L`, `42UL`, `42N`, `1.0F` | number |
92| `//` and `///` to the end of the line | comment |
93| `#deprecated("…")`, `#external`, `#custom.attribute(key="v")` — the whole line | attribute |
94| `name~` in a labelled argument, tilde included | attribute |
95| `@json`, `@moonbitlang/core/builtin`, `@my-pkg` — the `@` included, as one span | type |
96| `.0` in a tuple accessor | the dot as punctuation, the digits as number |
97| `..`, `..=`, `..<`, `...` | operator |
98| runs of `+-*/%=<>!&\|^~?:` | operator |
99| `()[]{},;.` | punctuation |
100
101**Nothing here is a table of built-in types, and nothing needs to be.** MoonBit's identifier case is a *lexical* rule rather than a convention: the grammar says a `uident` "begins with an ASCII uppercase letter", and only a type, a trait or an enum constructor may be spelt that way. `Int`, `StringBuilder` and a type somebody wrote this morning are all coloured by the same line. Every other scanner in this family needs a table here; this one does not.
102
103**An integer ends before `..`.** The grammar is explicit — "before `..`, an integer ends first, so `1..=2` begins with `1` and `..=`" — so a dot is only part of a number when a second one does not follow it. Without that rule `1..=2` reads as the double `1.` and then `.=2`, and every range in the file is miscoloured.
104
105**A number's suffix is upper case or it is not a suffix.** `42UL` is one number; `42u` is the number `42` followed by the name `u`, which is what the compiler sees too.
106
107**An attribute takes the whole line.** The grammar hands it everything after the dotted name: "everything through the next newline is the raw payload". Colouring less than the line would be inventing a structure the lexer does not have.
108
109**`#|` and `#deprecated` are told apart by the rune after the `#`.** An attribute's name must start with a letter or an underscore; a multi-line string line has a bar there.
110
111**A doc comment is coloured like any other comment.** `///`, `///|` and `//` all reach `syntax.comment`, because turbo-core's set of classes is closed on purpose — that is what lets one theme colour every language an editor will ever learn.
112
113**A name after a dot is never a keyword.** MoonBit's dot-identifiers "use the identifier case rules without consulting the keyword table, so `.if` is valid" — a record with a field called `type` is ordinary MoonBit.
114
115**`package` is coloured as a keyword in a `.mbt` file too**, although it is only a *reserved* word there. It is a real keyword in the `.mbti` interface files this editor also colours, and in a `.mbt` file the colour says exactly what the compiler is about to: this word is not yours to use. The rest of the reserved list — `move`, `ref`, `static`, `unsafe`, `await` and the forty others — is deliberately left alone, because those really are names you may use.
116
117**A tilde against the end of a lower-case name is a label**, and against anything else it is not: the grammar says "ASCII-uppercase identifiers and keywords cannot form labels", so `Foo~` is a type and a tilde.
118
119**Not recognised**, each for a stated reason:
120
121| Not recognised | Because |
122| --- | --- |
123| The expression inside `\{…}` | The grammar matches it to "the matching `}`", with braces inside nested literals not counting — finding the end needs the parser. `"a \{b} c"` is therefore one string span, brace to brace. **A string nested inside an interpolation is where that stops**: the scanner takes the first unescaped quote as the closer, so `"a \{f("x")} c"` scans as string, then `x` as an identifier, then string. The spans stay in order and never overlap; the cost is a wrong colour inside a nested literal, which is rarer than the brace-counting bugs the alternative would cause |
124| An enum constructor of your own, as anything but a type | Nothing in the syntax separates `Circle(1.0)` from a type applied to arguments; inventing a separation means being wrong in both directions instead of one |
125| `.5` as a number | MoonBit requires a digit before the point, so a leading dot is a tuple accessor or a dot-identifier and never a literal |
126| A reserved word as a keyword | `move`, `ref` and the rest are identifiers the compiler merely warns about, and colouring them would tell a reader they cannot write `let ref = 1` when they can |
127| An identifier holding non-ASCII letters | MoonBit allows CJK and several other ranges in a name; the rune predicates this scanner is built on are ASCII, so such a name is stepped over uncoloured rather than guessed at |
128| `.mbt.md` as MoonBit | It is a Markdown document with MoonBit in its fences. Its extension is `.md`, and Markdown is what colours it |
129| Whether a name is bound in this scope | Nothing here reads more than one line at a time; that is the language server's question, and [F1 answers it](../how-to/ask-about-code.md) |
130
131## TOML
132
133| Recognised | As |
134| --- | --- |
135| `# comment` | comment |
136| `[table]`, `[[array]]` | the name as a type, the brackets as punctuation |
137| `key =` | identifier, then operator |
138| `"basic"`, `'literal'`, `"""multi-line"""`, `'''multi-line'''` | string |
139| `true`, `false` | constant |
140| numbers, dates, times, `inf`, `nan` | number |
141
142## YAML
143
144A compose file, a Kubernetes manifest and a CI workflow are all this: there is no separate dialect, because a dialect would be somebody else's schema to keep in step with.
145
146| Recognised | As |
147| --- | --- |
148| `# comment` | comment |
149| `key:` before a space or the end of the line | the key as an identifier, the colon as punctuation |
150| `"quoted": 1`, `'quoted': 1` | the quoted key as an identifier |
151| `- ` opening a sequence entry | punctuation |
152| `"…"`, `'…'` | string |
153| `true`, `false`, `yes`, `no`, `on`, `off`, `null` | constant, whatever their case |
154| numbers, dates and times written without quotes | number |
155| `&anchor`, `*alias` | builtin |
156| `!!str`, `!Custom` | type |
157| `---`, `...` | the whole line as punctuation |
158| `{`, `}`, `[`, `]`, `,` | punctuation |
159| `\|`, `>`, with their chomping and indentation indicators | the header as an operator, the body as a string |
160
161**A colon is a separator only when a space or the end of the line follows it.** `image: nginx:1.27` is a key and one value, and `url: http://example.com/x` is a key and one URL — colouring the inner colons as separators would put every image tag and every URL in three colours.
162
163**A block scalar's extent is decided by indentation**, not by a delimiter. The first content line after `|` or `>` fixes the block's indentation; every line indented at least that far belongs to it, and the first line that is not ends it. **A blank line inside a block stays inside it**: a literal scalar keeps its empty lines, and ending the block at the first paragraph break would cut a shell script in a CI file in half.
164
165**A `#` needs a space before it to start a comment**, so `colour: ff#00aa` is one scalar.
166
167| Not recognised | Because |
168| --- | --- |
169| The schema of a compose file, a manifest or a workflow | Colouring `services:` differently from any other key means carrying somebody else's schema, and it goes stale the day they add a key |
170| Multi-document streams as separate documents | `---` is coloured, but nothing is reset at it; nothing in the colouring depends on document boundaries |
171| Whether a bare word is a string or a number to a parser | `1.2.3` is a version to a reader and a string to YAML; the scanner colours what it looks like |
172
173## Markdown
174
175| Recognised | As |
176| --- | --- |
177| `# Heading``###### Heading` | the whole line as a heading |
178| `**bold**`, `__bold__`, `*italic*`, `_italic_` | emphasis |
179| `` `code` `` | string |
180| `[text](target)`, `![alt](src)` | the whole thing as a link |
181| `- `, `* `, `+ `, `1. `, `1) ` | the marker as punctuation |
182| `>` | punctuation |
183| `---`, `***`, `___` | punctuation |
184| ` ``` ` and `~~~` fences | the whole block, opening and closing lines included, as a string |
185
186A fenced block is **one colour whatever language it announces**: ```` ```moonbit ```` does not colour its contents as MoonBit. The run of markers that opens a block must be matched by the same character to close it, so a backtick fence is not closed by a tilde one. An unclosed fence colours to the end of the file.
187
188The run of markers opening emphasis must be matched by a run of the same length, so `**bold**` is one span rather than two italics.
189
190## JavaScript
191
192| Recognised | As |
193| --- | --- |
194| `const`, `let`, `function`, `class`, `async`, `await`, `import`, `export`, … | keyword |
195| `true`, `false`, `null`, `undefined`, `NaN`, `Infinity`, `this` | constant |
196| `console`, `document`, `window`, `Array`, `Object`, `Promise`, `Math`, `JSON`, … | builtin |
197| a name immediately before `(` | function |
198| `"…"`, `'…'` | string |
199| `` `` ``, interpolations included, across lines | string |
200| `//` to end of line, `/* … */` across lines | comment |
201| `42`, `3.14`, `0x1f`, `0b1010`, `0o777`, `1_000_000`, `1e6`, `10n` | number |
202| runs of `+-*/%=<>!&|^~?:` | operator |
203| `()[]{},;.` | punctuation |
204
205**Regular-expression literals are not recognised.** Telling `/x/g` from a division needs to know whether the previous token could end an expression; a wrong guess colours the rest of a line as a string, which is worse than leaving a regex the colour of an operator.
206
207Globals are recognised by name, so a file that shadows `Math` still has it coloured as a builtin — the same rule MoonBit's builtins follow here.
208
209## HTML
210
211| Recognised | As |
212| --- | --- |
213| `<tag`, `</tag`, `>`, `/>` | tag |
214| attribute names, including `data-*`, `xlink:href`, `@click`, `v-bind.prop` | attribute |
215| `=` | operator |
216| `"…"`, `'…'` | string |
217| `<!-- … -->`, across lines | comment |
218| `&amp;`, `&#169;` | constant |
219| `<!DOCTYPE …>` and other declarations | keyword |
220
221Text between tags is not coloured. A bare `&` with no `;` within 32 characters is left alone, because it is legal text.
222
223**The contents of `<script>` and `<style>` are not coloured** as JavaScript and CSS.
224
225## XML
226
227Its own scanner rather than HTML's, for one reason that matters: CDATA. The whole point of `<![CDATA[ … ]]>` is that its contents are *not* markup, and colouring the tags inside one as tags is exactly backwards.
228
229| Recognised | As |
230| --- | --- |
231| `<?xml version="1.0"?>` and other processing instructions | the target and `?>` as keyword, the pairs between as attributes and strings |
232| `<!DOCTYPE …>` and the other `<!` forms | keyword |
233| `<!-- … -->`, across lines | comment |
234| `<![CDATA[ … ]]>`, across lines | string |
235| `<tag`, `</tag`, `>`, `/>` | tag |
236| `<ns:tag>`, `xsi:type` | the prefix and the local name as **one** span |
237| attribute names | attribute |
238| `=` | operator |
239| `"…"`, `'…'` | string |
240| `&amp;`, `&#169;` | constant |
241
242**A comment and a CDATA section close on different delimiters**, and are carried separately: a `-->` inside a CDATA section does not end it.
243
244**A bare `&` with no semicolon within 32 characters is left alone**, because it is legal text in plenty of documents and swallowing the rest of the line would be the bigger mistake.
245
246Text between tags is not coloured.
247
248## Shell
249
250Applies to `sh`, `bash` and `zsh` alike: the keywords recognised are the ones they share.
251
252| Recognised | As |
253| --- | --- |
254| `if`, `then`, `fi`, `for`, `while`, `case`, `esac`, `function`, `return`, … | keyword |
255| `true`, `false` | constant |
256| `echo`, `printf`, `export`, `local`, `read`, `cd`, `set`, `source`, … | builtin |
257| `$NAME`, `${…}`, `$(…)`, `$1`, `$?`, `$@` | builtin |
258| the **first bare word on a line** | function |
259| every later bare word, and `NAME` in `NAME=value` | identifier |
260| `'…'`, with nothing escaped or expanded inside | string |
261| `"…"`, with the expansions inside it coloured as expansions | string |
262| `#` to end of line | comment |
263
264`$(a $(b) c)` is one span: nesting is counted. An option such as `-euo` is one word, not a minus and a word.
265
266**Heredocs are not recognised.** `<<EOF` and the text after it are coloured as ordinary shell.
267
268## Dockerfile
269
270| Recognised | As |
271| --- | --- |
272| `FROM`, `RUN`, `COPY`, `ADD`, `ARG`, `ENV`, `CMD`, `ENTRYPOINT`, `EXPOSE`, `LABEL`, `USER`, `VOLUME`, `WORKDIR`, `HEALTHCHECK`, `ONBUILD`, `SHELL`, `STOPSIGNAL`, `MAINTAINER` | keyword, in any case |
273| `AS`, `NONE` | keyword |
274| `# comment`, including the `# syntax=` and `# escape=` directives | comment |
275| `--from=builder`, `--chown=me:me` | the flag name as an attribute |
276| `$NAME`, `${NAME}`, `${NAME:-default}` | builtin, as one span to the closing brace |
277| `"…"`, `'…'` | string |
278| a trailing `\` | operator |
279| numbers | number |
280| paths and image references — `/usr/local/bin`, `golang:1.26-alpine` | identifier, as **one** span |
281
282**Only the first word of a line can be an instruction**, and a word that is not one is an argument — which is what keeps a continuation line's first word out of the keyword colour.
283
284**Nothing crosses a line break.** A `\` joins two lines for Docker, but each half still reads as a command and is coloured on its own.
285
286| Not recognised | Because |
287| --- | --- |
288| The shell inside a `RUN` | It would mean running the shell scanner over part of a line and mapping its columns out, and `RUN` may hold any language |
289| Heredocs in a `RUN` | The same reason the shell scanner does not recognise them |
290| Which stage a `--from` names | Nothing here reads the rest of the file |
291
292## See also
293
294- [Theme file format](themes.md) — every key these classes resolve to
295- [Colouring and completion](../explanation/colouring-and-completion.md) — why the scanners are written this way
296- [How to write your own theme](../how-to/write-a-theme.md)