Skip to content
Parse ANSI color escape sequences to Hiccup syntax
Clojure Shell
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci
bin
dev
src/lambdaisland
test/lambdaisland
.gitignore
CHANGELOG.md
LICENSE.txt
README.md
deps.edn Update dependencies Nov 23, 2018
pom.xml
tests.edn

README.md

lambdaisland/ansi

CircleCI cljdoc badge Clojars Project codecov

Parse ANSI color codes, optionally convert to Hiccup.

Features

  • Clojure and ClojureScript support (cljc)
  • parses color codes and bold, including 8-bit and 24-bit (rgb) colors
  • Fine-grained building blocks, convert to Hiccup or a format of your choice

Usage

Colors in your terminal work by embedding "escape codes" in the text. This library contains utilities for parsing and transforming these escape codes.

Terminals are stateful, you can set a property like foreground, background, or bold, and it will stay that way until it gets changes or unset (reset).

The starting point for dealing with a textual stream is token-stream. It takes a string as input, and returns a sequence of "tokens", either plain text, or a map of properties that are being set at that point in the stream.

(require '[lambdaisland.ansi :as ansi])

(ansi/token-stream "Hello,\033[1;30;45m world!")
;;=> ["Hello," {:bold true, :foreground [:rgb 0 0 0], :background [:rgb 205 0 205]} " world!"]

To convert this to a different format, you need to know which styles apply to which piece of text, this is what the apply-props stateful transducer is for. It keeps track of the "terminal state", and returns pairs of style information and text.

(def text "\033[1m here \033[45m we \033[31m go! \033[0m done.")

(sequence ansi/apply-props (ansi/token-stream text))
;;=>
([{:bold true} " here "]
 [{:bold true
   :background [:rgb 205 0 205]} " we "]
 [{:bold true
   :background [:rgb 205 0 205]
   :foreground [:rgb 205 0 0]} " go! "]
 [{} " done."])

Now you have all the information available to convert this into Hiccup, you can convert a single one of these pairs with chunk->hiccup.

(ansi/chunk->hiccup [{:bold true, :foreground [:rgb 100 200 0]} "so far..."])
;;=> [:span {:style {:color "rgb(100,200,0)", :font-weight "bold"}} "so far..."]

There's a convenience function to do this in one go, text->hiccup

(ansi/text->hiccup text)
;;=>
([:span {:style {:font-weight "bold"}} " here "]
 [:span {:style {:background-color "rgb(205,0,205)", :font-weight "bold"}}
  " we "]
 [:span {:style {:color "rgb(205,0,0)",
                 :background-color "rgb(205,0,205)",
                 :font-weight "bold"}}
  " go! "]
 [:span {} " done."])

If you deal with streaming text, then you need to incrementally apply these state changes, carrying over the old state whenever you receive more input.

In this case the hiccup-xform might be useful.

(require '[clojure.core.async :as async])

(def channel (chan 1 ansi/hiccup-xform))

(async/go-loop [hiccup-el (async/<! channel)]
  (append-to-dom hiccup-el))

(async/go (async/>! channel "\033[1mhello,\033[35mworld!"))
(async/go (async/>! channel " life is \033[0mgreat!"))

License

© Arne Brasseur 2018 Available under the terms of the Mozilla Public License Version 2.0, see LICENSE.txt

You can’t perform that action at this time.