1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
type t =
| Empty
| Words of string
| Atom of
Colors.style * bool * string
| Raw of (Styled_printer.t -> unit)
| Seq of t * t
| Sep of t * t
| Group of t
let text s = Words s
let ident s = Atom (Colors.Identifier, true, s)
let code s = Atom (Colors.Keyword, true, s)
let type_ s = Atom (Colors.Type, true, s)
let styled style s = Atom (style, false, s)
let int n = Atom (Colors.Constant, false, string_of_int n)
let int64 n = Atom (Colors.Constant, false, Int64.to_string n)
let bool b = Atom (Colors.Constant, false, string_of_bool b)
let raw f = Raw f
let empty = Empty
let ( ^^ ) a b = Seq (a, b)
let ( ++ ) a b = Sep (a, b)
let concat l = List.fold_left ( ^^ ) empty l
let group t = Group t
let enumerate ?(conj = "or") items =
match List.rev items with
| [] -> empty
| [ x ] -> x
| last :: rev_init ->
let init = List.rev rev_init in
let commad =
List.fold_left
(fun acc x ->
match acc with
| None -> Some x
| Some a -> Some (a ^^ (text "," ++ x)))
None init
|> Option.get
in
commad ++ text conj ++ last
let render_words sp s =
let p = sp.Styled_printer.printer in
let lines = String.split_on_char '\n' s in
let lines =
match List.rev lines with "" :: rest -> List.rev rest | _ -> lines
in
List.iteri
(fun li line ->
if li > 0 then Printer.newline p ();
List.iteri
(fun i tok ->
if i > 0 then Printer.space p ();
if tok <> "" then Printer.string p tok)
(String.split_on_char ' ' line))
lines
let rec render_doc sp d =
let p = sp.Styled_printer.printer in
match d with
| Empty -> ()
| Words s -> render_words sp s
| Atom (style, quote, s) ->
if quote && Colors.escape_sequence sp.theme style = "" then
Printer.string p ("'" ^ s ^ "'")
else
Styled_printer.print_styled sp style
~len:(Some (Unicode.terminal_width s))
s
| Raw f -> f sp
| Seq (a, b) ->
render_doc sp a;
render_doc sp b
| Sep (a, b) ->
render_doc sp a;
Printer.space p ();
render_doc sp b
| Group d -> Printer.box p ~indent:2 (fun () -> render_doc sp d)
let render_top sp d =
Printer.hovbox sp.Styled_printer.printer (fun () -> render_doc sp d)
let styled_printer ~theme printer =
Styled_printer.create ~printer ~theme ~trivia:(Hashtbl.create 0) ()
let render_into ~theme ~width fmt t =
Printer.run ~width fmt (fun p -> render_top (styled_printer ~theme p) t)
let flat_margin = 1_000_000
let to_plain_string t =
let b = Buffer.create 128 in
let fmt = Format.formatter_of_buffer b in
Printer.run ~width:flat_margin fmt (fun p ->
render_top (styled_printer ~theme:Colors.no_color p) t);
Format.pp_print_flush fmt ();
Buffer.contents b