Skip to main content

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.

LanguageFence names
JavaScriptjavascript, js, jsx, mjs, cjs
TypeScripttypescript, ts, tsx, mts, cts
Kotlinkotlin, kt, kts
Javajava
C / C++c, h, cpp, c++, cc, cxx, hpp, hh
Pythonpython, py, py3
Gogo, golang
Rustrust, rs
JSONjson, jsonc, json5
YAMLyaml, yml
Shellshell, sh, bash, zsh, console, shellsession
SQLsql, mysql, postgres, postgresql, pgsql, plsql, sqlite, soql
CSScss, scss, less, postcss
HTML / XMLhtml, markup, xml, xhtml, svg, vue, rss, atom
Diffdiff, patch, and diff-<language>
Markdownmarkdown, 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.

Loading editor

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:

Loading editor

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.