Skip to main content

Code blocks

Fenced blocks are syntax highlighted in edit-md and view modes. The highlighter is the package's own: a small lexer driven by data-only grammars (no Prism, highlight.js or Shiki in your bundle). A language is a file of about 30 lines.

Built in: JavaScript / TypeScript, Kotlin, Java, C / C++, Python, Go, Rust, JSON, YAML, shell, SQL, CSS, HTML / XML, diff and markdown, plus the usual aliases (ts, tsx, kt, yml, sh, bash, …). BUILTIN_CODE_LANGUAGES lists them and getCodeLanguages() returns the current registry.

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, hasCodeLanguage(name) checks the registry, and codeTokenizer is the Lexical tokenizer adapter. registerCodeBlockHighlighting(editor) wires highlighting into a Lexical editor you assemble yourself.