• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ocaml / odoc / 3280

23 Jul 2026 11:25AM UTC coverage: 70.577%. First build
3280

Pull #1464

github

web-flow
Merge 0b677ed22 into 17e9ae9da
Pull Request #1464: OxCaml: support for parameterized libraries

12 of 97 new or added lines in 15 files covered. (12.37%)

10463 of 14825 relevant lines covered (70.58%)

5827.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.33
/src/document/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
open Odoc_utils
18
open Odoc_model.Names
19
module Location = Odoc_model.Location_
20
module Paths = Odoc_model.Paths
21
open Types
22
module O = Codefmt
23
open O.Infix
24

25
let tag tag t = O.span ~attr:tag t
13,407✔
26

27
let label t =
28
  match t with
3,263✔
29
  | Odoc_model.Lang.TypeExpr.Label s -> tag "label" (O.txt s)
574✔
30
  | Optional s | RawOptional s -> tag "optlabel" (O.txt "?" ++ O.txt s)
1✔
31

32
let type_var tv = tag "type-var" (O.txt tv)
9,487✔
33

34
let enclose ~l ~r x = O.span (O.txt l ++ x ++ O.txt r)
14,348✔
35

36
let resolved p content =
37
  let link = { Link.target = Internal (Resolved p); content; tooltip = None } in
159,791✔
38
  O.elt [ inline @@ Link link ]
159,791✔
39

40
let path p content = resolved (Url.from_path p) content
2,955✔
41

42
let unresolved content =
43
  let link = { Link.target = Internal Unresolved; content; tooltip = None } in
582✔
44
  O.elt [ inline @@ Link link ]
582✔
45

46
let path_to_id path =
47
  let url = Url.Anchor.from_identifier (path :> Paths.Identifier.t) in
7,529✔
48
  Some url
7,529✔
49

50
let source_anchor source_loc =
51
  match source_loc with
7,891✔
52
  | Some id ->
104✔
53
      Some
54
        (Url.Anchor.from_identifier
104✔
55
           (id : Paths.Identifier.SourceLocation.t :> Paths.Identifier.t))
56
  | _ -> None
7,787✔
57

58
let attach_expansion ?(status = `Default) (eq, o, e) page text =
1,602✔
59
  match page with
3,445✔
60
  | None -> O.documentedSrc text
701✔
61
  | Some (page : Page.t) ->
2,744✔
62
      let url = page.url in
63
      let summary = O.render text in
64
      let expansion =
2,744✔
65
        O.documentedSrc (O.txt eq ++ O.keyword o)
2,744✔
66
        @ DocumentedSrc.[ Subpage { status; content = page } ]
67
        @ O.documentedSrc (O.keyword e)
2,744✔
68
      in
69
      DocumentedSrc.
70
        [ Alternative (Expansion { summary; url; status; expansion }) ]
71

72
let mk_heading ?(level = 1) ?label text =
366✔
73
  let title = [ inline @@ Text text ] in
366✔
74
  Item.Heading { label; level; title; source_anchor = None }
75

76
(** Returns the preamble as an item. Stop the preamble at the first heading. The
77
    rest is inserted into [items]. *)
78
let prepare_preamble comment items =
79
  let preamble, first_comment =
3,314✔
80
    List.split_at
81
      ~f:(function
82
        | { Odoc_model.Location_.value = `Heading _; _ } -> true | _ -> false)
32✔
83
      comment
84
  in
85
  (Comment.standalone preamble, Comment.standalone first_comment @ items)
3,314✔
86

87
let make_expansion_page ~source_anchor url comments items =
88
  let comment = List.concat comments in
3,314✔
89
  let preamble, items = prepare_preamble comment items in
3,314✔
90
  { Page.preamble; items; url; source_anchor }
3,314✔
91

92
include Generator_signatures
93

94
module Make (Syntax : SYNTAX) = struct
95
  module Link : sig
96
    val from_path : Paths.Path.t -> text
97

98
    val from_fragment : Paths.Fragment.leaf -> text
99

100
    val render_fragment_any : Paths.Fragment.t -> string
101
  end = struct
102
    open Paths
103

104
    let rec from_path : Path.t -> text =
105
     fun path ->
106
      match path with
181,953✔
107
      | `Identifier (id, _) ->
245✔
108
          unresolved [ inline @@ Text (Identifier.name id) ]
245✔
109
      | `Substituted m -> from_path (m :> Path.t)
×
110
      | `SubstitutedMT m -> from_path (m :> Path.t)
×
111
      | `SubstitutedT m -> from_path (m :> Path.t)
×
112
      | `SubstitutedCT m -> from_path (m :> Path.t)
×
113
      | `Unbox t -> from_path (t :> Path.t)
×
114
      | `Root root -> unresolved [ inline @@ Text (ModuleName.to_string root) ]
305✔
115
      | `Forward root -> unresolved [ inline @@ Text root ] (* FIXME *)
×
116
      | `Dot (prefix, suffix) ->
305✔
117
          let link = from_path (prefix :> Path.t) in
118
          link ++ O.txt ("." ^ ModuleName.to_string suffix)
305✔
119
      | `DotT (prefix, suffix) ->
297✔
120
          let link = from_path (prefix :> Path.t) in
121
          link ++ O.txt ("." ^ TypeName.to_string suffix)
297✔
122
      | `DotMT (prefix, suffix) ->
×
123
          let link = from_path (prefix :> Path.t) in
124
          link ++ O.txt ("." ^ ModuleTypeName.to_string suffix)
×
125
      | `DotV (prefix, suffix) ->
×
126
          let link = from_path (prefix :> Path.t) in
127
          link ++ O.txt ("." ^ ValueName.to_string suffix)
×
128
      | `Apply (p1, p2) ->
×
129
          let link1 = from_path (p1 :> Path.t) in
130
          let link2 = from_path (p2 :> Path.t) in
×
131
          link1 ++ O.txt "(" ++ link2 ++ O.txt ")"
×
NEW
132
      | `ApplyParam (p1, p2, p3) ->
×
133
          let link1 = from_path (p1 :> Path.t) in
NEW
134
          let link2 = from_path (p2 :> Path.t) in
×
NEW
135
          let link3 = from_path (p3 :> Path.t) in
×
NEW
136
          link1 ++ O.txt "[" ++ link2 ++ O.txt ":" ++ link3 ++ O.txt "]"
×
137
      | `Resolved _ when Paths.Path.is_hidden path ->
180,801✔
138
          let txt = Url.render_path path in
32✔
139
          unresolved [ inline @@ Text txt ]
32✔
140
      | `Resolved rp -> (
180,769✔
141
          (* If the path is pointing to an opaque module or module type
142
             there won't be a page generated - so we stop before; at
143
             the parent page, and link instead to the anchor representing
144
             the declaration of the opaque module(_type) *)
145
          let stop_before =
146
            match rp with
147
            | `OpaqueModule _ | `OpaqueModuleType _ -> true
8✔
148
            | _ -> false
180,561✔
149
          in
150
          let txt = [ inline @@ Text (Url.render_path path) ] in
180,769✔
151
          match Paths.Path.Resolved.identifier rp with
152
          | Some id ->
156,330✔
153
              let href = Url.from_identifier ~stop_before id in
154
              resolved href txt
156,330✔
155
          | None -> O.elt txt)
24,439✔
156

157
    let dot prefix suffix = prefix ^ "." ^ suffix
49✔
158

159
    let rec render_fragment_any : Fragment.t -> string =
160
     fun fragment ->
161
      match fragment with
×
162
      | `Resolved rr -> render_resolved_fragment rr
×
163
      | `Dot (`Root, suffix) -> suffix
×
164
      | `Dot (prefix, suffix) ->
×
165
          dot (render_fragment_any (prefix :> Fragment.t)) suffix
×
166
      | `Root -> assert false
167

168
    and render_resolved_fragment : Fragment.Resolved.t -> string =
169
      let open Fragment.Resolved in
170
      fun fragment ->
171
        match fragment with
376✔
172
        | `Root _ -> assert false
173
        | `Subst (_, rr) -> render_resolved_fragment (rr :> t)
8✔
174
        | `Alias (_, rr) -> render_resolved_fragment (rr :> t)
24✔
175
        | `Module (`Root _, s) -> ModuleName.to_string s
121✔
176
        | `Module_type (`Root _, s) -> ModuleTypeName.to_string s
32✔
177
        | `Type (`Root _, s) -> TypeName.to_string s
142✔
178
        | `Class (`Root _, s) -> TypeName.to_string s
×
179
        | `ClassType (`Root _, s) -> TypeName.to_string s
×
180
        | `Module (rr, s) ->
8✔
181
            dot (render_resolved_fragment (rr :> t)) (ModuleName.to_string s)
8✔
182
        | `Module_type (rr, s) ->
16✔
183
            dot
184
              (render_resolved_fragment (rr :> t))
16✔
185
              (ModuleTypeName.to_string s)
16✔
186
        | `Type (rr, s) ->
25✔
187
            dot (render_resolved_fragment (rr :> t)) (TypeName.to_string s)
25✔
188
        | `Class (rr, s) ->
×
189
            dot (render_resolved_fragment (rr :> t)) (TypeName.to_string s)
×
190
        | `ClassType (rr, s) ->
×
191
            dot (render_resolved_fragment (rr :> t)) (TypeName.to_string s)
×
192
        | `OpaqueModule r -> render_resolved_fragment (r :> t)
×
193

194
    let resolved_fragment_to_ir : Fragment.Resolved.leaf -> text =
195
     fun fragment ->
196
      let open Fragment in
295✔
197
      let id = Resolved.identifier (fragment :> Resolved.t) in
198
      let txt = render_resolved_fragment (fragment :> Resolved.t) in
295✔
199
      match id with
295✔
200
      | Some id ->
295✔
201
          let href = Url.from_identifier ~stop_before:false id in
202
          resolved href [ inline @@ Text txt ]
295✔
203
      | None -> unresolved [ inline @@ Text txt ]
×
204

205
    let from_fragment : Fragment.leaf -> text = function
206
      | `Resolved r
295✔
207
        when not (Fragment.Resolved.is_hidden (r :> Fragment.Resolved.t)) ->
295✔
208
          resolved_fragment_to_ir r
295✔
209
      | f ->
×
210
          let txt = render_fragment_any (f :> Fragment.t) in
211
          unresolved [ inline @@ Text txt ]
×
212
  end
213

214
  module Impl = struct
215
    let impl ~infos src =
216
      let l =
28✔
217
        infos
218
        |> List.sort (fun (_, (l1, e1)) (_, (l2, e2)) ->
219
               if l1 = l2 then compare e2 e1
133✔
220
                 (* If two intervals open at the same time, we open
221
                    first the one that closes last *)
222
               else compare l1 l2)
3,166✔
223
      in
224
      let get_src a b =
28✔
225
        let in_bound x = min (max x 0) (String.length src) in
1,696✔
226
        let a = in_bound a and b = in_bound b in
1,696✔
227
        let a, b = (min a b, max a b) in
1,696✔
228
        String.with_range src ~first:a ~len:(b - a)
229
      in
230
      let plain_code = function
231
        | "" -> []
596✔
232
        | s -> [ Types.Source_page.Plain_code s ]
1,100✔
233
      in
234
      let min (a : int) b = if a < b then a else b in
161✔
235
      let rec extract from to_ list aux =
236
        match list with
1,696✔
237
        | (k, (loc_start, loc_end)) :: q when loc_start < to_ ->
1,640✔
238
            let loc_end = min loc_end to_ in
834✔
239
            (* In case of inconsistent [a  [b    a] b]
240
               we do                   [a  [b  b]a] *)
241
            let initial = plain_code (get_src from loc_start) in
834✔
242
            let next, q = extract loc_start loc_end q [] in
834✔
243
            extract loc_end to_ q
834✔
244
              ([ Types.Source_page.Tagged_code (k, List.rev next) ]
834✔
245
              @ initial @ aux)
246
        | q -> (plain_code (get_src from to_) @ aux, q)
862✔
247
      in
248
      let doc, _ = extract 0 (String.length src) l [] in
28✔
249
      List.rev doc
28✔
250
  end
251

252
  module Source_page : sig
253
    val source :
254
      Paths.Identifier.SourcePage.t ->
255
      Syntax_highlighter.infos ->
256
      Lang.Source_info.t ->
257
      string ->
258
      Source_page.t
259
  end = struct
260
    let path id = Url.Path.from_identifier id
28✔
261

262
    let to_link { Lang.Source_info.documentation; implementation } =
263
      let documentation =
34✔
264
        (* Since documentation link are not rendered, we comment the code to
265
           extract the href, and always output [None] *)
266
        ignore documentation;
267
        None
268
        (* let open Paths.Path.Resolved in *)
269
        (* match documentation with *)
270
        (* | Some (`Resolved p) when not (is_hidden (p :> t)) -> ( *)
