highlight

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2026 License: MIT Imports: 5 Imported by: 0

README

highlight

A small syntax highlighter for server-rendered HTML. One line-oriented scanner per language: Go, SQL, CSS, JavaScript, C, Ruby, JSON, Lua, HTML, Markdown, shell, and hml.

highlight.Code(w, "main.go", src)   // or "go", from a markdown fence
highlight.Diff("main.go", patch)    // a unified-diff patch

That generates CSS classes k keywords, n names, s strings, m numbers, c comments and a few for a diff's rows: gi inserted, gd deleted, gu hunk header, gc context. Colors are yours to style. Those names are the contract.

It is fast and small because it is deliberately incomplete and covers only a few curated languages. A format with no scanner is escaped and rendered uncolored, so callers can mix. See doc.go.

GitHub repo is a mirror

Development happens on cibot, a self-hosted review and CI server, which holds in progress branches. GitHub receives main and the tags so go get works.

License

MIT

Documentation

Overview

Package highlight colors source code for server-rendered HTML.

It is a hand-written, line-oriented scanner per language over a shared token type, for a curated set of formats: Go, sh, SQL, CSS, JavaScript, Markdown, and hml. A format it has no scanner for is escaped and rendered uncolored, so callers can mix.

Scope

Five classes come out -- k keywords, n names, s strings, m numbers, c comments -- and nothing else. The curation is the product: this is fast and small because it is deliberately incomplete and covers only a handful of languages. It is not a lexer framework, not a plugin registry, and not correctness-complete for any language. A word a keyword list misses is uncolored, not wrong.

Two layers

Tokens are the lower layer: a scanFunc reads one line, given the state the line before it ended in, and returns the state this one ended in. Carrying state is the whole point -- a lexer restarted per line colors the second half of a raw string as code.

Emitters are the upper layer, and HTML is one consumer rather than the interface. Code writes a whole file; Diff writes a unified-diff patch, where each row also carries a diff class (gi inserted, gd deleted, gu hunk header, gc context).

The two do not share a signature, and the difference is the distinction rather than an oversight. Code takes an io.Writer because a whole file is something a caller streams and frames: it writes its own <pre> and <code> around the call, with no string in between. Diff returns template.HTML because a patch is a fragment that becomes a value: a page renders one per file into a row a template reads, where a writer would mean a buffer for each.

So a third emitter takes the shape of what it emits rather than one this package picked. A whole file takes a writer; a fragment a template will hold returns one. Nothing needs both shapes for the same thing, and offering both on each emitter would double the surface to save a caller a line.

Escaping

Both emitters HTML-escape every byte they write. The source may be a file a stranger pushed, so nothing in it can become markup.

Styling

The nine class names are the whole contract with a stylesheet, and there is no shipped palette: colors are the consuming site's.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Code

func Code(w io.Writer, name, src string) error

Code writes src as HTML-escaped text with each colored run wrapped in a class span. name is a filename or a language name; a format with no scanner is escaped and left uncolored.

Nothing frames the output: no <pre>, no <code>, no wrapper class. The caller already knows what it is putting the code inside, and a markup opinion here would be one to work around there.

func Diff

func Diff(filename, patch string) template.HTML

Diff renders a unified-diff patch with two layers of coloring: each line carries a row-wide diff class (gi inserted, gd deleted, gu hunk header, gc context) and, underneath, its content is tokenized by the scanner matched to filename.

Style the per-line spans display:block and the row tint extends across the full <pre> width, GitHub-style. Marker chars (+/-) inherit the row's foreground color; language tokens override it.

A patch is two interleaved programs, so it needs two carry states: context plus deletions is the old file, context plus insertions is the new one. One state would let an edit inside a string miscolor the rest of the hunk. A hunk header resets both, since the file jumped.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL