Files
khosra/reference/syntax-highlighting-choices.md
Claude Opus 5andbdeshi 2609f69a87 record what this session established, with how it was established
Three reference files, each stating whether a fact was measured, read from
source, or asserted — an unattributed number is a rumour.

math-on-the-web: MathML is the only no-JS route, the one pure-Go TeX→MathML
library is an untagged 2023 commit, and writing MathML directly costs nothing
since raw HTML renders.

syntax-highlighting-choices: chroma is two modules and ~5MB, every alternative a
search returns is JavaScript, and custom lexers load from XML at runtime without
a rebuild.

goldmark-behaviours: the seven surprises that caused or nearly caused defects —
strikethrough claiming a single tilde, delimiter runs pairing across whitespace,
per-parse heading id counters, footnote id prefixes, raw HTML being dropped
rather than escaped, the language class already in the output, and ParseFS
globbing.
2026-08-02 01:04:14 +06:00

35 lines
1.7 KiB
Markdown

# Syntax highlighting, and what it costs
Established 2026-08-02: module footprint and binary sizes measured by building a probe module, the
alternatives surveyed by search. Numbers are for chroma v2.27.0 on darwin/arm64.
## There is one mature pure-Go option
chroma. Every "alternative to chroma" a search returns — Prism, highlight.js, Rainbow — is JavaScript, and
therefore unavailable to a theme that ships no script. `zyedidia/highlight` exists but needs syntax files
supplied per language and covers far less.
## What chroma costs
- **Two modules**: `github.com/alecthomas/chroma/v2` and `github.com/dlclark/regexp2/v2`. Nothing else.
- **~5MB of binary.** A probe importing lexers, formatters and styles built at 7.4MB against a 2.4MB
baseline; khosra itself went from ~15MB to ~19MB.
- chroma is larger than khosra.
## What it gives beyond colour
`html.WithLineNumbers`, `html.BaseLineNumber(n)` for a custom starting number, and
`html.HighlightLines([][2]int)`. With `html.WithClasses(true)` it emits token classes and no inline colour,
so the palette stays in a stylesheet the theme owns.
## Custom lexers need no rebuild
`chroma.Unmarshal(data []byte) (*RegexLexer, error)` reads a lexer from XML — the same format chroma's own
lexers ship in, under `lexers/embedded/*.xml`. With `chroma.NewLexerRegistry()` a site root could carry its
own language definition and register it at startup. Not built; the door is open by construction.
## What chroma does not give
A filename or title on a block, and content read from a file. Those are the engine's, whatever highlights.
So a design that parses the fence's info string works identically with chroma, without it, or after it.