271
        (*     let id = identifier (p :> t) in *)
272
        (*     match Url.from_identifier ~stop_before:false id with *)
273
        (*     | Ok link -> Some link *)
274
        (*     | _ -> None) *)
275
        (* | _ -> None *)
276
      in
277
      let implementation =
278
        match implementation with
279
        | Some (Odoc_model.Lang.Source_info.Resolved id) ->
15✔
280
            Some (Url.Anchor.from_identifier (id :> Paths.Identifier.t))
15✔
281
        | _ -> None
19✔
282
      in
283
      Some (Source_page.Link { implementation; documentation })
284

285
    let info_of_info : Lang.Source_info.annotation -> Source_page.info option =
286
      function
287
      | Definition id -> (
99✔
288
          match id.iv with
289
          | `SourceLocation (_, def) -> Some (Anchor (DefName.to_string def))
95✔
290
          | `SourceLocationInternal (_, local) ->
4✔
291
              Some (Anchor (LocalName.to_string local))
4✔
292
          | _ -> None)
×
293
      | Module v -> to_link v
11✔
294
      | ModuleType v -> to_link v
1✔
295
      | Type v -> to_link v
7✔
296
      | Value v -> to_link v
15✔
297

298
    let source id syntax_info infos source_code =
299
      let url = path id in
28✔
300
      let mapper (info, loc) =
28✔
301
        match info_of_info info with Some x -> Some (x, loc) | None -> None
×
302
      in
303
      let infos = Odoc_utils.List.filter_map mapper infos in
304
      let syntax_info =
28✔
305
        List.rev_map (fun (ty, loc) -> (Source_page.Syntax ty, loc)) syntax_info
701✔
306
        |> List.rev
28✔
307
      in
308
      let contents = Impl.impl ~infos:(infos @ syntax_info) source_code in
28✔
309
      { Source_page.url; contents }
28✔
310
  end
311

312
  module Modalities : sig
313
    val format : Odoc_model.Lang.Modalities.t -> text
314
  end = struct
315
    let format = function
316
      | [] -> O.noop
1,492✔
317
      | mods ->
×
318
          O.txt " " ++ O.txt "@@" ++ O.txt " "
×
319
          ++ O.txt (String.concat ~sep:" " mods)
×
320
  end
321

322
  module Type_expression : sig
323
    val type_expr : ?needs_parentheses:bool -> Lang.TypeExpr.t -> text
324

325
    val format_type_path :
326
      delim:[ `parens | `brackets ] -> Lang.TypeExpr.t list -> text -> text
327

328
    val kind_annotation :
329
      ?needs_parentheses:bool -> Odoc_model.Lang.Kind.t -> text
330

331
    val with_kind_annotation : Odoc_model.Lang.Kind.t -> text -> text
332
  end = struct
333
    let rec te_variant (t : Odoc_model.Lang.TypeExpr.Polymorphic_variant.t) =
334
      let style_arguments ~constant arguments =
75,143✔
335
        (* Multiple arguments in a polymorphic variant constructor correspond
336
           to a conjunction of types, not a product: [`Lbl int&float].
337
           If constant is [true], the conjunction starts with an empty type,
338
           for instance [`Lbl &int].
339
        *)
340
        let wrapped_type_expr =
1,873✔
341
          (* type conjunction in Reason is printed as `Lbl (t1)&(t2)` *)
342
          if Syntax.Type.Variant.parenthesize_params then fun x ->
×
343
            enclose ~l:"(" ~r:")" (type_expr x)
×
344
          else fun x -> type_expr x
1,873✔
345
        in
346
        let arguments =
347
          O.list arguments ~sep:(O.txt " & ") ~f:wrapped_type_expr
1,873✔
348
        in
349
        if constant then O.txt "& " ++ arguments else arguments
×
350
      in
351
      let rec style_elements ~add_pipe = function
352
        | [] -> O.noop
75,143✔
353
        | first :: rest ->
108,795✔
354
            let first =
355
              match first with
356
              | Odoc_model.Lang.TypeExpr.Polymorphic_variant.Type te ->
40,832✔
357
                  let res = O.box_hv @@ type_expr te in
40,832✔
358
                  if add_pipe then O.sp ++ O.span (O.txt "| " ++ res) else res
2,565✔
359
              | Constructor { constant; name; arguments; _ } ->
67,963✔
360
                  let constr =
361
                    let name = "`" ^ name in
362
                    if add_pipe then O.span (O.txt ("| " ^ name))
31,095✔
363
                    else O.txt name
36,868✔
364
                  in
365
                  let res =
366
                    O.box_hv
367
                      (match arguments with
368
                      | [] -> constr
66,090✔
369
                      | _ ->
1,873✔
370
                          let arguments = style_arguments ~constant arguments in
371
                          O.span
1,873✔
372
                            (if Syntax.Type.Variant.parenthesize_params then
373
                               constr ++ arguments
×
374
                             else constr ++ O.txt " of" ++ O.sp ++ arguments))
1,873✔
375
                  in
376
                  if add_pipe then O.sp ++ res else res
31,095✔
377
            in
378
            first ++ style_elements ~add_pipe:true rest
108,795✔
379
      in
380
      let elements = style_elements ~add_pipe:false t.elements in
381
      O.box_hv_no_indent
75,143✔
382
      @@ O.span
75,143✔
383
           (match t.kind with
384
           | Fixed -> O.txt "[ " ++ elements ++ O.txt " ]"
7,068✔
385
           | Open -> O.txt "[> " ++ elements ++ O.txt " ]"
40,908✔
386
           | Closed [] -> O.txt "[< " ++ elements ++ O.txt " ]"
27,132✔
387
           | Closed lst ->
35✔
388
               let constrs = String.concat ~sep:" " lst in
389
               O.txt "[< " ++ elements ++ O.txt (" " ^ constrs ^ " ]"))
35✔
390

391
    and te_object (t : Odoc_model.Lang.TypeExpr.Object.t) =
392
      let fields =
80✔
393
        O.list
394
          ~sep:(O.sp ++ O.txt Syntax.Obj.field_separator)
80✔
395
          t.fields
396
          ~f:(function
397
            | Odoc_model.Lang.TypeExpr.Object.Method { name; type_ } ->
120✔
398
                O.box_hv_no_indent
399
                @@ O.txt (name ^ Syntax.Type.annotation_separator)
120✔
400
                   ++ O.cut ++ type_expr type_
120✔
401
            | Inherit type_ -> O.box_hv_no_indent @@ type_expr type_)
×
402
      in
403
      let open_tag =
80✔
404
        if t.open_ then O.txt Syntax.Obj.open_tag_extendable
32✔
405
        else O.txt Syntax.Obj.open_tag_closed
48✔
406
      in
407
      let close_tag =
408
        if t.open_ then O.txt Syntax.Obj.close_tag_extendable
32✔
409
        else O.txt Syntax.Obj.close_tag_closed
48✔
410
      in
411
      O.span (open_tag ++ fields ++ close_tag)
80✔
412

413
    and format_type_path ~delim (params : Odoc_model.Lang.TypeExpr.t list)
414
        (path : text) : text =
415
      O.box_hv
179,533✔
416
      @@
417
      match params with
418
      | [] -> path
94,880✔
419
      | [ param ] ->
71,291✔
420
          let param = type_expr ~needs_parentheses:true param in
421
          let args =
71,291✔
422
            if Syntax.Type.parenthesize_constructor then
423
              O.txt "(" ++ param ++ O.txt ")"
×
424
            else param
71,291✔
425
          in
426
          Syntax.Type.handle_constructor_params path args
71,291✔
427
      | params ->
13,362✔
428
          let params = O.list params ~sep:(O.txt "," ++ O.sp) ~f:type_expr in
13,362✔
429
          let params =
13,362✔
430
            match delim with
431
            | `parens -> enclose ~l:"(" params ~r:")"
13,362✔
432
            | `brackets -> enclose ~l:"[" params ~r:"]"
×
433
          in
434
          Syntax.Type.handle_constructor_params path (O.box_hv params)
13,362✔
435

436
    and tuple ?(needs_parentheses = false) ~boxed lst =
×
437
      let opt_label = function
677✔
438
        | None -> O.noop
1,544✔
439
        | Some lbl -> tag "label" (O.txt lbl) ++ O.txt ":" ++ O.cut
×
440
      in
441
      let res =
442
        O.box_hv_no_indent
443
          (O.list lst ~sep:Syntax.Type.Tuple.element_separator
677✔
444
             ~f:(fun (lbl, typ) ->
445
               opt_label lbl ++ type_expr ~needs_parentheses:true typ))
1,544✔
446
      in
447
      let lparen = if boxed then "(" else "#(" in
×
448
      if Syntax.Type.Tuple.always_parenthesize || needs_parentheses || not boxed
×
449
      then enclose ~l:lparen res ~r:")"
131✔
450
      else res
546✔
451

452
    and kind_annotation ?(needs_parentheses = false)
×
453
        (k : Odoc_model.Lang.Kind.t) =
454
      let enclose_parens_if_needed res =
×
455
        if needs_parentheses then enclose ~l:"(" res ~r:")" else res
×
456
      in
457
      match k with
458
      | Default -> O.noop
×
459
      | Abbreviation frag ->
×
460
          O.txt (Link.render_fragment_any (frag :> Paths.Fragment.t))
×
461
      | Mod (base, modes) ->
×
462
          let res =
463
            kind_annotation ~needs_parentheses:true base
×
464
            ++ O.txt " " ++ O.keyword "mod"
×
465
            ++ O.txt (" " ^ String.concat ~sep:" " modes)
×
466
          in
467
          enclose_parens_if_needed res
×
468
      | With (base, ty, modalities) ->
×
469
          let res =
470
            kind_annotation ~needs_parentheses:true base
×
471
            ++ O.txt " " ++ O.keyword "with" ++ O.txt " " ++ type_expr ty
×
472
            ++ Modalities.format modalities
×
473
          in
474
          enclose_parens_if_needed res
×
475
      | Kind_of ty ->
×
476
          let res = O.keyword "kind_of_" ++ O.txt " " ++ type_expr ty in
×
477
          enclose_parens_if_needed res
×
478
      | Product ks ->
×
479
          let res =
480
            O.list ks ~sep:(O.txt " & ") ~f:(fun k ->
×
481
                kind_annotation ~needs_parentheses:true k)
×
482
          in
483
          enclose_parens_if_needed res
×
484

485
    and with_kind_annotation kind base =
486
      match kind with
648✔
487
      | Odoc_model.Lang.Kind.Default -> base
648✔
488
      | k -> O.txt "(" ++ base ++ O.txt " : " ++ kind_annotation k ++ O.txt ")"
×
489

490
    and type_expr ?(needs_parentheses = false) (t : Odoc_model.Lang.TypeExpr.t)
191,635✔
491
        =
492
      let enclose_parens_if_needed res =
312,108✔
493
        if needs_parentheses then enclose ~l:"(" res ~r:")" else res
831✔
494
      in
495
      match t with
496
      | Var s -> type_var (Syntax.Type.var_prefix ^ s)
9,469✔
497
      | Any -> type_var Syntax.Type.any
18✔
498
      | Alias (te, alias) ->
133✔
499
          enclose_parens_if_needed
500
            (type_expr ~needs_parentheses:true te
133✔
501
            ++ O.txt " " ++ O.keyword "as" ++ O.txt " '" ++ O.txt alias)
133✔
502
      | Arrow (None, src, dst) ->
43,802✔
503
          let res =
504
            O.span
43,802✔
505
              ((O.box_hv @@ type_expr ~needs_parentheses:true src)
43,802✔
506
              ++ O.txt " " ++ Syntax.Type.arrow)
43,802✔
507
            ++ O.sp ++ type_expr dst
43,802✔
508
            (* ++ O.end_hv *)
509
          in
510
          enclose_parens_if_needed res
43,802✔
511
      | Arrow (Some (RawOptional _ as lbl), _src, dst) ->
1✔
512
          let res =
513
            O.span
1✔
514
              (O.box_hv
1✔
515
              @@ label lbl ++ O.txt ":"
1✔
516
                 ++ tag "error" (O.txt "???")
1✔
517
                 ++ O.txt " " ++ Syntax.Type.arrow)
1✔
518
            ++ O.sp ++ type_expr dst
1✔
519
          in
