package odoc
Source file raw.ml
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 238(** 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 pageref_star 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 includegraphics pp = create "includegraphics" pp 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 = (* printing description inside a group make them more robust *) 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 | [] -> () (* empty description are not supported *) | _ :: _ -> raw_description all ppf x let url ppf s = create "url" Fmt.string ppf (Escape.text ~code_hyphenation:false s) let footnote 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 (* we add line breaks to never insert tables between delimiters, to avoid rendering: | `A [ | `B ] | `C or field_1: int; { field_2: int; } field_3: int; *) 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 (); (* A blank line before \end{equation*} is a latex error, we trim on the right the user input to avoid any surprise *) 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