printbox

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

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

  • text (assuming monospace font)

  • HTML (using tyxml )

  • LaTeX (not implemented yet)

Documentation

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

Build

Ideally, use opam:

$ opam install printbox

Manually:

$ make install

A few examples

importing the module
# #require "printbox";;

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

Handling unicode

If the text boxes contain unicode (utf8) text, naive size computation for
boxes will not cut it.

The easy way (since 0.3)

The advice below can be replaced by simply using printbox.unicode with:

# #require "printbox.unicode";;
# PrintBox_unicode.setup();;
The manual way

Let's use the libraries uutf and uucp to compute more accurate size hints.

# #require "uutf";;
# #require "uucp";;
let string_len s i len =
  Uutf.String.fold_utf_8 ~pos:i ~len
    (fun n _ c -> match c with
      | `Malformed _ -> 0
      | `Uchar c -> n+ max 0 (Uucp.Break.tty_width_hint c))
    0 s

let () = PrintBox_text.set_string_len string_len

And now:

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

Install
Published
29 Feb 2020
Sources
0.5.tar.gz
md5=2cd18b6198d58c27d1bbec4d18836353
sha512=6aefd975403751288de02104f0bf21d455e37c057fd9237d38af42dd8e01643514acc3a9ab96d42b0c1f5064c3a330833352ac09cd6198275f1c165db2bace7f
Dependencies
mdx
with-test & >= "1.4" & < "1.6"
uucp
with-test
uutf
with-test
ocaml
>= "4.03" & < "5.0"
odoc
with-doc
dune
>= "1.1"
Reverse Dependencies