520
          enclose_parens_if_needed res
1✔
521
      | Arrow (Some lbl, src, dst) ->
3,262✔
522
          let res =
523
            O.span
3,262✔
524
              ((O.box_hv
3,262✔
525
               @@ label lbl ++ O.txt ":" ++ O.cut
3,262✔
526
                  ++ (O.box_hv @@ type_expr ~needs_parentheses:true src))
3,262✔
527
              ++ O.txt " " ++ Syntax.Type.arrow)
3,262✔
528
            ++ O.sp ++ type_expr dst
3,262✔
529
          in
530
          enclose_parens_if_needed res
3,262✔
531
      | Tuple lst -> tuple ~needs_parentheses ~boxed:true lst
677✔
532
      | Unboxed_tuple lst -> tuple ~needs_parentheses ~boxed:false lst
×
533
      | Constr (path, args) ->
179,467✔
534
          let link = Link.from_path (path :> Paths.Path.t) in
535
          format_type_path ~delim:`parens args link
179,467✔
536
      | Polymorphic_variant v -> te_variant v
75,143✔
537
      | Object o -> te_object o
80✔
538
      | Class (path, args) ->
8✔
539
          format_type_path ~delim:`brackets args
540
            (Link.from_path (path :> Paths.Path.t))
8✔
541
      | Poly (polyvars, t) ->
24✔
542
          let format_poly_var (name, kind) =
543
            with_kind_annotation kind (O.txt ("'" ^ name))
24✔
544
          in
545
          let vars = O.list polyvars ~sep:(O.txt " ") ~f:format_poly_var in
24✔
546
          enclose_parens_if_needed @@ (vars ++ O.txt ". " ++ type_expr t)
24✔
547
      | Quote t -> O.span (O.txt "<[ " ++ O.box_hv (type_expr t) ++ O.txt " ]>")
×
548
      | Splice t -> O.span (O.txt "$" ++ type_expr ~needs_parentheses:true t)
×
549
      | Package pkg ->
24✔
550
          enclose ~l:"(" ~r:")"
551
            (O.keyword "module" ++ O.txt " " ++ package_path pkg)
24✔
552
      | Arrow_functor (lbl, m_arg, dst) ->
×
553
          let lbl =
554
            match lbl with None -> O.noop | Some lbl -> label lbl ++ O.txt ":"
×
555
          in
556
          let name =
557
            match m_arg.id.iv with
558
            | `Parameter (_, name) -> ModuleName.to_string name
×
559
          in
560
          let dst = type_expr dst in
561
          let pkg =
×
562
            enclose ~l:"(" ~r:")"
563
            @@ O.keyword "module" ++ O.txt " " ++ O.txt name ++ O.txt " : "
×
564
               ++ package_path m_arg.package
×
565
          in
566
          lbl ++ pkg ++ O.sp ++ Syntax.Type.arrow ++ O.sp ++ dst
×
567

568
    and package_path pkg =
569
      Link.from_path (pkg.path :> Paths.Path.t)
24✔
570
      ++
571
      match pkg.substitutions with
572
      | [] -> O.noop
16✔
573
      | fst :: lst ->
8✔
574
          O.sp
575
          ++ O.box_hv (O.keyword "with" ++ O.txt " " ++ package_subst fst)
8✔
576
          ++ O.list lst ~f:(fun s ->
8✔
577
                 O.cut
8✔
578
                 ++ (O.box_hv
8✔
579
                    @@ O.txt " " ++ O.keyword "and" ++ O.txt " "
8✔
580
                       ++ package_subst s))
8✔
581

582
    and package_subst
583
        ((frag_typ, te) : Paths.Fragment.Type.t * Odoc_model.Lang.TypeExpr.t) :
584
        text =
585
      let typath = Link.from_fragment (frag_typ :> Paths.Fragment.leaf) in
16✔
586
      O.keyword "type" ++ O.txt " " ++ typath ++ O.txt " =" ++ O.sp
16✔
587
      ++ type_expr te
16✔
588
  end
589

590
  open Type_expression
591

592
  (* Also handles constructor declarations for exceptions and extensible
593
     variants, and exposes a few helpers used in formatting classes and signature
594
     constraints. *)
595
  module Type_declaration : sig
596
    val type_decl :
597
      ?is_substitution:bool ->
598
      Lang.Signature.recursive * Lang.TypeDecl.t ->
599
      Item.t
600

601
    val extension : Lang.Extension.t -> Item.t
602

603
    val record : Lang.TypeDecl.Field.t list -> DocumentedSrc.one list
604

605
    val unboxed_record :
606
      Lang.TypeDecl.UnboxedField.t list -> DocumentedSrc.one list
607

608
    val exn : Lang.Exception.t -> Item.t
609

610
    val format_params :
611
      ?delim:[ `parens | `brackets ] -> Lang.TypeDecl.param list -> text
612

613
    val format_manifest :
614
      ?is_substitution:bool ->
615
      ?compact_variants:bool ->
616
      Lang.TypeDecl.Equation.t ->
617
      text * bool
618

619
    val format_constraints : (Lang.TypeExpr.t * Lang.TypeExpr.t) list -> text
620
  end = struct
621
    let record fields =
622
      let field mutable_ id typ modalities =
86✔
623
        let url = Url.from_identifier ~stop_before:true id in
155✔
624
        let name = Paths.Identifier.name id in
155✔
625
        let attrs = [ "def"; "record"; Url.Anchor.string_of_kind url.kind ] in
155✔
626
        let cell =
627
          O.code
628
            ((if mutable_ then O.keyword "mutable" ++ O.txt " " else O.noop)
24✔
629
            ++ O.txt name
155✔
630
            ++ O.txt Syntax.Type.annotation_separator
155✔
631
            ++ type_expr typ
155✔
632
            ++ Modalities.format modalities
155✔
633
            ++ O.txt Syntax.Type.Record.field_separator)
155✔
634
        in
635
        (url, attrs, cell)
155✔
636
      in
637
      let rows =
638
        fields
639
        |> List.map (fun fld ->
640
               let open Odoc_model.Lang.TypeDecl.Field in
155✔
641
               let url, attrs, code =
642
                 field fld.mutable_
643
                   (fld.id :> Paths.Identifier.t)
644
                   fld.type_ fld.modalities
645
               in
646
               let anchor = Some url in
155✔
647
               let doc = fld.doc.elements in
648
               let rhs = Comment.to_ir doc in
649
               let doc = if not (Comment.has_doc doc) then [] else rhs in
64✔
650
               let markers = Syntax.Comment.markers in
651
               DocumentedSrc.Documented { anchor; attrs; code; doc; markers })
652
      in
653
      let content =
86✔
654
        O.documentedSrc (O.txt "{") @ rows @ O.documentedSrc (O.txt "}")
86✔
655
      in
656
      content
657

658
    let unboxed_record fields =
659
      let field mutable_ id typ =
×
660
        let url = Url.from_identifier ~stop_before:true id in
×
661
        let name = Paths.Identifier.name id in
×
662
        let attrs = [ "def"; "record"; Url.Anchor.string_of_kind url.kind ] in
×
663
        let cell =
664
          (* O.td ~a:[ O.a_class ["def"; kind ] ]
665
           *   [O.a ~a:[O.a_href ("#" ^ anchor); O.a_class ["anchor"]] []
666
           *   ; *)
667
          O.code
668
            ((if mutable_ then O.keyword "mutable" ++ O.txt " " else O.noop)
×
669
            ++ O.txt name
×
670
            ++ O.txt Syntax.Type.annotation_separator
×
671
            ++ type_expr typ
×
672
            ++ O.txt Syntax.Type.Record.field_separator)
×
673
          (* ] *)
674
        in
675
        (url, attrs, cell)
×
676
      in
677
      let rows =
678
        fields
679
        |> List.map (fun fld ->
680
               let open Odoc_model.Lang.TypeDecl.UnboxedField in
×
681
               let url, attrs, code =
682
                 field fld.mutable_ (fld.id :> Paths.Identifier.t) fld.type_
683
               in
684
               let anchor = Some url in
×
685
               let doc = fld.doc.elements in
686
               let rhs = Comment.to_ir doc in
687
               let doc = if not (Comment.has_doc doc) then [] else rhs in
×
688
               let markers = Syntax.Comment.markers in
689
               DocumentedSrc.Documented { anchor; attrs; code; doc; markers })
690
      in
691
      let content =
×
692
        O.documentedSrc (O.txt "#{") @ rows @ O.documentedSrc (O.txt "}")
×
693
      in
694
      content
695

696
    let constructor :
697
        Paths.Identifier.t ->
698
        Odoc_model.Lang.TypeDecl.Constructor.argument ->
699
        Odoc_model.Lang.TypeExpr.t option ->
700
        DocumentedSrc.t =
701
     fun id args ret_type ->
702
      let name = Paths.Identifier.name id in
656✔
703
      let kind = Url.(kind id |> Anchor.string_of_kind) in
656✔
704
      let cstr = tag kind (O.txt name) in
656✔
705
      let is_gadt, ret_type =
656✔
706
        match ret_type with
707
        | None -> (false, O.noop)
520✔
708
        | Some te ->
136✔
709
            let constant = match args with Tuple [] -> true | _ -> false in
48✔
710
            let ret_type =
711
              O.txt " "
136✔
712
              ++ (if constant then O.txt ":" else Syntax.Type.GADT.arrow)
48✔
713
              ++ O.txt " " ++ type_expr te
136✔
714
            in
715
            (true, ret_type)
136✔
716
      in
717
      match args with
718
      | Tuple [] -> O.documentedSrc (cstr ++ ret_type)
289✔
719
      | Tuple lst ->
350✔
720
          let params =
721
            O.list lst ~sep:Syntax.Type.Tuple.element_separator
722
              ~f:(fun (te, mods) ->
723
                type_expr ~needs_parentheses:is_gadt te
425✔
724
                ++ Modalities.format mods)
425✔
725
          in
726
          O.documentedSrc
350✔
727
            (cstr
728
            ++ (if Syntax.Type.Variant.parenthesize_params then
350✔
729
                  O.txt "(" ++ params ++ O.txt ")"
×
730
                else
731
                  (if is_gadt then O.txt Syntax.Type.annotation_separator
88✔
732
                   else O.txt " " ++ O.keyword "of" ++ O.txt " ")
262✔
733
                  ++ params)
350✔
734
            ++ ret_type)
350✔
735
      | Record fields ->
17✔
736
          if is_gadt then
737
            O.documentedSrc (cstr ++ O.txt Syntax.Type.annotation_separator)
×
738
            @ record fields @ O.documentedSrc ret_type
×
739
          else
740
            O.documentedSrc (cstr ++ O.txt " " ++ O.keyword "of" ++ O.txt " ")
17✔
741
            @ record fields
17✔
742

743
    let variant cstrs : DocumentedSrc.t =
744
      let constructor id args res =
235✔
745
        let url = Url.from_identifier ~stop_before:true id in
436✔
746
        let attrs = [ "def"; "variant"; Url.Anchor.string_of_kind url.kind ] in
436✔
747
        let content =
748
          let doc = constructor id args res in
749
          O.documentedSrc (O.txt "| ") @ doc
436✔
750
        in
751
        (url, attrs, content)
752
      in
753
      match cstrs with
754
      | [] -> O.documentedSrc (O.txt "|")
×
755
      | _ :: _ ->
235✔
756
          let rows =
757
            cstrs
758
            |> List.map (fun cstr ->
759
                   let open Odoc_model.Lang.TypeDecl.Constructor in
436✔
760
                   let url, attrs, code =
761
                     constructor
762
                       (cstr.id :> Paths.Identifier.t)
763
                       cstr.args cstr.res
764
                   in
765
                   let anchor = Some url in
436✔
766
                   let doc = cstr.doc.elements in
767
                   let rhs = Comment.to_ir doc in
768
                   let doc = if not (Comment.has_doc doc) then [] else rhs in
73✔
769
                   let markers = Syntax.Comment.markers in
770
                   DocumentedSrc.Nested { anchor; attrs; code; doc; markers })
771
          in
772
          rows
235✔
773

774
    let extension_constructor (t : Odoc_model.Lang.Extension.Constructor.t) =
775
      let id = (t.id :> Paths.Identifier.t) in
154✔
776
      let url = Url.from_identifier ~stop_before:true id in
777
      let anchor = Some url in
154✔
778
      let attrs = [ "def"; "variant"; Url.Anchor.string_of_kind url.kind ] in
154✔
779
      let code = O.documentedSrc (O.txt "| ") @ constructor id t.args t.res in
154✔
780
      let doc = Comment.to_ir t.doc.elements in
781
      let markers = Syntax.Comment.markers in
154✔
782
      DocumentedSrc.Nested { anchor; attrs; code; doc; markers }
783

784
    let extension (t : Odoc_model.Lang.Extension.t) =
785
      let prefix =
126✔
786
        O.keyword "type" ++ O.txt " "
126✔
787
        ++ Link.from_path (t.type_path :> Paths.Path.t)
126✔
788
        ++ O.txt " +=" ++ O.sp
126✔
789
        ++
790
        if t.private_ then O.keyword Syntax.Type.private_keyword ++ O.sp
8✔
791
        else O.noop
118✔
792
      in
793
      let content =
126✔
794
        O.documentedSrc prefix
126✔
795
        @ List.map extension_constructor t.constructors
126✔
796
        @ O.documentedSrc
126✔
797
            (if Syntax.Type.type_def_semicolon then O.txt ";" else O.noop)
×
798
      in
799
      let attr = [ "type"; "extension" ] in
800
      let anchor = Some (Url.Anchor.extension_decl t) in
126✔
801
      let doc = Comment.to_ir t.doc.elements in
802
      let source_anchor =
126✔
803
        (* Take the anchor from the first constructor only for consistency with
804
           regular variants. *)
805
        match t.constructors with
806
        | hd :: _ -> source_anchor hd.source_loc
126✔
807
        | [] -> None
×
808
      in
809
      Item.Declaration { attr; anchor; doc; content; source_anchor }
810

811
    let exn (t : Odoc_model.Lang.Exception.t) =
812
      let cstr = constructor (t.id :> Paths.Identifier.t) t.args t.res in
66✔
813
      let content =
66✔
814
        O.documentedSrc (O.keyword "exception" ++ O.txt " ")
66✔
815
        @ cstr
816
        @ O.documentedSrc
66✔
817
            (if Syntax.Type.Exception.semicolon then O.txt ";" else O.noop)
×
818
      in
819
      let attr = [ "exception" ] in
820
      let anchor = path_to_id t.id in
821
      let doc = Comment.to_ir t.doc.elements in
66✔
822
      let source_anchor = source_anchor t.source_loc in
66✔
823
      Item.Declaration { attr; anchor; doc; content; source_anchor }
66✔
824

825
    let polymorphic_variant ~type_ident
826
        (t : Odoc_model.Lang.TypeExpr.Polymorphic_variant.t) =
827
      let row item =
74✔
828
        let kind_approx, cstr, doc =
156✔
829
          match item with
830
          | Odoc_model.Lang.TypeExpr.Polymorphic_variant.Type te ->
24✔
831
              ("unknown", O.documentedSrc (type_expr te), None)
24✔
832
          | Constructor { constant; name; arguments; doc; _ } -> (
132✔
833
              let cstr = "`" ^ name in
