turbo-editors/turbo-jspublic Fork 0
main
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-js.git
git clone ssh://git@rickub.com/turbo-editors/turbo-js.git

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

languages.md · 296 lines · 19.3 KBmarkdown Blame HistoryRaw
📦 Turbo JS 91999d1 k33g 10h ago1# Reference: languages coloured
2
3> Neutral description of which files Turbo JS 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| `.js`, `.mjs`, `.cjs` | JavaScript |
12| `.json`, `.jsonc` | JSON |
13| `.toml` | TOML |
14| `.yaml`, `.yml` | YAML |
15| `.md`, `.markdown` | Markdown |
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: `notes.js.md` is Markdown, `main.js.backup` is not JavaScript, and `main.test.js` is JavaScript.
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
31A file that neither table claims is read by its **first line**. A shebang naming `node` makes it JavaScript: a command-line tool written in JavaScript is a file with no extension whose first line is `#!/usr/bin/env node`, and Node strips that line before parsing. A shebang naming a shell — `sh`, `bash`, `zsh`, `dash` or `ksh` — makes it a shell script. The interpreter is recognised as a path element or as the argument to `env`.
32
33| First line | Result |
34| --- | --- |
35| `#!/usr/bin/env node` | JavaScript |
36| `#!/usr/local/bin/node` | JavaScript |
37| `#!/usr/bin/env -S node --no-warnings` | JavaScript |
38| `#!/bin/sh` | Shell |
39| `#!/usr/bin/env bash` | Shell |
40| `#!/usr/bin/env python3` | Not coloured |
41| Anything not starting `#!` | Not coloured |
42
43The order is fixed — extension, then name, then first line — and the first to decide wins.
44
45Everything 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. In particular **TypeScript (`.ts`, `.tsx`), JSX (`.jsx`) and CSS are not coloured**: the language server serves `.ts` files all the same, but the scanner here is for JavaScript.
46
47## Classes
48
49Every scanner produces the same vocabulary of classes, and each maps to one theme key.
50
51| Class | Theme key | Produced by |
52| --- | --- | --- |
53| `identifier` | `syntax.identifier` | JavaScript, JSON (a bare word, which is not JSON), TOML, shell, YAML, Dockerfile |
54| `keyword` | `syntax.keyword` | JavaScript, shell, HTML (doctype), XML, Dockerfile |
55| `type` | `syntax.type` | JavaScript (class names and capitalised names), TOML (table headers), YAML (tags) |
56| `builtin` | `syntax.builtin` | JavaScript (the standard library's and Node's globals), shell (builtins and expansions), YAML (anchors and aliases), Dockerfile (variables) |
57| `constant` | `syntax.constant` | JavaScript, JSON, TOML, shell, YAML, HTML and XML (entities) |
58| `function` | `syntax.function` | JavaScript, shell (the command) |
59| `string` | `syntax.string` | all |
60| `char` | `syntax.char` | JavaScript (regular-expression literals) |
61| `number` | `syntax.number` | JavaScript, JSON, TOML, shell, YAML, Dockerfile |
62| `comment` | `syntax.comment` | JavaScript, JSON, TOML, shell, HTML, YAML, XML, Dockerfile |
63| `operator` | `syntax.operator` | JavaScript, TOML, shell, HTML, YAML (block scalar headers), XML, Dockerfile |
64| `punctuation` | `syntax.punctuation` | JavaScript, JSON, TOML, shell, Markdown, YAML, Dockerfile |
65| `heading` | `syntax.heading` | Markdown |
66| `tag` | `syntax.tag` | HTML, XML |
67| `attribute` | `syntax.attribute` | JSON (keys), JavaScript (decorators), HTML, XML, Dockerfile (flags) |
68| `emphasis` | `syntax.emphasis` | Markdown |
69| `link` | `syntax.link` | Markdown |
70
71JavaScript produces no `heading`, `tag`, `emphasis` or `link` span. In `turbo-classic`, `syntax.number` and `syntax.constant` are both magenta and `syntax.char` is the green of `syntax.string`, so `42` and `true` share a colour there and so do `/re/` and `"re"`; other themes separate them. See [how to write your own theme](../how-to/write-a-theme.md) if you want to change it.
72
73## JavaScript
74
75Hand-written, in `internal/jslang`, and registered under the same name as the scanner turbo-core ships for JavaScript, which it replaces. What it adds over the library's: regular-expression literals, the hashbang line, Node's globals, the name after `function` and `class`, private names, decorators, and a leading capital read as a class name.
76
77**Two constructs cross a line break**, and are carried to the next line: a `/* … */` block comment to its closing `*/`, and a `` `` `` template literal to its closing backtick. Neither nests. An ordinary `'…'` or `"…"` string does **not** cross a line: an unterminated one is coloured to the end of its line, and the next line is code again.
78
79| Recognised | As |
80| --- | --- |
81| `as`, `async`, `await`, `break`, `case`, `catch`, `class`, `const`, `continue`, `debugger`, `default`, `delete`, `do`, `else`, `export`, `extends`, `finally`, `for`, `from`, `function`, `get`, `if`, `import`, `in`, `instanceof`, `let`, `new`, `of`, `return`, `set`, `static`, `super`, `switch`, `throw`, `try`, `typeof`, `var`, `void`, `while`, `with`, `yield`, and the reserved `enum`, `implements`, `interface`, `package`, `private`, `protected`, `public` | keyword |
82| `true`, `false`, `null`, `undefined`, `NaN`, `Infinity`, `this` | constant |
83| the standard library's globals — `Array`, `Object`, `Promise`, `Math`, `JSON`, `Map`, `Set`, `Symbol`, `BigInt`, `Error`, `TypeError`, `parseInt`, `setTimeout`, `structuredClone`, `fetch`, `URL`, … | builtin |
84| Node's globals — `process`, `Buffer`, `console`, `require`, `module`, `exports`, `__dirname`, `__filename`, `setImmediate`, `performance`, `crypto` — and the browser's `document` and `window` | builtin |
85| the name after `function`, `function*` or `async function` — `parse` in `function parse(input) {}` | function |
86| the name after `class` — `widget` in `class widget extends Base {}` | type |
87| any other name starting with an ASCII capital — `Greeter`, `EventEmitter`, `MyError` — even before a `(` | type |
88| any other name immediately before `(` — `compute(3)`, `obj.method()`, `this.#reset()` | function |
89| any word after `.` or `?.`, whatever it is spelt like — `map.get(k)` a function, `options.default` a name, `user?.class` a name | property, never a keyword |
90| `get`, `set`, `static`, `of`, `from` and `as` before a `(` — `get(key)` | function |
91| `#count`, a private member, the `#` included | identifier, or function before `(` |
92| `@decorator`, `@observable.ref`, the dotted path included | attribute |
93| any other name: a Unicode letter, `_` or `$`, then letters, digits and the same — `x`, `$el`, `_`, `café`, `名前` | identifier |
94| `"…"`, `'…'` with backslash escapes, on one line | string |
95| `` `` ``, `${…}` interpolations included, across lines | string |
96| `/…/gi`, a regular-expression literal, flags included, where the previous token allows one and a closing `/` exists on the line | char |
97| `42`, `1_000`, `0xFF`, `0o17`, `0b1010`, `1.5e-3`, `2E+10`, `.5`, `10n`, `0xFFn` | number |
98| `//` to the end of the line; `/* … */` and `/** … */` across lines | comment |
99| `#!` on the first line of the file, to its end | comment |
100| `...` | punctuation, as one span |
101| `?.` | operator, as one span |
102| runs of `+-*/%=<>!&\|^~?:` — including `=>`, `??`, `===`, `**`, `>>>=` | operator |
103| `()[]{},;` and a lone `.` | punctuation |
104
105**A slash divides after a value and opens a regular expression everywhere else.** After a name, a number, a string, a template, a regular expression, `)` or `]`, a `/` is division. After an operator, `(`, `[`, `{`, `}`, `,`, `;`, `:`, a keyword, or at the start of a line, it opens a regular expression — **provided a closing `/` exists on the same line**; otherwise it divides, whatever came before it. A regular expression cannot cross a line, so that bound keeps a wrong guess to one line.
106
107**A point joins a number only when a digit follows it**, so `1.toString()` is the number `1`, a dot and a method. **The sign after an `e` is part of the number**, except in a hexadecimal literal, where `0xE+1` is a sum.
108
109**A capitalised name is a class by convention, not by rule.** JavaScript lets you write `const Count = 1`, and it is coloured as a class all the same, as is `MAX_SIZE`. Classes and constructors are what people capitalise, and the colour follows the people.
110
111**Not recognised**, each for a stated reason:
112
113| Not recognised | Because |
114| --- | --- |
115| The code inside `${…}` in a template | Colouring it means the scanner reaching back into itself with a nesting depth to carry, for a construct that is usually one short expression; the whole literal is a string |
116| A backtick inside `${…}` | For the same reason: it ends the template early, and the code after it is coloured as code until the next backtick |
117| A regular expression with no closing slash on its line | It cannot be one — the language forbids a newline in the literal — so the slash divides |
118| JSX | `<div>` is read as an operator, a name and an operator; there is no HTML inside JavaScript here |
119| TypeScript's annotations and types | A different language; `.ts` files are not coloured at all |
120| Nested block comments | JavaScript ends a block comment at the first `*/`; a depth would be a claim about a different language |
121| A string continued with a trailing backslash | The string stops at its line and the next line is code; the continuation is rare enough not to be worth carrying |
122| Whether a `SCREAMING_SNAKE_CASE` name is a constant | The leading capital says class, and nothing in the spelling separates the two conventions |
123| `#` as a comment | It is one only in the hashbang, on the first line; anywhere else `#` begins a private name or is stepped over |
124| 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) |
125
126## JSON
127
128Hand-written, in `internal/jslang/json.go`, thirty lines, because the language is thirty lines: strings, numbers, three words, six punctuation marks. It is the language every Node project's manifest is written in, and turbo-core does not colour it.
129
130| Recognised | As |
131| --- | --- |
132| `"name"` followed by a colon — a key | attribute |
133| `"demo"` anywhere else — a value | string |
134| `-12.5e+3`, `42`, `0.5` | number |
135| `true`, `false`, `null` | constant |
136| `{`, `}`, `[`, `]`, `,`, `:` | punctuation |
137| `//` to the end of the line; `/* … */` across lines | comment |
138| any bare word — `undefined`, `NaN`, a key without quotes | identifier |
139
140**A string is a key when a colon follows it**, past any spaces, and a value otherwise. `{"a" 1}` colours `"a"` as a value: it is not a key yet.
141
142**Comments are tolerated, not endorsed.** Strict JSON has none; `tsconfig.json`, `.jsonc` files and every editor's own settings file do, and a scanner that painted them as broken would be wrong in exactly the files most likely to hold one.
143
144| Not recognised | Because |
145| --- | --- |
146| Whether the document is valid JSON | The scanner colours tokens; a bare word comes out as an identifier rather than stopping the line, and a duplicated key looks like any other |
147| Single-quoted strings, trailing commas and the rest of JSON5 | Not JSON; a `'` is stepped over uncoloured |
148| An unterminated string stopping anywhere but its line | It is coloured to the end of the line, as a value, and the next line is JSON again |
149
150## TOML
151
152| Recognised | As |
153| --- | --- |
154| `# comment` | comment |
155| `[table]`, `[[array]]` | the name as a type, the brackets as punctuation |
156| `key =` | identifier, then operator |
157| `"basic"`, `'literal'`, `"""multi-line"""`, `'''multi-line'''` | string |
158| `true`, `false` | constant |
159| numbers, dates, times, `inf`, `nan` | number |
160
161## YAML
162
163A 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.
164
165| Recognised | As |
166| --- | --- |
167| `# comment` | comment |
168| `key:` before a space or the end of the line | the key as an identifier, the colon as punctuation |
169| `"quoted": 1`, `'quoted': 1` | the quoted key as an identifier |
170| `- ` opening a sequence entry | punctuation |
171| `"…"`, `'…'` | string |
172| `true`, `false`, `yes`, `no`, `on`, `off`, `null` | constant, whatever their case |
173| numbers, dates and times written without quotes | number |
174| `&anchor`, `*alias` | builtin |
175| `!!str`, `!Custom` | type |
176| `---`, `...` | the whole line as punctuation |
177| `{`, `}`, `[`, `]`, `,` | punctuation |
178| `\|`, `>`, with their chomping and indentation indicators | the header as an operator, the body as a string |
179
180**A colon is a separator only when a space or the end of the line follows it.** `image: node:24-alpine` 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.
181
182**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.
183
184**A `#` needs a space before it to start a comment**, so `colour: ff#00aa` is one scalar.
185
186| Not recognised | Because |
187| --- | --- |
188| 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 |
189| Multi-document streams as separate documents | `---` is coloured, but nothing is reset at it; nothing in the colouring depends on document boundaries |
190| 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 |
191
192## Markdown
193
194| Recognised | As |
195| --- | --- |
196| `# Heading``###### Heading` | the whole line as a heading |
197| `**bold**`, `__bold__`, `*italic*`, `_italic_` | emphasis |
198| `` `code` `` | string |
199| `[text](target)`, `![alt](src)` | the whole thing as a link |
200| `- `, `* `, `+ `, `1. `, `1) ` | the marker as punctuation |
201| `>` | punctuation |
202| `---`, `***`, `___` | punctuation |
203| ` ``` ` and `~~~` fences | the whole block, opening and closing lines included, as a string |
204
205A fenced block is **one colour whatever language it announces**: ```` ```javascript ```` does not colour its contents as JavaScript in a Markdown file. 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.
206
207The run of markers opening emphasis must be matched by a run of the same length, so `**bold**` is one span rather than two italics.
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, even in this editor: the HTML scanner is turbo-core's, and it does not hand a region of a line to another scanner.
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`, `node:24-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)