package spectrum

  1. Overview
  2. Docs
Library for colour and formatting in the terminal

Install

Dune Dependency

Authors

Maintainers

Sources

0.3.0.tar.gz
md5=1118193729fbd479e6bdbbc140b654d7
sha512=416dbec0ac5d3609ff50a60115f6ce195574140f5b1a15e08a32fa41bf5bbe618f93db1b77fd4c758a1ee3804ab9e923b174d28c645cccfdc9ed1ff1e579f816

Description

Using OCaml Format module's 'semantic tags' with named colours and CSS-style hex colours.

Published: 02 Jan 2022

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 comma-separated format, e.g.:

Spectrum.Printer.printf "@{<bg:#f00,bold,yellow>%s@}\n" "RED ALERT";;

Interface

We provide two modules:

  1. 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).

  2. 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

Dependencies (3)

  1. color >= "0.2"
  2. ocaml >= "4.10"
  3. dune >= "2.8"

Dev Dependencies (3)

  1. odoc with-doc
  2. junit_alcotest with-test & >= "2.0"
  3. alcotest with-test & >= "1.4"

Used by

None

Conflicts

None

OCaml

Innovation. Community. Security.