834
              ( "constructor",
835
                (match arguments with
836
                | [] -> O.documentedSrc (O.txt cstr)
58✔
837
                | _ ->
74✔
838
                    (* Multiple arguments in a polymorphic variant constructor correspond
839
                       to a conjunction of types, not a product: [`Lbl int&float].
840
                       If constant is [true], the conjunction starts with an empty type,
841
                       for instance [`Lbl &int].
842
                    *)
843
                    let wrapped_type_expr =
844
                      (* type conjunction in Reason is printed as `Lbl (t1)&(t2)` *)
845
                      if Syntax.Type.Variant.parenthesize_params then fun x ->
×
846
                        O.txt "(" ++ type_expr x ++ O.txt ")"
×
847
                      else fun x -> type_expr x
74✔
848
                    in
849
                    let params =
850
                      O.box_hv
851
                      @@ O.list arguments
74✔
852
                           ~sep:(O.txt " &" ++ O.sp)
74✔
853
                           ~f:wrapped_type_expr
854
                    in
855
                    let params =
74✔
856
                      if constant then O.txt "& " ++ params else params
×
857
                    in
858
                    O.documentedSrc
74✔
859
                      (O.txt cstr
74✔
860
                      ++
74✔
861
                      if Syntax.Type.Variant.parenthesize_params then params
×
862
                      else O.txt " " ++ O.keyword "of" ++ O.sp ++ params)),
74✔
863
                match doc with
864
                | { elements = []; _ } -> None
132✔
865
                | _ -> Some (Comment.to_ir doc.elements) ))
×
866
        in
867
        let markers = Syntax.Comment.markers in
868
        try
869
          let url = Url.Anchor.polymorphic_variant ~type_ident item in
870
          let attrs =
156✔
871
            [ "def"; "variant"; Url.Anchor.string_of_kind url.kind ]
156✔
872
          in
873
          let anchor = Some url in
874
          let code = O.documentedSrc (O.txt "| ") @ cstr in
156✔
875
          let doc = match doc with None -> [] | Some doc -> doc in
×
876
          DocumentedSrc.Nested { attrs; anchor; code; doc; markers }
877
        with Failure s ->
×
878
          Printf.eprintf "ERROR: %s\n%!" s;
879
          let code = O.documentedSrc (O.txt "| ") @ cstr in
×
880
          let attrs = [ "def"; kind_approx ] in
881
          let doc = [] in
882
          let anchor = None in
883
          DocumentedSrc.Nested { attrs; anchor; code; doc; markers }
884
      in
885
      let variants = List.map row t.elements in
886
      let intro, ending =
74✔
887
        match t.kind with
888
        | Fixed -> (O.documentedSrc (O.txt "[ "), O.documentedSrc (O.txt " ]"))
66✔
889
        | Open -> (O.documentedSrc (O.txt "[> "), O.documentedSrc (O.txt " ]"))
8✔
890
        | Closed [] ->
×
891
            (O.documentedSrc (O.txt "[< "), O.documentedSrc (O.txt " ]"))
×
892
        | Closed lst ->
×
893
            let constrs = String.concat ~sep:" " lst in
894
            ( O.documentedSrc (O.txt "[< "),
×
895
              O.documentedSrc (O.txt (" " ^ constrs ^ " ]")) )
×
896
      in
897
      intro @ variants @ ending
898

899
    let format_params :
900
        'row.
901
        ?delim:[ `parens | `brackets ] ->
902
        Odoc_model.Lang.TypeDecl.param list ->
903
        text =
904
     fun ?(delim = `parens) params ->
472✔
905
      let format_param_str
563✔
906
          { Odoc_model.Lang.TypeDecl.desc; variance; injectivity; kind = _ } =
907
        let desc =
624✔
908
          match desc with
909
          | Odoc_model.Lang.TypeDecl.Any -> [ "_" ]
37✔
910
          | Var s -> [ "'"; s ]
587✔
911
        in
912
        let var_desc =
913
          match variance with
914
          | None -> desc
608✔
915
          | Some Odoc_model.Lang.TypeDecl.Pos -> "+" :: desc
8✔
916
          | Some Odoc_model.Lang.TypeDecl.Neg -> "-" :: desc
8✔
917
          | Some Odoc_model.Lang.TypeDecl.Bivariant -> "+" :: "-" :: desc
×
918
        in
919
        let final = if injectivity then "!" :: var_desc else var_desc in
×
920
        String.concat ~sep:"" final
921
      in
922
      let format_param p =
923
        Type_expression.with_kind_annotation p.Odoc_model.Lang.TypeDecl.kind
236✔
924
          (O.txt (format_param_str p))
236✔
925
      in
926
      match params with
927
      | [] -> O.noop
59✔
928
      | [ x ] ->
388✔
929
          let base = format_param_str x |> Syntax.Type.handle_format_params in
388✔
930
          Type_expression.with_kind_annotation x.kind (O.txt base)
388✔
931
      | lst ->
116✔
932
          let left, right =
933
            match delim with `parens -> ("(", ")") | `brackets -> ("[", "]")
×
934
          in
935
          O.txt left
116✔
936
          ++ O.list lst ~sep:(O.txt ", ") ~f:format_param
116✔
937
          ++ O.txt right
116✔
938

939
    let format_constraints constraints =
940
      O.list constraints ~f:(fun (t1, t2) ->
3,103✔
941
          O.sp
104✔
942
          ++ (O.box_hv
104✔
943
             @@ O.keyword "constraint" ++ O.sp
104✔
944
                ++ O.box_hv_no_indent (type_expr t1)
104✔
945
                ++ O.txt " =" ++ O.sp
104✔
946
                ++ O.box_hv_no_indent (type_expr t2)))
104✔
947

948
    let format_manifest :
949
        'inner_row 'outer_row.
950
        ?is_substitution:bool ->
951
        ?compact_variants:bool ->
952
        Odoc_model.Lang.TypeDecl.Equation.t ->
953
        text * bool =
954
     fun ?(is_substitution = false) ?(compact_variants = true) equation ->
104✔
955
      let _ = compact_variants in
3,021✔
956
      (* TODO *)
957
      let private_ = equation.private_ in
958
      match equation.manifest with
959
      | None -> (O.noop, private_)
1,753✔
960
      | Some t ->
1,268✔
961
          let manifest =
962
            O.txt (if is_substitution then " :=" else " =")
23✔
963
            ++ O.sp
1,268✔
964
            ++ (if private_ then
1,268✔
965
                  O.keyword Syntax.Type.private_keyword ++ O.txt " "
8✔
966
                else O.noop)
1,260✔
967
            ++ type_expr t
1,268✔
968
          in
969
          (manifest, false)
1,268✔
970

971
    let type_decl ?(is_substitution = false)
2,968✔
972
        ((recursive, t) : Lang.Signature.recursive * Lang.TypeDecl.t) =
973
      let keyword' =
2,991✔
974
        match recursive with
975
        | Ordinary | Rec -> O.keyword "type"
×
976
        | And -> O.keyword "and"
18✔
977
        | Nonrec -> O.keyword "type" ++ O.txt " " ++ O.keyword "nonrec"
1✔
978
      in
979
      let tyname = Paths.Identifier.name t.id in
980
      let tconstr =
2,991✔
981
        match t.equation.params with
982
        | [] -> O.txt tyname
2,545✔
983
        | l ->
446✔
984
            let params = format_params l in
985
            Syntax.Type.handle_constructor_params (O.txt tyname) params
446✔
986
      in
987
      let kind_annot =
988
        match t.equation.kind with
989
        | Default -> O.noop
2,991✔
990
        | k -> O.txt " : " ++ Type_expression.kind_annotation k
×
991
      in
992
      let intro = keyword' ++ O.txt " " ++ tconstr ++ kind_annot in
2,991✔
993
      let constraints = format_constraints t.equation.constraints in
2,991✔
994
      let manifest, need_private, long_prefix =
2,991✔
995
        match t.equation.manifest with
996
        | Some (Odoc_model.Lang.TypeExpr.Polymorphic_variant variant) ->
74✔
997
            let code =
998
              polymorphic_variant
999
                ~type_ident:(t.id :> Paths.Identifier.t)
1000
                variant
1001
            in
1002
            let manifest =
74✔
1003
              O.documentedSrc
74✔
1004
                (O.ignore intro
74✔
1005
                ++ O.txt (if is_substitution then " :=" else " =")
×
1006
                ++ O.sp
74✔
1007
                ++
74✔
1008
                if t.equation.private_ then
1009
                  O.keyword Syntax.Type.private_keyword ++ O.txt " "
8✔
1010
                else O.noop)
66✔
1011
              @ code
1012
            in
1013
            (manifest, false, O.noop)
1014
        | _ ->
2,917✔
1015
            let manifest, need_private =
1016
              format_manifest ~is_substitution t.equation
1017
            in
1018
            let text = O.ignore intro ++ manifest in
2,917✔
1019
            (O.documentedSrc @@ text, need_private, text)
2,917✔
1020
      in
1021
      let representation =
1022
        match t.representation with
1023
        | None -> []
2,629✔
1024
        | Some repr ->
362✔
1025
            let content =
1026
              match repr with
1027
              | Extensible -> O.documentedSrc (O.txt "..")
61✔
1028
              | Variant cstrs -> variant cstrs
235✔
1029
              | Record fields -> record fields
66✔
1030
              | Record_unboxed_product fields -> unboxed_record fields
×
1031
            in
1032
            if List.length content > 0 then
362✔
1033
              O.documentedSrc
362✔
1034
                (O.ignore long_prefix ++ O.txt " =" ++ O.sp
362✔
1035
                ++
362✔
1036
                if need_private then
1037
                  O.keyword Syntax.Type.private_keyword ++ O.txt " "
8✔
1038
                else O.noop)
354✔
1039
              @ content
1040
            else []
×
1041
      in
1042
      let content =
1043
        O.documentedSrc intro @ manifest @ representation
2,991✔
1044
        @ O.documentedSrc constraints
2,991✔
1045
        @ O.documentedSrc
2,991✔
1046
            (if Syntax.Type.type_def_semicolon then O.txt ";" else O.noop)
×
1047
      in
1048
      let attr = "type" :: (if is_substitution then [ "subst" ] else []) in
23✔
1049
      let anchor = path_to_id t.id in
1050
      let doc = Comment.to_ir t.doc.elements in
2,991✔
1051
      let source_anchor = source_anchor t.source_loc in
2,991✔
1052
      Item.Declaration { attr; anchor; doc; content; source_anchor }
2,991✔
1053
  end
1054

1055
  open Type_declaration
1056

1057
  module Value : sig
1058
    val value : Lang.Value.t -> Item.t
1059
  end = struct
1060
    let rec arity_of_type_expr = function
1061
      | Lang.TypeExpr.Arrow (_lbl, _arg, curried) ->
×
1062
          1 + arity_of_type_expr curried
×
1063
      | _ -> 0
×
1064

1065
    let value (t : Odoc_model.Lang.Value.t) =
1066
      let extra_attr, semicolon =
912✔
1067
        match t.value with
1068
        | Abstract -> ([], Syntax.Value.semicolon)
888✔
1069
        | External _ -> ([ "external" ], Syntax.Type.External.semicolon)
24✔
1070
      in
1071
      let name = Paths.Identifier.name t.id in
1072
      let zero_alloc =
912✔
1073
        match
1074
          List.find
1075
            (function Odoc_model.Lang.Value.Zero_alloc _ -> true)
×
1076
            t.ext_attrs
1077
        with
1078
        | exception Not_found -> O.noop
912✔
1079
        | Zero_alloc { opt; strict; arity; custom_error_msg } ->
×
1080
            let ext_arg =
1081
              match (opt, strict) with
1082
              | true, false -> " opt"
×
1083
              | false, true -> " strict"
×
1084
              | true, true -> " strict opt"
×
1085
              | false, false -> ""
×
1086
            in
1087
            let ext_arg =
1088
              match Int.equal (arity_of_type_expr t.type_) arity with
×
1089
              | true -> ext_arg
×
1090
              | false -> ext_arg ^ Printf.sprintf " arity %d" arity
×
1091
            in
1092
            let ext_arg =
1093
              match custom_error_msg with
1094
              | None -> ext_arg
×
1095
              | Some s -> ext_arg ^ Printf.sprintf "custom_error_message %S" s
×
1096
            in
1097
            let ext_attr = Printf.sprintf "[@@zero_alloc%s]" ext_arg in
1098
            O.cut ++ O.txt " " ++ O.txt ext_attr
×
1099
      in
1100
      let content =
1101
        O.documentedSrc
1102
          (O.box_hv
912✔
1103
          @@ O.keyword Syntax.Value.variable_keyword
912✔
1104
             ++ O.txt " " ++ O.txt name
912✔
1105
             ++ O.txt Syntax.Type.annotation_separator
912✔
1106
             ++ O.cut ++ type_expr t.type_
912✔
1107
             ++ Modalities.format t.modalities
912✔
1108
             ++ zero_alloc
912✔
1109
             ++ if semicolon then O.txt ";" else O.noop)
×
1110
      in
1111
      let attr = [ "value" ] @ extra_attr in
912✔
1112
      let anchor = path_to_id t.id in
1113
      let doc = Comment.to_ir t.doc.elements in
912✔
1114
      let source_anchor = source_anchor t.source_loc in
912✔
1115
      Item.Declaration { attr; anchor; doc; content; source_anchor }
912✔
1116
  end
1117

1118
  open Value
1119

1120
  (* This chunk of code is responsible for sectioning list of items
1121
     according to headings by extracting headings as Items.
1122

1123
     TODO: This sectioning would be better done as a pass on the model directly.
1124
  *)
1125
  module Sectioning : sig
1126
    open Odoc_model
1127

1128
    val comment_items : Comment.elements -> Item.t list
1129

1130
    val docs : Comment.elements -> Item.t list * Item.t list
1131
  end = struct
1132
    let take_until_heading_or_end (docs : Odoc_model.Comment.elements) =
1133
      let content, _, rest =
693✔
1134
        Doctree.Take.until docs ~classify:(fun b ->
1135
            match b.Location.value with
1,481✔
1136
            | `Heading _ -> Stop_and_keep
202✔
1137
            | #Odoc_model.Comment.attached_block_element as doc ->
1,279✔
1138
                let content = Comment.attached_block_element doc in
1139
                Accum content)
