package spectrum
Install
Dune Dependency
Authors
Maintainers
Sources
md5=1118193729fbd479e6bdbbc140b654d7
sha512=416dbec0ac5d3609ff50a60115f6ce195574140f5b1a15e08a32fa41bf5bbe618f93db1b77fd4c758a1ee3804ab9e923b174d28c645cccfdc9ed1ff1e579f816
doc/README.html
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 comma-separated format, e.g.:
Spectrum.Printer.printf "@{<bg:#f00,bold,yellow>%s@}\n" "RED ALERT";;
Interface
We provide two modules:
- The default is
Spectrum.Printer
and it will raise an exception if your tags are invalid (i.e. malformed or unrecognised colour name, style name). - Alternatively
Spectrum.Printer.Noexn
will swallow any errors, invalid tags will simply have no effect on the output string.
Both modules expose the same interface:
(** equivalent to [Format.fprintf] *)
val fprintf :
Format.formatter -> ('a, Format.formatter, unit, unit) format4 -> 'a
(** equivalent to [Format.printf] *)
val printf : ('a, Format.formatter, unit, unit) format4 -> 'a
(** equivalent to [Format.eprintf] *)
val eprintf : ('a, Format.formatter, unit, unit) format4 -> 'a
(** equivalent to [Format.sprintf] *)
val sprintf : ('a, Format.formatter, unit, string) format4 -> 'a
As you can see in the examples in the previous section, Spectrum.Printer.printf
works just like Format.printf
from the OCaml stdlib, and fprintf
, eprintf
and sprintf
also work just like their Format
counterparts.
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
. Unfortunately I couldn't work out how to use it from reading the docs, which don't give any examples. 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. (Maybe you can do that by )
In other languages we have libs like colored (Python) and chalk (JS) ...the latter being one of the most comprehensive I've seen.
Update:
I worked out how to use Fmt
, which is like this:
Fmt.set_style_renderer Fmt.stdout Fmt.(`Ansi_tty);;
Fmt.styled Fmt.(`Fg `Red) Fmt.string Fmt.stdout "wtf\n";;
Fmt.styled Fmt.(`Bg `Blue) Fmt.int Fmt.stdout 999;;
TODOs
- tests for all methods (
sprintf
and the lexer are tested currently) - terminal capabilities detection, as per
chalk
- auto coercion to nearest supported colour, for high res colours on unsupported terminals, as per
chalk