this repo has no description

Abstract block_table and remove opens

authored by

David Sancho Moreno and committed by jon.recoil.org faceb5e2 b00163d4

+255 -317
-2
src/markdown2/config.ml
··· 1 1 (* Markdown output configuration *) 2 2 3 - [@@@warning "-69"] 4 - 5 3 type t = { root_url : string option } 6 4 7 5 let v ~root_url () = { root_url }
+155 -190
src/markdown2/generator.ml
··· 1 - (* 2 - * Copyright (c) 2016 Thomas Refis <trefis@janestreet.com> 3 - * 4 - * Permission to use, copy, modify, and distribute this software for any 5 - * purpose with or without fee is hereby granted, provided that the above 6 - * copyright notice and this permission notice appear in all copies. 7 - * 8 - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 - *) 16 - 17 - [@@@warning "-26-27-32"] 18 - 19 1 open Odoc_utils 2 + module HLink = Link 20 3 21 - module HLink = Link 22 - open Odoc_document.Types 4 + module Types = Odoc_document.Types 23 5 module Doctree = Odoc_document.Doctree 24 6 module Url = Odoc_document.Url 25 7 module Link = HLink 26 8 27 9 module Md = struct 28 10 include Cmarkit 29 - 30 11 let meta = Cmarkit.Meta.none 31 12 end 32 13 33 - let source fn (t : Source.t) = 34 - let rec token (x : Source.token) = 14 + let source fn (t : Types.Source.t) = 15 + let rec token (x : Types.Source.token) = 35 16 match x with Elt i -> fn i | Tag (_, l) -> tokens l 36 17 and tokens t = List.concat_map token t in 37 18 tokens t ··· 56 37 57 38 let rec inline_text_only inline = 58 39 List.concat_map 59 - (fun (i : Inline.one) -> 40 + (fun (i : Types.Inline.one) -> 60 41 match i.desc with 61 42 | Text "" -> [] 62 43 | Text s -> [ s ] ··· 68 49 | _ -> []) 69 50 inline 70 51 71 - and block_text_only (blocks : Block.t) : string list = 52 + and block_text_only blocks : string list = 72 53 List.concat_map 73 - (fun (b : Block.one) -> 54 + (fun (b : Types.Block.one) -> 74 55 match b.desc with 75 56 | Paragraph inline | Inline inline -> inline_text_only inline 76 57 | Source (_, s) -> source inline_text_only s ··· 79 60 | _ -> []) 80 61 blocks 81 62 82 - and inline ~config ?(emph_level = 0) ~resolve (l : Inline.t) = 83 - let one (t : Inline.one) = 63 + and inline ~config ?(emph_level = 0) ~resolve l = 64 + let one (t : Types.Inline.one) = 84 65 match t.desc with 85 66 | Text s -> [ Md.Inline.Text (s, Md.meta) ] 86 67 | Entity s -> ··· 141 122 in 142 123 List.concat_map one l 143 124 144 - let heading ~config ~resolve (h : Heading.t) : Md.Block.t list = 145 - let inlines = inline ~config ~resolve h.title in 146 - let content = Md.Inline.Inlines (inlines, Md.meta) in 147 - let heading = 148 - Md.Block.Heading 149 - (Md.Block.Heading.make ~level:(h.level + 1) content, Md.meta) 150 - in 151 - [ heading ] 152 - 153 - let rec block ~config ~resolve (l : Block.t) : Md.Block.t list = 154 - let one (t : Block.one) : Md.Block.t list = 125 + let rec block ~config ~resolve l = 126 + let one (t : Types.Block.one) = 155 127 match t.desc with 156 128 | Paragraph paragraph -> 157 129 let inlines = inline ~config ~resolve paragraph in ··· 175 147 l 176 148 in 177 149 [ 178 - (* TODO: Do we need to make it tight based on something? *) 150 + (* TODO: Do we need the list (~tight:false) based on surrounding content or can we always be ~tight:true? *) 179 151 Md.Block.List 180 152 (Md.Block.List'.make ~tight:true list_type list_items, Md.meta); 181 153 ] 182 154 | Inline i -> 183 155 let inlines = Md.Inline.Inlines (inline ~config ~resolve i, Md.meta) in 184 156 [ Md.Block.Paragraph (Md.Block.Paragraph.make inlines, Md.meta) ] 185 - | Table t -> 186 - let rows_data : (string * [ `Data | `Header ]) list list = 187 - match t.data with 188 - | [] -> [] 189 - | rows -> 190 - List.map 191 - (fun (row : (Block.t * [ `Data | `Header ]) list) -> 192 - List.map 193 - (fun (content, cell_type) -> 194 - let cell_text = 195 - String.concat ~sep:" " (block_text_only content) 196 - in 197 - (cell_text, cell_type)) 198 - row) 199 - rows 200 - in 201 - 202 - if rows_data = [] then 203 - [ 204 - Md.Block.Paragraph 205 - ( Md.Block.Paragraph.make (Md.Inline.Inlines ([], Md.meta)), 206 - Md.meta ); 207 - ] 208 - else 209 - let max_columns = 210 - List.fold_left 211 - (fun max_cols row -> 212 - let row_cols = List.length row in 213 - if row_cols > max_cols then row_cols else max_cols) 214 - 0 rows_data 215 - in 216 - 217 - let has_header_row = 218 - match rows_data with 219 - | first_row :: _ -> 220 - List.exists 221 - (fun (_, cell_type) -> cell_type = `Header) 222 - first_row 223 - | [] -> false 224 - in 225 - 226 - let rec make_list n v = 227 - if n <= 0 then [] else v :: make_list (n - 1) v 228 - in 229 - 230 - let header_cells, content_rows = 231 - match rows_data with 232 - | first_row :: rest when has_header_row -> 233 - (* Pad header cells to match max_columns *) 234 - let padded_header = 235 - let cells = List.map fst first_row in 236 - let missing = max_columns - List.length cells in 237 - if missing > 0 then cells @ make_list missing "" else cells 238 - in 239 - (padded_header, rest) 240 - | _ -> 241 - (* No header - create an empty header matching the max columns *) 242 - (make_list max_columns "", rows_data) 243 - in 244 - 245 - let pad_row row = 246 - let cells = List.map fst row in 247 - let missing = max_columns - List.length cells in 248 - if missing > 0 then cells @ make_list missing "" else cells 249 - in 250 - 251 - let header_inline = 252 - let header_text = 253 - "| " ^ String.concat ~sep:" | " header_cells ^ " |" 254 - in 255 - let header_md = Md.Inline.Text (header_text, Md.meta) in 256 - Md.Inline.Inlines ([ header_md ], Md.meta) 257 - in 258 - 259 - (* Create the separator row (based on column alignment) *) 260 - let separator_inline = 261 - let alignments = 262 - if List.length t.align >= max_columns then 263 - (* Take only the first max_columns elements *) 264 - let rec take n lst = 265 - if n <= 0 then [] 266 - else match lst with [] -> [] | h :: t -> h :: take (n - 1) t 267 - in 268 - take max_columns t.align 269 - else 270 - t.align 271 - @ make_list (max_columns - List.length t.align) Table.Default 272 - in 273 - 274 - let separator_cells = 275 - List.map 276 - (fun align -> 277 - match align with 278 - | Table.Left -> ":---" 279 - | Table.Center -> ":---:" 280 - | Table.Right -> "---:" 281 - | Table.Default -> "---") 282 - alignments 283 - in 284 - let sep_text = 285 - "| " ^ String.concat ~sep:" | " separator_cells ^ " |" 286 - in 287 - let sep_md = Md.Inline.Text (sep_text, Md.meta) in 288 - Md.Inline.Inlines ([ sep_md ], Md.meta) 289 - in 290 - 291 - let content_inlines = 292 - List.map 293 - (fun row -> 294 - let cells = pad_row row in 295 - let row_text = "| " ^ String.concat ~sep:" | " cells ^ " |" in 296 - let row_md = Md.Inline.Text (row_text, Md.meta) in 297 - Md.Inline.Inlines ([ row_md ], Md.meta)) 298 - content_rows 299 - in 300 - List.map 301 - (fun inline -> 302 - Md.Block.Paragraph (Md.Block.Paragraph.make inline, Md.meta)) 303 - ([ header_inline; separator_inline ] @ content_inlines) 157 + | Table t -> block_table t 304 158 | Description l -> 305 - let item ({ key; definition; attr = _ } : Description.one) = 159 + let item ({ key; definition; attr = _ } : Types.Description.one) = 306 160 let term = inline ~config ~resolve key in 307 161 (* We extract definition as inline, since it came as "Block". There seems to be no way (in Cmarkit) to make it inline *) 308 162 let definition_inline = ··· 352 206 in 353 207 failwith msg) 354 208 | Audio (_target, _alt) -> 355 - (* TODO: Raise a decent error here? Only saw assert false :( *) 209 + (* TODO: Raise a decent error here? Maybe warnings, I only saw assert false *) 356 210 failwith "Audio isn't supported in markdown" 357 211 | Video (_target, _alt) -> 358 - (* TODO: Raise a decent error here? Only saw assert false :( *) 212 + (* TODO: Raise a decent error here? Maybe warnings, I only saw assert false *) 359 213 failwith "Video isn't supported in markdown" 360 214 | Image (target, alt) -> 361 215 let dest = 362 - match target with 363 - | Target.External url -> (url, Md.meta) 364 - | Target.Internal (Resolved uri) -> 216 + match (target : Types.Target.t) with 217 + | External url -> (url, Md.meta) 218 + | Internal (Resolved uri) -> 365 219 let url = Link.href ~config ~resolve uri in 366 220 (url, Md.meta) 367 - | Target.Internal Unresolved -> 221 + | Internal Unresolved -> 368 222 (* TODO: What's unresolved? A non-existing page/link? *) 369 223 ("", Md.meta) 370 224 in ··· 383 237 in 384 238 List.concat_map one l 385 239 240 + and block_table t = 241 + let rows_data : (string * [ `Data | `Header ]) list list = 242 + match t.data with 243 + | [] -> [] 244 + | rows -> 245 + List.map 246 + (fun (row : (Types.Block.t * [ `Data | `Header ]) list) -> 247 + List.map 248 + (fun (content, cell_type) -> 249 + let cell_text = 250 + String.concat ~sep:" " (block_text_only content) 251 + in 252 + (cell_text, cell_type)) 253 + row) 254 + rows 255 + in 256 + 257 + if rows_data = [] then 258 + [ 259 + Md.Block.Paragraph 260 + (Md.Block.Paragraph.make (Md.Inline.Inlines ([], Md.meta)), Md.meta); 261 + ] 262 + else 263 + let max_columns = 264 + List.fold_left 265 + (fun max_cols row -> 266 + let row_cols = List.length row in 267 + if row_cols > max_cols then row_cols else max_cols) 268 + 0 rows_data 269 + in 270 + 271 + let has_header_row = 272 + match rows_data with 273 + | first_row :: _ -> 274 + List.exists (fun (_, cell_type) -> cell_type = `Header) first_row 275 + | [] -> false 276 + in 277 + 278 + let rec make_list n v = if n <= 0 then [] else v :: make_list (n - 1) v in 279 + 280 + let header_cells, content_rows = 281 + match rows_data with 282 + | first_row :: rest when has_header_row -> 283 + (* Pad header cells to match max_columns *) 284 + let padded_header = 285 + let cells = List.map fst first_row in 286 + let missing = max_columns - List.length cells in 287 + if missing > 0 then cells @ make_list missing "" else cells 288 + in 289 + (padded_header, rest) 290 + | _ -> 291 + (* No header - create an empty header matching the max columns *) 292 + (make_list max_columns "", rows_data) 293 + in 294 + 295 + let pad_row row = 296 + let cells = List.map fst row in 297 + let missing = max_columns - List.length cells in 298 + if missing > 0 then cells @ make_list missing "" else cells 299 + in 300 + 301 + let header_inline = 302 + let header_text = "| " ^ String.concat ~sep:" | " header_cells ^ " |" in 303 + let header_md = Md.Inline.Text (header_text, Md.meta) in 304 + Md.Inline.Inlines ([ header_md ], Md.meta) 305 + in 306 + 307 + (* Create the separator row (based on column alignment) *) 308 + let separator_inline = 309 + let alignments = 310 + if List.length t.align >= max_columns then 311 + (* Take only the first max_columns elements *) 312 + let rec take n lst = 313 + if n <= 0 then [] 314 + else match lst with [] -> [] | h :: t -> h :: take (n - 1) t 315 + in 316 + take max_columns t.align 317 + else 318 + t.align 319 + @ make_list (max_columns - List.length t.align) Types.Table.Default 320 + in 321 + 322 + let separator_cells = 323 + List.map 324 + (fun align -> 325 + match (align : Types.Table.alignment) with 326 + | Left -> ":---" 327 + | Center -> ":---:" 328 + | Right -> "---:" 329 + | Default -> "---") 330 + alignments 331 + in 332 + let sep_text = "| " ^ String.concat ~sep:" | " separator_cells ^ " |" in 333 + let sep_md = Md.Inline.Text (sep_text, Md.meta) in 334 + Md.Inline.Inlines ([ sep_md ], Md.meta) 335 + in 336 + 337 + let content_inlines = 338 + List.map 339 + (fun row -> 340 + let cells = pad_row row in 341 + let row_text = "| " ^ String.concat ~sep:" | " cells ^ " |" in 342 + let row_md = Md.Inline.Text (row_text, Md.meta) in 343 + Md.Inline.Inlines ([ row_md ], Md.meta)) 344 + content_rows 345 + in 346 + List.map 347 + (fun inline -> 348 + Md.Block.Paragraph (Md.Block.Paragraph.make inline, Md.meta)) 349 + ([ header_inline; separator_inline ] @ content_inlines) 350 + 386 351 and items ~config ~resolve l : Md.Block.t list = 387 - let rec walk_items acc (t : Item.t list) = 352 + let rec walk_items acc (t : Types.Item.t list) = 388 353 let continue_with rest elts = 389 354 (walk_items [@tailcall]) (List.rev_append elts acc) rest 390 355 in ··· 393 358 | Text _ :: _ as t -> 394 359 let text, _, rest = 395 360 Doctree.Take.until t ~classify:(function 396 - | Item.Text text -> Accum text 361 + | Types.Item.Text text -> Accum text 397 362 | _ -> Stop_and_keep) 398 363 in 399 364 let content = block ~config ~resolve text in 400 365 (continue_with [@tailcall]) rest content 401 366 | Heading h :: rest -> 402 - (continue_with [@tailcall]) rest (heading ~config ~resolve h) 367 + let inlines = inline ~config ~resolve h.title in 368 + let content = Md.Inline.Inlines (inlines, Md.meta) in 369 + let block = Md.Block.Heading.make ~level:(h.level + 1) content in 370 + let heading = [ Md.Block.Heading (block, Md.meta) ] in 371 + (continue_with [@tailcall]) rest heading 403 372 | Include 404 373 { 405 374 attr = _attr; ··· 413 382 (continue_with [@tailcall]) rest content 414 383 | Declaration 415 384 { 416 - Item.attr = _attr; 385 + attr = _attr; 417 386 anchor = _anchor; 418 387 source_anchor = _source_anchor; 419 388 content; ··· 427 396 and items l = walk_items [] l in 428 397 items l 429 398 430 - and documentedSrc ~config ~resolve (t : DocumentedSrc.t) = 431 - let open DocumentedSrc in 399 + and documentedSrc ~config ~resolve t = 400 + let open Types.DocumentedSrc in 432 401 let take_code l = 433 402 Doctree.Take.until l ~classify:(fun x -> 434 - match (x : DocumentedSrc.one) with 403 + match (x : one) with 435 404 | Code code -> Accum code 436 405 | Alternative (Expansion { summary; _ }) -> Accum summary 437 406 | _ -> Stop_and_keep) ··· 439 408 let take_descr l = 440 409 Doctree.Take.until l ~classify:(function 441 410 | Documented { attrs; anchor; code; doc; markers } -> 442 - Accum 443 - [ { DocumentedSrc.attrs; anchor; code = `D code; doc; markers } ] 411 + Accum [ { attrs; anchor; code = `D code; doc; markers } ] 444 412 | Nested { attrs; anchor; code; doc; markers } -> 445 - Accum 446 - [ { DocumentedSrc.attrs; anchor; code = `N code; doc; markers } ] 413 + Accum [ { attrs; anchor; code = `N code; doc; markers } ] 447 414 | _ -> Stop_and_keep) 448 415 in 449 416 let rec to_markdown t : Md.Block.t list = ··· 466 433 | Subpage subp :: _ -> subpage ~config ~resolve subp 467 434 | (Documented _ | Nested _) :: _ -> 468 435 let l, _, rest = take_descr t in 469 - let one { DocumentedSrc.attrs = _; anchor = _; code; doc; markers = _ } 470 - = 436 + let one { attrs = _; anchor = _; code; doc; markers = _ } = 471 437 let content = 472 438 match code with 473 439 | `D code -> ··· 488 454 in 489 455 to_markdown t 490 456 491 - and subpage ~config ~resolve (subp : Subpage.t) = 457 + and subpage ~config ~resolve (subp : Types.Subpage.t) = 492 458 items ~config ~resolve subp.content.items 493 459 494 460 module Page = struct 495 461 let on_sub = function 496 462 | `Page _ -> None 497 - | `Include x -> ( 498 - match x.Include.status with 463 + | `Include (x : Types.Include.t) -> ( 464 + match x.status with 499 465 | `Closed | `Open | `Default -> None 500 466 | `Inline -> Some 0) 501 467 502 - let rec include_ ~config { Subpage.content; _ } = page ~config content 468 + let rec include_ ~config { Types.Subpage.content; _ } = page ~config content 503 469 504 470 and subpages ~config subpages = List.map (include_ ~config) subpages 505 471 506 472 and page ~config p : Odoc_document.Renderer.page = 507 - let { Page.preamble = _; items = i; url; source_anchor } = 473 + let { Types.Page.preamble = _; items = i; url; source_anchor } = 508 474 Doctree.Labels.disambiguate_page ~enter_subpages:false p 509 475 in 510 476 let subpages = subpages ~config @@ Doctree.Subpages.compute p in ··· 512 478 let i = Doctree.Shift.compute ~on_sub i in 513 479 let content = items ~config ~resolve i in 514 480 let root_block = Md.Block.Blocks (content, Md.meta) in 515 - let doc = Cmarkit.Doc.make root_block in 481 + let doc = Md.Doc.make root_block in 516 482 let header, preamble = Doctree.PageTitle.render_title ?source_anchor p in 517 483 let header = items ~config ~resolve header in 518 484 let preamble = items ~config ~resolve preamble in 519 485 Markdown_page.make ~config ~header:(header @ preamble) ~url doc subpages 520 486 521 487 and source_page ~config sp = 522 - let { Source_page.url; contents = _ } = sp in 488 + let { Types.Source_page.url; contents = _ } = sp in 523 489 let _resolve = Link.Current sp.url in 524 490 let title = url.Url.Path.name and doc = [ Md.Block.empty ] in 525 491 (* What's the header? *) ··· 527 493 Markdown_page.make_src ~header ~config ~url title doc 528 494 end 529 495 530 - let render ~(config : Config.t) = function 496 + let render ~(config : Config.t) doc = 497 + match (doc : Types.Document.t) with 531 498 (* .mld *) 532 - | Document.Page page -> [ Page.page ~config page ] 499 + | Page page -> [ Page.page ~config page ] 533 500 (* .mli docs *) 534 501 | Source_page src -> [ Page.source_page ~config src ] 535 - 536 - let filepath ~config url = Link.Path.as_filename ~config url 537 502 538 503 let inline ~config ~xref_base_uri b = 539 504 let resolve = Link.Base xref_base_uri in
+1 -1
src/markdown2/generator.mli
··· 3 3 Odoc_document.Types.Document.t -> 4 4 Odoc_document.Renderer.page list 5 5 6 - val filepath : config:Config.t -> Odoc_document.Url.Path.t -> Fpath.t 6 + (* val filepath : config:Config.t -> Odoc_document.Url.Path.t -> Fpath.t *) 7 7 8 8 val items : 9 9 config:Config.t ->
+1
src/markdown2/link.ml
··· 111 111 in 112 112 match (relative_target, anchor) with 113 113 | [], "" -> "#" 114 + (* TODO: This looks wrong ./ could technically be the current page *) 114 115 | page, _ -> "./" ^ add_anchor @@ String.concat "/" page))
+12 -16
src/markdown2/markdown_page.ml
··· 16 16 17 17 module Url = Odoc_document.Url 18 18 19 - let page_creator doc = 20 - fun (ppf : Format.formatter) -> 21 - let renderer = Cmarkit_commonmark.renderer () in 22 - Format.fprintf ppf "%s" (Cmarkit_renderer.doc_to_string renderer doc) 23 - 24 - let make ~config ~url ~header:_ content children = 19 + let make ~config ~url ~header:_ doc children = 25 20 let filename = Link.Path.as_filename ~config url in 26 - let content = page_creator content in 21 + let content ppf = 22 + let renderer = Cmarkit_commonmark.renderer () in 23 + Format.fprintf ppf "%s" (Cmarkit_renderer.doc_to_string renderer doc) 24 + in 27 25 { Odoc_document.Renderer.filename; content; children; path = url } 28 26 29 - let src_page_creator _name (block_list : Cmarkit.Block.t list) = 30 - fun (ppf : Format.formatter) -> 31 - let renderer = Cmarkit_commonmark.renderer () in 32 - let root_block = Cmarkit.Block.Blocks (block_list, Cmarkit.Meta.none) in 33 - let doc = Cmarkit.Doc.make root_block in 34 - Format.fprintf ppf "%s" (Cmarkit_renderer.doc_to_string renderer doc) 35 - 36 - let make_src ~config ~url ~header:_ title content = 27 + let make_src ~config ~url ~header:_ _title block_list = 37 28 let filename = Link.Path.as_filename ~config url in 38 - let content = src_page_creator title content in 29 + let content (ppf : Format.formatter) = 30 + let renderer = Cmarkit_commonmark.renderer () in 31 + let root_block = Cmarkit.Block.Blocks (block_list, Cmarkit.Meta.none) in 32 + let doc = Cmarkit.Doc.make root_block in 33 + Format.fprintf ppf "%s" (Cmarkit_renderer.doc_to_string renderer doc) 34 + in 39 35 { Odoc_document.Renderer.filename; content; children = []; path = url }
-81
src/markdown2/markdown_source.ml
··· 1 - open Odoc_utils 2 - module HLink = Link 3 - open Odoc_document.Types 4 - open Tyxml 5 - module Link = HLink 6 - 7 - let html_of_doc ~config ~resolve docs = 8 - let open Html in 9 - let a : 10 - ( [< Html_types.a_attrib ], 11 - [< Html_types.span_content_fun ], 12 - [> Html_types.span ] ) 13 - star = 14 - Unsafe.node "a" 15 - (* Makes it possible to use <a> inside span. Although this is not standard (see 16 - https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories) 17 - it is validated by the {{:https://validator.w3.org/nu/#textarea}W3C}. *) 18 - in 19 - (* [a] tags should not contain in other [a] tags. If this happens, browsers 20 - start to be really weird. If PPX do bad things, such a situation could 21 - happen. We manually avoid this situation. *) 22 - let rec doc_to_html ~is_in_a doc = 23 - match doc with 24 - | Source_page.Plain_code s -> [ txt s ] 25 - | Tagged_code (info, docs) -> ( 26 - let is_in_a = match info with Link _ -> true | _ -> is_in_a in 27 - let children = List.concat_map (doc_to_html ~is_in_a) docs in 28 - match info with 29 - | Syntax tok -> [ span ~a:[ a_class [ tok ] ] children ] 30 - (* Currently, we do not render links to documentation *) 31 - | Link { documentation = _; implementation = None } -> children 32 - | Link { documentation = _; implementation = Some anchor } -> 33 - let href = Link.href ~config ~resolve anchor in 34 - [ a ~a:[ a_href href ] children ] 35 - | Anchor lbl -> [ span ~a:[ a_id lbl ] children ]) 36 - in 37 - let span_content = List.concat_map (doc_to_html ~is_in_a:false) docs in 38 - span ~a:[] span_content 39 - 40 - let count_lines_in_string s = 41 - let n = ref 0 in 42 - String.iter (function '\n' -> incr n | _ -> ()) s; 43 - !n 44 - 45 - (** Traverse the doc to count the number of lines. *) 46 - let rec count_lines_in_span = function 47 - | Source_page.Plain_code s -> count_lines_in_string s 48 - | Tagged_code (_, docs) -> count_lines docs 49 - 50 - and count_lines l = 51 - let rec inner l acc = 52 - match l with 53 - | [] -> acc 54 - | hd :: tl -> inner tl (count_lines_in_span hd + acc) 55 - in 56 - inner l 0 57 - 58 - let rec line_numbers acc n = 59 - let open Html in 60 - if n < 1 then acc 61 - else 62 - let l = string_of_int n in 63 - let anchor = 64 - a 65 - ~a:[ a_id ("L" ^ l); a_class [ "source_line" ]; a_href ("#L" ^ l) ] 66 - [ txt l ] 67 - in 68 - line_numbers (anchor :: txt "\n" :: acc) (n - 1) 69 - 70 - let html_of_doc ~config ~resolve docs = 71 - let open Html in 72 - pre 73 - ~a:[ a_class [ "source_container" ] ] 74 - [ 75 - code 76 - ~a:[ a_class [ "source_line_column" ] ] 77 - (line_numbers [] (count_lines docs)); 78 - code 79 - ~a:[ a_class [ "source_code" ] ] 80 - [ html_of_doc ~config ~resolve docs ]; 81 - ]
-5
src/markdown2/markdown_source.mli
··· 1 - val html_of_doc : 2 - config:Config.t -> 3 - resolve:Link.resolve -> 4 - Odoc_document.Types.Source_page.code -> 5 - [> Html_types.pre ] Tyxml.Html.elt
-5
src/markdown2/odoc_markdown.ml
··· 1 - module Types = Types 2 1 module Config = Config 3 - 4 2 module Markdown_page = Markdown_page 5 - (** @canonical Odoc_html.Html_page *) 6 - 7 3 module Generator = Generator 8 4 module Link = Link 9 - module Json = Odoc_utils.Json
-5
src/markdown2/types.ml
··· 1 - (* Type definitions for the Markdown renderer *) 2 - 3 - type uri = Absolute of string | Relative of Odoc_document.Url.Path.t option 4 - 5 - type file_uri = Absolute of string | Relative of Odoc_document.Url.Path.t
+3 -3
test/integration/markdown.t/array.mli
··· 1 - (** {0 List} 1 + (** {0 Array} 2 2 3 - Utilities for List data type. 3 + Utilities for Array data type. 4 4 5 5 This module is compatible with original ocaml stdlib. In general, all 6 6 functions comes with the original stdlib also applies to this collection, ··· 25 25 26 26 val head : 'a t -> 'a option 27 27 (** [head xs] returns [None] if [xs] is the empty list, otherwise it returns 28 - [Some value] where [value] is the first element in the list. 28 + [Some value] where [val ue] is the first element in the list. 29 29 {[ 30 30 head [] = None;; 31 31 head [ 1; 2; 3 ] = Some 1
+2 -4
test/integration/markdown.t/page.mld
··· 22 22 23 23 {3 References} 24 24 25 - See [Odoc_odoc.Compile.compile]. 26 - 27 - See [Odoc_odoc.Compile.compile]. 25 + See an empty reference {{!test.v}}. 28 26 29 - See {{!/test.v}this function from another library}. 27 + See {{!test.v}this function from another library}. 30 28 31 29 See {{!./test.mli}this page from another package}. 32 30
+81 -5
test/integration/markdown.t/run.t
··· 2 2 $ ocamlc -c -bin-annot test2.mli 3 3 $ ocamlc -c -bin-annot list.mli 4 4 $ odoc compile --package test -I . page.mld 5 - File "page.mld", line 123, characters 0-11: 5 + File "page.mld", line 25, characters 23-34: 6 + Warning: '{{!...} ...}' (cross-reference) should not be empty. 7 + File "page.mld", line 121, characters 0-11: 6 8 Warning: Tags are not allowed in pages. 7 9 $ odoc compile --package test test.cmti 8 10 $ odoc compile --package test -I . test2.cmti ··· 15 17 File "list.mli", line 37, characters 12-19: 16 18 Warning: Reference to 'head' is ambiguous. Please specify its kind: section-head, val-head. 17 19 $ odoc link page-page.odoc 18 - File "page.mld", line 83, characters 0-33: 20 + File "page.mld", line 81, characters 0-33: 19 21 Warning: Failed to resolve reference ./odoc_logo_placeholder.jpg Path 'odoc_logo_placeholder.jpg' not found 20 - File "page.mld", line 31, characters 4-49: 22 + File "page.mld", line 29, characters 4-49: 21 23 Warning: Failed to resolve reference ./test.mli Path 'test' not found 22 - File "page.mld", line 29, characters 4-50: 23 - Warning: Failed to resolve reference /test.v Path '/test' not found 24 24 $ odoc markdown-generate test.odocl -o markdown 25 25 $ odoc markdown-generate test2.odocl -o markdown 26 26 $ odoc markdown-generate page-page.odocl -o markdown ··· 40 40 ``` 41 41 module List : sig ... end 42 42 ``` 43 + 44 + $ cat markdown/test/page.md 45 + ## Title 46 + ### Subtitle 47 + #### Referenceable title 48 + See [Referenceable title](./#my_id). 49 + #### Styled 50 + **bold** text, *italic* text, *emphasized* text 51 + H2O and 1st 52 + #### Link 53 + Here is a link: [https://www.example.com](https://www.example.com). 54 + You can also click [here](https://www.example.com). 55 + #### References 56 + See an empty reference [`Test.v`](./Test.md#val-v). 57 + See [this function from another library](./Test.md#val-v). 58 + See [this page from another package](). 59 + See [this section](./#styled) for the syntax of references. 60 + #### Lists 61 + - First item 62 + - Second item 63 + 0. First ordered item 64 + 1. Second numbered item 65 + - First item 66 + - Second item 67 + - can also be used 68 + 0. First numbered item 69 + 1. Second numbered item 70 + 2. can also be used 71 + #### Code blocks 72 + Inline `code`. 73 + ```ocaml 74 + let _ = "Block code" 75 + ``` 76 + ```text 77 + Code block with {[inner code block syntax]} 78 + ``` 79 + ```python 80 + [i+1 for i in xrange(2)] 81 + ``` 82 + #### Verbatim 83 + ``` 84 + verbatim text 85 + ``` 86 + #### Math 87 + For inline math: `\sqrt 2`. 88 + For display math: 89 + ``` 90 + \sqrt 2 91 + ``` 92 + #### Images 93 + ![./odoc\_logo\_placeholder.jpg]() 94 + ![https://picsum.photos/200/100](https://picsum.photos/200/100) 95 + #### Table 96 + ##### Explicit syntax 97 + \| Header 1 \| Header 2 \| 98 + \| --- \| --- \| 99 + \| Cell 1 \| Cell 2 \| 100 + \| Cell 3 \| Cell 4 \| 101 + ##### Light syntax 102 + \| Header 1 \| Header 2 \| 103 + \| --- \| --- \| 104 + \| Cell 1 \| Cell 2 \| 105 + \| Cell 3 \| Cell 4 \| 106 + #### HTML 107 + This is a strong tag: <strong> Odoc language lack support for quotation! </strong> 108 + 109 + 110 + <div> 111 + <blockquote> 112 + Odoc language lack support for quotation! 113 + </blockquote> 114 + </div> 115 + 116 + #### Tags 117 + since 4\.08 118 + Tags are explained in this section.