1,279✔
1140
      in
1141
      (content, rest)
693✔
1142

1143
    let comment_items (input0 : Odoc_model.Comment.elements) =
1144
      let rec loop input_comment acc =
963✔
1145
        match input_comment with
2,493✔
1146
        | [] -> List.rev acc
963✔
1147
        | element :: input_comment -> (
1,530✔
1148
            match element.Location.value with
1149
            | `Heading h ->
837✔
1150
                let item = Comment.heading h in
1151
                loop input_comment (item :: acc)
837✔
1152
            | _ ->
693✔
1153
                let content, input_comment =
1154
                  take_until_heading_or_end (element :: input_comment)
1155
                in
1156
                let item = Item.Text content in
693✔
1157
                loop input_comment (item :: acc))
1158
      in
1159
      loop input0 []
1160

1161
    (* For doc pages, we want the header to contain everything until
1162
       the first heading, then everything before the next heading which
1163
       is either lower, or a section.
1164
    *)
1165
    let docs input_comment =
1166
      let items = comment_items input_comment in
69✔
1167
      let until_first_heading, o, items =
69✔
1168
        Doctree.Take.until items ~classify:(function
1169
          | Item.Heading h as i -> Stop_and_accum ([ i ], Some h.level)
69✔
1170
          | i -> Accum [ i ])
×
1171
      in
1172
      match o with
69✔
1173
      | None -> (until_first_heading, items)
×
1174
      | Some level ->
69✔
1175
          let max_level = if level = 1 then 2 else level in
×
1176
          let before_second_heading, _, items =
1177
            Doctree.Take.until items ~classify:(function
1178
              | Item.Heading h when h.level >= max_level -> Stop_and_keep
13✔
1179
              | i -> Accum [ i ])
38✔
1180
          in
1181
          let header = until_first_heading @ before_second_heading in
69✔
1182
          (header, items)
1183
  end
1184

1185
  module Class : sig
1186
    val class_ : Lang.Class.t -> Item.t
1187

1188
    val class_type : Lang.ClassType.t -> Item.t
1189
  end = struct
1190
    let class_type_expr (cte : Odoc_model.Lang.ClassType.expr) =
1191
      match cte with
249✔
1192
      | Constr (path, args) ->
58✔
1193
          let link = Link.from_path (path :> Paths.Path.t) in
1194
          format_type_path ~delim:`brackets args link
58✔
1195
      | Signature _ ->
191✔
1196
          Syntax.Class.open_tag ++ O.txt " ... " ++ Syntax.Class.close_tag
191✔
1197

1198
    let method_ (t : Odoc_model.Lang.Method.t) =
1199
      let name = Paths.Identifier.name t.id in
90✔
1200
      let virtual_ =
90✔
1201
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
8✔
1202
      in
1203
      let private_ =
1204
        if t.private_ then O.keyword "private" ++ O.txt " " else O.noop
8✔
1205
      in
1206
      let content =
1207
        O.documentedSrc
1208
          (O.keyword "method" ++ O.txt " " ++ private_ ++ virtual_ ++ O.txt name
90✔
1209
          ++ O.txt Syntax.Type.annotation_separator
90✔
1210
          ++ type_expr t.type_)
90✔
1211
      in
1212
      let attr = [ "method" ] in
90✔
1213
      let anchor = path_to_id t.id in
1214
      let doc = Comment.to_ir t.doc.elements in
90✔
1215
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
90✔
1216

1217
    let instance_variable (t : Odoc_model.Lang.InstanceVariable.t) =
1218
      let name = Paths.Identifier.name t.id in
17✔
1219
      let virtual_ =
17✔
1220
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
8✔
1221
      in
1222
      let mutable_ =
1223
        if t.mutable_ then O.keyword "mutable" ++ O.txt " " else O.noop
8✔
1224
      in
1225
      let content =
1226
        O.documentedSrc
1227
          (O.keyword "val" ++ O.txt " " ++ mutable_ ++ virtual_ ++ O.txt name
17✔
1228
          ++ O.txt Syntax.Type.annotation_separator
17✔
1229
          ++ type_expr t.type_)
17✔
1230
      in
1231
      let attr = [ "value"; "instance-variable" ] in
17✔
1232
      let anchor = path_to_id t.id in
1233
      let doc = Comment.to_ir t.doc.elements in
17✔
1234
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
17✔
1235

1236
    let inherit_ (ih : Lang.ClassSignature.Inherit.t) =
1237
      let cte =
16✔
1238
        match ih.expr with
1239
        | Signature _ -> assert false (* Bold. *)
1240
        | cty -> cty
16✔
1241
      in
1242
      let content =
1243
        O.documentedSrc (O.keyword "inherit" ++ O.txt " " ++ class_type_expr cte)
16✔
1244
      in
1245
      let attr = [ "inherit" ] in
16✔
1246
      let anchor = None in
1247
      let doc = Comment.to_ir ih.doc.elements in
1248
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
16✔
1249

1250
    let constraint_ (cst : Lang.ClassSignature.Constraint.t) =
1251
      let content =
8✔
1252
        O.documentedSrc (format_constraints [ (cst.left, cst.right) ])
8✔
1253
      in
1254
      let attr = [] in
8✔
1255
      let anchor = None in
1256
      let doc = Comment.to_ir cst.doc.elements in
1257
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
8✔
1258

1259
    let class_signature (c : Lang.ClassSignature.t) =
1260
      let rec loop l acc_items =
233✔
1261
        match l with
388✔
1262
        | [] -> List.rev acc_items
233✔
1263
        | item :: rest -> (
155✔
1264
            let continue item = loop rest (item :: acc_items) in
131✔
1265
            match (item : Lang.ClassSignature.item) with
1266
            | Inherit cty -> continue @@ inherit_ cty
16✔
1267
            | Method m -> continue @@ method_ m
90✔
1268
            | InstanceVariable v -> continue @@ instance_variable v
17✔
1269
            | Constraint cst -> continue @@ constraint_ cst
8✔
1270
            | Comment `Stop ->
8✔
1271
                let rest =
1272
                  List.skip_until rest ~p:(function
1273
                    | Lang.ClassSignature.Comment `Stop -> true
8✔
1274
                    | _ -> false)
8✔
1275
                in
1276
                loop rest acc_items
8✔
1277
            | Comment (`Docs c) ->
16✔
1278
                let items = Sectioning.comment_items c.elements in
1279
                loop rest (List.rev_append items acc_items))
16✔
1280
      in
1281
      (* FIXME: use [t.self] *)
1282
      (c.doc.elements, loop c.items [])
233✔
1283

1284
    let rec class_decl (cd : Odoc_model.Lang.Class.decl) =
1285
      match cd with
182✔
1286
      | ClassType expr -> class_type_expr expr
166✔
1287
      (* TODO: factorize the following with [type_expr] *)
1288
      | Arrow (None, src, dst) ->
16✔
1289
          O.span
16✔
1290
            (type_expr ~needs_parentheses:true src
16✔
1291
            ++ O.txt " " ++ Syntax.Type.arrow)
16✔
1292
          ++ O.txt " " ++ class_decl dst
16✔
1293
      | Arrow (Some (RawOptional _ as lbl), _src, dst) ->
×
1294
          O.span
×
1295
            (O.box_hv
×
1296
            @@ label lbl ++ O.txt ":"
×
1297
               ++ tag "error" (O.txt "???")
×
1298
               ++ O.txt " " ++ Syntax.Type.arrow)
×
1299
          ++ O.sp ++ class_decl dst
×
1300
      | Arrow (Some lbl, src, dst) ->
×
1301
          O.span
×
1302
            (label lbl ++ O.txt ":"
×
1303
            ++ type_expr ~needs_parentheses:true src
×
1304
            ++ O.txt " " ++ Syntax.Type.arrow)
×
1305
          ++ O.txt " " ++ class_decl dst
×
1306

1307
    let class_ (t : Odoc_model.Lang.Class.t) =
1308
      let name = Paths.Identifier.name t.id in
166✔
1309
      let params =
166✔
1310
        match t.params with
1311
        | [] -> O.noop
142✔
1312
        | _ :: _ as params -> format_params ~delim:`brackets params ++ O.txt " "
24✔
1313
      in
1314
      let virtual_ =
1315
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
24✔
1316
      in
1317

1318
      let source_anchor = source_anchor t.source_loc in
1319
      let cname, expansion, expansion_doc =
166✔
1320
        match t.expansion with
1321
        | None -> (O.documentedSrc @@ O.txt name, None, None)
×
1322
        | Some csig ->
166✔
1323
            let expansion_doc, items = class_signature csig in
1324
            let url = Url.Path.from_identifier t.id in
166✔
1325
            let page =
166✔
1326
              make_expansion_page ~source_anchor url
1327
                [ t.doc.elements; expansion_doc ]
1328
                items
1329
            in
1330
            ( O.documentedSrc @@ path url [ inline @@ Text name ],
166✔
1331
              Some page,
1332
              Some expansion_doc )
1333
      in
1334
      let summary =
1335
        O.txt Syntax.Type.annotation_separator ++ class_decl t.type_
166✔
1336
      in
1337
      let cd =
166✔
1338
        attach_expansion
1339
          (Syntax.Type.annotation_separator, "object", "end")
1340
          expansion summary
1341
      in
1342
      let content =
166✔
1343
        O.documentedSrc (O.keyword "class" ++ O.txt " " ++ virtual_ ++ params)
166✔
1344
        @ cname @ cd
1345
      in
1346
      let attr = [ "class" ] in
1347
      let anchor = path_to_id t.id in
1348
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
166✔
1349
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1350

1351
    let class_type (t : Odoc_model.Lang.ClassType.t) =
1352
      let name = Paths.Identifier.name t.id in
67✔
1353
      let params = format_params ~delim:`brackets t.params in
67✔
1354
      let virtual_ =
67✔
1355
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
8✔
1356
      in
1357
      let source_anchor = source_anchor t.source_loc in
1358
      let cname, expansion, expansion_doc =
67✔
1359
        match t.expansion with
1360
        | None -> (O.documentedSrc @@ O.txt name, None, None)
×
1361
        | Some csig ->
67✔
1362
            let url = Url.Path.from_identifier t.id in
1363
            let expansion_doc, items = class_signature csig in
67✔
1364
            let page =
67✔
1365
              make_expansion_page ~source_anchor url
1366
                [ t.doc.elements; expansion_doc ]
1367
                items
1368
            in
1369
            ( O.documentedSrc @@ path url [ inline @@ Text name ],
67✔
1370
              Some page,
1371
              Some expansion_doc )
1372
      in
1373
      let summary = O.txt " = " ++ class_type_expr t.expr in
67✔
1374
      let expr = attach_expansion (" = ", "object", "end") expansion summary in
67✔
1375
      let content =
67✔
1376
        O.documentedSrc
67✔
1377
          (O.keyword "class" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
67✔
1378
         ++ virtual_ ++ params ++ O.txt " ")
67✔
1379
        @ cname @ expr
1380
      in
1381
      let attr = [ "class-type" ] in
1382
      let anchor = path_to_id t.id in
1383
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
67✔
1384
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1385
  end
1386

1387
  open Class
1388

1389
  module Module : sig
1390
    val signature : Lang.Signature.t -> Comment.Comment.elements * Item.t list
1391
    (** Returns [header_doc, content]. *)
1392
  end = struct
1393
    let internal_module m =
1394
      let open Lang.Module in
1,924✔
1395
      match m.id.iv with
1396
      | `Module (_, name) when ModuleName.is_hidden name -> true
81✔
1397
      | _ -> false
1,843✔
1398

1399
    let internal_type t =
1400
      let open Lang.TypeDecl in
2,969✔
1401
      match t.id.iv with
1402
      | `Type (_, name) when TypeName.is_hidden name -> true
1✔
1403
      | _ -> false
2,968✔
1404

1405
    let internal_value v =
1406
      let open Lang.Value in
1,018✔
1407
      match v.id.iv with
1408
      | `Value (_, name) when ValueName.is_hidden name -> true
106✔
1409
      | _ -> false
912✔
1410

1411
    let internal_module_type t =
1412
      let open Lang.ModuleType in
1,361✔
1413
      match t.id.iv with
1414
      | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true
×
1415
      | _ -> false
1,361✔
1416

1417
    let internal_module_substitution t =
1418
      let open Lang.ModuleSubstitution in
8✔
1419
      match t.id.iv with
1420
      | `Module (_, name) when ModuleName.is_hidden name -> true
×
1421
      | _ -> false
8✔
1422

1423
    let internal_module_type_substitution t =
1424
      let open Lang.ModuleTypeSubstitution in
8✔
1425
      match t.id.iv with
1426
      | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true
×
1427
      | _ -> false
8✔
1428

1429
    let rec signature (s : Lang.Signature.t) =
1430
      let rec loop l acc_items =
3,582✔
1431
        match l with
12,543✔
1432
        | [] -> List.rev acc_items
3,582✔
1433
        | item :: rest -> (
8,961✔
1434
            let continue (item : Item.t) = loop rest (item :: acc_items) in
7,838✔
1435
            match (item : Lang.Signature.item) with
1436
            | Module (_, m) when internal_module m -> loop rest acc_items
81✔
1437
            | Type (_, t) when internal_type t -> loop rest acc_items
1✔
1438
            | Value v when internal_value v -> loop rest acc_items
106✔
1439
            | ModuleType m when internal_module_type m -> loop rest acc_items
×
1440
            | ModuleSubstitution m when internal_module_substitution m ->
8✔
1441
                loop rest acc_items
×
1442
            | ModuleTypeSubstitution m when internal_module_type_substitution m
8✔
1443
              ->
1444
                loop rest acc_items
×
1445
            | ModuleTypeSubstitution m -> continue @@ module_type_substitution m
8✔
1446
            | Module (_, m) -> continue @@ module_ m
1,843✔
1447
            | ModuleType m -> continue @@ module_type m
1,361✔
1448
            | Class (_, c) -> continue @@ class_ c
166✔
1449
            | ClassType (_, c) -> continue @@ class_type c
67✔
1450
            | Include m -> continue @@ include_ m
290✔
1451
            | ModuleSubstitution m -> continue @@ module_substitution m
8✔
1452
            | TypeSubstitution t ->
23✔
1453
                continue @@ type_decl ~is_substitution:true (Ordinary, t)
23✔
1454
            | Type (r, t) -> continue @@ type_decl (r, t)
2,968✔
1455
            | TypExt e -> continue @@ extension e
126✔
1456
            | Exception e -> continue @@ exn e
66✔
1457
            | Value v -> continue @@ value v
912✔
1458
            | Open o ->
82✔
1459
                let items = Sectioning.comment_items o.doc.elements in
1460
                loop rest (List.rev_append items acc_items)
82✔
1461
            | Comment `Stop ->
57✔
1462
                let rest =
1463
                  List.skip_until rest ~p:(function
1464
                    | Lang.Signature.Comment `Stop -> true
49✔
1465
                    | _ -> false)
65✔
1466
                in
1467
                loop rest acc_items
57✔
1468
            | Comment (`Docs c) ->
796✔
1469
                let items = Sectioning.comment_items c.elements in
1470
                loop rest (List.rev_append items acc_items))
796✔
1471
      in
1472
      ((Lang.extract_signature_doc s).elements, loop s.items [])
3,582✔
1473

1474
    and functor_parameter :
1475
        Odoc_model.Lang.FunctorParameter.parameter -> DocumentedSrc.t =
1476
     fun arg ->
1477
      let open Odoc_model.Lang.FunctorParameter in
211✔
1478
      let name = Paths.Identifier.name arg.id in
1479
      let render_ty = arg.expr in
211✔
1480
      let modtyp =
1481
        mty_in_decl (arg.id :> Paths.Identifier.Signature.t) render_ty
1482
      in
1483
      let modname, mod_decl =
211✔
1484
        match expansion_of_module_type_expr arg.expr with
1485
        | None ->
×
1486
            let modname = O.txt (Paths.Identifier.name arg.id) in
×
1487
            (modname, O.documentedSrc modtyp)
×
1488
        | Some (expansion_doc, items) ->
211✔
1489
            let url = Url.Path.from_identifier arg.id in
1490
            let modname = path url [ inline @@ Text name ] in
211✔
1491
            let type_with_expansion =
211✔
1492
              let content =
1493
                make_expansion_page ~source_anchor:None url [ expansion_doc ]
1494
                  items
1495
              in
1496
              let summary = O.render modtyp in
211✔
1497
              let status = `Default in
211✔
1498
              let expansion =
1499
                O.documentedSrc
211✔
1500
                  (O.txt Syntax.Type.annotation_separator ++ O.keyword "sig")
211✔
1501
                @ DocumentedSrc.[ Subpage { content; status } ]
1502
                @ O.documentedSrc (O.keyword "end")
211✔
1503
              in
1504
              DocumentedSrc.
1505
                [
1506
                  Alternative
1507
                    (Expansion { status = `Default; summary; url; expansion });
