package spectrum
Install
Dune Dependency
Authors
Maintainers
Sources
md5=e6c2a404dd2b34e69325b9ed4eb4db82
sha512=9d74bd83c9393f1ac6e5700d98f678d329584c4da3f7907121e7172ff17bc4874cf3a5c125f7e3171d55f8972640e954519424cdc34557317bd14ae58a7bc2d9
Description
Using OCaml Format module's 'semantic tags' with named colours and CSS-style hex colours.
Published: 18 Oct 2021
README
spectrum
Library for colour and formatting in the terminal.
Using OCaml Format module's "semantic tags" feature, with tags defined for named colours from the xterm 256-color palette, as well as 24-bit colours via CSS-style hex codes.
It's inspired by the examples given in Format Unraveled, a paper by Richard Bonichon & Pierre Weis, which also explains the cleverness behind OCaml's (mostly) type-safe format string system.
Usage
The basic usage looks like:
Spectrum.Printer.printf "@{<green>%s@}\n" "Hello world ๐";;
The pattern is @{<TAG-NAME>CONTENT@}
. So in the example above green
is matching one of the 256 xterm color names. Tag names are case-insensitive.
Tags
We can have arbitrarily nested tags, e.g.:
Spectrum.Printer.printf "@{<green>%s @{<bold>%s@} %s@}\n" "Hello" "world" "I'm here";;
Which should look like: "
Here the tag bold
is used to output one the ANSI style codes. Spectrum defines tags for:
bold
dim
italic
underline
blink
rapid-blink
inverse
hidden
strikethru
As well as the named palette colours you can directly specify an arbitrary colour using short or long CSS-style hex codes:
Spectrum.Printer.printf "@{<#f0c090>%s@}\n" "Hello world ๐";;
Spectrum.Printer.printf "@{<#f00>%s@}\n" "RED ALERT";;
By default we are setting the "foreground" colour, i.e. the text colour. But any colour tag can be prefixed with a foreground fg:
or background bg:
qualifier, e.g.:
Spectrum.Printer.printf "@{<bg:#f00>%s@}\n" "RED ALERT";;

Finally, Spectrum also supports compound tags in colour:style
format, e.g.:
Spectrum.Printer.printf "@{<#f00:bold>%s@}\n" "RED ALERT";;
Interface
As you can see in the examples above, Spectrum.Printer.printf
works just like Format.printf
from the OCaml stdlib.
We also expose equivalents of fprintf
and eprintf
.
Under the hood all of these work via partial application, which is how Spectrum is able to support formats with arbitrary numbers of args.
However this causes a problem when we want an equivalent to sprintf
since that has to return a value.
So far I couldn't think of a clever workaround so Spectrum provides this kludge instead:
let result = ref "" in
Spectrum.Printer.sprintf_into result "@{<green>%s@}\n" "Hello world ๐";
Format.print_string !result;
The sprintf_into
method takes a string ref
as its first arg and will update that with the result value.
Alternatives
AFAICT the main lib for this in the OCaml world at the moment is ANSITerminal
. It supports more than just colour and styles, providing tools for other things you might need in a terminal app like interacting with the cursor. It doesn't use "semantic tags", but provides analogs of the *printf
functions which now take a list of styles as the first arg, with that styling applied to the formatted string as a whole. For named colours it supports only the basic set of eight i.e. those which should be supported by any terminal.
There is also Fmt
. I couldn't work out how to use it from reading the docs alone, at least when I first looked at it. I think it may also integrate with Cmdliner
somehow, which could be handy. It appears to support the eight basic colours and styles and exposes a val styled : style -> 'a t -> 'a t
signature (where 'a t
is "the type for formatters of values of type 'a.
"), which looks similar to ANSITerminal but only applying a single style at a time (?) i.e. no bold+red.
In other languages we have libs like colored (Python) and chalk (JS) ...the latter being one of the most comprehensive I've seen.
TODOs
- more flexible compound tags (fg+bg color, arbitrary order)
- tests and docs for exceptions, tests for all methods
- terminal capabilities detection, as per
chalk
- auto coercion to nearest supported colour, for high res colours on unsupported terminals, as per
chalk
Dev Dependencies (9)
-
odoc
with-doc
-
dune-release
dev
-
opam-publish
dev
-
opam-format
dev
-
ocaml-lsp-server
dev
-
ocp-indent
dev
-
utop
dev
-
junit_alcotest
with-test & >= "2.0" & < "2.1"
-
alcotest
with-test & >= "1.4" & < "1.5"
Used by
None
Conflicts
None