Code blocks
Fenced blocks are syntax highlighted in edit-rich and view modes. The
highlighter is the package's own: a small lexer driven by data-only
grammars, with 16 built in, and no Prism, highlight.js, Shiki or other
highlighter. (@lexical/code only runs a tokenizer for language names it
finds in the Prism table it installs itself; the plugin adds an empty
entry per name to open that gate, and Prism never tokenizes.) A language
is a file of about 30 lines.
| Language | Fence names |
|---|---|
| JavaScript | javascript, js, jsx, mjs, cjs |
| TypeScript | typescript, ts, tsx, mts, cts |
| Kotlin | kotlin, kt, kts |
| Java | java |
| C / C++ | c, h, cpp, c++, cc, cxx, hpp, hh |
| Python | python, py, py3 |
| Go | go, golang |
| Rust | rust, rs |
| JSON | json, jsonc, json5 |
| YAML | yaml, yml |
| Shell | shell, sh, bash, zsh, console, shellsession |
| SQL | sql, mysql, postgres, postgresql, pgsql, plsql, sqlite, soql |
| CSS | css, scss, less, postcss |
| HTML / XML | html, markup, xml, xhtml, svg, vue, rss, atom |
| Diff | diff, patch, and diff-<language> |
| Markdown | markdown, md, mdx |
Nothing else ships: PHP, Ruby, Swift, C#, TOML, GraphQL and the rest
render as plain text until you register a grammar (below). The exact
alias lists live in BUILTIN_CODE_LANGUAGES; getCodeLanguages() returns
the current registry, which loads the built-ins on first lookup rather
than at import time.
Unknown languages and untagged blocks render as plain text with the same
line structure, so cursor movement and indentation behave identically.
Untagged blocks export as a bare ``` fence (the CODE_BLOCK
transformer does this; it replaces Lexical's CODE).
Type ``` (optionally followed by a language) and press Enter to
start a block.
Colours
Each token type has one class (zui-token-<type>) and one custom property
(--zui-code-<type> on .zui-code), with light defaults and .dark
overrides. Retheme by overriding the properties:
.zui-code {
--zui-code-keyword: #7c3aed;
--zui-code-string: #047857;
}
.dark .zui-code {
--zui-code-keyword: #c4b5fd;
}
Token types (CODE_TOKEN_TYPES): comment, string, number,
keyword, builtin, type, function, property, variable,
constant, operator, punctuation, tag, attr, regex, meta,
inserted, deleted. Operators and punctuation inherit the text colour on
purpose: fewer colours, calmer blocks. inserted / deleted (diff) also
get --zui-code-inserted-background / --zui-code-deleted-background.
The same block with a wrapper class that overrides five of the properties (purple keywords, green strings, blue functions), in both colour modes:
Adding a language
A grammar (CodeGrammar) is a list of ordered regex rules plus word lists.
Rules are regex sources (no capturing groups or backreferences;
unterminated strings should stop at the line end). Identifiers no rule
claims are classified by the lists, then by two optional heuristics.
Register it before the editor mounts:
import { registerCodeLanguage } from '@zuilib/text-editor'
registerCodeLanguage({
name: 'toml',
rules: [
{ type: 'comment', match: '#[^\\n]*' },
{ type: 'meta', match: '(?<=^|\\n)\\[[^\\]]*\\]' },
{ type: 'property', match: '(?<=^|\\n)[\\w.-]+(?=\\s*=)' },
{ type: 'string', match: '"(?:[^"\\\\\\n]|\\\\.)*"?' },
{ type: 'number', match: '\\b\\d[\\d_]*(?:\\.\\d+)?\\b' },
],
constants: ['true', 'false'],
})
Word lists: keywords, builtins, types, constants. Heuristics:
callIsFunction marks an identifier followed by ( as a function;
capitalizedIsType marks Capitalized identifiers as type and
SCREAMING_CASE ones as constant. aliases adds extra names for the
same grammar.
Outside the editor, tokenizeCode(code, grammar) returns the flat
CodeToken list, resolveCodeLanguage(name) resolves aliases and
hasCodeLanguage(name) checks the registry. On
@zuilib/text-editor/lexical, codeTokenizer is the Lexical tokenizer
adapter and registerCodeBlockHighlighting(editor) wires highlighting
into a Lexical editor you assemble yourself.