Liking cljdoc? Tell your friends :D

clj-string-layout Recipes

This page collects common layouts you can paste into a REPL and adapt.

(require '[clj-string-layout.core :refer [layout layout-seq layout-str]]
         '[clj-string-layout.escape :as escape]
         '[clj-string-layout.layout :as layouts]
         '[clj-string-layout.predicates :as pred])

Simple Columns

Use [L], [C], and [R] for left, center, and right alignment.

(layout [["name" "qty" "price"]
         ["apple" "12" "$1.50"]
         ["pear" "4" "$2.00"]]
        {:layout {:cols ["[L]  [R]  [R]"]}})
;; => ["name   qty  price"
;;     "apple   12  $1.50"
;;     "pear     4  $2.00"]

Width-Filled Rows

Use f where extra width should be distributed.

(layout [["left" "right"]]
        {:width 24
         :fill-char \.
         :layout {:cols ["[L]f[R]"]}})
;; => ["left...............right"]

Multiple fill markers split the extra width.

(layout [["x" "y"]]
        {:width 9
         :fill-char \-
         :layout {:cols ["f[L]f[R]f"]}})
;; => ["--x--y---"]

Variable Column Counts

Repeat groups let one layout handle any number of columns.

(layout [["a" "b" "c"]
         ["10" "200" "3"]]
        {:layout {:cols ["|{ [R] |}" :repeat-for [pred/all-cols?]]}})
;; => ["|  a |   b | c |"
;;     "| 10 | 200 | 3 |"]

Use multiple repeat groups when the first, interior, and last columns need different delimiters.

(layout [["a" "b" "c"]]
        {:layout {:cols ["{[L]}{, [L]}"
                         :repeat-for [pred/first-col? pred/not-first-col?]]}})
;; => ["a, b, c"]

Box-Drawing Tables

Use a built-in preset when you want a complete table shape.

(layout [["name" "qty"]
         ["apple" "12"]]
        layouts/layout-ascii-box-left)
;; => ["┌───────┬─────┐"
;;     "│ name  │ qty │"
;;     "├───────┼─────┤"
;;     "│ apple │ 12  │"
;;     "└───────┴─────┘"]

The *-fill-* presets consume :width.

(layout [["name" "qty"]
         ["apple" "12"]]
        (assoc layouts/layout-ascii-box-fill-center :width 30))

Markdown Tables

Markdown cells are emitted verbatim, so escape data first when values may contain |, backslashes, or line breaks.

(layout (escape/map-cells escape/markdown-cell
                          [["name" "notes"]
                           ["apple" "red|green"]])
        layouts/layout-markdown-left)
;; => ["| name  | notes      |"
;;     "|:----- |:---------- |"
;;     "| apple | red\\|green |"]

HTML Tables

HTML cells are emitted verbatim by design. Escape user-provided values first.

(layout (escape/map-cells escape/html
                          [["<Alice>" "tea & cake"]])
        layouts/layout-html-table)
;; => ["<table>"
;;     "  <tr><td>&lt;Alice&gt;</td><td>tea &amp; cake</td></tr>"
;;     "</table>"]

Wide Glyphs

By default, widths use count. Pass :display-width when terminal display width differs from string length.

(defn demo-width [s]
  (reduce + (map #(if (= \界 %) 2 1) s)))

(layout [["界" "x"]
         ["ab" "yy"]]
        {:display-width demo-width
         :layout {:cols ["[R] [L]"]}})
;; => ["界 x "
;;     "ab yy"]

Raw Output For Styling

Use :raw? true to get row pieces before they are joined. This is useful when a later step needs to color cells or otherwise decorate pieces.

(layout [["a" "b"]]
        {:raw? true
         :layout {:cols ["| [L] | [R] |"]}})
;; => [["| " "a" " | " "b" " |"]]

Large Data Sets

Exact automatic widths require a full scan of the data. If your data set is large and the schema widths are known, use layout-seq with :col-widths so output can be consumed row-by-row. If you need escaping for a large lazy input, use escape/map-cell-seq rather than escape/map-cells.

(def huge-rows
  (map (fn [n] [(str "item-" n) (str n)])
       (range)))

(take 3
      (layout-seq huge-rows
                  {:col-widths [12 8]
                   :layout {:cols ["[L] [R]"]}}))
;; => ("item-0              0"
;;     "item-1              1"
;;     "item-2              2")

When row layouts are present, pass :row-count for finite data so predicates can identify the last virtual row without counting the input.

(layout-seq (map vector ["a" "bb"])
            {:col-widths [3]
             :row-count 2
             :layout {:cols ["[L]"]
                      :rows [["[-]" :apply-for layouts/all-rows?]]}})
;; => ("---" "a  " "---" "bb " "---")

Custom Split Characters

String input is split by :row-split-char and :word-split-char.

(layout "name|qty;apple|12"
        {:word-split-char \|
         :row-split-char \;
         :layout {:cols ["[L] [R]"]}})
;; => ["name  qty"
;;     "apple  12"]

Diagnostics

Use diagnostics while writing custom layout strings.

(clj-string-layout.core/parse-layout "[L]f[R]")

(clj-string-layout.core/explain-layout "[x]")
;; => {:valid? false, :message "...", :data {:type :layout-parse-error, ...}}

Single String Output

Use layout-str when the consumer wants one newline-delimited string.

(layout-str [["a" "b"]
             ["aa" "bb"]]
            {:layout {:cols ["[L] [R]"]}})
;; => "a   b\naa bb"

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close