1508
                ]
1509
            in
1510
            (modname, type_with_expansion)
1511
      in
1512
      O.documentedSrc (O.keyword "module" ++ O.txt " ")
211✔
1513
      @ O.documentedSrc modname @ mod_decl
211✔
1514

1515
    and module_substitution (t : Odoc_model.Lang.ModuleSubstitution.t) =
1516
      let name = Paths.Identifier.name t.id in
8✔
1517
      let path = Link.from_path (t.manifest :> Paths.Path.t) in
8✔
1518
      let content =
8✔
1519
        O.documentedSrc
1520
          (O.keyword "module" ++ O.txt " " ++ O.txt name ++ O.txt " :=" ++ O.sp
8✔
1521
         ++ path)
8✔
1522
      in
1523
      let attr = [ "module-substitution" ] in
8✔
1524
      let anchor = path_to_id t.id in
1525
      let doc = Comment.to_ir t.doc.elements in
8✔
1526
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
8✔
1527

1528
    and module_type_substitution (t : Odoc_model.Lang.ModuleTypeSubstitution.t)
1529
        =
1530
      let prefix =
8✔
1531
        O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
8✔
1532
      in
1533
      let source_anchor = None in
8✔
1534
      let modname = Paths.Identifier.name t.id in
1535
      let modname, expansion_doc, mty =
8✔
1536
        module_type_manifest ~subst:true ~source_anchor modname t.id
1537
          t.doc.elements (Some t.manifest) prefix
1538
      in
1539
      let content =
8✔
1540
        O.documentedSrc (prefix ++ modname)
8✔
1541
        @ mty
1542
        @ O.documentedSrc
8✔
1543
            (if Syntax.Mod.close_tag_semicolon then O.txt ";" else O.noop)
×
1544
      in
1545
      let attr = [ "module-type" ] in
1546
      let anchor = path_to_id t.id in
1547
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
8✔
1548
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1549

1550
    and simple_expansion :
1551
        Odoc_model.Lang.ModuleType.simple_expansion ->
1552
        Comment.Comment.elements * Item.t list =
1553
     fun t ->
1554
      let rec extract_functor_params
2,933✔
1555
          (f : Odoc_model.Lang.ModuleType.simple_expansion) =
1556
        match f with
3,152✔
1557
        | Signature sg -> (None, sg)
2,933✔
1558
        | Functor (p, expansion) ->
219✔
1559
            let add_to params =
1560
              match p with Unit -> params | Named p -> p :: params
8✔
1561
            in
1562
            let params, sg = extract_functor_params expansion in
1563
            let params = match params with None -> [] | Some p -> p in
36✔
1564
            (Some (add_to params), sg)
219✔
1565
      in
1566
      match extract_functor_params t with
1567
      | None, sg -> signature sg
2,750✔
1568
      | Some params, sg ->
183✔
1569
          let sg_doc, content = signature sg in
1570
          let params =
183✔
1571
            let decl_of_arg arg =
1572
              let content = functor_parameter arg in
211✔
1573
              let attr = [ "parameter" ] in
211✔
1574
              let anchor =
1575
                Some (Url.Anchor.from_identifier (arg.id :> Paths.Identifier.t))
211✔
1576
              in
1577
              let doc = [] in
1578
              [
1579
                Item.Declaration
1580
                  { content; anchor; attr; doc; source_anchor = None };
1581
              ]
1582
            in
1583
            List.concat_map decl_of_arg params
183✔
1584
          in
1585
          let prelude = mk_heading ~label:"parameters" "Parameters" :: params
183✔
1586
          and content = mk_heading ~label:"signature" "Signature" :: content in
