1![Chroma](chroma.jpg)
  2
  3# A general purpose syntax highlighter in pure Go
  4
  5[![Go Reference](https://pkg.go.dev/badge/github.com/alecthomas/chroma/v2.svg)](https://pkg.go.dev/github.com/alecthomas/chroma/v2) [![CI](https://github.com/alecthomas/chroma/actions/workflows/ci.yml/badge.svg)](https://github.com/alecthomas/chroma/actions/workflows/ci.yml) [![Slack chat](https://img.shields.io/static/v1?logo=slack&style=flat&label=slack&color=green&message=gophers)](https://invite.slack.golangbridge.org/)
  6
  7
  8Chroma takes source code and other structured text and converts it into syntax
  9highlighted HTML, ANSI-coloured text, etc.
 10
 11Chroma is based heavily on [Pygments](http://pygments.org/), and includes
 12translators for Pygments lexers and styles.
 13
 14## Table of Contents
 15
 16<!-- TOC -->
 17
 181. [Supported languages](#supported-languages)
 192. [Try it](#try-it)
 203. [Using the library](#using-the-library)
 21   1. [Quick start](#quick-start)
 22   2. [Identifying the language](#identifying-the-language)
 23   3. [Formatting the output](#formatting-the-output)
 24   4. [The HTML formatter](#the-html-formatter)
 254. [More detail](#more-detail)
 26   1. [Lexers](#lexers)
 27   2. [Formatters](#formatters)
 28   3. [Styles](#styles)
 295. [Command-line interface](#command-line-interface)
 306. [Testing lexers](#testing-lexers)
 317. [What's missing compared to Pygments?](#whats-missing-compared-to-pygments)
 32
 33<!-- /TOC -->
 34
 35## Supported languages
 36
 37| Prefix | Language
 38| :----: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 39|   A    | ABAP, ABNF, ActionScript, ActionScript 3, Ada, Agda, AL, Alloy, AMPL, Angular2, ANTLR, ApacheConf, APL, AppleScript, ArangoDB AQL, Arduino, ArmAsm, ATL, AutoHotkey, AutoIt, Awk
 40|   B    | Ballerina, Bash, Bash Session, Batchfile, Beef, BibTeX, Bicep, BlitzBasic, BNF, BQN, Brainfuck
 41|   C    | C, C#, C++, C3, Caddyfile, Caddyfile Directives, Cap'n Proto, Cassandra CQL, Ceylon, CFEngine3, cfstatement, ChaiScript, Chapel, Cheetah, Clojure, CMake, COBOL, CoffeeScript, Common Lisp, Coq, Core, Crystal, CSS, CSV, CUE, Cython
 42|   D    | D, Dart, Dax, Desktop file, Diff, Django/Jinja, dns, Docker, DTD, Dylan
 43|   E    | EBNF, Elixir, Elm, EmacsLisp, Erlang
 44|   F    | Factor, Fennel, Fish, Forth, Fortran, FortranFixed, FSharp
 45|   G    | GAS, GDScript, GDScript3, Gemtext, Genshi, Genshi HTML, Genshi Text, Gettext, Gherkin, Gleam, GLSL, Gnuplot, Go, Go HTML Template, Go Template, Go Text Template, GraphQL, Groff, Groovy
 46|   H    | Handlebars, Hare, Haskell, Haxe, HCL, Hexdump, HLB, HLSL, HolyC, HTML, HTTP, Hy
 47|   I    | Idris, Igor, INI, Io, ISCdhcpd
 48|   J    | J, Janet, Java, JavaScript, JSON, JSONata, Jsonnet, Julia, Jungle
 49|   K    | Kakoune, Kotlin
 50|   L    | Lean4, Lighttpd configuration file, LLVM, lox, Lua, Luau
 51|   M    | Makefile, Mako, markdown, Markless, Mason, Materialize SQL dialect, Mathematica, Matlab, MCFunction, Meson, Metal, MiniZinc, MLIR, Modelica, Modula-2, Mojo, MonkeyC, MoonScript, MorrowindScript, Myghty, MySQL
 52|   N    | NASM, Natural, NDISASM, Newspeak, Nginx configuration file, Nim, Nix, NSIS, Nu
 53|   O    | Objective-C, ObjectPascal, OCaml, Octave, Odin, OnesEnterprise, OpenEdge ABL, OpenSCAD, Org Mode
 54|   P    | PacmanConf, Perl, PHP, PHTML, Pig, PkgConfig, PL/pgSQL, plaintext, Plutus Core, Pony, PostgreSQL SQL dialect, PostScript, POVRay, PowerQuery, PowerShell, Prolog, Promela, PromQL, properties, Protocol Buffer, Protocol Buffer Text Format, PRQL, PSL, Puppet, Python, Python 2
 55|   Q    | QBasic, QML
 56|   R    | R, Racket, Ragel, Raku, react, ReasonML, reg, Rego, reStructuredText, Rexx, RGBDS Assembly, Ring, RPGLE, RPMSpec, Ruby, Rust
 57|   S    | SAS, Sass, Scala, Scheme, Scilab, SCSS, Sed, Sieve, Smali, Smalltalk, Smarty, SNBT, Snobol, Solidity, SourcePawn, Spade, SPARQL, SQL, SquidConf, Standard ML, stas, Stylus, Svelte, Swift, SYSTEMD, systemverilog
 58|   T    | TableGen, Tal, TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turing, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData, Typst
 59|   U    | ucode
 60|   V    | V, V shell, Vala, VB.net, verilog, VHDL, VHS, VimL, vue
 61|   W    | WDTE, WebAssembly Text Format, WebGPU Shading Language, WebVTT, Whiley
 62|   X    | XML, Xorg
 63|   Y    | YAML, YANG
 64|   Z    | Z80 Assembly, Zed, Zig
 65
 66_I will attempt to keep this section up to date, but an authoritative list can be
 67displayed with `chroma --list`._
 68
 69## Try it
 70
 71Try out various languages and styles on the [Chroma Playground](https://swapoff.org/chroma/playground/).
 72
 73## Using the library
 74
 75This is version 2 of Chroma, use the import path:
 76
 77```go
 78import "github.com/alecthomas/chroma/v2"
 79```
 80
 81Chroma, like Pygments, has the concepts of
 82[lexers](https://github.com/alecthomas/chroma/tree/master/lexers),
 83[formatters](https://github.com/alecthomas/chroma/tree/master/formatters) and
 84[styles](https://github.com/alecthomas/chroma/tree/master/styles).
 85
 86Lexers convert source text into a stream of tokens, styles specify how token
 87types are mapped to colours, and formatters convert tokens and styles into
 88formatted output.
 89
 90A package exists for each of these, containing a global `Registry` variable
 91with all of the registered implementations. There are also helper functions
 92for using the registry in each package, such as looking up lexers by name or
 93matching filenames, etc.
 94
 95In all cases, if a lexer, formatter or style can not be determined, `nil` will
 96be returned. In this situation you may want to default to the `Fallback`
 97value in each respective package, which provides sane defaults.
 98
 99### Quick start
100
101A convenience function exists that can be used to simply format some source
102text, without any effort:
103
104```go
105err := quick.Highlight(os.Stdout, someSourceCode, "go", "html", "monokai")
106```
107
108### Identifying the language
109
110To highlight code, you'll first have to identify what language the code is
111written in. There are three primary ways to do that:
112
1131. Detect the language from its filename.
114
115   ```go
116   lexer := lexers.Match("foo.go")
117   ```
118
1192. Explicitly specify the language by its Chroma syntax ID (a full list is available from `lexers.Names()`).
120
121   ```go
122   lexer := lexers.Get("go")
123   ```
124
1253. Detect the language from its content.
126
127   ```go
128   lexer := lexers.Analyse("package main\n\nfunc main()\n{\n}\n")
129   ```
130
131In all cases, `nil` will be returned if the language can not be identified.
132
133```go
134if lexer == nil {
135  lexer = lexers.Fallback
136}
137```
138
139At this point, it should be noted that some lexers can be extremely chatty. To
140mitigate this, you can use the coalescing lexer to coalesce runs of identical
141token types into a single token:
142
143```go
144lexer = chroma.Coalesce(lexer)
145```
146
147### Formatting the output
148
149Once a language is identified you will need to pick a formatter and a style (theme).
150
151```go
152style := styles.Get("swapoff")
153if style == nil {
154  style = styles.Fallback
155}
156formatter := formatters.Get("html")
157if formatter == nil {
158  formatter = formatters.Fallback
159}
160```
161
162Then obtain an iterator over the tokens:
163
164```go
165contents, err := ioutil.ReadAll(r)
166iterator, err := lexer.Tokenise(nil, string(contents))
167```
168
169And finally, format the tokens from the iterator:
170
171```go
172err := formatter.Format(w, style, iterator)
173```
174
175### The HTML formatter
176
177By default the `html` registered formatter generates standalone HTML with
178embedded CSS. More flexibility is available through the `formatters/html` package.
179
180Firstly, the output generated by the formatter can be customised with the
181following constructor options:
182
183- `Standalone()` - generate standalone HTML with embedded CSS.
184- `WithClasses()` - use classes rather than inlined style attributes.
185- `ClassPrefix(prefix)` - prefix each generated CSS class.
186- `TabWidth(width)` - Set the rendered tab width, in characters.
187- `WithLineNumbers()` - Render line numbers (style with `LineNumbers`).
188- `WithLinkableLineNumbers()` - Make the line numbers linkable and be a link to themselves.
189- `HighlightLines(ranges)` - Highlight lines in these ranges (style with `LineHighlight`).
190- `LineNumbersInTable()` - Use a table for formatting line numbers and code, rather than spans.
191
192If `WithClasses()` is used, the corresponding CSS can be obtained from the formatter with:
193
194```go
195formatter := html.New(html.WithClasses(true))
196err := formatter.WriteCSS(w, style)
197```
198
199## More detail
200
201### Lexers
202
203See the [Pygments documentation](http://pygments.org/docs/lexerdevelopment/)
204for details on implementing lexers. Most concepts apply directly to Chroma,
205but see existing lexer implementations for real examples.
206
207In many cases lexers can be automatically converted directly from Pygments by
208using the included Python 3 script `pygments2chroma_xml.py`. I use something like
209the following:
210
211```sh
212uv run --script _tools/pygments2chroma_xml.py \
213  pygments.lexers.jvm.KotlinLexer \
214  > lexers/embedded/kotlin.xml
215```
216
217A list of all lexers available in Pygments can be found in [pygments-lexers.txt](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt).
218
219### Formatters
220
221Chroma supports HTML output, as well as terminal output in 8 colour, 256 colour, and true-colour.
222
223A `noop` formatter is included that outputs the token text only, and a `tokens`
224formatter outputs raw tokens. The latter is useful for debugging lexers.
225
226### Styles
227
228Chroma styles are defined in XML. The style entries use the
229[same syntax](http://pygments.org/docs/styles/) as Pygments. All Pygments styles have been converted to Chroma using the `_tools/style.py`
230script.
231
232Style names are case-insensitive. For example, `monokai` and `Monokai` are treated as the same style.
233
234When you work with one of [Chroma's styles](https://github.com/alecthomas/chroma/tree/master/styles),
235know that the `Background` token type provides the default style for tokens. It does so
236by defining a foreground color and background color.
237
238For example, this gives each token name not defined in the style a default color
239of `#f8f8f8` and uses `#000000` for the highlighted code block's background:
240
241```xml
242<entry type="Background" style="#f8f8f2 bg:#000000"/>
243```
244
245Also, token types in a style file are hierarchical. For instance, when `CommentSpecial` is not defined, Chroma uses the token style from `Comment`. So when several comment tokens use the same color, you'll only need to define `Comment` and override the one that has a different color.
246
247For a quick overview of the available styles and how they look, check out the [Chroma Style Gallery](https://xyproto.github.io/splash/docs/).
248
249## Command-line interface
250
251A command-line interface to Chroma is included.
252
253Binaries are available to install from [the releases page](https://github.com/alecthomas/chroma/releases).
254
255The CLI can be used as a preprocessor to colorise output of `less(1)`,
256see documentation for the `LESSOPEN` environment variable.
257
258The `--fail` flag can be used to suppress output and return with exit status
2591 to facilitate falling back to some other preprocessor in case chroma
260does not resolve a specific lexer to use for the given file. For example:
261
262```shell
263export LESSOPEN='| p() { chroma --fail "$1" || cat "$1"; }; p "%s"'
264```
265
266Replace `cat` with your favourite fallback preprocessor.
267
268When invoked as `.lessfilter`, the `--fail` flag is automatically turned
269on under the hood for easy integration with [lesspipe shipping with
270Debian and derivatives](https://manpages.debian.org/lesspipe#USER_DEFINED_FILTERS);
271for that setup the `chroma` executable can be just symlinked to `~/.lessfilter`.
272
273## Projects using Chroma
274
275* [`moor`](https://github.com/walles/moor) is a full-blown pager that colorizes
276  its input using Chroma
277* [Hugo](https://gohugo.io/) is a static site generator that [uses Chroma for syntax
278  highlighting code examples](https://gohugo.io/content-management/syntax-highlighting/)
279
280## Testing lexers
281
282If you edit some lexers and want to try it, open a shell in `cmd/chromad` and run:
283
284```shell
285go run . --csrf-key=securekey
286```
287
288A Link will be printed. Open it in your Browser. Now you can test on the Playground with your local changes.
289
290If you want to run the tests and the lexers, open a shell in the root directory and run:
291
292```shell
293go test ./lexers
294```
295
296When updating or adding a lexer, please add tests. See [lexers/README.md](lexers/README.md) for more.
297
298## What's missing compared to Pygments?
299
300- Quite a few lexers, for various reasons (pull-requests welcome):
301  - Pygments lexers for complex languages often include custom code to
302    handle certain aspects, such as Raku's ability to nest code inside
303    regular expressions. These require time and effort to convert.
304  - I mostly only converted languages I had heard of, to reduce the porting cost.
305- Some more esoteric features of Pygments are omitted for simplicity.
306- Though the Chroma API supports content detection, very few languages support them.
307  I have plans to implement a statistical analyser at some point, but not enough time.