PrintBox build

Allows to print nested boxes, lists, arrays, tables in several formats, including:

Documentation

See https://c-cube.github.io/printbox/

Build

Ideally, use opam:

$ opam install printbox printbox-text

Manually:

$ make install

A few examples

importing the module
# #require "printbox";;
# #require "printbox-text";;

# module B = PrintBox;;
module B = PrintBox
simple box
# let box = B.(hlist [ text "hello"; text "world"; ]);;
val box : B.t = <abstr>

# PrintBox_text.output stdout box;;
helloβ”‚world
- : unit = ()
less simple boxes
# let box =
  B.(hlist
  [ text "I love\nto\npress\nenter";
    grid_text [| [|"a"; "bbb"|];
    [|"c"; "hello world"|] |]
  ])
  |> B.frame;;
val box : B.t = <abstr>

# PrintBox_text.output stdout box;;
β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚I loveβ”‚aβ”‚bbb        β”‚
β”‚to    β”œβ”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚press β”‚cβ”‚hello worldβ”‚
β”‚enter β”‚ β”‚           β”‚
β””β”€β”€β”€β”€β”€β”€β”΄β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
- : unit = ()
printing a table
# let square n =
  (* function to make a square *)
  Array.init n
    (fun i -> Array.init n (fun j -> B.sprintf "(%d,%d)" i j))
  |> B.grid ;;
val square : int -> B.t = <fun>

# let sq = square 5;;
val sq : B.t = <abstr>
# PrintBox_text.output stdout sq;;
(0,0)β”‚(0,1)β”‚(0,2)β”‚(0,3)β”‚(0,4)
─────┼─────┼─────┼─────┼─────
(1,0)β”‚(1,1)β”‚(1,2)β”‚(1,3)β”‚(1,4)
─────┼─────┼─────┼─────┼─────
(2,0)β”‚(2,1)β”‚(2,2)β”‚(2,3)β”‚(2,4)
─────┼─────┼─────┼─────┼─────
(3,0)β”‚(3,1)β”‚(3,2)β”‚(3,3)β”‚(3,4)
─────┼─────┼─────┼─────┼─────
(4,0)β”‚(4,1)β”‚(4,2)β”‚(4,3)β”‚(4,4)
- : unit = ()
frame

Why not put a frame around this? That's easy.

# let sq2 = square 3 |> B.frame ;;
val sq2 : B.t = <abstr>

# PrintBox_text.output stdout sq2;;
β”Œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”
β”‚(0,0)β”‚(0,1)β”‚(0,2)β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€
β”‚(1,0)β”‚(1,1)β”‚(1,2)β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€
β”‚(2,0)β”‚(2,1)β”‚(2,2)β”‚
β””β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”˜
- : unit = ()
tree

We can also create trees and display them using indentation:

# let tree =
  B.tree (B.text "root")
    [ B.tree (B.text "a") [B.text "a1\na1"; B.text "a2\na2\na2"];
      B.tree (B.text "b") [B.text "b1\nb1"; B.text "b2"; B.text "b3"];
    ];;
val tree : B.t = <abstr>

# PrintBox_text.output stdout tree;;
root
β”œβ”€a
β”‚ β”œβ”€a1
β”‚ β”‚ a1
β”‚ └─a2
β”‚   a2
β”‚   a2
└─b
  β”œβ”€b1
  β”‚ b1
  β”œβ”€b2
  └─b3
- : unit = ()
Installing the pretty-printer in the toplevel

PrintBox_text contains a Format-compatible pretty-printer that can be used as a default printer for boxes.

# #install_printer PrintBox_text.pp;;
# PrintBox.(frame @@ frame @@ init_grid ~line:3 ~col:2 (fun ~line:i ~col:j -> sprintf "%d.%d" i j));;
- : B.t =
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚β”Œβ”€β”€β”€β”¬β”€β”€β”€β”β”‚
β”‚β”‚0.0β”‚0.1β”‚β”‚
β”‚β”œβ”€β”€β”€β”Όβ”€β”€β”€β”€β”‚
β”‚β”‚1.0β”‚1.1β”‚β”‚
β”‚β”œβ”€β”€β”€β”Όβ”€β”€β”€β”€β”‚
β”‚β”‚2.0β”‚2.1β”‚β”‚
β”‚β””β”€β”€β”€β”΄β”€β”€β”€β”˜β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
# #remove_printer PrintBox_text.pp;;

Note that this pretty-printer plays nicely with Format boxes:

# let b = PrintBox.(frame @@ hlist [text "a\nb"; text "c"]);;
val b : B.t = <abstr>
# Format.printf "some text %a around@." PrintBox_text.pp b;;
some text β”Œβ”€β”¬β”€β”
          β”‚aβ”‚cβ”‚
          β”‚bβ”‚ β”‚
          β””β”€β”΄β”€β”˜ around
- : unit = ()

Also works with basic styling on text now:

# let b2 = PrintBox.(
    let style = Style.(fg_color Red) in
  frame @@ hlist [text_with_style style "a\nb"; text "c"]);;
val b2 : B.t = <abstr>
# Format.printf "some text %a around@." (PrintBox_text.pp_with ~style:true) b2;;
some text β”Œβ”€β”¬β”€β”
          β”‚aβ”‚cβ”‚
          β”‚bβ”‚ β”‚
          β””β”€β”΄β”€β”˜ around
- : unit = ()
# let b3 = PrintBox.(
    let style = Style.(fg_color Red) in
    frame @@ grid_l [
      [text_with_style style "a\nb";
       line_with_style Style.(set_bold true @@ bg_color Green) "OH!"];
      [text "c"; text "ballot"];
    ])
val b3 : PrintBox.t = <abstr>
utop [1]: print_endline @@ PrintBox_text.to_string b3;;

gives the following image.

Handling unicode

Unicode (utf8) text is handled.

# let b =
  PrintBox.(frame @@
    hlist [
      vlist[text "oΓ― ωΡird nums:\nΟ€/2\nΟ„/4";
        tree (text "0")[text "1"; tree (text "Ο‰") [text "ω²"]]];
      frame @@ vlist [text "sum=Ξ£_i aΒ·xα΅’Β²\nβ€”β€”β€”β€”β€”\n1+1"; text "ΕŒβ‚’\nΓ€"]]);;
val b : B.t = <abstr>

# print_endline @@ PrintBox_text.to_string b;;
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚oΓ― ωΡird nums:β”‚β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚Ο€/2           β”‚β”‚sum=Ξ£_i aΒ·xα΅’Β²β”‚β”‚
β”‚Ο„/4           β”‚β”‚β€”β€”β€”β€”β€”        β”‚β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚1+1          β”‚β”‚
β”‚0             β”‚β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚
β”‚β”œβ”€1           β”‚β”‚ΕŒβ‚’           β”‚β”‚
│└─ω           β”‚β”‚Γ€            β”‚β”‚
β”‚  └─ω²        β”‚β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
- : unit = ()
HTML output (with tyxml)

Assuming you have loaded printbox-html somehow:

let out = open_out "/tmp/foo.html";;
output_string out (PrintBox_html.to_string_doc (square 5));;

which prints some HTML in the file foo.html. Note that trees are printed in HTML using nested lists, and that PrintBox_html.to_string_doc will insert some javascript to make sub-lists fold/unfold on click (this is useful to display very large trees compactly and exploring them incrementally).