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