Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
doc.ml1 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 239 240 241 242open Stdlib_extra type inline = Doc_types.inline = | Text of string | Code of string type block = Doc_types.block = | Paragraph of inline list | Pre of string list type doc = block list (* Parse and print ATD's own "text" format *) module Text = struct let parse loc s = try (Doc_lexer.parse_string s : block list) with e -> failwith (Printf.sprintf "%s:\nInvalid format for doc.text %S:\n%s" (Ast.string_of_loc loc) s (Printexc.to_string e)) (* Escape as little as we can get away with depending on the context: - always: \ -> \\ - normal text mode: {{ -> \{\{ - code: }} -> \}\} - pre: }}} -> \}\}\} *) let escape_text_re = Re.Pcre.regexp {|\{\{\|\\|} let escape_code_re = Re.Pcre.regexp {|\}\}|\\|} let escape_pre_re = Re.Pcre.regexp {|\}\}\}|\\|} let escape_text s = Re.Pcre.substitute ~rex:escape_text_re ~subst:(function | "{{" -> {|\{\{|} | {|\|} -> {|\\|} | s -> s (* bug *) ) s let escape_code s = Re.Pcre.substitute ~rex:escape_code_re ~subst:(function | "}}" -> {|\}\}|} | {|\|} -> {|\\|} | s -> s (* bug *) ) s let escape_pre_line s = Re.Pcre.substitute ~rex:escape_pre_re ~subst:(function | "}}}" -> {|\}\}\}|} | {|\|} -> {|\\|} | s -> s (* bug *) ) s let escape_pre lines = lines |> List.map escape_pre_line |> String.concat "\n" let compact_whitespace = let rex = Re.Pcre.regexp "(?: \t\r\n)+" in fun s -> Re.Pcre.substitute ~rex ~subst:(fun _ -> " ") s (* - remove leading and trailing whitespace - turn inner whitespace sequences into a single space *) let normalize_inline s = s |> String.trim |> compact_whitespace let concat_nonempty sep xs = xs |> List.filter ((<>) "") |> String.concat sep let print_inline (x : Doc_types.inline) = match x with | Text s -> s |> normalize_inline |> escape_text | Code s -> match s |> normalize_inline |> escape_code with | "" -> "" | s -> let first_space = if s.[0] = '{' then " " else "" in let last_space = if s.[String.length s - 1] = '}' then " " else "" in sprintf "{{%s%s%s}}" first_space s last_space let print_block (x : Doc_types.block) = match x with | Paragraph xs -> xs |> List.map print_inline |> concat_nonempty " " | Pre lines -> let content = escape_pre lines in match content with | "" -> "" | s -> let first_newline = if s.[0] <> '\n' then "\n" else "" in let last_newline = if s.[String.length s - 1] <> '\n' then "\n" else "" in sprintf "{{{%s%s%s}}}" first_newline s last_newline let print_blocks blocks = blocks |> List.map print_block |> String.concat "\n\n" end let parse_text = Text.parse let print_text = Text.print_blocks (* This must hold all the valid annotations of the form '<doc ...>'. *) let annot_schema : Annot.schema = [ { section = "doc"; fields = [ Module_head, "text"; Type_def, "text"; Variant, "text"; Field, "text"; (* Tolerate but deprecate? Type_expr, "text"; *) ] } ] let get_doc loc an : doc option = Annot.get_opt_field ~parse:(fun s -> Some (parse_text loc s)) ~sections:["doc"] ~field:"text" an (* Conversion to HTML *) let html_escape buf s = String.iter ( function '<' -> Buffer.add_string buf "<" | '>' -> Buffer.add_string buf ">" | '&' -> Buffer.add_string buf "&" | '"' -> Buffer.add_string buf """ | c -> Buffer.add_char buf c ) s let print_inline buf = function | Text s -> html_escape buf s | Code s -> bprintf buf "<code>%a</code>" html_escape s let html_of_doc blocks = let buf = Buffer.create 300 in bprintf buf "\n<div class=\"atd-doc\">\n"; List.iter (function | Paragraph l -> Buffer.add_string buf "<p>\n"; List.iter (print_inline buf) l; Buffer.add_string buf "\n</p>\n" | Pre lines -> Buffer.add_string buf "<pre>\n"; List.iter (fun line -> html_escape buf line; Buffer.add_char buf '\n' ) lines; Buffer.add_string buf "</pre>\n" ) blocks; bprintf buf "\n</div>\n"; Buffer.contents buf let split_on_blank = let rex = Re.Pcre.regexp {|[ \t\r\n]+|} in fun str -> Re.Pcre.split ~rex str |> (* make sure to ignore leading and trailing whitespace *) List.filter (function "" -> false | _ -> true) let concatenate_into_lines ~max_length (words : string list) : string list = let max_length = max 0 max_length in let buf = Buffer.create max_length in let finish_line () = let line = Buffer.contents buf in Buffer.clear buf; line in let rec make_lines orig_words = match orig_words with | [] -> [finish_line ()] | word :: words -> let word_len = String.length word in let len = Buffer.length buf in if len = 0 then ( (* The word may be longer than 'max_length'. Putting it on its own line is the best we can do without hyphenating it. *) Buffer.add_string buf word; make_lines words ) else (* Add the word to the current line only if it fits. *) let new_len = len + 1 + word_len in if new_len <= max_length then ( bprintf buf " %s" word; make_lines words ) else (* The new word doesn't fit on the current line. Start a new one. *) let line = finish_line () in line :: make_lines orig_words in make_lines words let rewrap_paragraph ~max_length str = str |> split_on_blank |> concatenate_into_lines ~max_length