this repo has no description
at main 632 lines 24 kB view raw
1let strf = Printf.sprintf 2 3(* ocamlmark parsing *) 4 5open Odoc_parser 6open Cmarkit 7 8(* Text location and comment massaging. 9 10 One slight annoyance is that CommonMark is sensitive to leading 11 blanks on lines and ocamldoc comments are usually indented by [n] 12 spaces up the … of (***). So we can't just feed it the comment 13 text: we would mostly get CommonMark indented code blocks. 14 15 So we massage the comment to trim up to [n] initial spaces after 16 newlines. [n] being the number of columns until … in (***). We 17 need to remember how much we trimmed on each line in order to patch 18 the locations reported by cmarkit. Below we keep pass that info 19 around using the [~locator] argument. 20 21 This is not needed in [md] files, but the code is kept in case we 22 add support for markdown in docstrings. *) 23 24let comment_col ~location = location.Lexing.pos_cnum - location.Lexing.pos_bol 25 26let massage_comment ~location b s = 27 let rec next_non_space s ~max i = 28 if i > max || not (s.[i] = ' ') then i else next_non_space s ~max (i + 1) 29 in 30 let rec find_after_trim ~max_trim s max ~start i = 31 if i - start + 1 > max_trim || i > max || s.[i] <> ' ' then i 32 else find_after_trim ~max_trim s max ~start (i + 1) 33 in 34 let flush b s start last = 35 Buffer.add_substring b s start (last - start + 1) 36 in 37 let rec loop b s acc ~max_trim max start k = 38 if k > max then ( 39 flush b s start max; 40 ((location, Array.of_list (List.rev acc)), Buffer.contents b)) 41 else if not (s.[k] = '\n' || s.[k] = '\r') then 42 loop b s acc ~max_trim max start (k + 1) 43 else 44 let next = k + 1 in 45 let next = 46 if s.[k] = '\r' && next <= max && s.[next] = '\n' then next + 1 47 else next 48 in 49 let after_trim = find_after_trim ~max_trim s max ~start:next next in 50 let trim = after_trim - next in 51 flush b s start (next - 1); 52 loop b s (trim :: acc) ~max_trim max after_trim after_trim 53 in 54 if s = "" then ((location, [| 0 |]), s) 55 else 56 let max = String.length s - 1 in 57 let nsp = next_non_space s ~max 0 in 58 let max_trim = comment_col ~location + nsp in 59 loop b s [ nsp (* trim *) ] ~max_trim max nsp nsp 60 61let textloc_to_loc ~locator textloc = 62 (* Note: if you get an [Invalid_argument] from this function suspect a bug 63 in cmarkit's location computation. *) 64 let point_of_line_and_byte_pos ~locator:(location, line_trim_counts) l pos = 65 let line_num, line_pos = l in 66 let line = location.Lexing.pos_lnum + line_num - 1 in 67 try 68 let column = line_trim_counts.(line_num - 1) + (pos - line_pos) in 69 let column = 70 match line_num with 1 -> comment_col ~location + column | _ -> column 71 in 72 { Loc.line; column } 73 with _ -> 74 (* Presumably this is the above-mentioned bug that's being hit. *) 75 { Loc.line = -1; column = -1 } 76 in 77 let file = Textloc.file textloc in 78 let first_line = Textloc.first_line textloc in 79 let first_byte = Textloc.first_byte textloc in 80 let last_line = Textloc.last_line textloc in 81 let last_byte = Textloc.last_byte textloc + 1 in 82 let start = point_of_line_and_byte_pos ~locator first_line first_byte in 83 let end_ = point_of_line_and_byte_pos ~locator last_line last_byte in 84 { Loc.file; start; end_ } 85 86let meta_to_loc ~locator meta = textloc_to_loc ~locator (Meta.textloc meta) 87 88(* Sometimes we need to munge a bit the cmarkit metas and textlocs. 89 These function do that. They are not general and make assumptions 90 about the nature of data they apply to. E.g. most assume the 91 textloc is on the same line. *) 92 93let chop_end_of_meta_textloc ~count meta = 94 let textloc = Meta.textloc meta in 95 let last_line = Textloc.last_line textloc in 96 let last_byte = Textloc.last_byte textloc - count in 97 let textloc = Textloc.set_last textloc ~last_byte ~last_line in 98 Meta.with_textloc ~keep_id:true meta textloc 99 100let split_info_string_locs ~left_count ~right_count m = 101 if right_count = 0 then (Meta.textloc m, Textloc.none) 102 else 103 let textloc = Meta.textloc m in 104 let line = Textloc.first_line textloc in 105 let last_byte = Textloc.first_byte textloc + left_count - 1 in 106 let first_byte = Textloc.last_byte textloc - right_count + 1 in 107 ( Textloc.set_last textloc ~last_byte ~last_line:line, 108 Textloc.set_first textloc ~first_byte ~first_line:line ) 109 110let textloc_of_sub textloc ~first ~last (* in textloc relative space *) = 111 let file = Textloc.file textloc in 112 let line = Textloc.first_line textloc in 113 let first_byte = Textloc.first_byte textloc + first in 114 let last_byte = Textloc.first_byte textloc + last in 115 Textloc.v ~file ~first_byte ~last_byte ~first_line:line ~last_line:line 116 117(* Warnings *) 118 119let warn_unsupported_hard_break = 120 "Hard breaks are unsupported in ocamlmark, using a soft break." 121 122let warn_unsupported_header_nesting = 123 "Headers in list items are unsupported in ocamlmark, dropped." 124 125let warn_heading_level_6 = 126 "Heading level 6 is unsupported in ocamlmark, using 5." 127 128let warn_unsupported_list_start_number start = 129 strf "List start numbers are unsupported in ocamlmark, replacing %d with 1." 130 start 131 132let warn_unsupported_cmark kind = 133 strf "%s are unsupported in ocamlmark, dropped." kind 134 135let warn_unsupported_link_title = 136 "Link titles are unsupported in ocamlmark, dropped." 137 138let warn ~loc:location message warns = { Warning.location; message } :: warns 139 140let warn_unsupported_cmark ~locator kind meta (acc, warns) = 141 let msg = warn_unsupported_cmark kind in 142 (acc, warn ~loc:(meta_to_loc ~locator meta) msg warns) 143 144let warn_unsupported_header_nesting ~locator meta (acc, warns) = 145 let msg = warn_unsupported_header_nesting in 146 (acc, warn ~loc:(meta_to_loc ~locator meta) msg warns) 147 148let is_blank = function ' ' | '\t' -> true | _ -> false 149let rec next_blank s ~max i = 150 if i > max || is_blank s.[i] then i else next_blank s ~max (i + 1) 151 152let rec next_nonblank s ~max i = 153 if i > max || not (is_blank s.[i]) then i else next_nonblank s ~max (i + 1) 154 155(* Translating blocks and inlines. *) 156 157(* A few type definitions for better variant typing. *) 158 159type inlines_acc = Ast.inline_element Ast.with_location list * Warning.t list 160type ast_acc = Ast.t * Warning.t list 161type nestable_ast_acc = 162 Ast.nestable_block_element Ast.with_location list * Warning.t list 163 164(* Inline translations *) 165 166let link_definition defs l = 167 match Inline.Link.reference_definition defs l with 168 | Some (Link_definition.Def (ld, _)) -> Some ld 169 | Some (Block.Footnote.Def (_, _)) -> None 170 | Some _ -> assert false 171 | None -> assert false (* assert [l]'s referenced label is not synthetic *) 172 173let autolink_to_inline_element ~locator a m (is, warns) = 174 let loc = meta_to_loc ~locator m in 175 let link, link_loc = Inline.Autolink.link a in 176 let link_loc = meta_to_loc ~locator link_loc in 177 let text = [ Loc.at link_loc (`Word link) ] in 178 (Loc.at loc (`Link (link, text)) :: is, warns) 179 180let break_to_inline_element ~locator br m (is, warns) = 181 let loc = meta_to_loc ~locator m in 182 let warns = 183 match Inline.Break.type' br with 184 | `Soft -> warns 185 | `Hard -> warn ~loc warn_unsupported_hard_break warns 186 in 187 (Loc.at loc (`Space "\n") :: is, warns) 188 189let code_span_to_inline_element ~locator cs m (is, warns) = 190 let loc = meta_to_loc ~locator m in 191 let code = Inline.Code_span.code cs in 192 (Loc.at loc (`Code_span code) :: is, warns) 193 194let math_span_to_inline_element ~locator ms m (is, warns) = 195 let loc = meta_to_loc ~locator m in 196 let tex = Inline.Math_span.tex ms in 197 (Loc.at loc (`Math_span tex) :: is, warns) 198 199let raw_html_to_inline_element ~locator html m (is, warns) = 200 let loc = meta_to_loc ~locator m in 201 let html = String.concat "\n" (List.map Block_line.tight_to_string html) in 202 (Loc.at loc (`Raw_markup (Some "html", html)) :: is, warns) 203 204let image_to_inline_element ~locator defs i m (is, warns) = 205 (* We map to raw html, ocamldoc's ast should have a case for that. *) 206 let escape esc b s = 207 Buffer.clear b; 208 esc b s; 209 Buffer.contents b 210 in 211 let pct_esc = escape Cmarkit_html.buffer_add_pct_encoded_string in 212 let html_esc = escape Cmarkit_html.buffer_add_html_escaped_string in 213 let loc = meta_to_loc ~locator m in 214 let b = Buffer.create 255 in 215 let ld = link_definition defs i in 216 match ld with 217 | None -> (is, warns) 218 | Some ld -> 219 let link = 220 match Link_definition.dest ld with 221 | None -> "" 222 | Some (link, _) -> pct_esc b link 223 in 224 let title = 225 match Link_definition.title ld with 226 | None -> "" 227 | Some title -> 228 let title = List.map Block_line.tight_to_string title in 229 html_esc b (String.concat "\n" title) 230 in 231 let alt = 232 let ls = 233 Inline.to_plain_text ~break_on_soft:false (Inline.Link.text i) 234 in 235 html_esc b (String.concat "\n" (List.map (String.concat "") ls)) 236 in 237 let img = 238 String.concat "" 239 [ 240 {|<img src="|}; 241 link; 242 {|" alt="|}; 243 alt; 244 {|" title="|}; 245 title; 246 {|" >|}; 247 ] 248 in 249 (Loc.at loc (`Raw_markup (Some "html", img)) :: is, warns) 250 251let text_to_inline_elements ~locator s meta ((is, warns) as acc) = 252 (* [s] is on a single source line (but may have newlines because of 253 character references) we need to tokenize it for ocamldoc's ast. *) 254 let flush_tok s meta acc is_space first last = 255 let textloc = textloc_of_sub (Meta.textloc meta) ~first ~last in 256 let loc = textloc_to_loc ~locator textloc in 257 let s = String.sub s first (last - first + 1) in 258 Loc.at loc (if is_space then `Space s else `Word s) :: acc 259 in 260 let rec tokenize s meta acc max start is_space = 261 if start > max then (List.rev_append acc is, warns) 262 else 263 let next_start = 264 if is_space then next_nonblank s ~max start else next_blank s ~max start 265 in 266 let acc = flush_tok s meta acc is_space start (next_start - 1) in 267 tokenize s meta acc max next_start (not is_space) 268 in 269 let max = String.length s - 1 in 270 if max < 0 then acc else tokenize s meta [] max 0 (is_blank s.[0]) 271 272let rec link_reference_to_inline_element ~locator defs l m (is, warns) = 273 let loc = meta_to_loc ~locator m in 274 let ld = link_definition defs l in 275 match ld with 276 | None -> 277 let text, warns = 278 inline_to_inline_elements ~locator defs ([], warns) (Inline.Link.text l) 279 in 280 (text @ is, warns) 281 | Some ld -> 282 let replace_md_mdx s = 283 let add_html x = x ^ ".html" in 284 if String.ends_with ~suffix:".md" s then 285 String.sub s 0 (String.length s - 3) |> add_html 286 else if String.ends_with ~suffix:".mdx" s then 287 String.sub s 0 (String.length s - 4) |> add_html 288 else s 289 in 290 let link = 291 match Link_definition.dest ld with 292 | None -> "" 293 | Some (l, _) -> 294 if String.contains l ':' then (* Assume it's a URL *) l 295 else 296 (* If it ends with `.md` or `.mdx`, drop the extension and add `.html` *) 297 replace_md_mdx l 298 in 299 let warns = 300 match Link_definition.title ld with 301 | None -> warns 302 | Some title -> 303 let textloc = Block_line.tight_list_textloc title in 304 let loc = textloc_to_loc ~locator textloc in 305 warn ~loc warn_unsupported_link_title warns 306 in 307 let text, warns = 308 inline_to_inline_elements ~locator defs ([], warns) (Inline.Link.text l) 309 in 310 (Loc.at loc (`Link (link, text)) :: is, warns) 311 312and link_to_inline_element ~locator defs l m acc = 313 link_reference_to_inline_element ~locator defs l m acc 314 315and emphasis_to_inline_element ~locator defs style e m (is, warns) = 316 let loc = meta_to_loc ~locator m in 317 let i = Inline.Emphasis.inline e in 318 let inlines, warns = inline_to_inline_elements ~locator defs ([], warns) i in 319 (Loc.at loc (`Styled (style, inlines)) :: is, warns) 320 321and inline_to_inline_elements ~locator defs acc i : inlines_acc = 322 match i with 323 | Inline.Autolink (a, m) -> autolink_to_inline_element ~locator a m acc 324 | Inline.Break (b, m) -> break_to_inline_element ~locator b m acc 325 | Inline.Code_span (cs, m) -> code_span_to_inline_element ~locator cs m acc 326 | Inline.Emphasis (e, m) -> 327 emphasis_to_inline_element ~locator defs `Emphasis e m acc 328 | Inline.Image (i, m) -> image_to_inline_element ~locator defs i m acc 329 | Inline.Inlines (is, _m) -> 330 let inline = inline_to_inline_elements ~locator defs in 331 List.fold_left inline acc (List.rev is) 332 | Inline.Link (l, m) -> link_to_inline_element ~locator defs l m acc 333 | Inline.Raw_html (html, m) -> raw_html_to_inline_element ~locator html m acc 334 | Inline.Strong_emphasis (e, m) -> 335 emphasis_to_inline_element ~locator defs `Bold e m acc 336 | Inline.Text (t, m) -> text_to_inline_elements ~locator t m acc 337 | Inline.Ext_math_span (ms, m) -> 338 math_span_to_inline_element ~locator ms m acc 339 | Inline.Ext_strikethrough (s, meta) -> 340 let i = Inline.Strikethrough.inline s in 341 let acc = warn_unsupported_cmark ~locator "strikethrough" meta acc in 342 inline_to_inline_elements ~locator defs acc i 343 | _ -> assert false 344 345(* Heading label support - CommonMark extension. Parses a potential 346 final {#id} in heading inlines. In [id] braces must be escaped 347 otherwise parsing fails; if the rightmost brace is escaped it's 348 not a heading label. The parse runs from right to left *) 349 350let parse_heading_label s = 351 let rec loop s max prev i = 352 if i < 0 then None 353 else 354 match s.[i] with 355 | '{' as c -> 356 if i > 0 && s.[i - 1] = '\\' then loop s max c (i - 1) 357 else if prev = '#' then Some i 358 else None 359 | '}' as c -> 360 if i > 0 && s.[i - 1] = '\\' then loop s max c (i - 1) else None 361 | c -> loop s max c (i - 1) 362 in 363 let max = String.length s - 1 in 364 let last = 365 (* [last] is rightmost non blank, if any. *) 366 let k = ref max in 367 while (not (!k < 0)) && is_blank s.[!k] do 368 decr k 369 done; 370 !k 371 in 372 if last < 1 || s.[last] <> '}' || s.[last - 1] = '\\' then None 373 else 374 match loop s max s.[last] (last - 1) with 375 | None -> None 376 | Some first -> 377 let chop = max - first + 1 in 378 let text = String.sub s 0 first in 379 let first = first + 2 and last = last - 1 in 380 (* remove delims *) 381 let label = String.sub s first (last - first + 1) in 382 Some (text, chop, label) 383 384let heading_inline_and_label h = 385 (* cmarkit claims it's already normalized but let's be defensive :-) *) 386 match Inline.normalize (Block.Heading.inline h) with 387 | Inline.Text (t, m) as inline -> ( 388 match parse_heading_label t with 389 | None -> (inline, None) 390 | Some (t, chop, label) -> 391 let m = chop_end_of_meta_textloc ~count:chop m in 392 (Inline.Text (t, m), Some label)) 393 | Inline.Inlines (is, m0) as inline -> ( 394 match List.rev is with 395 | Inline.Text (t, m1) :: ris -> ( 396 match parse_heading_label t with 397 | None -> (inline, None) 398 | Some (t, chop, label) -> 399 let m0 = chop_end_of_meta_textloc ~count:chop m0 in 400 let m1 = chop_end_of_meta_textloc ~count:chop m1 in 401 ( Inline.Inlines (List.rev (Inline.Text (t, m1) :: ris), m0), 402 Some label )) 403 | _ -> (inline, None)) 404 | inline -> (inline, None) 405 406(* Block translations *) 407 408let raw_paragraph ~loc ~raw_loc backend raw = 409 Loc.at loc (`Paragraph [ Loc.at raw_loc (`Raw_markup (Some backend, raw)) ]) 410 411let code_block_to_nestable_block_element ~locator cb m (bs, warns) = 412 let loc = meta_to_loc ~locator m in 413 let code = Block.Code_block.code cb in 414 let code_loc = textloc_to_loc ~locator (Block_line.list_textloc code) in 415 let code = String.concat "\n" (List.map Block_line.to_string code) in 416 match Block.Code_block.info_string cb with 417 | None -> 418 let code_block = 419 { 420 Ast.meta = None; 421 delimiter = None; 422 content = Loc.at code_loc code; 423 output = None; 424 } 425 (* (None, Loc.at code_loc code) *) 426 in 427 (Loc.at loc (`Code_block code_block) :: bs, warns) 428 | Some (info, im) -> ( 429 match Block.Code_block.language_of_info_string info with 430 | None -> 431 let code_block = 432 { 433 Ast.meta = None; 434 delimiter = None; 435 content = Loc.at code_loc code; 436 output = None; 437 } 438 in 439 (* (None, Loc.at code_loc code) *) 440 (Loc.at loc (`Code_block code_block) :: bs, warns) 441 | Some ("verb", _) -> (Loc.at loc (`Verbatim code) :: bs, warns) 442 | Some ("=html", _) -> 443 (raw_paragraph ~loc ~raw_loc:code_loc "html" code :: bs, warns) 444 | Some ("=latex", _) -> 445 (raw_paragraph ~loc ~raw_loc:code_loc "latex" code :: bs, warns) 446 | Some ("=texi", _) -> 447 (raw_paragraph ~loc ~raw_loc:code_loc "texi" code :: bs, warns) 448 | Some ("=man", _) -> 449 (raw_paragraph ~loc ~raw_loc:code_loc "man" code :: bs, warns) 450 | Some (lang, env) -> 451 let left_count = String.length lang in 452 let right_count = String.length env in 453 let lang_loc, env_loc = 454 split_info_string_locs ~left_count ~right_count im 455 in 456 let env = 457 if env = "" then [] 458 else [ `Tag (Loc.at (textloc_to_loc ~locator env_loc) env) ] 459 in 460 let lang = Loc.at (textloc_to_loc ~locator lang_loc) lang in 461 let metadata = Some { Ast.language = lang; tags = env } in 462 let code_block = 463 { 464 Ast.meta = metadata; 465 delimiter = None; 466 content = Loc.at code_loc code; 467 output = None; 468 } 469 (* (metadata, Loc.at code_loc code) *) 470 in 471 (Loc.at loc (`Code_block code_block) :: bs, warns)) 472 473let math_block_to_nestable_block_element ~locator mb m (bs, warns) = 474 let loc = meta_to_loc ~locator m in 475 let math = Block.Code_block.code mb in 476 let math = String.concat "\n" (List.map Block_line.to_string math) in 477 (Loc.at loc (`Math_block math) :: bs, warns) 478 479let html_block_to_nestable_block_element ~locator html m (bs, warns) = 480 let loc = meta_to_loc ~locator m in 481 let html = String.concat "\n" (List.map fst html) in 482 (raw_paragraph ~loc ~raw_loc:loc "html" html :: bs, warns) 483 484let heading_to_block_element ~locator defs h m (bs, warns) = 485 let loc = meta_to_loc ~locator m in 486 let level, warns = 487 match Block.Heading.level h with 488 | 6 -> (5, warn ~loc warn_heading_level_6 warns) 489 | level -> (level, warns) 490 in 491 let inline, label = heading_inline_and_label h in 492 let inlines, warns = 493 inline_to_inline_elements ~locator defs ([], warns) inline 494 in 495 (Loc.at loc (`Heading (level, label, inlines)) :: bs, warns) 496 497let paragraph_to_nestable_block_element ~locator defs p m (bs, warns) = 498 (* TODO Parse inlines for @tags support. *) 499 let loc = meta_to_loc ~locator m in 500 let i = Block.Paragraph.inline p in 501 let is, warns = inline_to_inline_elements ~locator defs ([], warns) i in 502 (Loc.at loc (`Paragraph is) :: bs, warns) 503 504let thematic_break_to_nestable_block_element ~locator m (bs, warns) = 505 let loc = meta_to_loc ~locator m in 506 (raw_paragraph ~loc ~raw_loc:loc "html" "<hr>" :: bs, warns) 507 508let rec list_to_nestable_block_element ~locator defs l m (bs, warns) = 509 let loc = meta_to_loc ~locator m in 510 let style = 511 `Heavy 512 (* Note this is a layout property of ocamldoc *) 513 in 514 let kind, warns = 515 match Block.List'.type' l with 516 | `Unordered _ -> (`Unordered, warns) 517 | `Ordered (start, _) -> 518 ( `Ordered, 519 if start = 1 then warns 520 else warn ~loc (warn_unsupported_list_start_number start) warns ) 521 in 522 let add_item ~locator (acc, warns) (i, _meta) = 523 let b = Block.List_item.block i in 524 let bs, warns = 525 block_to_nestable_block_elements ~locator defs ([], warns) b 526 in 527 (bs :: acc, warns) 528 in 529 let ritems = List.rev (Block.List'.items l) in 530 let items, warns = List.fold_left (add_item ~locator) ([], warns) ritems in 531 (Loc.at loc (`List (kind, style, items)) :: bs, warns) 532 533and table_to_nestable_block_element ~locator defs tbl m (bs, warns) = 534 let loc = meta_to_loc ~locator m in 535 let style = 536 `Light 537 (* Note this is a layout property of ocamldoc *) 538 in 539 let col_count = Block.Table.col_count tbl in 540 let add_cell typ (n_cell, acc, warns) (cell, _) = 541 let content, warns = 542 inline_to_inline_elements ~locator defs ([], warns) cell 543 in 544 let loc = Loc.span (List.map Loc.location content) in 545 let cell = Loc.at loc (`Paragraph content) in 546 (n_cell + 1, ([ cell ], typ) :: acc, warns) 547 in 548 let add_cells (acc, warns) typ cells = 549 let n_cell, res, warns = 550 List.fold_left (add_cell typ) (0, [], warns) cells 551 in 552 let res = 553 (* Pad with empty entries to reach the number of columns *) 554 List.init (col_count - n_cell) (fun _ -> ([], `Data)) @ res |> List.rev 555 in 556 (res :: acc, warns) 557 in 558 let add_row ~locator:_ (acc, warns) (row, _meta) = 559 match row with 560 | `Header cells, _layout -> add_cells (acc, warns) `Header cells 561 | `Data cells, _ -> add_cells (acc, warns) `Data cells 562 | `Sep _, _ -> (acc, warns) 563 in 564 let rows = List.rev (Block.Table.rows tbl) in 565 let items, warns = List.fold_left (add_row ~locator) ([], warns) rows in 566 let alignment = 567 let rec find_sep rows = 568 match rows with 569 | [] -> None 570 | ((`Sep s, _layout), _meta) :: _ -> Some s 571 | _ :: q -> find_sep q 572 in 573 match find_sep rows with 574 | None -> None 575 | Some sep -> Some (List.map (function (align, _layout), _ -> align) sep) 576 in 577 let table = `Table ((items, alignment), style) in 578 let res = (Loc.at loc table :: bs, warns) in 579 res 580 581and block_to_nestable_block_elements ~locator defs acc b : nestable_ast_acc = 582 match b with 583 | Block.Blocks (bs, _) -> 584 let block = block_to_nestable_block_elements ~locator defs in 585 List.fold_left block acc (List.rev bs) 586 | Block.Code_block (c, m) -> 587 code_block_to_nestable_block_element ~locator c m acc 588 | Block.Heading (_, m) -> warn_unsupported_header_nesting ~locator m acc 589 | Block.Html_block (html, m) -> 590 html_block_to_nestable_block_element ~locator html m acc 591 | Block.List (l, m) -> list_to_nestable_block_element ~locator defs l m acc 592 | Block.Paragraph (p, m) -> 593 paragraph_to_nestable_block_element ~locator defs p m acc 594 | Block.Block_quote (_, m) -> 595 warn_unsupported_cmark ~locator "Block quotes" m acc 596 | Block.Thematic_break (_, m) -> 597 thematic_break_to_nestable_block_element ~locator m acc 598 | Block.Blank_line _ | Block.Link_reference_definition _ -> 599 (* layout cases *) acc 600 | Block.Ext_table (tbl, m) -> 601 table_to_nestable_block_element ~locator defs tbl m acc 602 | Block.Ext_math_block (math, m) -> 603 math_block_to_nestable_block_element ~locator math m acc 604 | Block.Ext_footnote_definition (_, meta) -> 605 warn_unsupported_cmark ~locator "Footnotes" meta acc 606 | _ -> assert false 607 608let rec block_to_ast ~locator defs acc b : ast_acc = 609 match b with 610 | Block.Heading (h, m) -> heading_to_block_element ~locator defs h m acc 611 | Block.Blocks (bs, _) -> 612 List.fold_left (block_to_ast ~locator defs) acc (List.rev bs) 613 | b -> 614 (* We can't go directy with acc because of nestable typing. *) 615 let bs, ws = acc in 616 let bs', ws = block_to_nestable_block_elements ~locator defs ([], ws) b in 617 (List.rev_append (List.rev (bs' :> Ast.t)) bs, ws) 618 619(* Parsing comments *) 620 621let parse_comment ?buffer:b ~location ~text:s () : Ast.t * Warning.t list = 622 let b = 623 match b with 624 | None -> Buffer.create (String.length s) 625 | Some b -> 626 Buffer.reset b; 627 b 628 in 629 let locator, text = massage_comment ~location b s in 630 let warns = ref [] and file = location.Lexing.pos_fname in 631 let doc = Doc.of_string ~file ~locs:true ~strict:false text in 632 block_to_ast ~locator (Doc.defs doc) ([], !warns) (Doc.block doc)