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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
(** Raw latex primitives:
- macro definitions
- env defitions
- text escaping
*)
type pr = Format.formatter -> unit
type 'a with_options = ?options:pr list -> 'a
type ('a, 'b) tr = 'a Fmt.t -> 'b Fmt.t
type 'a t = ('a, 'a) tr
module Escape = struct
let text ~code_hyphenation =
let b = Buffer.create 17 in
fun s ->
for i = 0 to String.length s - 1 do
match s.[i] with
| '{' -> Buffer.add_string b "\\{"
| '}' -> Buffer.add_string b "\\}"
| '\\' -> Buffer.add_string b "\\textbackslash{}"
| '%' -> Buffer.add_string b "\\%"
| '~' -> Buffer.add_string b "\\textasciitilde{}"
| '^' -> Buffer.add_string b "\\textasciicircum{}"
| '_' ->
if code_hyphenation then Buffer.add_string b {|\_\allowbreak{}|}
else Buffer.add_string b {|\_|}
| '.' when code_hyphenation -> Buffer.add_string b {|.\allowbreak{}|}
| ';' when code_hyphenation -> Buffer.add_string b {|;\allowbreak{}|}
| ',' when code_hyphenation -> Buffer.add_string b {|,\allowbreak{}|}
| '&' -> Buffer.add_string b "\\&"
| '#' -> Buffer.add_string b "\\#"
| '$' -> Buffer.add_string b "\\$"
| c -> Buffer.add_char b c
done;
let s = Buffer.contents b in
Buffer.reset b;
s
let pp ~code_hyphenation ppf x =
Format.pp_print_string ppf (text ~code_hyphenation x)
let ref ppf s =
for i = 0 to String.length s - 1 do
match s.[i] with
| '~' -> Fmt.pf ppf "+t+"
| '&' -> Fmt.pf ppf "+a+"
| '^' -> Fmt.pf ppf "+c+"
| '%' -> Fmt.pf ppf "+p+"
| '{' -> Fmt.pf ppf "+ob+"
| '}' -> Fmt.pf ppf "+cb+"
| '+' -> Fmt.pf ppf "+++"
| c -> Fmt.pf ppf "%c" c
done
end
let option ppf pp = Fmt.pf ppf "[%t]" pp
let create name ?(options = []) pp ppf content =
Fmt.pf ppf {|\%s%a{%a}|} name (Fmt.list option) options pp content
let math name ppf = Fmt.pf ppf {|$\%s$|} name
let create2 name ?(options = []) pp_x pp_y ppf x y =
Fmt.pf ppf {|\%s%a{%a}{%a}|} name (Fmt.list option) options pp_x x pp_y y
let bind pp x ppf = pp ppf x
let label ppf = create "label" Escape.ref ppf
let mbegin ?options = create "begin" ?options Fmt.string
let mend = create "end" Fmt.string
let code_fragment pp = create "ocamlcodefragment" pp
let break ppf level =
let pre : _ format6 =
match level with
| Types.Aesthetic -> "%%"
| Line -> {|\\|}
| Separation -> {|\medbreak|}
| _ -> ""
in
let post : _ format6 =
match level with
| Types.Line | Separation | Aesthetic | Simple -> ""
| Paragraph -> "@,"
in
Fmt.pf ppf (pre ^^ "@," ^^ post)
let env name pp ?(with_break = false) ?(opts = []) ?(args = []) ppf content =
mbegin ppf name;
List.iter (Fmt.pf ppf "[%t]") opts;
List.iter (Fmt.pf ppf "{%t}") args;
pp ppf content;
mend ppf name;
break ppf (if with_break then Simple else Aesthetic)
let indent pp ppf x = env "ocamlindent" pp ppf x
let inline_code pp = create "ocamlinlinecode" pp
let verbatim ppf x = env "verbatim" Fmt.string ppf x
let x = create "pageref*" Escape.ref x
let hyperref s = create "hyperref" ~options:[ bind Escape.ref s ]
let ref x = create "ref" Escape.ref x
let emph pp = create "emph" pp
let bold pp = create "bold" pp
let subscript pp = create "textsubscript" pp
let superscript pp = create "textsuperscript" pp
let code_block pp ppf x =
let name = "ocamlcodeblock" in
mbegin ppf name;
Fmt.cut ppf ();
pp ppf x;
Fmt.cut ppf ();
mend ppf name
let section pp = create "section" pp
let subsection pp = create "subsection" pp
let subsubsection pp = create "subsubsection" pp
let paragraph pp = create "paragraph" pp
let enumerate pp ppf x = env "enumerate" pp ppf x
let itemize pp ppf x = env "itemize" pp ppf x
let raw_description pp ppf x = env "description" pp ppf x
let href x pp ppf y =
create2 "href" (Escape.pp ~code_hyphenation:false) pp ppf x y
let item ?options = create "item" ?options
let description pp ppf x =
let group_printer d ppf = Fmt.pf ppf "{%a}" pp d in
let elt ppf (d, elt) = item ~options:[ group_printer d ] pp ppf elt in
let all ppf x =
Fmt.pf ppf
{|\kern-\topsep
\makeatletter\advance\%@topsepadd-\topsep\makeatother%% topsep is hardcoded
|};
Fmt.list ~sep:(fun ppf () -> break ppf Aesthetic) elt ppf x
in
match x with
| [] -> ()
| _ :: _ -> raw_description all ppf x
let url ppf s =
create "url" Fmt.string ppf (Escape.text ~code_hyphenation:false s)
let x = create "footnote" url x
let rightarrow ppf = math "rightarrow" ppf
(** Latex uses forward slash even on Windows. *)
let latex_path ppf path =
let path_s = String.concat "/" (Fpath.segs path) in
Fmt.string ppf path_s
let input ppf x = create "input" latex_path ppf x
let ocamltabular ~column_desc pp ppf x =
env "ocamltabular" ~args:[ column_desc ] pp ppf x
let small_table pp ppf (alignment, tbl) =
let columns = match tbl with [] -> 1 | _ -> List.length (List.hd tbl) in
let row ppf x =
let ampersand ppf () = Fmt.pf ppf "& " in
Fmt.list ~sep:ampersand pp ppf x;
break ppf Line
in
let matrix ppf m = List.iter (row ppf) m in
let column_desc =
let pp_alignment ppf align =
match align with
| Odoc_document.Types.Table.Default -> Fmt.pf ppf "p"
| Left -> Fmt.pf ppf "w{l}"
| Right -> Fmt.pf ppf "w{r}"
| Center -> Fmt.pf ppf "w{c}"
in
let cell ppf align =
Fmt.pf ppf "%a{%.3f\\textwidth}" pp_alignment align
(1.0 /. float_of_int columns)
in
match alignment with
| None ->
let rec repeat n s ppf =
if n = 0 then () else Fmt.pf ppf "%t%t" s (repeat (n - 1) s)
in
repeat columns (fun ppf -> cell ppf Default)
| Some alignment -> fun ppf -> List.iter (cell ppf) alignment
in
let table ppf tbl = ocamltabular ~column_desc matrix ppf tbl in
break ppf Line;
table ppf tbl;
break ppf Line
let ocamltag tag pp ppf x = create2 "ocamltag" Fmt.string pp ppf tag x
let math ppf x = Fmt.pf ppf {|$%s$|} x
let equation ppf x =
let name = "equation*" in
mbegin ppf name;
Fmt.cut ppf ();
let x = Astring.String.drop ~rev:true ~sat:Astring.Char.Ascii.is_white x in
Fmt.string ppf x;
Fmt.cut ppf ();
mend ppf name