183✔
1587
          (sg_doc, prelude @ content)
1588

1589
    and expansion_of_module_type_expr :
1590
        Odoc_model.Lang.ModuleType.expr ->
1591
        (Comment.Comment.elements * Item.t list) option =
1592
     fun t ->
1593
      let rec simple_expansion_of (t : Odoc_model.Lang.ModuleType.expr) =
3,218✔
1594
        match t with
3,437✔
1595
        | Path { p_expansion = None; _ }
379✔
1596
        | TypeOf { t_expansion = None; _ }
8✔
1597
        | With { w_expansion = None; _ }
×
1598
        | Strengthen { s_expansion = None; _ } ->
×
1599
            None
1600
        | Path { p_expansion = Some e; _ }
426✔
1601
        | TypeOf { t_expansion = Some e; _ }
56✔
1602
        | With { w_expansion = Some e; _ }
218✔
1603
        | Strengthen { s_expansion = Some e; _ } ->
×
1604
            Some e
1605
        | Signature sg -> Some (Signature sg)
2,131✔
1606
        | Functor (f_parameter, e) -> (
219✔
1607
            match simple_expansion_of e with
1608
            | Some e -> Some (Functor (f_parameter, e))
211✔
1609
            | None -> None)
8✔
1610
      in
1611
      match simple_expansion_of t with
1612
      | None -> None
387✔
1613
      | Some e -> Some (simple_expansion e)
2,831✔
1614

1615
    and module_ : Odoc_model.Lang.Module.t -> Item.t =
1616
     fun t ->
1617
      let modname = Paths.Identifier.name t.id in
1,843✔
1618
      let expansion =
1,843✔
1619
        match t.type_ with
1620
        | Alias (_, Some e) -> Some (simple_expansion e)
102✔
1621
        | Alias (_, None) -> None
186✔
1622
        | ModuleType e -> expansion_of_module_type_expr e
1,555✔
1623
      in
1624
      let source_anchor = source_anchor t.source_loc in
1625
      let modname, status, expansion, expansion_doc =
1,843✔
1626
        match expansion with
