this repo has no description
at main 1965 lines 77 kB view raw
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 17open Odoc_utils 18open Odoc_model.Names 19module Location = Odoc_model.Location_ 20module Paths = Odoc_model.Paths 21open Types 22module O = Codefmt 23open O.Infix 24 25let tag tag t = O.span ~attr:tag t 26 27let label t = 28 match t with 29 | Odoc_model.Lang.TypeExpr.Label s -> tag "label" (O.txt s) 30 | Optional s | RawOptional s -> tag "optlabel" (O.txt "?" ++ O.txt s) 31 32let type_var tv = tag "type-var" (O.txt tv) 33 34let enclose ~l ~r x = O.span (O.txt l ++ x ++ O.txt r) 35 36let mode_names ms = 37 O.list ms ~sep:(O.txt " ") ~f:(fun m -> O.mode m) 38 39let resolved p content = 40 let link = { Link.target = Internal (Resolved p); content; tooltip = None } in 41 O.elt [ inline @@ Link link ] 42 43let path p content = resolved (Url.from_path p) content 44 45let unresolved content = 46 let link = { Link.target = Internal Unresolved; content; tooltip = None } in 47 O.elt [ inline @@ Link link ] 48 49let path_to_id path = 50 let url = Url.Anchor.from_identifier (path :> Paths.Identifier.t) in 51 Some url 52 53let source_anchor source_loc = 54 match source_loc with 55 | Some id -> 56 Some 57 (Url.Anchor.from_identifier 58 (id : Paths.Identifier.SourceLocation.t :> Paths.Identifier.t)) 59 | _ -> None 60 61let attach_expansion ?(status = `Default) (eq, o, e) page text = 62 match page with 63 | None -> O.documentedSrc text 64 | Some (page : Page.t) -> 65 let url = page.url in 66 let summary = O.render text in 67 let expansion = 68 O.documentedSrc (O.txt eq ++ O.keyword o) 69 @ DocumentedSrc.[ Subpage { status; content = page } ] 70 @ O.documentedSrc (O.keyword e) 71 in 72 DocumentedSrc. 73 [ Alternative (Expansion { summary; url; status; expansion }) ] 74 75let mk_heading ?(level = 1) ?label text = 76 let title = [ inline @@ Text text ] in 77 Item.Heading { label; level; title; source_anchor = None } 78 79(** Returns the preamble as an item. Stop the preamble at the first heading. The 80 rest is inserted into [items]. *) 81let prepare_preamble comment items = 82 let preamble, first_comment = 83 List.split_at 84 ~f:(function 85 | { Odoc_model.Location_.value = `Heading _; _ } -> true | _ -> false) 86 comment 87 in 88 (Comment.standalone preamble, Comment.standalone first_comment @ items) 89 90let make_expansion_page ~source_anchor url comments items = 91 (* Save any resources accumulated before this page - they belong to 92 the parent/main page's content, not this nested expansion. *) 93 let saved_resources = Comment.Resources.take () in 94 let saved_assets = Comment.Assets.take () in 95 let comment = List.concat comments in 96 let preamble, items = prepare_preamble comment items in 97 let resources = Comment.Resources.take () in 98 let assets = Comment.Assets.take () in 99 (* Restore the parent's resources *) 100 Comment.Resources.add saved_resources; 101 Comment.Assets.add saved_assets; 102 { Page.preamble; items; url; source_anchor; resources; assets } 103 104include Generator_signatures 105 106module Make (Syntax : SYNTAX) = struct 107 module Link : sig 108 val from_path : Paths.Path.t -> text 109 110 val from_fragment : Paths.Fragment.leaf -> text 111 end = struct 112 open Paths 113 114 let rec from_path : Path.t -> text = 115 fun path -> 116 match path with 117 | `Identifier (id, _) -> 118 unresolved [ inline @@ Text (Identifier.name id) ] 119 | `Substituted m -> from_path (m :> Path.t) 120 | `SubstitutedMT m -> from_path (m :> Path.t) 121 | `SubstitutedT m -> from_path (m :> Path.t) 122 | `SubstitutedCT m -> from_path (m :> Path.t) 123 | `Root root -> unresolved [ inline @@ Text (ModuleName.to_string root) ] 124 | `Forward root -> unresolved [ inline @@ Text root ] (* FIXME *) 125 | `Dot (prefix, suffix) -> 126 let link = from_path (prefix :> Path.t) in 127 link ++ O.txt ("." ^ ModuleName.to_string suffix) 128 | `DotT (prefix, suffix) -> 129 let link = from_path (prefix :> Path.t) in 130 link ++ O.txt ("." ^ TypeName.to_string suffix) 131 | `DotMT (prefix, suffix) -> 132 let link = from_path (prefix :> Path.t) in 133 link ++ O.txt ("." ^ ModuleTypeName.to_string suffix) 134 | `DotV (prefix, suffix) -> 135 let link = from_path (prefix :> Path.t) in 136 link ++ O.txt ("." ^ ValueName.to_string suffix) 137 | `Apply (p1, p2) -> 138 let link1 = from_path (p1 :> Path.t) in 139 let link2 = from_path (p2 :> Path.t) in 140 link1 ++ O.txt "(" ++ link2 ++ O.txt ")" 141 | `Resolved _ when Paths.Path.is_hidden path -> 142 let txt = Url.render_path path in 143 unresolved [ inline @@ Text txt ] 144 | `Resolved rp -> ( 145 (* If the path is pointing to an opaque module or module type 146 there won't be a page generated - so we stop before; at 147 the parent page, and link instead to the anchor representing 148 the declaration of the opaque module(_type) *) 149 let stop_before = 150 match rp with 151 | `OpaqueModule _ | `OpaqueModuleType _ -> true 152 | _ -> false 153 in 154 let txt = [ inline @@ Text (Url.render_path path) ] in 155 match Paths.Path.Resolved.identifier rp with 156 | Some id -> 157 let href = Url.from_identifier ~stop_before id in 158 resolved href txt 159 | None -> O.elt txt) 160 161 let dot prefix suffix = prefix ^ "." ^ suffix 162 163 let rec render_fragment_any : Fragment.t -> string = 164 fun fragment -> 165 match fragment with 166 | `Resolved rr -> render_resolved_fragment rr 167 | `Dot (`Root, suffix) -> suffix 168 | `Dot (prefix, suffix) -> 169 dot (render_fragment_any (prefix :> Fragment.t)) suffix 170 | `Root -> assert false 171 172 and render_resolved_fragment : Fragment.Resolved.t -> string = 173 let open Fragment.Resolved in 174 fun fragment -> 175 match fragment with 176 | `Root _ -> assert false 177 | `Subst (_, rr) -> render_resolved_fragment (rr :> t) 178 | `Alias (_, rr) -> render_resolved_fragment (rr :> t) 179 | `Module (`Root _, s) -> ModuleName.to_string s 180 | `Module_type (`Root _, s) -> ModuleTypeName.to_string s 181 | `Type (`Root _, s) -> TypeName.to_string s 182 | `Class (`Root _, s) -> TypeName.to_string s 183 | `ClassType (`Root _, s) -> TypeName.to_string s 184 | `Module (rr, s) -> 185 dot (render_resolved_fragment (rr :> t)) (ModuleName.to_string s) 186 | `Module_type (rr, s) -> 187 dot 188 (render_resolved_fragment (rr :> t)) 189 (ModuleTypeName.to_string s) 190 | `Type (rr, s) -> 191 dot (render_resolved_fragment (rr :> t)) (TypeName.to_string s) 192 | `Class (rr, s) -> 193 dot (render_resolved_fragment (rr :> t)) (TypeName.to_string s) 194 | `ClassType (rr, s) -> 195 dot (render_resolved_fragment (rr :> t)) (TypeName.to_string s) 196 | `OpaqueModule r -> render_resolved_fragment (r :> t) 197 198 let resolved_fragment_to_ir : Fragment.Resolved.leaf -> text = 199 fun fragment -> 200 let open Fragment in 201 let id = Resolved.identifier (fragment :> Resolved.t) in 202 let txt = render_resolved_fragment (fragment :> Resolved.t) in 203 match id with 204 | Some id -> 205 let href = Url.from_identifier ~stop_before:false id in 206 resolved href [ inline @@ Text txt ] 207 | None -> unresolved [ inline @@ Text txt ] 208 209 let from_fragment : Fragment.leaf -> text = function 210 | `Resolved r 211 when not (Fragment.Resolved.is_hidden (r :> Fragment.Resolved.t)) -> 212 resolved_fragment_to_ir r 213 | f -> 214 let txt = render_fragment_any (f :> Fragment.t) in 215 unresolved [ inline @@ Text txt ] 216 end 217 218 module Impl = struct 219 let impl ~infos src = 220 let l = 221 infos 222 |> List.sort (fun (_, (l1, e1)) (_, (l2, e2)) -> 223 if l1 = l2 then compare e2 e1 224 (* If two intervals open at the same time, we open 225 first the one that closes last *) 226 else compare l1 l2) 227 in 228 let get_src a b = 229 let in_bound x = min (max x 0) (String.length src) in 230 let a = in_bound a and b = in_bound b in 231 let a, b = (min a b, max a b) in 232 String.with_range src ~first:a ~len:(b - a) 233 in 234 let plain_code = function 235 | "" -> [] 236 | s -> [ Types.Source_page.Plain_code s ] 237 in 238 let min (a : int) b = if a < b then a else b in 239 let rec extract from to_ list aux = 240 match list with 241 | (k, (loc_start, loc_end)) :: q when loc_start < to_ -> 242 let loc_end = min loc_end to_ in 243 (* In case of inconsistent [a [b a] b] 244 we do [a [b b]a] *) 245 let initial = plain_code (get_src from loc_start) in 246 let next, q = extract loc_start loc_end q [] in 247 extract loc_end to_ q 248 ([ Types.Source_page.Tagged_code (k, List.rev next) ] 249 @ initial @ aux) 250 | q -> (plain_code (get_src from to_) @ aux, q) 251 in 252 let doc, _ = extract 0 (String.length src) l [] in 253 List.rev doc 254 end 255 256 module Source_page : sig 257 val source : 258 Paths.Identifier.SourcePage.t -> 259 Syntax_highlighter.infos -> 260 Lang.Source_info.t -> 261 string -> 262 Source_page.t 263 end = struct 264 let path id = Url.Path.from_identifier id 265 266 let to_link { Lang.Source_info.documentation; implementation } = 267 let documentation = 268 (* Since documentation link are not rendered, we comment the code to 269 extract the href, and always output [None] *) 270 ignore documentation; 271 None 272 (* let open Paths.Path.Resolved in *) 273 (* match documentation with *) 274 (* | Some (`Resolved p) when not (is_hidden (p :> t)) -> ( *) 275 (* let id = identifier (p :> t) in *) 276 (* match Url.from_identifier ~stop_before:false id with *) 277 (* | Ok link -> Some link *) 278 (* | _ -> None) *) 279 (* | _ -> None *) 280 in 281 let implementation = 282 match implementation with 283 | Some (Odoc_model.Lang.Source_info.Resolved id) -> 284 Some (Url.Anchor.from_identifier (id :> Paths.Identifier.t)) 285 | _ -> None 286 in 287 Some (Source_page.Link { implementation; documentation }) 288 289 let info_of_info : Lang.Source_info.annotation -> Source_page.info option = 290 function 291 | Definition id -> ( 292 match id.iv with 293 | `SourceLocation (_, def) -> Some (Anchor (DefName.to_string def)) 294 | `SourceLocationInternal (_, local) -> 295 Some (Anchor (LocalName.to_string local)) 296 | _ -> None) 297 | Module v -> to_link v 298 | ModuleType v -> to_link v 299 | Type v -> to_link v 300 | Value v -> to_link v 301 302 let source id syntax_info infos source_code = 303 let url = path id in 304 let mapper (info, (loc : Lang.Source_info.location_in_file)) = 305 match info_of_info info with Some x -> Some (x, (loc.loc_start.pos_cnum, loc.loc_end.pos_cnum)) | None -> None 306 in 307 let infos = Odoc_utils.List.filter_map mapper infos in 308 let syntax_info = 309 List.rev_map (fun (ty, loc) -> (Source_page.Syntax ty, loc)) syntax_info 310 |> List.rev 311 in 312 let contents = Impl.impl ~infos:(infos @ syntax_info) source_code in 313 { Source_page.url; contents } 314 end 315 316 module Type_expression : sig 317 val type_expr : ?needs_parentheses:bool -> Lang.TypeExpr.t -> text 318 319 val format_type_path : 320 delim:[ `parens | `brackets ] -> Lang.TypeExpr.t list -> text -> text 321 end = struct 322 let rec te_variant (t : Odoc_model.Lang.TypeExpr.Polymorphic_variant.t) = 323 let style_arguments ~constant arguments = 324 (* Multiple arguments in a polymorphic variant constructor correspond 325 to a conjunction of types, not a product: [`Lbl int&float]. 326 If constant is [true], the conjunction starts with an empty type, 327 for instance [`Lbl &int]. 328 *) 329 let wrapped_type_expr = 330 (* type conjunction in Reason is printed as `Lbl (t1)&(t2)` *) 331 if Syntax.Type.Variant.parenthesize_params then fun x -> 332 enclose ~l:"(" ~r:")" (type_expr x) 333 else fun x -> type_expr x 334 in 335 let arguments = 336 O.list arguments ~sep:(O.txt " & ") ~f:wrapped_type_expr 337 in 338 if constant then O.txt "& " ++ arguments else arguments 339 in 340 let rec style_elements ~add_pipe = function 341 | [] -> O.noop 342 | first :: rest -> 343 let first = 344 match first with 345 | Odoc_model.Lang.TypeExpr.Polymorphic_variant.Type te -> 346 let res = O.box_hv @@ type_expr te in 347 if add_pipe then O.sp ++ O.span (O.txt "| " ++ res) else res 348 | Constructor { constant; name; arguments; _ } -> 349 let constr = 350 let name = "`" ^ name in 351 if add_pipe then O.span (O.txt ("| " ^ name)) 352 else O.txt name 353 in 354 let res = 355 O.box_hv 356 (match arguments with 357 | [] -> constr 358 | _ -> 359 let arguments = style_arguments ~constant arguments in 360 O.span 361 (if Syntax.Type.Variant.parenthesize_params then 362 constr ++ arguments 363 else constr ++ O.txt " of" ++ O.sp ++ arguments)) 364 in 365 if add_pipe then O.sp ++ res else res 366 in 367 first ++ style_elements ~add_pipe:true rest 368 in 369 let elements = style_elements ~add_pipe:false t.elements in 370 O.box_hv_no_indent 371 @@ O.span 372 (match t.kind with 373 | Fixed -> O.txt "[ " ++ elements ++ O.txt " ]" 374 | Open -> O.txt "[> " ++ elements ++ O.txt " ]" 375 | Closed [] -> O.txt "[< " ++ elements ++ O.txt " ]" 376 | Closed lst -> 377 let constrs = String.concat ~sep:" " lst in 378 O.txt "[< " ++ elements ++ O.txt (" " ^ constrs ^ " ]")) 379 380 and te_object (t : Odoc_model.Lang.TypeExpr.Object.t) = 381 let fields = 382 O.list 383 ~sep:(O.sp ++ O.txt Syntax.Obj.field_separator) 384 t.fields 385 ~f:(function 386 | Odoc_model.Lang.TypeExpr.Object.Method { name; type_ } -> 387 O.box_hv_no_indent 388 @@ O.txt (name ^ Syntax.Type.annotation_separator) 389 ++ O.cut ++ type_expr type_ 390 | Inherit type_ -> O.box_hv_no_indent @@ type_expr type_) 391 in 392 let open_tag = 393 if t.open_ then O.txt Syntax.Obj.open_tag_extendable 394 else O.txt Syntax.Obj.open_tag_closed 395 in 396 let close_tag = 397 if t.open_ then O.txt Syntax.Obj.close_tag_extendable 398 else O.txt Syntax.Obj.close_tag_closed 399 in 400 O.span (open_tag ++ fields ++ close_tag) 401 402 and format_type_path ~delim (params : Odoc_model.Lang.TypeExpr.t list) 403 (path : text) : text = 404 O.box_hv 405 @@ 406 match params with 407 | [] -> path 408 | [ param ] -> 409 let param = type_expr ~needs_parentheses:true param in 410 let args = 411 if Syntax.Type.parenthesize_constructor then 412 O.txt "(" ++ param ++ O.txt ")" 413 else param 414 in 415 Syntax.Type.handle_constructor_params path args 416 | params -> 417 let params = O.list params ~sep:(O.txt "," ++ O.sp) ~f:type_expr in 418 let params = 419 match delim with 420 | `parens -> enclose ~l:"(" params ~r:")" 421 | `brackets -> enclose ~l:"[" params ~r:"]" 422 in 423 Syntax.Type.handle_constructor_params path (O.box_hv params) 424 425 and tuple ?(needs_parentheses = false) ~boxed lst = 426 let opt_label = function 427 None -> O.noop 428 | Some lbl -> O.txt lbl ++ O.txt ":" ++ O.cut 429 in 430 let res = 431 O.box_hv_no_indent 432 (O.list lst ~sep:Syntax.Type.Tuple.element_separator 433 ~f:(fun (lbl, typ) -> 434 opt_label lbl ++ type_expr ~needs_parentheses:true typ)) 435 in 436 let lparen = if boxed then "(" else "#(" in 437 if Syntax.Type.Tuple.always_parenthesize || needs_parentheses || not boxed then 438 enclose ~l:lparen res ~r:")" 439 else res 440 441 and type_expr ?(needs_parentheses = false) (t : Odoc_model.Lang.TypeExpr.t) 442 = 443 let enclose_parens_if_needed res = 444 if needs_parentheses then enclose ~l:"(" res ~r:")" else res 445 in 446 match t with 447 | Var (s, None) -> type_var (Syntax.Type.var_prefix ^ s) 448 | Var (s, Some jkind) -> 449 enclose ~l:"(" ~r:")" 450 (type_var (Syntax.Type.var_prefix ^ s) 451 ++ O.txt " " ++ O.keyword ":" ++ O.txt " " ++ O.mode jkind) 452 | Any -> type_var Syntax.Type.any 453 | Alias (te, alias) -> 454 enclose_parens_if_needed 455 (type_expr ~needs_parentheses:true te 456 ++ O.txt " " ++ O.keyword "as" ++ O.txt " '" ++ O.txt alias) 457 | Arrow (None, src, dst, modes, ret_modes) -> 458 let mode_suffix = match modes with 459 | [] -> O.noop 460 | ms -> 461 O.txt " " ++ O.keyword "@" ++ O.txt " " 462 ++ mode_names ms 463 in 464 let dst_needs_parens = ret_modes <> [] && (match dst with Arrow _ -> true | _ -> false) in 465 let dst_rendered = type_expr ~needs_parentheses:dst_needs_parens dst in 466 let ret_suffix = match ret_modes with 467 | [] -> O.noop 468 | ms -> 469 O.txt " " ++ O.keyword "@" ++ O.txt " " 470 ++ mode_names ms 471 in 472 let res = 473 O.span 474 ((O.box_hv @@ type_expr ~needs_parentheses:true src ++ mode_suffix) 475 ++ O.txt " " ++ Syntax.Type.arrow) 476 ++ O.sp ++ dst_rendered ++ ret_suffix 477 in 478 if not needs_parentheses then res else enclose ~l:"(" res ~r:")" 479 | Arrow (Some (RawOptional _ as lbl), _src, dst, _modes, _ret_modes) -> 480 let res = 481 O.span 482 (O.box_hv 483 @@ label lbl ++ O.txt ":" 484 ++ tag "error" (O.txt "???") 485 ++ O.txt " " ++ Syntax.Type.arrow) 486 ++ O.sp ++ type_expr dst 487 in 488 if not needs_parentheses then res else enclose ~l:"(" res ~r:")" 489 | Arrow (Some lbl, src, dst, modes, ret_modes) -> 490 let mode_suffix = match modes with 491 | [] -> O.noop 492 | ms -> 493 O.txt " " ++ O.keyword "@" ++ O.txt " " 494 ++ mode_names ms 495 in 496 let dst_needs_parens = ret_modes <> [] && (match dst with Arrow _ -> true | _ -> false) in 497 let dst_rendered = type_expr ~needs_parentheses:dst_needs_parens dst in 498 let ret_suffix = match ret_modes with 499 | [] -> O.noop 500 | ms -> 501 O.txt " " ++ O.keyword "@" ++ O.txt " " 502 ++ mode_names ms 503 in 504 let res = 505 O.span 506 ((O.box_hv 507 @@ label lbl ++ O.txt ":" ++ O.cut 508 ++ (O.box_hv @@ type_expr ~needs_parentheses:true src) 509 ++ mode_suffix) 510 ++ O.txt " " ++ Syntax.Type.arrow) 511 ++ O.sp ++ dst_rendered ++ ret_suffix 512 in 513 if not needs_parentheses then res else enclose ~l:"(" res ~r:")" 514 | Tuple lst -> tuple ~needs_parentheses ~boxed:true lst 515 | Unboxed_tuple lst -> tuple ~needs_parentheses ~boxed:false lst 516 | Constr (path, args) -> 517 let link = Link.from_path (path :> Paths.Path.t) in 518 format_type_path ~delim:`parens args link 519 | Polymorphic_variant v -> te_variant v 520 | Object o -> te_object o 521 | Class (path, args) -> 522 format_type_path ~delim:`brackets args 523 (Link.from_path (path :> Paths.Path.t)) 524 | Poly (polyvars, t) -> 525 let render_var (name, jkind) = match jkind with 526 | None -> O.txt ("'" ^ name) 527 | Some jk -> O.txt ("('" ^ name ^ " : ") ++ O.mode jk ++ O.txt ")" 528 in 529 O.list polyvars ~sep:(O.txt " ") ~f:render_var ++ O.txt ". " 530 ++ type_expr t 531 | Quote t -> 532 O.span (O.txt "<[ " ++ O.box_hv (type_expr t) ++ O.txt " ]>") 533 | Splice t -> 534 O.span (O.txt "$" ++ type_expr ~needs_parentheses:true t) 535 | Package pkg -> 536 enclose ~l:"(" ~r:")" 537 (O.keyword "module" ++ O.txt " " 538 ++ Link.from_path (pkg.path :> Paths.Path.t) 539 ++ 540 match pkg.substitutions with 541 | [] -> O.noop 542 | fst :: lst -> 543 O.sp 544 ++ O.box_hv (O.keyword "with" ++ O.txt " " ++ package_subst fst) 545 ++ O.list lst ~f:(fun s -> 546 O.cut 547 ++ (O.box_hv 548 @@ O.txt " " ++ O.keyword "and" ++ O.txt " " 549 ++ package_subst s))) 550 551 and package_subst 552 ((frag_typ, te) : Paths.Fragment.Type.t * Odoc_model.Lang.TypeExpr.t) : 553 text = 554 let typath = Link.from_fragment (frag_typ :> Paths.Fragment.leaf) in 555 O.keyword "type" ++ O.txt " " ++ typath ++ O.txt " =" ++ O.sp 556 ++ type_expr te 557 end 558 559 open Type_expression 560 561 (* Also handles constructor declarations for exceptions and extensible 562 variants, and exposes a few helpers used in formatting classes and signature 563 constraints. *) 564 module Type_declaration : sig 565 val type_decl : 566 ?is_substitution:bool -> 567 Lang.Signature.recursive * Lang.TypeDecl.t -> 568 Item.t 569 570 val extension : Lang.Extension.t -> Item.t 571 572 val record : Lang.TypeDecl.Field.t list -> DocumentedSrc.one list 573 574 val unboxed_record : Lang.TypeDecl.UnboxedField.t list -> DocumentedSrc.one list 575 576 val exn : Lang.Exception.t -> Item.t 577 578 val format_params : 579 ?delim:[ `parens | `brackets ] -> Lang.TypeDecl.param list -> text 580 581 val format_manifest : 582 ?is_substitution:bool -> 583 ?compact_variants:bool -> 584 Lang.TypeDecl.Equation.t -> 585 text * bool 586 587 val format_constraints : (Lang.TypeExpr.t * Lang.TypeExpr.t) list -> text 588 end = struct 589 let record fields = 590 let field mutable_ id typ = 591 let url = Url.from_identifier ~stop_before:true id in 592 let name = Paths.Identifier.name id in 593 let attrs = [ "def"; "record"; Url.Anchor.string_of_kind url.kind ] in 594 let cell = 595 (* O.td ~a:[ O.a_class ["def"; kind ] ] 596 * [O.a ~a:[O.a_href ("#" ^ anchor); O.a_class ["anchor"]] [] 597 * ; *) 598 O.code 599 ((if mutable_ then O.keyword "mutable" ++ O.txt " " else O.noop) 600 ++ O.txt name 601 ++ O.txt Syntax.Type.annotation_separator 602 ++ type_expr typ 603 ++ O.txt Syntax.Type.Record.field_separator) 604 (* ] *) 605 in 606 (url, attrs, cell) 607 in 608 let rows = 609 fields 610 |> List.map (fun fld -> 611 let open Odoc_model.Lang.TypeDecl.Field in 612 let url, attrs, code = 613 field fld.mutable_ (fld.id :> Paths.Identifier.t) fld.type_ 614 in 615 let anchor = Some url in 616 let doc = fld.doc.elements in 617 let rhs = Comment.to_ir doc in 618 let doc = if not (Comment.has_doc doc) then [] else rhs in 619 let markers = Syntax.Comment.markers in 620 DocumentedSrc.Documented { anchor; attrs; code; doc; markers }) 621 in 622 let content = 623 O.documentedSrc (O.txt "{") @ rows @ O.documentedSrc (O.txt "}") 624 in 625 content 626 627 let unboxed_record fields = 628 let field mutable_ id typ = 629 let url = Url.from_identifier ~stop_before:true id in 630 let name = Paths.Identifier.name id in 631 let attrs = 632 [ "def"; "record"; Url.Anchor.string_of_kind url.kind ] 633 in 634 let cell = 635 (* O.td ~a:[ O.a_class ["def"; kind ] ] 636 * [O.a ~a:[O.a_href ("#" ^ anchor); O.a_class ["anchor"]] [] 637 * ; *) 638 O.code 639 ((if mutable_ then O.keyword "mutable" ++ O.txt " " else O.noop) 640 ++ O.txt name 641 ++ O.txt Syntax.Type.annotation_separator 642 ++ type_expr typ 643 ++ O.txt Syntax.Type.Record.field_separator) 644 (* ] *) 645 in 646 (url, attrs, cell) 647 in 648 let rows = 649 fields 650 |> List.map (fun fld -> 651 let open Odoc_model.Lang.TypeDecl.UnboxedField in 652 let url, attrs, code = 653 field fld.mutable_ (fld.id :> Paths.Identifier.t) fld.type_ 654 in 655 let anchor = Some url in 656 let doc = fld.doc.elements in 657 let rhs = Comment.to_ir doc in 658 let doc = if not (Comment.has_doc doc) then [] else rhs in 659 let markers = Syntax.Comment.markers in 660 DocumentedSrc.Documented { anchor; attrs; code; doc; markers }) 661 in 662 let content = 663 O.documentedSrc (O.txt "#{") @ rows @ O.documentedSrc (O.txt "}") 664 in 665 content 666 667 let constructor : 668 Paths.Identifier.t -> 669 Odoc_model.Lang.TypeDecl.Constructor.argument -> 670 Odoc_model.Lang.TypeExpr.t option -> 671 DocumentedSrc.t = 672 fun id args ret_type -> 673 let name = Paths.Identifier.name id in 674 let kind = Url.(kind id |> Anchor.string_of_kind) in 675 let cstr = tag kind (O.txt name) in 676 let is_gadt, ret_type = 677 match ret_type with 678 | None -> (false, O.noop) 679 | Some te -> 680 let constant = match args with Tuple [] -> true | _ -> false in 681 let ret_type = 682 O.txt " " 683 ++ (if constant then O.txt ":" else Syntax.Type.GADT.arrow) 684 ++ O.txt " " ++ type_expr te 685 in 686 (true, ret_type) 687 in 688 match args with 689 | Tuple [] -> O.documentedSrc (cstr ++ ret_type) 690 | Tuple lst -> 691 let params = 692 O.list lst ~sep:Syntax.Type.Tuple.element_separator 693 ~f:(type_expr ~needs_parentheses:is_gadt) 694 in 695 O.documentedSrc 696 (cstr 697 ++ (if Syntax.Type.Variant.parenthesize_params then 698 O.txt "(" ++ params ++ O.txt ")" 699 else 700 (if is_gadt then O.txt Syntax.Type.annotation_separator 701 else O.txt " " ++ O.keyword "of" ++ O.txt " ") 702 ++ params) 703 ++ ret_type) 704 | Record fields -> 705 if is_gadt then 706 O.documentedSrc (cstr ++ O.txt Syntax.Type.annotation_separator) 707 @ record fields @ O.documentedSrc ret_type 708 else 709 O.documentedSrc (cstr ++ O.txt " " ++ O.keyword "of" ++ O.txt " ") 710 @ record fields 711 712 let variant cstrs : DocumentedSrc.t = 713 let constructor id args res = 714 let url = Url.from_identifier ~stop_before:true id in 715 let attrs = [ "def"; "variant"; Url.Anchor.string_of_kind url.kind ] in 716 let content = 717 let doc = constructor id args res in 718 O.documentedSrc (O.txt "| ") @ doc 719 in 720 (url, attrs, content) 721 in 722 match cstrs with 723 | [] -> O.documentedSrc (O.txt "|") 724 | _ :: _ -> 725 let rows = 726 cstrs 727 |> List.map (fun cstr -> 728 let open Odoc_model.Lang.TypeDecl.Constructor in 729 let url, attrs, code = 730 constructor 731 (cstr.id :> Paths.Identifier.t) 732 cstr.args cstr.res 733 in 734 let anchor = Some url in 735 let doc = cstr.doc.elements in 736 let rhs = Comment.to_ir doc in 737 let doc = if not (Comment.has_doc doc) then [] else rhs in 738 let markers = Syntax.Comment.markers in 739 DocumentedSrc.Nested { anchor; attrs; code; doc; markers }) 740 in 741 rows 742 743 let extension_constructor (t : Odoc_model.Lang.Extension.Constructor.t) = 744 let id = (t.id :> Paths.Identifier.t) in 745 let url = Url.from_identifier ~stop_before:true id in 746 let anchor = Some url in 747 let attrs = [ "def"; "variant"; Url.Anchor.string_of_kind url.kind ] in 748 let code = O.documentedSrc (O.txt "| ") @ constructor id t.args t.res in 749 let doc = Comment.to_ir t.doc.elements in 750 let markers = Syntax.Comment.markers in 751 DocumentedSrc.Nested { anchor; attrs; code; doc; markers } 752 753 let extension (t : Odoc_model.Lang.Extension.t) = 754 let prefix = 755 O.keyword "type" ++ O.txt " " 756 ++ Link.from_path (t.type_path :> Paths.Path.t) 757 ++ O.txt " +=" ++ O.sp 758 ++ 759 if t.private_ then O.keyword Syntax.Type.private_keyword ++ O.sp 760 else O.noop 761 in 762 let content = 763 O.documentedSrc prefix 764 @ List.map extension_constructor t.constructors 765 @ O.documentedSrc 766 (if Syntax.Type.type_def_semicolon then O.txt ";" else O.noop) 767 in 768 let attr = [ "type"; "extension" ] in 769 let anchor = Some (Url.Anchor.extension_decl t) in 770 let doc = Comment.to_ir t.doc.elements in 771 let source_anchor = 772 (* Take the anchor from the first constructor only for consistency with 773 regular variants. *) 774 match t.constructors with 775 | hd :: _ -> source_anchor hd.source_loc 776 | [] -> None 777 in 778 Item.Declaration { attr; anchor; doc; content; source_anchor } 779 780 let exn (t : Odoc_model.Lang.Exception.t) = 781 let cstr = constructor (t.id :> Paths.Identifier.t) t.args t.res in 782 let content = 783 O.documentedSrc (O.keyword "exception" ++ O.txt " ") 784 @ cstr 785 @ O.documentedSrc 786 (if Syntax.Type.Exception.semicolon then O.txt ";" else O.noop) 787 in 788 let attr = [ "exception" ] in 789 let anchor = path_to_id t.id in 790 let doc = Comment.to_ir t.doc.elements in 791 let source_anchor = source_anchor t.source_loc in 792 Item.Declaration { attr; anchor; doc; content; source_anchor } 793 794 let polymorphic_variant ~type_ident 795 (t : Odoc_model.Lang.TypeExpr.Polymorphic_variant.t) = 796 let row item = 797 let kind_approx, cstr, doc = 798 match item with 799 | Odoc_model.Lang.TypeExpr.Polymorphic_variant.Type te -> 800 ("unknown", O.documentedSrc (type_expr te), None) 801 | Constructor { constant; name; arguments; doc; _ } -> ( 802 let cstr = "`" ^ name in 803 ( "constructor", 804 (match arguments with 805 | [] -> O.documentedSrc (O.txt cstr) 806 | _ -> 807 (* Multiple arguments in a polymorphic variant constructor correspond 808 to a conjunction of types, not a product: [`Lbl int&float]. 809 If constant is [true], the conjunction starts with an empty type, 810 for instance [`Lbl &int]. 811 *) 812 let wrapped_type_expr = 813 (* type conjunction in Reason is printed as `Lbl (t1)&(t2)` *) 814 if Syntax.Type.Variant.parenthesize_params then fun x -> 815 O.txt "(" ++ type_expr x ++ O.txt ")" 816 else fun x -> type_expr x 817 in 818 let params = 819 O.box_hv 820 @@ O.list arguments 821 ~sep:(O.txt " &" ++ O.sp) 822 ~f:wrapped_type_expr 823 in 824 let params = 825 if constant then O.txt "& " ++ params else params 826 in 827 O.documentedSrc 828 (O.txt cstr 829 ++ 830 if Syntax.Type.Variant.parenthesize_params then params 831 else O.txt " " ++ O.keyword "of" ++ O.sp ++ params)), 832 match doc with 833 | { elements = []; _ } -> None 834 | _ -> Some (Comment.to_ir doc.elements) )) 835 in 836 let markers = Syntax.Comment.markers in 837 try 838 let url = Url.Anchor.polymorphic_variant ~type_ident item in 839 let attrs = 840 [ "def"; "variant"; Url.Anchor.string_of_kind url.kind ] 841 in 842 let anchor = Some url in 843 let code = O.documentedSrc (O.txt "| ") @ cstr in 844 let doc = match doc with None -> [] | Some doc -> doc in 845 DocumentedSrc.Nested { attrs; anchor; code; doc; markers } 846 with Failure s -> 847 Printf.eprintf "ERROR: %s\n%!" s; 848 let code = O.documentedSrc (O.txt "| ") @ cstr in 849 let attrs = [ "def"; kind_approx ] in 850 let doc = [] in 851 let anchor = None in 852 DocumentedSrc.Nested { attrs; anchor; code; doc; markers } 853 in 854 let variants = List.map row t.elements in 855 let intro, ending = 856 match t.kind with 857 | Fixed -> (O.documentedSrc (O.txt "[ "), O.documentedSrc (O.txt " ]")) 858 | Open -> (O.documentedSrc (O.txt "[> "), O.documentedSrc (O.txt " ]")) 859 | Closed [] -> 860 (O.documentedSrc (O.txt "[< "), O.documentedSrc (O.txt " ]")) 861 | Closed lst -> 862 let constrs = String.concat ~sep:" " lst in 863 ( O.documentedSrc (O.txt "[< "), 864 O.documentedSrc (O.txt (" " ^ constrs ^ " ]")) ) 865 in 866 intro @ variants @ ending 867 868 let format_params : 869 'row. 870 ?delim:[ `parens | `brackets ] -> 871 Odoc_model.Lang.TypeDecl.param list -> 872 text = 873 fun ?(delim = `parens) params -> 874 let format_param { Odoc_model.Lang.TypeDecl.desc; variance; injectivity } 875 = 876 let desc = 877 match desc with 878 | Odoc_model.Lang.TypeDecl.Any -> O.txt "_" 879 | Var (s, None) -> O.txt ("'" ^ s) 880 | Var (s, Some jkind) -> 881 O.txt ("('" ^ s ^ " : ") ++ O.mode jkind ++ O.txt ")" 882 in 883 let var_desc = 884 match variance with 885 | None -> desc 886 | Some Odoc_model.Lang.TypeDecl.Pos -> O.txt "+" ++ desc 887 | Some Odoc_model.Lang.TypeDecl.Neg -> O.txt "-" ++ desc 888 | Some Odoc_model.Lang.TypeDecl.Bivariant -> O.txt "+" ++ O.txt "-" ++ desc 889 in 890 if injectivity then O.txt "!" ++ var_desc else var_desc 891 in 892 match params with 893 | [] -> O.noop 894 | [ x ] -> Syntax.Type.handle_format_params (format_param x) 895 | lst -> 896 O.txt (match delim with `parens -> "(" | `brackets -> "[") 897 ++ O.list lst ~sep:(O.txt ", ") ~f:format_param 898 ++ O.txt (match delim with `parens -> ")" | `brackets -> "]") 899 900 let format_constraints constraints = 901 O.list constraints ~f:(fun (t1, t2) -> 902 O.sp 903 ++ (O.box_hv 904 @@ O.keyword "constraint" ++ O.sp 905 ++ O.box_hv_no_indent (type_expr t1) 906 ++ O.txt " =" ++ O.sp 907 ++ O.box_hv_no_indent (type_expr t2))) 908 909 let format_manifest : 910 'inner_row 'outer_row. 911 ?is_substitution:bool -> 912 ?compact_variants:bool -> 913 Odoc_model.Lang.TypeDecl.Equation.t -> 914 text * bool = 915 fun ?(is_substitution = false) ?(compact_variants = true) equation -> 916 let _ = compact_variants in 917 (* TODO *) 918 let private_ = equation.private_ in 919 match equation.manifest with 920 | None -> (O.noop, private_) 921 | Some t -> 922 let manifest = 923 O.txt (if is_substitution then " :=" else " =") 924 ++ O.sp 925 ++ (if private_ then 926 O.keyword Syntax.Type.private_keyword ++ O.txt " " 927 else O.noop) 928 ++ type_expr t 929 in 930 (manifest, false) 931 932 let type_decl ?(is_substitution = false) 933 ((recursive, t) : Lang.Signature.recursive * Lang.TypeDecl.t) = 934 let keyword' = 935 match recursive with 936 | Ordinary | Rec -> O.keyword "type" 937 | And -> O.keyword "and" 938 | Nonrec -> O.keyword "type" ++ O.txt " " ++ O.keyword "nonrec" 939 in 940 let tyname = Paths.Identifier.name t.id in 941 let tconstr = 942 match t.equation.params with 943 | [] -> O.txt tyname 944 | l -> 945 let params = format_params l in 946 Syntax.Type.handle_constructor_params (O.txt tyname) params 947 in 948 let intro = keyword' ++ O.txt " " ++ tconstr in 949 let constraints = format_constraints t.equation.constraints in 950 let manifest, need_private, long_prefix = 951 match t.equation.manifest with 952 | Some (Odoc_model.Lang.TypeExpr.Polymorphic_variant variant) -> 953 let code = 954 polymorphic_variant 955 ~type_ident:(t.id :> Paths.Identifier.t) 956 variant 957 in 958 let manifest = 959 O.documentedSrc 960 (O.ignore intro 961 ++ O.txt (if is_substitution then " :=" else " =") 962 ++ O.sp 963 ++ 964 if t.equation.private_ then 965 O.keyword Syntax.Type.private_keyword ++ O.txt " " 966 else O.noop) 967 @ code 968 in 969 (manifest, false, O.noop) 970 | _ -> 971 let manifest, need_private = 972 format_manifest ~is_substitution t.equation 973 in 974 let text = O.ignore intro ++ manifest in 975 (O.documentedSrc @@ text, need_private, text) 976 in 977 let representation = 978 match t.representation with 979 | None -> [] 980 | Some repr -> 981 let content = 982 match repr with 983 | Extensible -> O.documentedSrc (O.txt "..") 984 | Variant cstrs -> variant cstrs 985 | Record fields -> record fields 986 | Record_unboxed_product fields -> unboxed_record fields 987 in 988 if List.length content > 0 then 989 O.documentedSrc 990 (O.ignore long_prefix ++ O.txt " =" ++ O.sp 991 ++ 992 if need_private then 993 O.keyword Syntax.Type.private_keyword ++ O.txt " " 994 else O.noop) 995 @ content 996 else [] 997 in 998 let content = 999 O.documentedSrc intro @ manifest @ representation 1000 @ O.documentedSrc constraints 1001 @ O.documentedSrc 1002 (if Syntax.Type.type_def_semicolon then O.txt ";" else O.noop) 1003 in 1004 let attr = "type" :: (if is_substitution then [ "subst" ] else []) in 1005 let anchor = path_to_id t.id in 1006 let doc = Comment.to_ir t.doc.elements in 1007 let source_anchor = source_anchor t.source_loc in 1008 Item.Declaration { attr; anchor; doc; content; source_anchor } 1009 end 1010 1011 open Type_declaration 1012 1013 module Value : sig 1014 val value : Lang.Value.t -> Item.t 1015 end = struct 1016 let value (t : Odoc_model.Lang.Value.t) = 1017 let extra_attr, semicolon = 1018 match t.value with 1019 | Abstract -> ([], Syntax.Value.semicolon) 1020 | External _ -> ([ "external" ], Syntax.Type.External.semicolon) 1021 in 1022 let name = Paths.Identifier.name t.id in 1023 let content = 1024 O.documentedSrc 1025 (O.box_hv 1026 @@ O.keyword Syntax.Value.variable_keyword 1027 ++ O.txt " " ++ O.txt name 1028 ++ O.txt Syntax.Type.annotation_separator 1029 ++ O.cut ++ type_expr t.type_ 1030 ++ (match t.modalities with 1031 | [] -> O.noop 1032 | ms -> O.txt " " ++ O.keyword "@@" ++ O.txt " " 1033 ++ mode_names ms) 1034 ++ if semicolon then O.txt ";" else O.noop) 1035 in 1036 let attr = [ "value" ] @ extra_attr in 1037 let anchor = path_to_id t.id in 1038 let doc = Comment.to_ir t.doc.elements in 1039 let source_anchor = source_anchor t.source_loc in 1040 Item.Declaration { attr; anchor; doc; content; source_anchor } 1041 end 1042 1043 open Value 1044 1045 (* This chunk of code is responsible for sectioning list of items 1046 according to headings by extracting headings as Items. 1047 1048 TODO: This sectioning would be better done as a pass on the model directly. 1049 *) 1050 module Sectioning : sig 1051 open Odoc_model 1052 1053 val comment_items : Comment.elements -> Item.t list 1054 1055 val docs : Comment.elements -> Item.t list * Item.t list 1056 end = struct 1057 let take_until_heading_or_end (docs : Odoc_model.Comment.elements) = 1058 let content, _, rest = 1059 Doctree.Take.until docs ~classify:(fun b -> 1060 match b.Location.value with 1061 | `Heading _ -> Stop_and_keep 1062 | #Odoc_model.Comment.attached_block_element as doc -> 1063 let content = Comment.attached_block_element doc in 1064 Accum content) 1065 in 1066 (content, rest) 1067 1068 let comment_items (input0 : Odoc_model.Comment.elements) = 1069 let rec loop input_comment acc = 1070 match input_comment with 1071 | [] -> List.rev acc 1072 | element :: input_comment -> ( 1073 match element.Location.value with 1074 | `Heading h -> 1075 let item = Comment.heading h in 1076 loop input_comment (item :: acc) 1077 | _ -> 1078 let content, input_comment = 1079 take_until_heading_or_end (element :: input_comment) 1080 in 1081 let item = Item.Text content in 1082 loop input_comment (item :: acc)) 1083 in 1084 loop input0 [] 1085 1086 (* For doc pages, we want the header to contain everything until 1087 the first heading, then everything before the next heading which 1088 is either lower, or a section. 1089 *) 1090 let docs input_comment = 1091 let items = comment_items input_comment in 1092 let until_first_heading, o, items = 1093 Doctree.Take.until items ~classify:(function 1094 | Item.Heading h as i -> Stop_and_accum ([ i ], Some h.level) 1095 | i -> Accum [ i ]) 1096 in 1097 match o with 1098 | None -> (until_first_heading, items) 1099 | Some level -> 1100 let max_level = if level = 1 then 2 else level in 1101 let before_second_heading, _, items = 1102 Doctree.Take.until items ~classify:(function 1103 | Item.Heading h when h.level >= max_level -> Stop_and_keep 1104 | i -> Accum [ i ]) 1105 in 1106 let header = until_first_heading @ before_second_heading in 1107 (header, items) 1108 end 1109 1110 module Class : sig 1111 val class_ : Lang.Class.t -> Item.t 1112 1113 val class_type : Lang.ClassType.t -> Item.t 1114 end = struct 1115 let class_type_expr (cte : Odoc_model.Lang.ClassType.expr) = 1116 match cte with 1117 | Constr (path, args) -> 1118 let link = Link.from_path (path :> Paths.Path.t) in 1119 format_type_path ~delim:`brackets args link 1120 | Signature _ -> 1121 Syntax.Class.open_tag ++ O.txt " ... " ++ Syntax.Class.close_tag 1122 1123 let method_ (t : Odoc_model.Lang.Method.t) = 1124 let name = Paths.Identifier.name t.id in 1125 let virtual_ = 1126 if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop 1127 in 1128 let private_ = 1129 if t.private_ then O.keyword "private" ++ O.txt " " else O.noop 1130 in 1131 let content = 1132 O.documentedSrc 1133 (O.keyword "method" ++ O.txt " " ++ private_ ++ virtual_ ++ O.txt name 1134 ++ O.txt Syntax.Type.annotation_separator 1135 ++ type_expr t.type_) 1136 in 1137 let attr = [ "method" ] in 1138 let anchor = path_to_id t.id in 1139 let doc = Comment.to_ir t.doc.elements in 1140 Item.Declaration { attr; anchor; doc; content; source_anchor = None } 1141 1142 let instance_variable (t : Odoc_model.Lang.InstanceVariable.t) = 1143 let name = Paths.Identifier.name t.id in 1144 let virtual_ = 1145 if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop 1146 in 1147 let mutable_ = 1148 if t.mutable_ then O.keyword "mutable" ++ O.txt " " else O.noop 1149 in 1150 let content = 1151 O.documentedSrc 1152 (O.keyword "val" ++ O.txt " " ++ mutable_ ++ virtual_ ++ O.txt name 1153 ++ O.txt Syntax.Type.annotation_separator 1154 ++ type_expr t.type_) 1155 in 1156 let attr = [ "value"; "instance-variable" ] in 1157 let anchor = path_to_id t.id in 1158 let doc = Comment.to_ir t.doc.elements in 1159 Item.Declaration { attr; anchor; doc; content; source_anchor = None } 1160 1161 let inherit_ (ih : Lang.ClassSignature.Inherit.t) = 1162 let cte = 1163 match ih.expr with 1164 | Signature _ -> assert false (* Bold. *) 1165 | cty -> cty 1166 in 1167 let content = 1168 O.documentedSrc (O.keyword "inherit" ++ O.txt " " ++ class_type_expr cte) 1169 in 1170 let attr = [ "inherit" ] in 1171 let anchor = None in 1172 let doc = Comment.to_ir ih.doc.elements in 1173 Item.Declaration { attr; anchor; doc; content; source_anchor = None } 1174 1175 let constraint_ (cst : Lang.ClassSignature.Constraint.t) = 1176 let content = 1177 O.documentedSrc (format_constraints [ (cst.left, cst.right) ]) 1178 in 1179 let attr = [] in 1180 let anchor = None in 1181 let doc = Comment.to_ir cst.doc.elements in 1182 Item.Declaration { attr; anchor; doc; content; source_anchor = None } 1183 1184 let class_signature (c : Lang.ClassSignature.t) = 1185 let rec loop l acc_items = 1186 match l with 1187 | [] -> List.rev acc_items 1188 | item :: rest -> ( 1189 let continue item = loop rest (item :: acc_items) in 1190 match (item : Lang.ClassSignature.item) with 1191 | Inherit cty -> continue @@ inherit_ cty 1192 | Method m -> continue @@ method_ m 1193 | InstanceVariable v -> continue @@ instance_variable v 1194 | Constraint cst -> continue @@ constraint_ cst 1195 | Comment `Stop -> 1196 let rest = 1197 List.skip_until rest ~p:(function 1198 | Lang.ClassSignature.Comment `Stop -> true 1199 | _ -> false) 1200 in 1201 loop rest acc_items 1202 | Comment (`Docs c) -> 1203 let items = Sectioning.comment_items c.elements in 1204 loop rest (List.rev_append items acc_items)) 1205 in 1206 (* FIXME: use [t.self] *) 1207 (c.doc.elements, loop c.items []) 1208 1209 let rec class_decl (cd : Odoc_model.Lang.Class.decl) = 1210 match cd with 1211 | ClassType expr -> class_type_expr expr 1212 (* TODO: factorize the following with [type_expr] *) 1213 | Arrow (None, src, dst) -> 1214 O.span 1215 (type_expr ~needs_parentheses:true src 1216 ++ O.txt " " ++ Syntax.Type.arrow) 1217 ++ O.txt " " ++ class_decl dst 1218 | Arrow (Some (RawOptional _ as lbl), _src, dst) -> 1219 O.span 1220 (O.box_hv 1221 @@ label lbl ++ O.txt ":" 1222 ++ tag "error" (O.txt "???") 1223 ++ O.txt " " ++ Syntax.Type.arrow) 1224 ++ O.sp ++ class_decl dst 1225 | Arrow (Some lbl, src, dst) -> 1226 O.span 1227 (label lbl ++ O.txt ":" 1228 ++ type_expr ~needs_parentheses:true src 1229 ++ O.txt " " ++ Syntax.Type.arrow) 1230 ++ O.txt " " ++ class_decl dst 1231 1232 let class_ (t : Odoc_model.Lang.Class.t) = 1233 let name = Paths.Identifier.name t.id in 1234 let params = 1235 match t.params with 1236 | [] -> O.noop 1237 | _ :: _ as params -> format_params ~delim:`brackets params ++ O.txt " " 1238 in 1239 let virtual_ = 1240 if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop 1241 in 1242 1243 let source_anchor = source_anchor t.source_loc in 1244 let cname, expansion, expansion_doc = 1245 match t.expansion with 1246 | None -> (O.documentedSrc @@ O.txt name, None, None) 1247 | Some csig -> 1248 let expansion_doc, items = class_signature csig in 1249 let url = Url.Path.from_identifier t.id in 1250 let page = 1251 make_expansion_page ~source_anchor url 1252 [ t.doc.elements; expansion_doc ] 1253 items 1254 in 1255 ( O.documentedSrc @@ path url [ inline @@ Text name ], 1256 Some page, 1257 Some expansion_doc ) 1258 in 1259 let summary = 1260 O.txt Syntax.Type.annotation_separator ++ class_decl t.type_ 1261 in 1262 let cd = 1263 attach_expansion 1264 (Syntax.Type.annotation_separator, "object", "end") 1265 expansion summary 1266 in 1267 let content = 1268 O.documentedSrc (O.keyword "class" ++ O.txt " " ++ virtual_ ++ params) 1269 @ cname @ cd 1270 in 1271 let attr = [ "class" ] in 1272 let anchor = path_to_id t.id in 1273 let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in 1274 Item.Declaration { attr; anchor; doc; content; source_anchor } 1275 1276 let class_type (t : Odoc_model.Lang.ClassType.t) = 1277 let name = Paths.Identifier.name t.id in 1278 let params = format_params ~delim:`brackets t.params in 1279 let virtual_ = 1280 if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop 1281 in 1282 let source_anchor = source_anchor t.source_loc in 1283 let cname, expansion, expansion_doc = 1284 match t.expansion with 1285 | None -> (O.documentedSrc @@ O.txt name, None, None) 1286 | Some csig -> 1287 let url = Url.Path.from_identifier t.id in 1288 let expansion_doc, items = class_signature csig in 1289 let page = 1290 make_expansion_page ~source_anchor url 1291 [ t.doc.elements; expansion_doc ] 1292 items 1293 in 1294 ( O.documentedSrc @@ path url [ inline @@ Text name ], 1295 Some page, 1296 Some expansion_doc ) 1297 in 1298 let summary = O.txt " = " ++ class_type_expr t.expr in 1299 let expr = attach_expansion (" = ", "object", "end") expansion summary in 1300 let content = 1301 O.documentedSrc 1302 (O.keyword "class" ++ O.txt " " ++ O.keyword "type" ++ O.txt " " 1303 ++ virtual_ ++ params ++ O.txt " ") 1304 @ cname @ expr 1305 in 1306 let attr = [ "class-type" ] in 1307 let anchor = path_to_id t.id in 1308 let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in 1309 Item.Declaration { attr; anchor; doc; content; source_anchor } 1310 end 1311 1312 open Class 1313 1314 module Module : sig 1315 val signature : Lang.Signature.t -> Comment.Comment.elements * Item.t list 1316 (** Returns [header_doc, content]. *) 1317 end = struct 1318 let internal_module m = 1319 let open Lang.Module in 1320 match m.id.iv with 1321 | `Module (_, name) when ModuleName.is_hidden name -> true 1322 | _ -> false 1323 1324 let internal_type t = 1325 let open Lang.TypeDecl in 1326 match t.id.iv with 1327 | `Type (_, name) when TypeName.is_hidden name -> true 1328 | _ -> false 1329 1330 let internal_value v = 1331 let open Lang.Value in 1332 match v.id.iv with 1333 | `Value (_, name) when ValueName.is_hidden name -> true 1334 | _ -> false 1335 1336 let internal_module_type t = 1337 let open Lang.ModuleType in 1338 match t.id.iv with 1339 | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true 1340 | _ -> false 1341 1342 let internal_module_substitution t = 1343 let open Lang.ModuleSubstitution in 1344 match t.id.iv with 1345 | `Module (_, name) when ModuleName.is_hidden name -> true 1346 | _ -> false 1347 1348 let internal_module_type_substitution t = 1349 let open Lang.ModuleTypeSubstitution in 1350 match t.id.iv with 1351 | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true 1352 | _ -> false 1353 1354 let rec signature (s : Lang.Signature.t) = 1355 let rec loop l acc_items = 1356 match l with 1357 | [] -> List.rev acc_items 1358 | item :: rest -> ( 1359 let continue (item : Item.t) = loop rest (item :: acc_items) in 1360 match (item : Lang.Signature.item) with 1361 | Module (_, m) when internal_module m -> loop rest acc_items 1362 | Type (_, t) when internal_type t -> loop rest acc_items 1363 | Value v when internal_value v -> loop rest acc_items 1364 | ModuleType m when internal_module_type m -> loop rest acc_items 1365 | ModuleSubstitution m when internal_module_substitution m -> 1366 loop rest acc_items 1367 | ModuleTypeSubstitution m when internal_module_type_substitution m 1368 -> 1369 loop rest acc_items 1370 | ModuleTypeSubstitution m -> continue @@ module_type_substitution m 1371 | Module (_, m) -> continue @@ module_ m 1372 | ModuleType m -> continue @@ module_type m 1373 | Class (_, c) -> continue @@ class_ c 1374 | ClassType (_, c) -> continue @@ class_type c 1375 | Include m -> continue @@ include_ m 1376 | ModuleSubstitution m -> continue @@ module_substitution m 1377 | TypeSubstitution t -> 1378 continue @@ type_decl ~is_substitution:true (Ordinary, t) 1379 | Type (r, t) -> continue @@ type_decl (r, t) 1380 | TypExt e -> continue @@ extension e 1381 | Exception e -> continue @@ exn e 1382 | Value v -> continue @@ value v 1383 | Open o -> 1384 let items = Sectioning.comment_items o.doc.elements in 1385 loop rest (List.rev_append items acc_items) 1386 | Comment `Stop -> 1387 let rest = 1388 List.skip_until rest ~p:(function 1389 | Lang.Signature.Comment `Stop -> true 1390 | _ -> false) 1391 in 1392 loop rest acc_items 1393 | Comment (`Docs c) -> 1394 let items = Sectioning.comment_items c.elements in 1395 loop rest (List.rev_append items acc_items)) 1396 in 1397 ((Lang.extract_signature_doc s).elements, loop s.items []) 1398 1399 and functor_parameter : 1400 Odoc_model.Lang.FunctorParameter.parameter -> DocumentedSrc.t = 1401 fun arg -> 1402 let open Odoc_model.Lang.FunctorParameter in 1403 let name = Paths.Identifier.name arg.id in 1404 let render_ty = arg.expr in 1405 let modtyp = 1406 mty_in_decl (arg.id :> Paths.Identifier.Signature.t) render_ty 1407 in 1408 let modname, mod_decl = 1409 match expansion_of_module_type_expr arg.expr with 1410 | None -> 1411 let modname = O.txt (Paths.Identifier.name arg.id) in 1412 (modname, O.documentedSrc modtyp) 1413 | Some (expansion_doc, items) -> 1414 let url = Url.Path.from_identifier arg.id in 1415 let modname = path url [ inline @@ Text name ] in 1416 let type_with_expansion = 1417 let content = 1418 make_expansion_page ~source_anchor:None url [ expansion_doc ] 1419 items 1420 in 1421 let summary = O.render modtyp in 1422 let status = `Default in 1423 let expansion = 1424 O.documentedSrc 1425 (O.txt Syntax.Type.annotation_separator ++ O.keyword "sig") 1426 @ DocumentedSrc.[ Subpage { content; status } ] 1427 @ O.documentedSrc (O.keyword "end") 1428 in 1429 DocumentedSrc. 1430 [ 1431 Alternative 1432 (Expansion { status = `Default; summary; url; expansion }); 1433 ] 1434 in 1435 (modname, type_with_expansion) 1436 in 1437 O.documentedSrc (O.keyword "module" ++ O.txt " ") 1438 @ O.documentedSrc modname @ mod_decl 1439 1440 and module_substitution (t : Odoc_model.Lang.ModuleSubstitution.t) = 1441 let name = Paths.Identifier.name t.id in 1442 let path = Link.from_path (t.manifest :> Paths.Path.t) in 1443 let content = 1444 O.documentedSrc 1445 (O.keyword "module" ++ O.txt " " ++ O.txt name ++ O.txt " :=" ++ O.sp 1446 ++ path) 1447 in 1448 let attr = [ "module-substitution" ] in 1449 let anchor = path_to_id t.id in 1450 let doc = Comment.to_ir t.doc.elements in 1451 Item.Declaration { attr; anchor; doc; content; source_anchor = None } 1452 1453 and module_type_substitution (t : Odoc_model.Lang.ModuleTypeSubstitution.t) 1454 = 1455 let prefix = 1456 O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " " 1457 in 1458 let source_anchor = None in 1459 let modname = Paths.Identifier.name t.id in 1460 let modname, expansion_doc, mty = 1461 module_type_manifest ~subst:true ~source_anchor modname t.id 1462 t.doc.elements (Some t.manifest) prefix 1463 in 1464 let content = 1465 O.documentedSrc (prefix ++ modname) 1466 @ mty 1467 @ O.documentedSrc 1468 (if Syntax.Mod.close_tag_semicolon then O.txt ";" else O.noop) 1469 in 1470 let attr = [ "module-type" ] in 1471 let anchor = path_to_id t.id in 1472 let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in 1473 Item.Declaration { attr; anchor; doc; content; source_anchor } 1474 1475 and simple_expansion : 1476 Odoc_model.Lang.ModuleType.simple_expansion -> 1477 Comment.Comment.elements * Item.t list = 1478 fun t -> 1479 let rec extract_functor_params 1480 (f : Odoc_model.Lang.ModuleType.simple_expansion) = 1481 match f with 1482 | Signature sg -> (None, sg) 1483 | Functor (p, expansion) -> 1484 let add_to params = 1485 match p with Unit -> params | Named p -> p :: params 1486 in 1487 let params, sg = extract_functor_params expansion in 1488 let params = match params with None -> [] | Some p -> p in 1489 (Some (add_to params), sg) 1490 in 1491 match extract_functor_params t with 1492 | None, sg -> signature sg 1493 | Some params, sg -> 1494 let sg_doc, content = signature sg in 1495 let params = 1496 let decl_of_arg arg = 1497 let content = functor_parameter arg in 1498 let attr = [ "parameter" ] in 1499 let anchor = 1500 Some (Url.Anchor.from_identifier (arg.id :> Paths.Identifier.t)) 1501 in 1502 let doc = [] in 1503 [ 1504 Item.Declaration 1505 { content; anchor; attr; doc; source_anchor = None }; 1506 ] 1507 in 1508 List.concat_map decl_of_arg params 1509 in 1510 let prelude = mk_heading ~label:"parameters" "Parameters" :: params 1511 and content = mk_heading ~label:"signature" "Signature" :: content in 1512 (sg_doc, prelude @ content) 1513 1514 and expansion_of_module_type_expr : 1515 Odoc_model.Lang.ModuleType.expr -> 1516 (Comment.Comment.elements * Item.t list) option = 1517 fun t -> 1518 let rec simple_expansion_of (t : Odoc_model.Lang.ModuleType.expr) = 1519 match t with 1520 | Path { p_expansion = None; _ } 1521 | TypeOf { t_expansion = None; _ } 1522 | With { w_expansion = None; _ } 1523 | Strengthen { s_expansion = None; _ } -> 1524 None 1525 | Path { p_expansion = Some e; _ } 1526 | TypeOf { t_expansion = Some e; _ } 1527 | With { w_expansion = Some e; _ } 1528 | Strengthen { s_expansion = Some e; _ } -> 1529 Some e 1530 | Signature sg -> Some (Signature sg) 1531 | Functor (f_parameter, e) -> ( 1532 match simple_expansion_of e with 1533 | Some e -> Some (Functor (f_parameter, e)) 1534 | None -> None) 1535 in 1536 match simple_expansion_of t with 1537 | None -> None 1538 | Some e -> Some (simple_expansion e) 1539 1540 and module_ : Odoc_model.Lang.Module.t -> Item.t = 1541 fun t -> 1542 let modname = Paths.Identifier.name t.id in 1543 let expansion = 1544 match t.type_ with 1545 | Alias (_, Some e) -> Some (simple_expansion e) 1546 | Alias (_, None) -> None 1547 | ModuleType e -> expansion_of_module_type_expr e 1548 in 1549 let source_anchor = source_anchor t.source_loc in 1550 let modname, status, expansion, expansion_doc = 1551 match expansion with 1552 | None -> (O.txt modname, `Default, None, None) 1553 | Some (expansion_doc, items) -> 1554 let status = 1555 match t.type_ with 1556 | ModuleType (Signature _) -> `Inline 1557 | _ -> `Default 1558 in 1559 let url = Url.Path.from_identifier t.id in 1560 let link = path url [ inline @@ Text modname ] in 1561 let page = 1562 make_expansion_page ~source_anchor url 1563 [ t.doc.elements; expansion_doc ] 1564 items 1565 in 1566 (link, status, Some page, Some expansion_doc) 1567 in 1568 let intro = O.keyword "module" ++ O.txt " " ++ modname in 1569 let summary = O.ignore intro ++ mdexpr_in_decl t.id t.type_ in 1570 let modexpr = 1571 attach_expansion ~status 1572 (Syntax.Type.annotation_separator, "sig", "end") 1573 expansion summary 1574 in 1575 let content = 1576 O.documentedSrc intro @ modexpr 1577 @ O.documentedSrc 1578 (if Syntax.Mod.close_tag_semicolon then O.txt ";" else O.noop) 1579 in 1580 let attr = [ "module" ] in 1581 let anchor = path_to_id t.id in 1582 let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in 1583 Item.Declaration { attr; anchor; doc; content; source_anchor } 1584 1585 and simple_expansion_in_decl (base : Paths.Identifier.Module.t) se = 1586 let rec ty_of_se : 1587 Lang.ModuleType.simple_expansion -> Lang.ModuleType.expr = function 1588 | Signature sg -> Signature sg 1589 | Functor (arg, sg) -> Functor (arg, ty_of_se sg) 1590 in 1591 mty_in_decl (base :> Paths.Identifier.Signature.t) (ty_of_se se) 1592 1593 and mdexpr_in_decl (base : Paths.Identifier.Module.t) md = 1594 let sig_dotdotdot = 1595 O.txt Syntax.Type.annotation_separator 1596 ++ O.cut ++ Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag 1597 in 1598 match md with 1599 | Alias (_, Some se) -> simple_expansion_in_decl base se 1600 | Alias (p, _) when not Paths.Path.(is_hidden (p :> t)) -> 1601 O.txt " =" ++ O.sp ++ mdexpr md 1602 | Alias _ -> sig_dotdotdot 1603 | ModuleType mt -> mty_in_decl (base :> Paths.Identifier.Signature.t) mt 1604 1605 and mdexpr : Odoc_model.Lang.Module.decl -> text = function 1606 | Alias (mod_path, _) -> Link.from_path (mod_path :> Paths.Path.t) 1607 | ModuleType mt -> mty mt 1608 1609 and module_type_manifest ~subst ~source_anchor modname id doc manifest 1610 prefix = 1611 let expansion = 1612 match manifest with 1613 | None -> None 1614 | Some e -> expansion_of_module_type_expr e 1615 in 1616 let modname, expansion, expansion_doc = 1617 match expansion with 1618 | None -> (O.txt modname, None, None) 1619 | Some (expansion_doc, items) -> 1620 let url = Url.Path.from_identifier id in 1621 let link = path url [ inline @@ Text modname ] in 1622 let page = 1623 make_expansion_page ~source_anchor url [ doc; expansion_doc ] 1624 items 1625 in 1626 (link, Some page, Some expansion_doc) 1627 in 1628 let summary = 1629 match manifest with 1630 | None -> O.noop 1631 | Some expr -> 1632 O.ignore (prefix ++ modname) 1633 ++ (if subst then O.txt " :=" ++ O.sp else O.txt " =" ++ O.sp) 1634 ++ mty expr 1635 in 1636 ( modname, 1637 expansion_doc, 1638 attach_expansion (" = ", "sig", "end") expansion summary ) 1639 1640 and module_type (t : Odoc_model.Lang.ModuleType.t) = 1641 let prefix = 1642 O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " " 1643 in 1644 let modname = Paths.Identifier.name t.id in 1645 let source_anchor = source_anchor t.source_loc in 1646 let modname, expansion_doc, mty = 1647 module_type_manifest ~subst:false ~source_anchor modname t.id 1648 t.doc.elements t.expr prefix 1649 in 1650 let content = 1651 O.documentedSrc (prefix ++ modname) 1652 @ mty 1653 @ O.documentedSrc 1654 (if Syntax.Mod.close_tag_semicolon then O.txt ";" else O.noop) 1655 in 1656 let attr = [ "module-type" ] in 1657 let anchor = path_to_id t.id in 1658 let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in 1659 Item.Declaration { attr; anchor; doc; content; source_anchor } 1660 1661 and umty_hidden : Odoc_model.Lang.ModuleType.U.expr -> bool = function 1662 | Path p -> Paths.Path.(is_hidden (p :> t)) 1663 | With (_, expr) -> umty_hidden expr 1664 | TypeOf (ModPath m, _) | TypeOf (StructInclude m, _) -> 1665 Paths.Path.(is_hidden (m :> t)) 1666 | Signature _ -> false 1667 | Strengthen (expr, p, _) -> 1668 umty_hidden expr || Paths.Path.(is_hidden (p :> t)) 1669 1670 and mty_hidden : Odoc_model.Lang.ModuleType.expr -> bool = function 1671 | Path { p_path = mty_path; _ } -> Paths.Path.(is_hidden (mty_path :> t)) 1672 | With { w_expr; _ } -> umty_hidden w_expr 1673 | TypeOf { t_desc = ModPath m; _ } 1674 | TypeOf { t_desc = StructInclude m; _ } -> 1675 Paths.Path.(is_hidden (m :> t)) 1676 | _ -> false 1677 1678 and mty_with subs expr = 1679 umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " " 1680 ++ O.list 1681 ~sep:(O.cut ++ O.txt " " ++ O.keyword "and" ++ O.txt " ") 1682 ~f:(fun x -> O.span (substitution x)) 1683 subs 1684 1685 and mty_strengthen expr path = 1686 umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " " 1687 ++ Link.from_path (path :> Paths.Path.t) 1688 1689 and mty_typeof t_desc = 1690 match t_desc with 1691 | Odoc_model.Lang.ModuleType.ModPath m -> 1692 O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " " 1693 ++ O.keyword "of" ++ O.txt " " 1694 ++ Link.from_path (m :> Paths.Path.t) 1695 | StructInclude m -> 1696 O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " " 1697 ++ O.keyword "of" ++ O.txt " " ++ O.keyword "struct" ++ O.txt " " 1698 ++ O.keyword "include" ++ O.txt " " 1699 ++ Link.from_path (m :> Paths.Path.t) 1700 ++ O.txt " " ++ O.keyword "end" 1701 1702 and is_elidable_with_u : Odoc_model.Lang.ModuleType.U.expr -> bool = 1703 function 1704 | Path _ -> false 1705 | Signature _ -> true 1706 | With (_, expr) -> is_elidable_with_u expr 1707 | TypeOf _ -> false 1708 | Strengthen (expr,_,_) -> is_elidable_with_u expr 1709 1710 and umty : Odoc_model.Lang.ModuleType.U.expr -> text = 1711 fun m -> 1712 match m with 1713 | Path p -> Link.from_path (p :> Paths.Path.t) 1714 | Signature _ -> 1715 Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag 1716 | With (_, expr) when is_elidable_with_u expr -> 1717 Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag 1718 | With (subs, expr) -> mty_with subs expr 1719 | TypeOf (t_desc, _) -> mty_typeof t_desc 1720 | Strengthen (expr, _, _) when is_elidable_with_u expr -> 1721 Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag 1722 | Strengthen (expr, p, _) -> mty_strengthen expr (p :> Paths.Path.t) 1723 1724 and mty : Odoc_model.Lang.ModuleType.expr -> text = 1725 fun m -> 1726 if mty_hidden m then 1727 Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag 1728 else 1729 match m with 1730 | Path { p_path = mty_path; _ } -> 1731 Link.from_path (mty_path :> Paths.Path.t) 1732 | Functor (Unit, expr) -> 1733 (if Syntax.Mod.functor_keyword then O.keyword "functor" else O.noop) 1734 ++ O.span (O.txt " () " ++ Syntax.Type.arrow) 1735 ++ O.sp ++ mty expr 1736 | Functor (Named arg, expr) -> 1737 let arg_expr = arg.expr in 1738 let stop_before = expansion_of_module_type_expr arg_expr = None in 1739 let name = 1740 let open Odoc_model.Lang.FunctorParameter in 1741 let name = Paths.Identifier.name arg.id in 1742 let href = 1743 Url.from_identifier ~stop_before (arg.id :> Paths.Identifier.t) 1744 in 1745 resolved href [ inline @@ Text name ] 1746 in 1747 (if Syntax.Mod.functor_keyword then O.keyword "functor" else O.noop) 1748 ++ (O.box_hv @@ O.span 1749 @@ O.txt " (" ++ name 1750 ++ O.txt Syntax.Type.annotation_separator 1751 ++ mty arg_expr ++ O.txt ")" ++ O.txt " " ++ Syntax.Type.arrow 1752 ) 1753 ++ O.sp ++ mty expr 1754 | With { w_expr; _ } when is_elidable_with_u w_expr -> 1755 Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag 1756 | With { w_substitutions; w_expr; _ } -> 1757 O.box_hv @@ mty_with w_substitutions w_expr 1758 | TypeOf { t_desc; _ } -> mty_typeof t_desc 1759 | Signature _ -> 1760 Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag 1761 | Strengthen { s_expr; _ } when is_elidable_with_u s_expr -> 1762 Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag 1763 | Strengthen { s_expr; s_path; _ } -> 1764 O.box_hv @@ mty_strengthen s_expr (s_path :> Paths.Path.t) 1765 and mty_in_decl : 1766 Paths.Identifier.Signature.t -> Odoc_model.Lang.ModuleType.expr -> text 1767 = 1768 fun base -> function 1769 | (Path _ | Signature _ | With _ | TypeOf _ | Strengthen _) as m -> 1770 O.txt Syntax.Type.annotation_separator ++ O.cut ++ mty m 1771 | Functor _ as m when not Syntax.Mod.functor_contraction -> 1772 O.txt Syntax.Type.annotation_separator ++ O.cut ++ mty m 1773 | Functor (arg, expr) -> 1774 let text_arg = 1775 match arg with 1776 | Unit -> O.txt "()" 1777 | Named arg -> 1778 let arg_expr = arg.expr in 1779 let stop_before = 1780 expansion_of_module_type_expr arg_expr = None 1781 in 1782 let name = 1783 let open Odoc_model.Lang.FunctorParameter in 1784 let name = Paths.Identifier.name arg.id in 1785 let href = 1786 Url.from_identifier ~stop_before 1787 (arg.id :> Paths.Identifier.t) 1788 in 1789 resolved href [ inline @@ Text name ] 1790 in 1791 O.box_hv 1792 @@ O.txt "(" ++ name 1793 ++ O.txt Syntax.Type.annotation_separator 1794 ++ O.cut ++ mty arg.expr ++ O.txt ")" 1795 in 1796 O.sp ++ text_arg ++ mty_in_decl base expr 1797 1798 (* TODO : Centralize the list juggling for type parameters *) 1799 and type_expr_in_subst td typath = 1800 let typath = Link.from_fragment typath in 1801 match td.Lang.TypeDecl.Equation.params with 1802 | [] -> typath 1803 | l -> Syntax.Type.handle_substitution_params typath (format_params l) 1804 1805 and substitution : Odoc_model.Lang.ModuleType.substitution -> text = 1806 function 1807 | ModuleEq (frag_mod, md) -> 1808 O.box_hv 1809 @@ O.keyword "module" ++ O.txt " " 1810 ++ Link.from_fragment (frag_mod :> Paths.Fragment.leaf) 1811 ++ O.txt " =" ++ O.sp ++ mdexpr md 1812 | ModuleTypeEq (frag_mty, md) -> 1813 O.box_hv 1814 @@ O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " " 1815 ++ Link.from_fragment (frag_mty :> Paths.Fragment.leaf) 1816 ++ O.txt " =" ++ O.sp ++ mty md 1817 | TypeEq (frag_typ, td) -> 1818 O.box_hv 1819 @@ O.keyword "type" ++ O.txt " " 1820 ++ type_expr_in_subst td (frag_typ :> Paths.Fragment.leaf) 1821 ++ fst (format_manifest td) 1822 ++ format_constraints 1823 td.Odoc_model.Lang.TypeDecl.Equation.constraints 1824 | ModuleSubst (frag_mod, mod_path) -> 1825 O.box_hv 1826 @@ O.keyword "module" ++ O.txt " " 1827 ++ Link.from_fragment (frag_mod :> Paths.Fragment.leaf) 1828 ++ O.txt " :=" ++ O.sp 1829 ++ Link.from_path (mod_path :> Paths.Path.t) 1830 | ModuleTypeSubst (frag_mty, md) -> 1831 O.box_hv 1832 @@ O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " " 1833 ++ Link.from_fragment (frag_mty :> Paths.Fragment.leaf) 1834 ++ O.txt " :=" ++ O.sp ++ mty md 1835 | TypeSubst (frag_typ, td) -> ( 1836 O.box_hv 1837 @@ O.keyword "type" ++ O.txt " " 1838 ++ type_expr_in_subst td (frag_typ :> Paths.Fragment.leaf) 1839 ++ O.txt " :=" ++ O.sp 1840 ++ 1841 match td.Lang.TypeDecl.Equation.manifest with 1842 | None -> assert false (* cf loader/cmti *) 1843 | Some te -> type_expr te) 1844 1845 and include_ (t : Odoc_model.Lang.Include.t) = 1846 let decl_hidden = 1847 match t.decl with 1848 | Alias p -> Paths.Path.(is_hidden (p :> t)) 1849 | ModuleType mty -> umty_hidden mty 1850 in 1851 let status = if decl_hidden then `Inline else t.status in 1852 1853 let _, content = signature t.expansion.content in 1854 let summary = 1855 if decl_hidden then O.render (O.keyword "include" ++ O.txt " ...") 1856 else 1857 let include_decl = 1858 match t.decl with 1859 | Odoc_model.Lang.Include.Alias mod_path -> 1860 Link.from_path (mod_path :> Paths.Path.t) 1861 | ModuleType mt -> umty mt 1862 in 1863 O.render 1864 (O.keyword "include" ++ O.txt " " ++ include_decl 1865 ++ if Syntax.Mod.include_semicolon then O.keyword ";" else O.noop) 1866 in 1867 let content = { Include.content; status; summary } in 1868 let attr = [ "include" ] in 1869 let anchor = None in 1870 let doc = 1871 (* Documentation attached to includes behave differently than other 1872 declarations, which show only the synopsis. We can't only show the 1873 synopsis because no page is generated to render it and we'd loose 1874 the full documentation. 1875 The documentation from the expansion is not used. *) 1876 Comment.to_ir t.doc.elements 1877 in 1878 Item.Include { attr; anchor; doc; content; source_anchor = None } 1879 end 1880 1881 open Module 1882 1883 module Page : sig 1884 val compilation_unit : Lang.Compilation_unit.t -> Document.t 1885 1886 val page : Lang.Page.t -> Document.t 1887 1888 val implementation : 1889 Lang.Implementation.t -> 1890 Syntax_highlighter.infos -> 1891 string -> 1892 Document.t list 1893 end = struct 1894 let pack : Lang.Compilation_unit.Packed.t -> Item.t list = 1895 fun t -> 1896 let f x = 1897 let id = x.Lang.Compilation_unit.Packed.id in 1898 let modname = Paths.Identifier.name id in 1899 let md_def = 1900 O.keyword "module" ++ O.txt " " ++ O.txt modname ++ O.txt " = " 1901 ++ Link.from_path (x.path :> Paths.Path.t) 1902 in 1903 let content = O.documentedSrc md_def in 1904 let anchor = 1905 Some (Url.Anchor.from_identifier (id :> Paths.Identifier.t)) 1906 in 1907 let attr = [ "modules" ] in 1908 let doc = [] in 1909 let decl = { Item.anchor; content; attr; doc; source_anchor = None } in 1910 Item.Declaration decl 1911 in 1912 List.map f t 1913 1914 let compilation_unit (t : Odoc_model.Lang.Compilation_unit.t) = 1915 let url = Url.Path.from_identifier t.id in 1916 let unit_doc, items = 1917 match t.content with 1918 | Module sign -> signature sign 1919 | Pack packed -> ([], pack packed) 1920 in 1921 let source_anchor = source_anchor t.source_loc in 1922 let page = make_expansion_page ~source_anchor url [ unit_doc ] items in 1923 (* Collect any remaining resources that were accumulated during signature 1924 processing but not captured by nested pages. These belong to the 1925 top-level compilation unit. *) 1926 let remaining_resources = Comment.Resources.take () in 1927 let remaining_assets = Comment.Assets.take () in 1928 let page = 1929 { page with 1930 Page.resources = page.Page.resources @ remaining_resources; 1931 assets = page.assets @ remaining_assets } 1932 in 1933 Document.Page page 1934 1935 let page (t : Odoc_model.Lang.Page.t) = 1936 (*let name = 1937 match t.name.iv with `Page (_, name) | `LeafPage (_, name) -> name 1938 in*) 1939 (*let title = Odoc_model.Names.PageName.to_string name in*) 1940 let url = Url.Path.from_identifier t.name in 1941 let preamble, items = Sectioning.docs t.content.elements in 1942 let source_anchor = None in 1943 let resources = Comment.Resources.take () in 1944 let assets = Comment.Assets.take () in 1945 Document.Page { Page.preamble; items; url; source_anchor; resources; assets } 1946 1947 let implementation (v : Odoc_model.Lang.Implementation.t) syntax_info 1948 source_code = 1949 match v.id with 1950 | None -> [] 1951 | Some id -> 1952 [ 1953 Document.Source_page 1954 (Source_page.source id syntax_info v.source_info source_code); 1955 ] 1956 end 1957 1958 include Page 1959 1960 let type_expr = type_expr 1961 1962 let record = record 1963 1964 let unboxed_record = unboxed_record 1965end