spectrum

Library for colour and formatting in the terminal
README

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

Install
Published
18 Oct 2021
Authors
Sources
0.1.0.tar.gz
md5=e6c2a404dd2b34e69325b9ed4eb4db82
sha512=9d74bd83c9393f1ac6e5700d98f678d329584c4da3f7907121e7172ff17bc4874cf3a5c125f7e3171d55f8972640e954519424cdc34557317bd14ae58a7bc2d9
Dependencies
odoc
with-doc
utop
dev
junit_alcotest
with-test & >= "2.0" & < "2.1"
alcotest
with-test & >= "1.4" & < "1.5"
color
>= "0.2" & < "0.3"
ocaml
>= "4.08"
dune
>= "2.8"
Reverse Dependencies