1627
        | None -> (O.txt modname, `Default, None, None)
322✔
1628
        | Some (expansion_doc, items) ->
1,521✔
1629
            let status =
1630
              match t.type_ with
1631
              | ModuleType (Signature _) -> `Inline
987✔
1632
              | _ -> `Default
534✔
1633
            in
1634
            let url = Url.Path.from_identifier t.id in
1635
            let link = path url [ inline @@ Text modname ] in
1,521✔
1636
            let page =
1,521✔
1637
              make_expansion_page ~source_anchor url
1638
                [ t.doc.elements; expansion_doc ]
1639
                items
1640
            in
1641
            (link, status, Some page, Some expansion_doc)
1,521✔
1642
      in
1643
      let intro = O.keyword "module" ++ O.txt " " ++ modname in
1,843✔
1644
      let summary = O.ignore intro ++ mdexpr_in_decl t.id t.type_ in
1,843✔
1645
      let modexpr =
1,843✔
1646
        attach_expansion ~status
1647
          (Syntax.Type.annotation_separator, "sig", "end")
1648
          expansion summary
1649
      in
1650
      let content =
1,843✔
1651
        O.documentedSrc intro @ modexpr
1,843✔
1652
        @ O.documentedSrc
1,843✔
1653
            (if Syntax.Mod.close_tag_semicolon then O.txt ";" else O.noop)
×
1654
      in
1655
      let attr = [ "module" ] in
1656
      let anchor = path_to_id t.id in
1657
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
1,843✔
1658
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1659

1660
    and simple_expansion_in_decl (base : Paths.Identifier.Module.t) se =
1661
      let rec ty_of_se :
102✔
1662
          Lang.ModuleType.simple_expansion -> Lang.ModuleType.expr = function
1663
        | Signature sg -> Signature sg
102✔
1664
        | Functor (arg, sg) -> Functor (arg, ty_of_se sg)
×
1665
      in
1666
      mty_in_decl (base :> Paths.Identifier.Signature.t) (ty_of_se se)
102✔
1667

1668
    and mdexpr_in_decl (base : Paths.Identifier.Module.t) md =
1669
      let sig_dotdotdot =
1,843✔
1670
        O.txt Syntax.Type.annotation_separator
1,843✔
1671
        ++ O.cut ++ Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
1,843✔
1672
      in
1673
      match md with
1,843✔
1674
      | Alias (_, Some se) -> simple_expansion_in_decl base se
102✔
1675
      | Alias (p, _) when not Paths.Path.(is_hidden (p :> t)) ->
186✔
1676
          O.txt " =" ++ O.sp ++ mdexpr md
186✔
1677
      | Alias _ -> sig_dotdotdot
×
1678
      | ModuleType mt -> mty_in_decl (base :> Paths.Identifier.Signature.t) mt
1,555✔
1679

1680
    and mdexpr : Odoc_model.Lang.Module.decl -> text = function
1681
      | Alias (mod_path, _) -> Link.from_path (mod_path :> Paths.Path.t)
242✔
1682
      | ModuleType mt -> mty mt
×
1683

1684
    and module_type_manifest ~subst ~source_anchor modname id doc manifest
1685
        prefix =
1686
      let expansion =
1,369✔
1687
        match manifest with
1688
        | None -> None
128✔
1689
        | Some e -> expansion_of_module_type_expr e
1,241✔
1690
      in
1691
      let modname, expansion, expansion_doc =
1692
        match expansion with
1693
        | None -> (O.txt modname, None, None)
379✔
1694
        | Some (expansion_doc, items) ->
990✔
1695
            let url = Url.Path.from_identifier id in
1696
            let link = path url [ inline @@ Text modname ] in
990✔
1697
            let page =
990✔
1698
              make_expansion_page ~source_anchor url [ doc; expansion_doc ]
1699
                items
1700
            in
1701
            (link, Some page, Some expansion_doc)
990✔
1702
      in
1703
      let summary =
1704
        match manifest with
1705
        | None -> O.noop
128✔
1706
        | Some expr ->
1,241✔
1707
            O.ignore (prefix ++ modname)
1,241✔
1708
            ++ (if subst then O.txt " :=" ++ O.sp else O.txt " =" ++ O.sp)
8✔
1709
            ++ mty expr
1,241✔
1710
      in
1711
      ( modname,
1712
        expansion_doc,
1713
        attach_expansion (" = ", "sig", "end") expansion summary )
1,369✔
1714

1715
    and module_type (t : Odoc_model.Lang.ModuleType.t) =
1716
      let prefix =
1,361✔
1717
        O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
1,361✔
1718
      in
1719
      let modname = Paths.Identifier.name t.id in
1,361✔
1720
      let source_anchor = source_anchor t.source_loc in
1,361✔
1721
      let modname, expansion_doc, mty =
1,361✔
1722
        module_type_manifest ~subst:false ~source_anchor modname t.id
1723
          t.doc.elements t.expr prefix
1724
      in
1725
      let content =
1,361✔
1726
        O.documentedSrc (prefix ++ modname)
1,361✔
1727
        @ mty
1728
        @ O.documentedSrc
1,361✔
1729
            (if Syntax.Mod.close_tag_semicolon then O.txt ";" else O.noop)
×
1730
      in
1731
      let attr = [ "module-type" ] in
1732
      let anchor = path_to_id t.id in
1733
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
1,361✔
1734
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1735

1736
    and umty_hidden : Odoc_model.Lang.ModuleType.U.expr -> bool = function
1737
      | Path p -> Paths.Path.(is_hidden (p :> t))
403✔
1738
      | With (_, expr) -> umty_hidden expr
25✔
1739
      | TypeOf (ModPath m, _) | TypeOf (StructInclude m, _) ->
42✔
1740
          Paths.Path.(is_hidden (m :> t))
1741
      | Signature _ -> false
14✔
1742
      | Strengthen (expr, p, _) ->
×
1743
          umty_hidden expr || Paths.Path.(is_hidden (p :> t))
×
1744

1745
    and mty_hidden : Odoc_model.Lang.ModuleType.expr -> bool = function
1746
      | Path { p_path = mty_path; _ } -> Paths.Path.(is_hidden (mty_path :> t))
837✔
1747
      | With { w_expr; _ } -> umty_hidden w_expr
218✔
1748
      | TypeOf { t_desc = ModPath m; _ }
48✔
1749
      | TypeOf { t_desc = StructInclude m; _ } ->
16✔
1750
          Paths.Path.(is_hidden (m :> t))
1751
      | _ -> false
2,297✔
1752

1753
    and mty_with subs expr =
1754
      umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " "
238✔
1755
      ++ O.list
238✔
1756
           ~sep:(O.cut ++ O.txt " " ++ O.keyword "and" ++ O.txt " ")
238✔
1757
           ~f:(fun x -> O.span (substitution x))
279✔
1758
           subs
1759

1760
    and mty_strengthen expr path =
1761
      umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " "
×
1762
      ++ Link.from_path (path :> Paths.Path.t)
×
1763

1764
    and mty_typeof t_desc =
1765
      match t_desc with
154✔
1766
      | Odoc_model.Lang.ModuleType.ModPath m ->
90✔
1767
          O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
90✔
1768
          ++ O.keyword "of" ++ O.txt " "
90✔
1769
          ++ Link.from_path (m :> Paths.Path.t)
90✔
1770
      | StructInclude m ->
64✔
1771
          O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
64✔
1772
          ++ O.keyword "of" ++ O.txt " " ++ O.keyword "struct" ++ O.txt " "
64✔
1773
          ++ O.keyword "include" ++ O.txt " "
64✔
1774
          ++ Link.from_path (m :> Paths.Path.t)
64✔
1775
          ++ O.txt " " ++ O.keyword "end"
64✔
1776

1777
    and is_elidable_with_u : Odoc_model.Lang.ModuleType.U.expr -> bool =
1778
      function
1779
      | Path _ -> false
213✔
1780
      | Signature _ -> true
5✔
1781
      | With (_, expr) -> is_elidable_with_u expr
×
1782
      | TypeOf _ -> false
25✔
1783
      | Strengthen (expr, _, _) -> is_elidable_with_u expr
×
1784

1785
    and umty : Odoc_model.Lang.ModuleType.U.expr -> text =
1786
     fun m ->
1787
      match m with
527✔
1788
      | Path p -> Link.from_path (p :> Paths.Path.t)
403✔
1789
      | Signature _ ->
9✔
1790
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
9✔
1791
      | With (_, expr) when is_elidable_with_u expr ->
25✔
1792
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
5✔
1793
      | With (subs, expr) -> mty_with subs expr
20✔
1794
      | TypeOf (t_desc, _) -> mty_typeof t_desc
90✔
1795
      | Strengthen (expr, _, _) when is_elidable_with_u expr ->
×
1796
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
×
1797
      | Strengthen (expr, p, _) -> mty_strengthen expr (p :> Paths.Path.t)
×
1798

1799
    and mty : Odoc_model.Lang.ModuleType.expr -> text =
1800
     fun m ->
1801
      if mty_hidden m then
3,416✔
1802
        Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
×
1803
      else
1804
        match m with
3,416✔
1805
        | Path { p_path = mty_path; _ } ->
837✔
1806
            Link.from_path (mty_path :> Paths.Path.t)
1807
        | Functor (Unit, expr) ->
×
1808
            (if Syntax.Mod.functor_keyword then O.keyword "functor" else O.noop)
×
1809
            ++ O.span (O.txt " () " ++ Syntax.Type.arrow)
×
1810
            ++ O.sp ++ mty expr
×
1811
        | Functor (Named arg, expr) ->
48✔
1812
            let arg_expr = arg.expr in
1813
            let stop_before = expansion_of_module_type_expr arg_expr = None in
48✔
1814
            let name =
1815
              let open Odoc_model.Lang.FunctorParameter in
1816
              let name = Paths.Identifier.name arg.id in
1817
              let href =
48✔
1818
                Url.from_identifier ~stop_before (arg.id :> Paths.Identifier.t)
1819
              in
1820
              resolved href [ inline @@ Text name ]
48✔
1821
            in
1822
            (if Syntax.Mod.functor_keyword then O.keyword "functor" else O.noop)
×
1823
            ++ (O.box_hv @@ O.span
48✔
1824
               @@ O.txt " (" ++ name
48✔
1825
                  ++ O.txt Syntax.Type.annotation_separator
48✔
1826
                  ++ mty arg_expr ++ O.txt ")" ++ O.txt " " ++ Syntax.Type.arrow
48✔
1827
               )
1828
            ++ O.sp ++ mty expr
48✔
1829
        | With { w_expr; _ } when is_elidable_with_u w_expr ->
218✔
1830
            Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
×
1831
        | With { w_substitutions; w_expr; _ } ->
218✔
1832
            O.box_hv @@ mty_with w_substitutions w_expr
218✔
1833
        | TypeOf { t_desc; _ } -> mty_typeof t_desc
64✔
1834
        | Signature _ ->
2,249✔
1835
            Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
2,249✔
1836
        | Strengthen { s_expr; _ } when is_elidable_with_u s_expr ->
×
1837
            Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
×
1838
        | Strengthen { s_expr; s_path; _ } ->
×
1839
            O.box_hv @@ mty_strengthen s_expr (s_path :> Paths.Path.t)
×
1840

1841
    and mty_in_decl :
1842
        Paths.Identifier.Signature.t -> Odoc_model.Lang.ModuleType.expr -> text
1843
        =
1844
     fun base -> function
1845
      | (Path _ | Signature _ | With _ | TypeOf _ | Strengthen _) as m ->
×
1846
          O.txt Syntax.Type.annotation_separator ++ O.cut ++ mty m
1,868✔
1847
      | Functor _ as m when not Syntax.Mod.functor_contraction ->
171✔
1848
          O.txt Syntax.Type.annotation_separator ++ O.cut ++ mty m
×
1849
      | Functor (arg, expr) ->
171✔
1850
          let text_arg =
1851
            match arg with
1852
            | Unit -> O.txt "()"
8✔
1853
            | Named arg ->
163✔
1854
                let arg_expr = arg.expr in
1855
                let stop_before =
1856
                  expansion_of_module_type_expr arg_expr = None
163✔
1857
                in
1858
                let name =
1859
                  let open Odoc_model.Lang.FunctorParameter in
1860
                  let name = Paths.Identifier.name arg.id in
1861
                  let href =
163✔
1862
                    Url.from_identifier ~stop_before
1863
                      (arg.id :> Paths.Identifier.t)
1864
                  in
1865
                  resolved href [ inline @@ Text name ]
163✔
1866
                in
1867
                O.box_hv
163✔
1868
                @@ O.txt "(" ++ name
163✔
1869
                   ++ O.txt Syntax.Type.annotation_separator
163✔
1870
                   ++ O.cut ++ mty arg.expr ++ O.txt ")"
163✔
1871
          in
1872
          O.sp ++ text_arg ++ mty_in_decl base expr
171✔
1873

1874
    (* TODO : Centralize the list juggling for type parameters *)
1875
    and type_expr_in_subst td typath =
1876
      let typath = Link.from_fragment typath in
151✔
1877
      match td.Lang.TypeDecl.Equation.params with
151✔
1878
      | [] -> typath
125✔
1879
      | l -> Syntax.Type.handle_substitution_params typath (format_params l)
26✔
1880

1881
    and substitution : Odoc_model.Lang.ModuleType.substitution -> text =
1882
      function
1883
      | ModuleEq (frag_mod, md) ->
56✔
1884
          O.box_hv
1885
          @@ O.keyword "module" ++ O.txt " "
56✔
1886
             ++ Link.from_fragment (frag_mod :> Paths.Fragment.leaf)
56✔
1887
             ++ O.txt " =" ++ O.sp ++ mdexpr md
56✔
1888
      | ModuleTypeEq (frag_mty, md) ->
32✔
1889
          O.box_hv
1890
          @@ O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
32✔
1891
             ++ Link.from_fragment (frag_mty :> Paths.Fragment.leaf)
32✔
1892
             ++ O.txt " =" ++ O.sp ++ mty md
32✔
1893
      | TypeEq (frag_typ, td) ->
104✔
1894
          O.box_hv
1895
          @@ O.keyword "type" ++ O.txt " "
104✔
1896
             ++ type_expr_in_subst td (frag_typ :> Paths.Fragment.leaf)
104✔
1897
             ++ fst (format_manifest td)
104✔
1898
             ++ format_constraints
104✔
1899
                  td.Odoc_model.Lang.TypeDecl.Equation.constraints
1900
      | ModuleSubst (frag_mod, mod_path) ->
24✔
1901
          O.box_hv
1902
          @@ O.keyword "module" ++ O.txt " "
24✔
1903
             ++ Link.from_fragment (frag_mod :> Paths.Fragment.leaf)
24✔
1904
             ++ O.txt " :=" ++ O.sp
24✔
1905
             ++ Link.from_path (mod_path :> Paths.Path.t)
24✔
1906
      | ModuleTypeSubst (frag_mty, md) ->
16✔
1907
          O.box_hv
1908
          @@ O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
16✔
1909
             ++ Link.from_fragment (frag_mty :> Paths.Fragment.leaf)
16✔
1910
             ++ O.txt " :=" ++ O.sp ++ mty md
16✔
1911
      | TypeSubst (frag_typ, td) -> (
47✔
1912
          O.box_hv
1913
          @@ O.keyword "type" ++ O.txt " "
47✔
1914
             ++ type_expr_in_subst td (frag_typ :> Paths.Fragment.leaf)
47✔
1915
             ++ O.txt " :=" ++ O.sp
47✔
1916
             ++
47✔
1917
             match td.Lang.TypeDecl.Equation.manifest with
1918
             | None -> assert false (* cf loader/cmti *)
1919
             | Some te -> type_expr te)
47✔
1920

1921
    and include_ (t : Odoc_model.Lang.Include.t) =
1922
      let decl_hidden =
290✔
1923
        match t.decl with
1924
        | Alias p -> Paths.Path.(is_hidden (p :> t))
×
1925
        | ModuleType mty -> umty_hidden mty
290✔
1926
      in
1927
      let status = if decl_hidden then `Inline else t.status in
1✔
1928

1929
      let _, content = signature t.expansion.content in
1930
      let summary =
290✔
1931
        if decl_hidden then O.render (O.keyword "include" ++ O.txt " ...")
1✔
1932
        else
1933
          let include_decl =
289✔
1934
            match t.decl with
1935
            | Odoc_model.Lang.Include.Alias mod_path ->
×
1936
                Link.from_path (mod_path :> Paths.Path.t)
×
1937
            | ModuleType mt -> umty mt
289✔
1938
          in
1939
          O.render
289✔
1940
            (O.keyword "include" ++ O.txt " " ++ include_decl
289✔
1941
            ++ if Syntax.Mod.include_semicolon then O.keyword ";" else O.noop)
×
1942
      in
1943
      let content = { Include.content; status; summary } in
1944
      let attr = [ "include" ] in
1945
      let anchor = None in
1946
      let doc =
1947
        (* Documentation attached to includes behave differently than other
1948
           declarations, which show only the synopsis. We can't only show the
1949
           synopsis because no page is generated to render it and we'd loose
1950
           the full documentation.
1951
           The documentation from the expansion is not used. *)
1952
        Comment.to_ir t.doc.elements
1953
      in
1954
      Item.Include { attr; anchor; doc; content; source_anchor = None }
290✔
1955
  end
1956

1957
  open Module
1958

1959
  module Page : sig
1960
    val compilation_unit : Lang.Compilation_unit.t -> Document.t
1961

1962
    val page : Lang.Page.t -> Document.t
1963

1964
    val implementation :
1965
      Lang.Implementation.t ->
1966
      Syntax_highlighter.infos ->
1967
      string ->
1968
      Document.t list
1969
  end = struct
1970
    let pack : Lang.Compilation_unit.Packed.t -> Item.t list =
1971
     fun t ->
1972
      let f x =
×
1973
        let id = x.Lang.Compilation_unit.Packed.id in
×
1974
        let modname = Paths.Identifier.name id in
1975
        let md_def =
×
1976
          O.keyword "module" ++ O.txt " " ++ O.txt modname ++ O.txt " = "
×
1977
          ++ Link.from_path (x.path :> Paths.Path.t)
×
1978
        in
1979
        let content = O.documentedSrc md_def in
×
1980
        let anchor =
×
1981
          Some (Url.Anchor.from_identifier (id :> Paths.Identifier.t))
×
1982
        in
1983
        let attr = [ "modules" ] in
1984
        let doc = [] in
1985
        let decl = { Item.anchor; content; attr; doc; source_anchor = None } in
1986
        Item.Declaration decl
1987
      in
1988
      List.map f t
1989

1990
    let parameterisation_items
1991
        (p : Odoc_model.Lang.Compilation_unit.Parameterisation.t) =
NEW
1992
      let text s : Inline.t = [ inline (Inline.Text s) ] in
×
1993
      let link (path : Paths.Path.Module.t) : Inline.t =
NEW
1994
        let path = (path :> Paths.Path.t) in
×
NEW
1995
        let content = O.code (O.txt (Url.render_path path)) in
×
NEW
1996
        match path with
×
NEW
1997
        | `Resolved rp when not (Paths.Path.is_hidden path) -> (
×
1998
            match Paths.Path.Resolved.identifier rp with
NEW
1999
            | Some id ->
×
2000
                let href = Url.from_identifier ~stop_before:false id in
NEW
2001
                [
×
NEW
2002
                  inline
×
2003
                    (Inline.Link
2004
                       {
2005
                         target = Internal (Resolved href);
2006
                         content;
2007
                         tooltip = None;
2008
                       });
2009
                ]
NEW
2010
            | None -> content)
×
NEW
2011
        | _ -> content
×
2012
      in
2013
      let para parts =
NEW
2014
        Item.Text [ block (Block.Paragraph (List.concat parts)) ]
×
2015
      in
2016
      let implements =
2017
        match p.argument_for with
2018
        | None -> []
359✔
NEW
2019
        | Some path ->
×
2020
            [
NEW
2021
              para
×
2022
                [
NEW
2023
                  text "Implements the library parameter "; link path; text ".";
×
2024
                ];
2025
            ]
2026
      in
2027
      let parameters =
2028
        match p.parameters with
2029
        | [] -> []
359✔
NEW
2030
        | parameters ->
×
2031
            let decl_of_parameter (path : Paths.Path.Module.t) =
NEW
2032
              let path = (path :> Paths.Path.t) in
×
2033
              let content =
NEW
2034
                O.documentedSrc (O.keyword "parameter " ++ Link.from_path path)
×
2035
              in
NEW
2036
              Item.Declaration
×
2037
                {
2038
                  content;
2039
                  anchor = None;
2040
                  attr = [ "parameter" ];
2041
                  doc = [];
2042
                  source_anchor = None;
2043
                }
2044
            in
NEW
2045
            mk_heading ~label:"library-parameters" "Library parameters"
×
NEW
2046
            :: List.map decl_of_parameter parameters
×
NEW
2047
            @ [ mk_heading ~label:"signature" "Signature" ]
×
2048
      in
2049
      implements @ parameters
2050

2051
    let compilation_unit (t : Odoc_model.Lang.Compilation_unit.t) =
2052
      let url = Url.Path.from_identifier t.id in
359✔
2053
      let url =
359✔
2054
        if t.parameterisation.is_parameter then
NEW
2055
          { url with Url.Path.kind = `LibraryParameter }
×
2056
        else url
359✔
2057
      in
2058
      let unit_doc, items =
2059
        match t.content with
2060
        | Module sign -> signature sign
359✔
2061
        | Pack packed -> ([], pack packed)
×
2062
      in
2063
      let items = parameterisation_items t.parameterisation @ items in
359✔
2064
      let source_anchor = source_anchor t.source_loc in
2065
      let page = make_expansion_page ~source_anchor url [ unit_doc ] items in
359✔
2066
      Document.Page page
359✔
2067

2068
    let page (t : Odoc_model.Lang.Page.t) =
2069
      (*let name =
2070
          match t.name.iv with `Page (_, name) | `LeafPage (_, name) -> name
2071
        in*)
2072
      (*let title = Odoc_model.Names.PageName.to_string name in*)
2073
      let url = Url.Path.from_identifier t.name in
69✔
2074
      let preamble, items = Sectioning.docs t.content.elements in
69✔
2075
      let source_anchor = None in
69✔
2076
      Document.Page { Page.preamble; items; url; source_anchor }
2077

2078
    let implementation (v : Odoc_model.Lang.Implementation.t) syntax_info
2079
        source_code =
2080
      match v.id with
28✔
2081
      | None -> []
×
2082
      | Some id ->
28✔
2083
          [
2084
            Document.Source_page
2085
              (Source_page.source id syntax_info v.source_info source_code);
28✔
2086
          ]
2087
  end
2088

2089
  include Page
2090

2091
  let type_expr = type_expr
2092

2093
  let record = record
2094

2095
  let unboxed_record = unboxed_record
2096
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc