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

ocaml / odoc / 3180

20 May 2026 09:31AM UTC coverage: 71.021% (-0.1%) from 71.138%
3180

Pull #1410

github

web-flow
Merge 91731c749 into da35f940e
Pull Request #1410: OxCaml: Support for kind annotations

20 of 52 new or added lines in 2 files covered. (38.46%)

86 existing lines in 1 file now uncovered.

10443 of 14704 relevant lines covered (71.02%)

5874.45 hits per line

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

80.61
/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 ")"
×
132
      | `Resolved _ when Paths.Path.is_hidden path ->
180,801✔
133
          let txt = Url.render_path path in
32✔
134
          unresolved [ inline @@ Text txt ]
32✔
135
      | `Resolved rp -> (
180,769✔
136
          (* If the path is pointing to an opaque module or module type
137
             there won't be a page generated - so we stop before; at
138
             the parent page, and link instead to the anchor representing
139
             the declaration of the opaque module(_type) *)
140
          let stop_before =
141
            match rp with
142
            | `OpaqueModule _ | `OpaqueModuleType _ -> true
8✔
143
            | _ -> false
180,561✔
144
          in
145
          let txt = [ inline @@ Text (Url.render_path path) ] in
180,769✔
146
          match Paths.Path.Resolved.identifier rp with
147
          | Some id ->
156,330✔
148
              let href = Url.from_identifier ~stop_before id in
149
              resolved href txt
156,330✔
150
          | None -> O.elt txt)
24,439✔
151

152
    let dot prefix suffix = prefix ^ "." ^ suffix
49✔
153

154
    let rec render_fragment_any : Fragment.t -> string =
155
     fun fragment ->
156
      match fragment with
×
157
      | `Resolved rr -> render_resolved_fragment rr
×
158
      | `Dot (`Root, suffix) -> suffix
×
159
      | `Dot (prefix, suffix) ->
×
160
          dot (render_fragment_any (prefix :> Fragment.t)) suffix
×
161
      | `Root -> assert false
162

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

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

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

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

247
  module Source_page : sig
248
    val source :
249
      Paths.Identifier.SourcePage.t ->
250
      Syntax_highlighter.infos ->
251
      Lang.Source_info.t ->
252
      string ->
253
      Source_page.t
254
  end = struct
255
    let path id = Url.Path.from_identifier id
28✔
256

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

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

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

307
  module Type_expression : sig
308
    val type_expr : ?needs_parentheses:bool -> Lang.TypeExpr.t -> text
309

310
    val format_type_path :
311
      delim:[ `parens | `brackets ] -> Lang.TypeExpr.t list -> text -> text
312

313
    val kind_annotation :
314
      ?needs_parentheses:bool -> Odoc_model.Lang.KindAnnotation.t -> text
315

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

376
    and te_object (t : Odoc_model.Lang.TypeExpr.Object.t) =
377
      let fields =
80✔
378
        O.list
379
          ~sep:(O.sp ++ O.txt Syntax.Obj.field_separator)
80✔
380
          t.fields
381
          ~f:(function
382
            | Odoc_model.Lang.TypeExpr.Object.Method { name; type_ } ->
120✔
383
                O.box_hv_no_indent
384
                @@ O.txt (name ^ Syntax.Type.annotation_separator)
120✔
385
                   ++ O.cut ++ type_expr type_
120✔
386
            | Inherit type_ -> O.box_hv_no_indent @@ type_expr type_)
×
387
      in
388
      let open_tag =
80✔
389
        if t.open_ then O.txt Syntax.Obj.open_tag_extendable
32✔
390
        else O.txt Syntax.Obj.open_tag_closed
48✔
391
      in
392
      let close_tag =
393
        if t.open_ then O.txt Syntax.Obj.close_tag_extendable
32✔
394
        else O.txt Syntax.Obj.close_tag_closed
48✔
395
      in
396
      O.span (open_tag ++ fields ++ close_tag)
80✔
397

398
    and format_type_path ~delim (params : Odoc_model.Lang.TypeExpr.t list)
399
        (path : text) : text =
400
      O.box_hv
179,533✔
401
      @@
402
      match params with
403
      | [] -> path
94,880✔
404
      | [ param ] ->
71,291✔
405
          let param = type_expr ~needs_parentheses:true param in
406
          let args =
71,291✔
407
            if Syntax.Type.parenthesize_constructor then
408
              O.txt "(" ++ param ++ O.txt ")"
×
409
            else param
71,291✔
410
          in
411
          Syntax.Type.handle_constructor_params path args
71,291✔
412
      | params ->
13,362✔
413
          let params = O.list params ~sep:(O.txt "," ++ O.sp) ~f:type_expr in
13,362✔
414
          let params =
13,362✔
415
            match delim with
416
            | `parens -> enclose ~l:"(" params ~r:")"
13,362✔
417
            | `brackets -> enclose ~l:"[" params ~r:"]"
×
418
          in
419
          Syntax.Type.handle_constructor_params path (O.box_hv params)
13,362✔
420

421
    and tuple ?(needs_parentheses = false) ~boxed lst =
×
422
      let opt_label = function
677✔
423
        | None -> O.noop
1,544✔
424
        | Some lbl -> tag "label" (O.txt lbl) ++ O.txt ":" ++ O.cut
×
425
      in
426
      let res =
427
        O.box_hv_no_indent
428
          (O.list lst ~sep:Syntax.Type.Tuple.element_separator
677✔
429
             ~f:(fun (lbl, typ) ->
430
               opt_label lbl ++ type_expr ~needs_parentheses:true typ))
1,544✔
431
      in
432
      let lparen = if boxed then "(" else "#(" in
×
433
      if Syntax.Type.Tuple.always_parenthesize || needs_parentheses || not boxed
×
434
      then enclose ~l:lparen res ~r:")"
131✔
435
      else res
546✔
436

NEW
437
    and kind_annotation ?(needs_parentheses = false)
×
438
        (k : Odoc_model.Lang.KindAnnotation.t) =
NEW
439
      let enclose_parens_if_needed res =
×
NEW
440
        if needs_parentheses then enclose ~l:"(" res ~r:")" else res
×
441
      in
442
      match k with
NEW
443
      | Default -> O.noop
×
NEW
444
      | Abbreviation frag ->
×
NEW
445
          O.txt (Link.render_fragment_any (frag :> Paths.Fragment.t))
×
NEW
446
      | Mod (base, modes) ->
×
447
          let res =
NEW
448
            kind_annotation ~needs_parentheses:true base
×
NEW
449
            ++ O.txt " " ++ O.keyword "mod"
×
NEW
450
            ++ O.txt (" " ^ String.concat ~sep:" " modes)
×
451
          in
NEW
452
          enclose_parens_if_needed res
×
NEW
453
      | With (base, ty, modalities) ->
×
454
          let res =
NEW
455
            kind_annotation ~needs_parentheses:true base
×
NEW
456
            ++ O.txt " " ++ O.keyword "with" ++ O.txt " " ++ type_expr ty
×
457
            ++
458
            match modalities with
NEW
459
            | [] -> O.noop
×
NEW
460
            | mods -> O.txt " @@ " ++ O.txt (String.concat ~sep:" " mods)
×
461
          in
NEW
462
          enclose_parens_if_needed res
×
NEW
463
      | Kind_of ty -> O.keyword "kind_of_" ++ O.txt " " ++ type_expr ty
×
NEW
464
      | Product ks ->
×
465
          let res =
NEW
466
            O.list ks ~sep:(O.txt " & ") ~f:(fun k ->
×
NEW
467
                kind_annotation ~needs_parentheses:true k)
×
468
          in
NEW
469
          enclose_parens_if_needed res
×
470

471
    and with_kind_annotation kind base =
472
      match kind with
648✔
473
      | Odoc_model.Lang.KindAnnotation.Default -> base
648✔
NEW
474
      | k -> O.txt "(" ++ base ++ O.txt " : " ++ kind_annotation k ++ O.txt ")"
×
475

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

554
    and package_path pkg =
555
      Link.from_path (pkg.path :> Paths.Path.t)
24✔
556
      ++
557
      match pkg.substitutions with
558
      | [] -> O.noop
16✔
559
      | fst :: lst ->
8✔
560
          O.sp
561
          ++ O.box_hv (O.keyword "with" ++ O.txt " " ++ package_subst fst)
8✔
562
          ++ O.list lst ~f:(fun s ->
8✔
563
                 O.cut
8✔
564
                 ++ (O.box_hv
8✔
565
                    @@ O.txt " " ++ O.keyword "and" ++ O.txt " "
8✔
566
                       ++ package_subst s))
8✔
567

568
    and package_subst
569
        ((frag_typ, te) : Paths.Fragment.Type.t * Odoc_model.Lang.TypeExpr.t) :
570
        text =
571
      let typath = Link.from_fragment (frag_typ :> Paths.Fragment.leaf) in
16✔
572
      O.keyword "type" ++ O.txt " " ++ typath ++ O.txt " =" ++ O.sp
16✔
573
      ++ type_expr te
16✔
574
  end
575

576
  open Type_expression
577

578
  (* Also handles constructor declarations for exceptions and extensible
579
     variants, and exposes a few helpers used in formatting classes and signature
580
     constraints. *)
581
  module Type_declaration : sig
582
    val type_decl :
583
      ?is_substitution:bool ->
584
      Lang.Signature.recursive * Lang.TypeDecl.t ->
585
      Item.t
586

587
    val extension : Lang.Extension.t -> Item.t
588

589
    val record : Lang.TypeDecl.Field.t list -> DocumentedSrc.one list
590

591
    val unboxed_record :
592
      Lang.TypeDecl.UnboxedField.t list -> DocumentedSrc.one list
593

594
    val exn : Lang.Exception.t -> Item.t
595

596
    val format_params :
597
      ?delim:[ `parens | `brackets ] -> Lang.TypeDecl.param list -> text
598

599
    val format_manifest :
600
      ?is_substitution:bool ->
601
      ?compact_variants:bool ->
602
      Lang.TypeDecl.Equation.t ->
603
      text * bool
604

605
    val format_constraints : (Lang.TypeExpr.t * Lang.TypeExpr.t) list -> text
606
  end = struct
607
    let record fields =
608
      let field mutable_ id typ =
86✔
609
        let url = Url.from_identifier ~stop_before:true id in
155✔
610
        let name = Paths.Identifier.name id in
155✔
611
        let attrs = [ "def"; "record"; Url.Anchor.string_of_kind url.kind ] in
155✔
612
        let cell =
613
          (* O.td ~a:[ O.a_class ["def"; kind ] ]
614
           *   [O.a ~a:[O.a_href ("#" ^ anchor); O.a_class ["anchor"]] []
615
           *   ; *)
616
          O.code
617
            ((if mutable_ then O.keyword "mutable" ++ O.txt " " else O.noop)
24✔
618
            ++ O.txt name
155✔
619
            ++ O.txt Syntax.Type.annotation_separator
155✔
620
            ++ type_expr typ
155✔
621
            ++ O.txt Syntax.Type.Record.field_separator)
155✔
622
          (* ] *)
623
        in
624
        (url, attrs, cell)
155✔
625
      in
626
      let rows =
627
        fields
628
        |> List.map (fun fld ->
629
               let open Odoc_model.Lang.TypeDecl.Field in
155✔
630
               let url, attrs, code =
631
                 field fld.mutable_ (fld.id :> Paths.Identifier.t) fld.type_
632
               in
633
               let anchor = Some url in
155✔
634
               let doc = fld.doc.elements in
635
               let rhs = Comment.to_ir doc in
636
               let doc = if not (Comment.has_doc doc) then [] else rhs in
64✔
637
               let markers = Syntax.Comment.markers in
638
               DocumentedSrc.Documented { anchor; attrs; code; doc; markers })
639
      in
640
      let content =
86✔
641
        O.documentedSrc (O.txt "{") @ rows @ O.documentedSrc (O.txt "}")
86✔
642
      in
643
      content
644

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

683
    let constructor :
684
        Paths.Identifier.t ->
685
        Odoc_model.Lang.TypeDecl.Constructor.argument ->
686
        Odoc_model.Lang.TypeExpr.t option ->
687
        DocumentedSrc.t =
688
     fun id args ret_type ->
689
      let name = Paths.Identifier.name id in
656✔
690
      let kind = Url.(kind id |> Anchor.string_of_kind) in
656✔
691
      let cstr = tag kind (O.txt name) in
656✔
692
      let is_gadt, ret_type =
656✔
693
        match ret_type with
694
        | None -> (false, O.noop)
520✔
695
        | Some te ->
136✔
696
            let constant = match args with Tuple [] -> true | _ -> false in
48✔
697
            let ret_type =
698
              O.txt " "
136✔
699
              ++ (if constant then O.txt ":" else Syntax.Type.GADT.arrow)
48✔
700
              ++ O.txt " " ++ type_expr te
136✔
701
            in
702
            (true, ret_type)
136✔
703
      in
704
      match args with
705
      | Tuple [] -> O.documentedSrc (cstr ++ ret_type)
289✔
706
      | Tuple lst ->
350✔
707
          let params =
708
            O.list lst ~sep:Syntax.Type.Tuple.element_separator
709
              ~f:(type_expr ~needs_parentheses:is_gadt)
710
          in
711
          O.documentedSrc
350✔
712
            (cstr
713
            ++ (if Syntax.Type.Variant.parenthesize_params then
350✔
UNCOV
714
                  O.txt "(" ++ params ++ O.txt ")"
×
715
                else
716
                  (if is_gadt then O.txt Syntax.Type.annotation_separator
88✔
717
                   else O.txt " " ++ O.keyword "of" ++ O.txt " ")
262✔
718
                  ++ params)
350✔
719
            ++ ret_type)
350✔
720
      | Record fields ->
17✔
721
          if is_gadt then
UNCOV
722
            O.documentedSrc (cstr ++ O.txt Syntax.Type.annotation_separator)
×
UNCOV
723
            @ record fields @ O.documentedSrc ret_type
×
724
          else
725
            O.documentedSrc (cstr ++ O.txt " " ++ O.keyword "of" ++ O.txt " ")
17✔
726
            @ record fields
17✔
727

728
    let variant cstrs : DocumentedSrc.t =
729
      let constructor id args res =
235✔
730
        let url = Url.from_identifier ~stop_before:true id in
436✔
731
        let attrs = [ "def"; "variant"; Url.Anchor.string_of_kind url.kind ] in
436✔
732
        let content =
733
          let doc = constructor id args res in
734
          O.documentedSrc (O.txt "| ") @ doc
436✔
735
        in
736
        (url, attrs, content)
737
      in
738
      match cstrs with
UNCOV
739
      | [] -> O.documentedSrc (O.txt "|")
×
740
      | _ :: _ ->
235✔
741
          let rows =
742
            cstrs
743
            |> List.map (fun cstr ->
744
                   let open Odoc_model.Lang.TypeDecl.Constructor in
436✔
745
                   let url, attrs, code =
746
                     constructor
747
                       (cstr.id :> Paths.Identifier.t)
748
                       cstr.args cstr.res
749
                   in
750
                   let anchor = Some url in
436✔
751
                   let doc = cstr.doc.elements in
752
                   let rhs = Comment.to_ir doc in
753
                   let doc = if not (Comment.has_doc doc) then [] else rhs in
73✔
754
                   let markers = Syntax.Comment.markers in
755
                   DocumentedSrc.Nested { anchor; attrs; code; doc; markers })
756
          in
757
          rows
235✔
758

759
    let extension_constructor (t : Odoc_model.Lang.Extension.Constructor.t) =
760
      let id = (t.id :> Paths.Identifier.t) in
154✔
761
      let url = Url.from_identifier ~stop_before:true id in
762
      let anchor = Some url in
154✔
763
      let attrs = [ "def"; "variant"; Url.Anchor.string_of_kind url.kind ] in
154✔
764
      let code = O.documentedSrc (O.txt "| ") @ constructor id t.args t.res in
154✔
765
      let doc = Comment.to_ir t.doc.elements in
766
      let markers = Syntax.Comment.markers in
154✔
767
      DocumentedSrc.Nested { anchor; attrs; code; doc; markers }
768

769
    let extension (t : Odoc_model.Lang.Extension.t) =
770
      let prefix =
126✔
771
        O.keyword "type" ++ O.txt " "
126✔
772
        ++ Link.from_path (t.type_path :> Paths.Path.t)
126✔
773
        ++ O.txt " +=" ++ O.sp
126✔
774
        ++
775
        if t.private_ then O.keyword Syntax.Type.private_keyword ++ O.sp
8✔
776
        else O.noop
118✔
777
      in
778
      let content =
126✔
779
        O.documentedSrc prefix
126✔
780
        @ List.map extension_constructor t.constructors
126✔
781
        @ O.documentedSrc
126✔
UNCOV
782
            (if Syntax.Type.type_def_semicolon then O.txt ";" else O.noop)
×
783
      in
784
      let attr = [ "type"; "extension" ] in
785
      let anchor = Some (Url.Anchor.extension_decl t) in
126✔
786
      let doc = Comment.to_ir t.doc.elements in
787
      let source_anchor =
126✔
788
        (* Take the anchor from the first constructor only for consistency with
789
           regular variants. *)
790
        match t.constructors with
791
        | hd :: _ -> source_anchor hd.source_loc
126✔
UNCOV
792
        | [] -> None
×
793
      in
794
      Item.Declaration { attr; anchor; doc; content; source_anchor }
795

796
    let exn (t : Odoc_model.Lang.Exception.t) =
797
      let cstr = constructor (t.id :> Paths.Identifier.t) t.args t.res in
66✔
798
      let content =
66✔
799
        O.documentedSrc (O.keyword "exception" ++ O.txt " ")
66✔
800
        @ cstr
801
        @ O.documentedSrc
66✔
UNCOV
802
            (if Syntax.Type.Exception.semicolon then O.txt ";" else O.noop)
×
803
      in
804
      let attr = [ "exception" ] in
805
      let anchor = path_to_id t.id in
806
      let doc = Comment.to_ir t.doc.elements in
66✔
807
      let source_anchor = source_anchor t.source_loc in
66✔
808
      Item.Declaration { attr; anchor; doc; content; source_anchor }
66✔
809

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

884
    let format_params :
885
        'row.
886
        ?delim:[ `parens | `brackets ] ->
887
        Odoc_model.Lang.TypeDecl.param list ->
888
        text =
889
     fun ?(delim = `parens) params ->
472✔
890
      let format_param_str
563✔
891
          { Odoc_model.Lang.TypeDecl.desc; variance; injectivity; kind = _ } =
892
        let desc =
624✔
893
          match desc with
894
          | Odoc_model.Lang.TypeDecl.Any -> [ "_" ]
37✔
895
          | Var s -> [ "'"; s ]
587✔
896
        in
897
        let var_desc =
898
          match variance with
899
          | None -> desc
608✔
900
          | Some Odoc_model.Lang.TypeDecl.Pos -> "+" :: desc
8✔
901
          | Some Odoc_model.Lang.TypeDecl.Neg -> "-" :: desc
8✔
UNCOV
902
          | Some Odoc_model.Lang.TypeDecl.Bivariant -> "+" :: "-" :: desc
×
903
        in
904
        let final = if injectivity then "!" :: var_desc else var_desc in
×
905
        String.concat ~sep:"" final
906
      in
907
      let format_param p =
908
        Type_expression.with_kind_annotation p.Odoc_model.Lang.TypeDecl.kind
236✔
909
          (O.txt (format_param_str p))
236✔
910
      in
911
      match params with
912
      | [] -> O.noop
59✔
913
      | [ x ] ->
388✔
914
          let base = format_param_str x |> Syntax.Type.handle_format_params in
388✔
915
          Type_expression.with_kind_annotation x.kind (O.txt base)
388✔
916
      | lst ->
116✔
917
          let left, right =
NEW
918
            match delim with `parens -> ("(", ")") | `brackets -> ("[", "]")
×
919
          in
920
          O.txt left
116✔
921
          ++ O.list lst ~sep:(O.txt ", ") ~f:format_param
116✔
922
          ++ O.txt right
116✔
923

924
    let format_constraints constraints =
925
      O.list constraints ~f:(fun (t1, t2) ->
3,103✔
926
          O.sp
104✔
927
          ++ (O.box_hv
104✔
928
             @@ O.keyword "constraint" ++ O.sp
104✔
929
                ++ O.box_hv_no_indent (type_expr t1)
104✔
930
                ++ O.txt " =" ++ O.sp
104✔
931
                ++ O.box_hv_no_indent (type_expr t2)))
104✔
932

933
    let format_manifest :
934
        'inner_row 'outer_row.
935
        ?is_substitution:bool ->
936
        ?compact_variants:bool ->
937
        Odoc_model.Lang.TypeDecl.Equation.t ->
938
        text * bool =
939
     fun ?(is_substitution = false) ?(compact_variants = true) equation ->
104✔
940
      let _ = compact_variants in
3,021✔
941
      (* TODO *)
942
      let private_ = equation.private_ in
943
      match equation.manifest with
944
      | None -> (O.noop, private_)
1,753✔
945
      | Some t ->
1,268✔
946
          let manifest =
947
            O.txt (if is_substitution then " :=" else " =")
23✔
948
            ++ O.sp
1,268✔
949
            ++ (if private_ then
1,268✔
950
                  O.keyword Syntax.Type.private_keyword ++ O.txt " "
8✔
951
                else O.noop)
1,260✔
952
            ++ type_expr t
1,268✔
953
          in
954
          (manifest, false)
1,268✔
955

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

1040
  open Type_declaration
1041

1042
  module Value : sig
1043
    val value : Lang.Value.t -> Item.t
1044
  end = struct
1045
    let value (t : Odoc_model.Lang.Value.t) =
1046
      let extra_attr, semicolon =
912✔
1047
        match t.value with
1048
        | Abstract -> ([], Syntax.Value.semicolon)
888✔
1049
        | External _ -> ([ "external" ], Syntax.Type.External.semicolon)
24✔
1050
      in
1051
      let name = Paths.Identifier.name t.id in
1052
      let content =
912✔
1053
        O.documentedSrc
1054
          (O.box_hv
912✔
1055
          @@ O.keyword Syntax.Value.variable_keyword
912✔
1056
             ++ O.txt " " ++ O.txt name
912✔
1057
             ++ O.txt Syntax.Type.annotation_separator
912✔
1058
             ++ O.cut ++ type_expr t.type_
912✔
UNCOV
1059
             ++ if semicolon then O.txt ";" else O.noop)
×
1060
      in
1061
      let attr = [ "value" ] @ extra_attr in
912✔
1062
      let anchor = path_to_id t.id in
1063
      let doc = Comment.to_ir t.doc.elements in
912✔
1064
      let source_anchor = source_anchor t.source_loc in
912✔
1065
      Item.Declaration { attr; anchor; doc; content; source_anchor }
912✔
1066
  end
1067

1068
  open Value
1069

1070
  (* This chunk of code is responsible for sectioning list of items
1071
     according to headings by extracting headings as Items.
1072

1073
     TODO: This sectioning would be better done as a pass on the model directly.
1074
  *)
1075
  module Sectioning : sig
1076
    open Odoc_model
1077

1078
    val comment_items : Comment.elements -> Item.t list
1079

1080
    val docs : Comment.elements -> Item.t list * Item.t list
1081
  end = struct
1082
    let take_until_heading_or_end (docs : Odoc_model.Comment.elements) =
1083
      let content, _, rest =
693✔
1084
        Doctree.Take.until docs ~classify:(fun b ->
1085
            match b.Location.value with
1,481✔
1086
            | `Heading _ -> Stop_and_keep
202✔
1087
            | #Odoc_model.Comment.attached_block_element as doc ->
1,279✔
1088
                let content = Comment.attached_block_element doc in
1089
                Accum content)
1,279✔
1090
      in
1091
      (content, rest)
693✔
1092

1093
    let comment_items (input0 : Odoc_model.Comment.elements) =
1094
      let rec loop input_comment acc =
963✔
1095
        match input_comment with
2,493✔
1096
        | [] -> List.rev acc
963✔
1097
        | element :: input_comment -> (
1,530✔
1098
            match element.Location.value with
1099
            | `Heading h ->
837✔
1100
                let item = Comment.heading h in
1101
                loop input_comment (item :: acc)
837✔
1102
            | _ ->
693✔
1103
                let content, input_comment =
1104
                  take_until_heading_or_end (element :: input_comment)
1105
                in
1106
                let item = Item.Text content in
693✔
1107
                loop input_comment (item :: acc))
1108
      in
1109
      loop input0 []
1110

1111
    (* For doc pages, we want the header to contain everything until
1112
       the first heading, then everything before the next heading which
1113
       is either lower, or a section.
1114
    *)
1115
    let docs input_comment =
1116
      let items = comment_items input_comment in
69✔
1117
      let until_first_heading, o, items =
69✔
1118
        Doctree.Take.until items ~classify:(function
1119
          | Item.Heading h as i -> Stop_and_accum ([ i ], Some h.level)
69✔
UNCOV
1120
          | i -> Accum [ i ])
×
1121
      in
1122
      match o with
69✔
UNCOV
1123
      | None -> (until_first_heading, items)
×
1124
      | Some level ->
69✔
1125
          let max_level = if level = 1 then 2 else level in
×
1126
          let before_second_heading, _, items =
1127
            Doctree.Take.until items ~classify:(function
1128
              | Item.Heading h when h.level >= max_level -> Stop_and_keep
13✔
1129
              | i -> Accum [ i ])
38✔
1130
          in
1131
          let header = until_first_heading @ before_second_heading in
69✔
1132
          (header, items)
1133
  end
1134

1135
  module Class : sig
1136
    val class_ : Lang.Class.t -> Item.t
1137

1138
    val class_type : Lang.ClassType.t -> Item.t
1139
  end = struct
1140
    let class_type_expr (cte : Odoc_model.Lang.ClassType.expr) =
1141
      match cte with
249✔
1142
      | Constr (path, args) ->
58✔
1143
          let link = Link.from_path (path :> Paths.Path.t) in
1144
          format_type_path ~delim:`brackets args link
58✔
1145
      | Signature _ ->
191✔
1146
          Syntax.Class.open_tag ++ O.txt " ... " ++ Syntax.Class.close_tag
191✔
1147

1148
    let method_ (t : Odoc_model.Lang.Method.t) =
1149
      let name = Paths.Identifier.name t.id in
90✔
1150
      let virtual_ =
90✔
1151
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
8✔
1152
      in
1153
      let private_ =
1154
        if t.private_ then O.keyword "private" ++ O.txt " " else O.noop
8✔
1155
      in
1156
      let content =
1157
        O.documentedSrc
1158
          (O.keyword "method" ++ O.txt " " ++ private_ ++ virtual_ ++ O.txt name
90✔
1159
          ++ O.txt Syntax.Type.annotation_separator
90✔
1160
          ++ type_expr t.type_)
90✔
1161
      in
1162
      let attr = [ "method" ] in
90✔
1163
      let anchor = path_to_id t.id in
1164
      let doc = Comment.to_ir t.doc.elements in
90✔
1165
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
90✔
1166

1167
    let instance_variable (t : Odoc_model.Lang.InstanceVariable.t) =
1168
      let name = Paths.Identifier.name t.id in
17✔
1169
      let virtual_ =
17✔
1170
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
8✔
1171
      in
1172
      let mutable_ =
1173
        if t.mutable_ then O.keyword "mutable" ++ O.txt " " else O.noop
8✔
1174
      in
1175
      let content =
1176
        O.documentedSrc
1177
          (O.keyword "val" ++ O.txt " " ++ mutable_ ++ virtual_ ++ O.txt name
17✔
1178
          ++ O.txt Syntax.Type.annotation_separator
17✔
1179
          ++ type_expr t.type_)
17✔
1180
      in
1181
      let attr = [ "value"; "instance-variable" ] in
17✔
1182
      let anchor = path_to_id t.id in
1183
      let doc = Comment.to_ir t.doc.elements in
17✔
1184
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
17✔
1185

1186
    let inherit_ (ih : Lang.ClassSignature.Inherit.t) =
1187
      let cte =
16✔
1188
        match ih.expr with
1189
        | Signature _ -> assert false (* Bold. *)
1190
        | cty -> cty
16✔
1191
      in
1192
      let content =
1193
        O.documentedSrc (O.keyword "inherit" ++ O.txt " " ++ class_type_expr cte)
16✔
1194
      in
1195
      let attr = [ "inherit" ] in
16✔
1196
      let anchor = None in
1197
      let doc = Comment.to_ir ih.doc.elements in
1198
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
16✔
1199

1200
    let constraint_ (cst : Lang.ClassSignature.Constraint.t) =
1201
      let content =
8✔
1202
        O.documentedSrc (format_constraints [ (cst.left, cst.right) ])
8✔
1203
      in
1204
      let attr = [] in
8✔
1205
      let anchor = None in
1206
      let doc = Comment.to_ir cst.doc.elements in
1207
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
8✔
1208

1209
    let class_signature (c : Lang.ClassSignature.t) =
1210
      let rec loop l acc_items =
233✔
1211
        match l with
388✔
1212
        | [] -> List.rev acc_items
233✔
1213
        | item :: rest -> (
155✔
1214
            let continue item = loop rest (item :: acc_items) in
131✔
1215
            match (item : Lang.ClassSignature.item) with
1216
            | Inherit cty -> continue @@ inherit_ cty
16✔
1217
            | Method m -> continue @@ method_ m
90✔
1218
            | InstanceVariable v -> continue @@ instance_variable v
17✔
1219
            | Constraint cst -> continue @@ constraint_ cst
8✔
1220
            | Comment `Stop ->
8✔
1221
                let rest =
1222
                  List.skip_until rest ~p:(function
1223
                    | Lang.ClassSignature.Comment `Stop -> true
8✔
1224
                    | _ -> false)
8✔
1225
                in
1226
                loop rest acc_items
8✔
1227
            | Comment (`Docs c) ->
16✔
1228
                let items = Sectioning.comment_items c.elements in
1229
                loop rest (List.rev_append items acc_items))
16✔
1230
      in
1231
      (* FIXME: use [t.self] *)
1232
      (c.doc.elements, loop c.items [])
233✔
1233

1234
    let rec class_decl (cd : Odoc_model.Lang.Class.decl) =
1235
      match cd with
182✔
1236
      | ClassType expr -> class_type_expr expr
166✔
1237
      (* TODO: factorize the following with [type_expr] *)
1238
      | Arrow (None, src, dst) ->
16✔
1239
          O.span
16✔
1240
            (type_expr ~needs_parentheses:true src
16✔
1241
            ++ O.txt " " ++ Syntax.Type.arrow)
16✔
1242
          ++ O.txt " " ++ class_decl dst
16✔
UNCOV
1243
      | Arrow (Some (RawOptional _ as lbl), _src, dst) ->
×
UNCOV
1244
          O.span
×
1245
            (O.box_hv
×
1246
            @@ label lbl ++ O.txt ":"
×
1247
               ++ tag "error" (O.txt "???")
×
1248
               ++ O.txt " " ++ Syntax.Type.arrow)
×
1249
          ++ O.sp ++ class_decl dst
×
1250
      | Arrow (Some lbl, src, dst) ->
×
1251
          O.span
×
1252
            (label lbl ++ O.txt ":"
×
1253
            ++ type_expr ~needs_parentheses:true src
×
1254
            ++ O.txt " " ++ Syntax.Type.arrow)
×
1255
          ++ O.txt " " ++ class_decl dst
×
1256

1257
    let class_ (t : Odoc_model.Lang.Class.t) =
1258
      let name = Paths.Identifier.name t.id in
166✔
1259
      let params =
166✔
1260
        match t.params with
1261
        | [] -> O.noop
142✔
1262
        | _ :: _ as params -> format_params ~delim:`brackets params ++ O.txt " "
24✔
1263
      in
1264
      let virtual_ =
1265
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
24✔
1266
      in
1267

1268
      let source_anchor = source_anchor t.source_loc in
1269
      let cname, expansion, expansion_doc =
166✔
1270
        match t.expansion with
UNCOV
1271
        | None -> (O.documentedSrc @@ O.txt name, None, None)
×
1272
        | Some csig ->
166✔
1273
            let expansion_doc, items = class_signature csig in
1274
            let url = Url.Path.from_identifier t.id in
166✔
1275
            let page =
166✔
1276
              make_expansion_page ~source_anchor url
1277
                [ t.doc.elements; expansion_doc ]
1278
                items
1279
            in
1280
            ( O.documentedSrc @@ path url [ inline @@ Text name ],
166✔
1281
              Some page,
1282
              Some expansion_doc )
1283
      in
1284
      let summary =
1285
        O.txt Syntax.Type.annotation_separator ++ class_decl t.type_
166✔
1286
      in
1287
      let cd =
166✔
1288
        attach_expansion
1289
          (Syntax.Type.annotation_separator, "object", "end")
1290
          expansion summary
1291
      in
1292
      let content =
166✔
1293
        O.documentedSrc (O.keyword "class" ++ O.txt " " ++ virtual_ ++ params)
166✔
1294
        @ cname @ cd
1295
      in
1296
      let attr = [ "class" ] in
1297
      let anchor = path_to_id t.id in
1298
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
166✔
1299
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1300

1301
    let class_type (t : Odoc_model.Lang.ClassType.t) =
1302
      let name = Paths.Identifier.name t.id in
67✔
1303
      let params = format_params ~delim:`brackets t.params in
67✔
1304
      let virtual_ =
67✔
1305
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
8✔
1306
      in
1307
      let source_anchor = source_anchor t.source_loc in
1308
      let cname, expansion, expansion_doc =
67✔
1309
        match t.expansion with
UNCOV
1310
        | None -> (O.documentedSrc @@ O.txt name, None, None)
×
1311
        | Some csig ->
67✔
1312
            let url = Url.Path.from_identifier t.id in
1313
            let expansion_doc, items = class_signature csig in
67✔
1314
            let page =
67✔
1315
              make_expansion_page ~source_anchor url
1316
                [ t.doc.elements; expansion_doc ]
1317
                items
1318
            in
1319
            ( O.documentedSrc @@ path url [ inline @@ Text name ],
67✔
1320
              Some page,
1321
              Some expansion_doc )
1322
      in
1323
      let summary = O.txt " = " ++ class_type_expr t.expr in
67✔
1324
      let expr = attach_expansion (" = ", "object", "end") expansion summary in
67✔
1325
      let content =
67✔
1326
        O.documentedSrc
67✔
1327
          (O.keyword "class" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
67✔
1328
         ++ virtual_ ++ params ++ O.txt " ")
67✔
1329
        @ cname @ expr
1330
      in
1331
      let attr = [ "class-type" ] in
1332
      let anchor = path_to_id t.id in
1333
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
67✔
1334
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1335
  end
1336

1337
  open Class
1338

1339
  module Module : sig
1340
    val signature : Lang.Signature.t -> Comment.Comment.elements * Item.t list
1341
    (** Returns [header_doc, content]. *)
1342
  end = struct
1343
    let internal_module m =
1344
      let open Lang.Module in
1,924✔
1345
      match m.id.iv with
1346
      | `Module (_, name) when ModuleName.is_hidden name -> true
81✔
1347
      | _ -> false
1,843✔
1348

1349
    let internal_type t =
1350
      let open Lang.TypeDecl in
2,969✔
1351
      match t.id.iv with
1352
      | `Type (_, name) when TypeName.is_hidden name -> true
1✔
1353
      | _ -> false
2,968✔
1354

1355
    let internal_value v =
1356
      let open Lang.Value in
1,018✔
1357
      match v.id.iv with
1358
      | `Value (_, name) when ValueName.is_hidden name -> true
106✔
1359
      | _ -> false
912✔
1360

1361
    let internal_module_type t =
1362
      let open Lang.ModuleType in
1,361✔
1363
      match t.id.iv with
UNCOV
1364
      | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true
×
1365
      | _ -> false
1,361✔
1366

1367
    let internal_module_substitution t =
1368
      let open Lang.ModuleSubstitution in
8✔
1369
      match t.id.iv with
UNCOV
1370
      | `Module (_, name) when ModuleName.is_hidden name -> true
×
1371
      | _ -> false
8✔
1372

1373
    let internal_module_type_substitution t =
1374
      let open Lang.ModuleTypeSubstitution in
8✔
1375
      match t.id.iv with
UNCOV
1376
      | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true
×
1377
      | _ -> false
8✔
1378

1379
    let rec signature (s : Lang.Signature.t) =
1380
      let rec loop l acc_items =
3,582✔
1381
        match l with
12,543✔
1382
        | [] -> List.rev acc_items
3,582✔
1383
        | item :: rest -> (
8,961✔
1384
            let continue (item : Item.t) = loop rest (item :: acc_items) in
7,838✔
1385
            match (item : Lang.Signature.item) with
1386
            | Module (_, m) when internal_module m -> loop rest acc_items
81✔
1387
            | Type (_, t) when internal_type t -> loop rest acc_items
1✔
1388
            | Value v when internal_value v -> loop rest acc_items
106✔
UNCOV
1389
            | ModuleType m when internal_module_type m -> loop rest acc_items
×
1390
            | ModuleSubstitution m when internal_module_substitution m ->
8✔
1391
                loop rest acc_items
×
1392
            | ModuleTypeSubstitution m when internal_module_type_substitution m
8✔
1393
              ->
UNCOV
1394
                loop rest acc_items
×
1395
            | ModuleTypeSubstitution m -> continue @@ module_type_substitution m
8✔
1396
            | Module (_, m) -> continue @@ module_ m
1,843✔
1397
            | ModuleType m -> continue @@ module_type m
1,361✔
1398
            | Class (_, c) -> continue @@ class_ c
166✔
1399
            | ClassType (_, c) -> continue @@ class_type c
67✔
1400
            | Include m -> continue @@ include_ m
290✔
1401
            | ModuleSubstitution m -> continue @@ module_substitution m
8✔
1402
            | TypeSubstitution t ->
23✔
1403
                continue @@ type_decl ~is_substitution:true (Ordinary, t)
23✔
1404
            | Type (r, t) -> continue @@ type_decl (r, t)
2,968✔
1405
            | TypExt e -> continue @@ extension e
126✔
1406
            | Exception e -> continue @@ exn e
66✔
1407
            | Value v -> continue @@ value v
912✔
1408
            | Open o ->
82✔
1409
                let items = Sectioning.comment_items o.doc.elements in
1410
                loop rest (List.rev_append items acc_items)
82✔
1411
            | Comment `Stop ->
57✔
1412
                let rest =
1413
                  List.skip_until rest ~p:(function
1414
                    | Lang.Signature.Comment `Stop -> true
49✔
1415
                    | _ -> false)
65✔
1416
                in
1417
                loop rest acc_items
57✔
1418
            | Comment (`Docs c) ->
796✔
1419
                let items = Sectioning.comment_items c.elements in
1420
                loop rest (List.rev_append items acc_items))
796✔
1421
      in
1422
      ((Lang.extract_signature_doc s).elements, loop s.items [])
3,582✔
1423

1424
    and functor_parameter :
1425
        Odoc_model.Lang.FunctorParameter.parameter -> DocumentedSrc.t =
1426
     fun arg ->
1427
      let open Odoc_model.Lang.FunctorParameter in
211✔
1428
      let name = Paths.Identifier.name arg.id in
1429
      let render_ty = arg.expr in
211✔
1430
      let modtyp =
1431
        mty_in_decl (arg.id :> Paths.Identifier.Signature.t) render_ty
1432
      in
1433
      let modname, mod_decl =
211✔
1434
        match expansion_of_module_type_expr arg.expr with
UNCOV
1435
        | None ->
×
UNCOV
1436
            let modname = O.txt (Paths.Identifier.name arg.id) in
×
1437
            (modname, O.documentedSrc modtyp)
×
1438
        | Some (expansion_doc, items) ->
211✔
1439
            let url = Url.Path.from_identifier arg.id in
1440
            let modname = path url [ inline @@ Text name ] in
211✔
1441
            let type_with_expansion =
211✔
1442
              let content =
1443
                make_expansion_page ~source_anchor:None url [ expansion_doc ]
1444
                  items
1445
              in
1446
              let summary = O.render modtyp in
211✔
1447
              let status = `Default in
211✔
1448
              let expansion =
1449
                O.documentedSrc
211✔
1450
                  (O.txt Syntax.Type.annotation_separator ++ O.keyword "sig")
211✔
1451
                @ DocumentedSrc.[ Subpage { content; status } ]
1452
                @ O.documentedSrc (O.keyword "end")
211✔
1453
              in
1454
              DocumentedSrc.
1455
                [
1456
                  Alternative
1457
                    (Expansion { status = `Default; summary; url; expansion });
1458
                ]
1459
            in
1460
            (modname, type_with_expansion)
1461
      in
1462
      O.documentedSrc (O.keyword "module" ++ O.txt " ")
211✔
1463
      @ O.documentedSrc modname @ mod_decl
211✔
1464

1465
    and module_substitution (t : Odoc_model.Lang.ModuleSubstitution.t) =
1466
      let name = Paths.Identifier.name t.id in
8✔
1467
      let path = Link.from_path (t.manifest :> Paths.Path.t) in
8✔
1468
      let content =
8✔
1469
        O.documentedSrc
1470
          (O.keyword "module" ++ O.txt " " ++ O.txt name ++ O.txt " :=" ++ O.sp
8✔
1471
         ++ path)
8✔
1472
      in
1473
      let attr = [ "module-substitution" ] in
8✔
1474
      let anchor = path_to_id t.id in
1475
      let doc = Comment.to_ir t.doc.elements in
8✔
1476
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
8✔
1477

1478
    and module_type_substitution (t : Odoc_model.Lang.ModuleTypeSubstitution.t)
1479
        =
1480
      let prefix =
8✔
1481
        O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
8✔
1482
      in
1483
      let source_anchor = None in
8✔
1484
      let modname = Paths.Identifier.name t.id in
1485
      let modname, expansion_doc, mty =
8✔
1486
        module_type_manifest ~subst:true ~source_anchor modname t.id
1487
          t.doc.elements (Some t.manifest) prefix
1488
      in
1489
      let content =
8✔
1490
        O.documentedSrc (prefix ++ modname)
8✔
1491
        @ mty
1492
        @ O.documentedSrc
8✔
UNCOV
1493
            (if Syntax.Mod.close_tag_semicolon then O.txt ";" else O.noop)
×
1494
      in
1495
      let attr = [ "module-type" ] in
1496
      let anchor = path_to_id t.id in
1497
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
8✔
1498
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1499

1500
    and simple_expansion :
1501
        Odoc_model.Lang.ModuleType.simple_expansion ->
1502
        Comment.Comment.elements * Item.t list =
1503
     fun t ->
1504
      let rec extract_functor_params
2,933✔
1505
          (f : Odoc_model.Lang.ModuleType.simple_expansion) =
1506
        match f with
3,152✔
1507
        | Signature sg -> (None, sg)
2,933✔
1508
        | Functor (p, expansion) ->
219✔
1509
            let add_to params =
1510
              match p with Unit -> params | Named p -> p :: params
8✔
1511
            in
1512
            let params, sg = extract_functor_params expansion in
1513
            let params = match params with None -> [] | Some p -> p in
36✔
1514
            (Some (add_to params), sg)
219✔
1515
      in
1516
      match extract_functor_params t with
1517
      | None, sg -> signature sg
2,750✔
1518
      | Some params, sg ->
183✔
1519
          let sg_doc, content = signature sg in
1520
          let params =
183✔
1521
            let decl_of_arg arg =
1522
              let content = functor_parameter arg in
211✔
1523
              let attr = [ "parameter" ] in
211✔
1524
              let anchor =
1525
                Some (Url.Anchor.from_identifier (arg.id :> Paths.Identifier.t))
211✔
1526
              in
1527
              let doc = [] in
1528
              [
1529
                Item.Declaration
1530
                  { content; anchor; attr; doc; source_anchor = None };
1531
              ]
1532
            in
1533
            List.concat_map decl_of_arg params
183✔
1534
          in
1535
          let prelude = mk_heading ~label:"parameters" "Parameters" :: params
183✔
1536
          and content = mk_heading ~label:"signature" "Signature" :: content in
183✔
1537
          (sg_doc, prelude @ content)
1538

1539
    and expansion_of_module_type_expr :
1540
        Odoc_model.Lang.ModuleType.expr ->
1541
        (Comment.Comment.elements * Item.t list) option =
1542
     fun t ->
1543
      let rec simple_expansion_of (t : Odoc_model.Lang.ModuleType.expr) =
3,218✔
1544
        match t with
3,437✔
1545
        | Path { p_expansion = None; _ }
379✔
1546
        | TypeOf { t_expansion = None; _ }
8✔
UNCOV
1547
        | With { w_expansion = None; _ }
×
UNCOV
1548
        | Strengthen { s_expansion = None; _ } ->
×
1549
            None
1550
        | Path { p_expansion = Some e; _ }
426✔
1551
        | TypeOf { t_expansion = Some e; _ }
56✔
1552
        | With { w_expansion = Some e; _ }
218✔
UNCOV
1553
        | Strengthen { s_expansion = Some e; _ } ->
×
1554
            Some e
1555
        | Signature sg -> Some (Signature sg)
2,131✔
1556
        | Functor (f_parameter, e) -> (
219✔
1557
            match simple_expansion_of e with
1558
            | Some e -> Some (Functor (f_parameter, e))
211✔
1559
            | None -> None)
8✔
1560
      in
1561
      match simple_expansion_of t with
1562
      | None -> None
387✔
1563
      | Some e -> Some (simple_expansion e)
2,831✔
1564

1565
    and module_ : Odoc_model.Lang.Module.t -> Item.t =
1566
     fun t ->
1567
      let modname = Paths.Identifier.name t.id in
1,843✔
1568
      let expansion =
1,843✔
1569
        match t.type_ with
1570
        | Alias (_, Some e) -> Some (simple_expansion e)
102✔
1571
        | Alias (_, None) -> None
186✔
1572
        | ModuleType e -> expansion_of_module_type_expr e
1,555✔
1573
      in
1574
      let source_anchor = source_anchor t.source_loc in
1575
      let modname, status, expansion, expansion_doc =
1,843✔
1576
        match expansion with
1577
        | None -> (O.txt modname, `Default, None, None)
322✔
1578
        | Some (expansion_doc, items) ->
1,521✔
1579
            let status =
1580
              match t.type_ with
1581
              | ModuleType (Signature _) -> `Inline
987✔
1582
              | _ -> `Default
534✔
1583
            in
1584
            let url = Url.Path.from_identifier t.id in
1585
            let link = path url [ inline @@ Text modname ] in
1,521✔
1586
            let page =
1,521✔
1587
              make_expansion_page ~source_anchor url
1588
                [ t.doc.elements; expansion_doc ]
1589
                items
1590
            in
1591
            (link, status, Some page, Some expansion_doc)
1,521✔
1592
      in
1593
      let intro = O.keyword "module" ++ O.txt " " ++ modname in
1,843✔
1594
      let summary = O.ignore intro ++ mdexpr_in_decl t.id t.type_ in
1,843✔
1595
      let modexpr =
1,843✔
1596
        attach_expansion ~status
1597
          (Syntax.Type.annotation_separator, "sig", "end")
1598
          expansion summary
1599
      in
1600
      let content =
1,843✔
1601
        O.documentedSrc intro @ modexpr
1,843✔
1602
        @ O.documentedSrc
1,843✔
UNCOV
1603
            (if Syntax.Mod.close_tag_semicolon then O.txt ";" else O.noop)
×
1604
      in
1605
      let attr = [ "module" ] in
1606
      let anchor = path_to_id t.id in
1607
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
1,843✔
1608
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1609

1610
    and simple_expansion_in_decl (base : Paths.Identifier.Module.t) se =
1611
      let rec ty_of_se :
102✔
1612
          Lang.ModuleType.simple_expansion -> Lang.ModuleType.expr = function
1613
        | Signature sg -> Signature sg
102✔
UNCOV
1614
        | Functor (arg, sg) -> Functor (arg, ty_of_se sg)
×
1615
      in
1616
      mty_in_decl (base :> Paths.Identifier.Signature.t) (ty_of_se se)
102✔
1617

1618
    and mdexpr_in_decl (base : Paths.Identifier.Module.t) md =
1619
      let sig_dotdotdot =
1,843✔
1620
        O.txt Syntax.Type.annotation_separator
1,843✔
1621
        ++ O.cut ++ Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
1,843✔
1622
      in
1623
      match md with
1,843✔
1624
      | Alias (_, Some se) -> simple_expansion_in_decl base se
102✔
1625
      | Alias (p, _) when not Paths.Path.(is_hidden (p :> t)) ->
186✔
1626
          O.txt " =" ++ O.sp ++ mdexpr md
186✔
UNCOV
1627
      | Alias _ -> sig_dotdotdot
×
1628
      | ModuleType mt -> mty_in_decl (base :> Paths.Identifier.Signature.t) mt
1,555✔
1629

1630
    and mdexpr : Odoc_model.Lang.Module.decl -> text = function
1631
      | Alias (mod_path, _) -> Link.from_path (mod_path :> Paths.Path.t)
242✔
UNCOV
1632
      | ModuleType mt -> mty mt
×
1633

1634
    and module_type_manifest ~subst ~source_anchor modname id doc manifest
1635
        prefix =
1636
      let expansion =
1,369✔
1637
        match manifest with
1638
        | None -> None
128✔
1639
        | Some e -> expansion_of_module_type_expr e
1,241✔
1640
      in
1641
      let modname, expansion, expansion_doc =
1642
        match expansion with
1643
        | None -> (O.txt modname, None, None)
379✔
1644
        | Some (expansion_doc, items) ->
990✔
1645
            let url = Url.Path.from_identifier id in
1646
            let link = path url [ inline @@ Text modname ] in
990✔
1647
            let page =
990✔
1648
              make_expansion_page ~source_anchor url [ doc; expansion_doc ]
1649
                items
1650
            in
1651
            (link, Some page, Some expansion_doc)
990✔
1652
      in
1653
      let summary =
1654
        match manifest with
1655
        | None -> O.noop
128✔
1656
        | Some expr ->
1,241✔
1657
            O.ignore (prefix ++ modname)
1,241✔
1658
            ++ (if subst then O.txt " :=" ++ O.sp else O.txt " =" ++ O.sp)
8✔
1659
            ++ mty expr
1,241✔
1660
      in
1661
      ( modname,
1662
        expansion_doc,
1663
        attach_expansion (" = ", "sig", "end") expansion summary )
1,369✔
1664

1665
    and module_type (t : Odoc_model.Lang.ModuleType.t) =
1666
      let prefix =
1,361✔
1667
        O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
1,361✔
1668
      in
1669
      let modname = Paths.Identifier.name t.id in
1,361✔
1670
      let source_anchor = source_anchor t.source_loc in
1,361✔
1671
      let modname, expansion_doc, mty =
1,361✔
1672
        module_type_manifest ~subst:false ~source_anchor modname t.id
1673
          t.doc.elements t.expr prefix
1674
      in
1675
      let content =
1,361✔
1676
        O.documentedSrc (prefix ++ modname)
1,361✔
1677
        @ mty
1678
        @ O.documentedSrc
1,361✔
UNCOV
1679
            (if Syntax.Mod.close_tag_semicolon then O.txt ";" else O.noop)
×
1680
      in
1681
      let attr = [ "module-type" ] in
1682
      let anchor = path_to_id t.id in
1683
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
1,361✔
1684
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1685

1686
    and umty_hidden : Odoc_model.Lang.ModuleType.U.expr -> bool = function
1687
      | Path p -> Paths.Path.(is_hidden (p :> t))
403✔
1688
      | With (_, expr) -> umty_hidden expr
25✔
1689
      | TypeOf (ModPath m, _) | TypeOf (StructInclude m, _) ->
42✔
1690
          Paths.Path.(is_hidden (m :> t))
1691
      | Signature _ -> false
14✔
UNCOV
1692
      | Strengthen (expr, p, _) ->
×
UNCOV
1693
          umty_hidden expr || Paths.Path.(is_hidden (p :> t))
×
1694

1695
    and mty_hidden : Odoc_model.Lang.ModuleType.expr -> bool = function
1696
      | Path { p_path = mty_path; _ } -> Paths.Path.(is_hidden (mty_path :> t))
837✔
1697
      | With { w_expr; _ } -> umty_hidden w_expr
218✔
1698
      | TypeOf { t_desc = ModPath m; _ }
48✔
1699
      | TypeOf { t_desc = StructInclude m; _ } ->
16✔
1700
          Paths.Path.(is_hidden (m :> t))
1701
      | _ -> false
2,297✔
1702

1703
    and mty_with subs expr =
1704
      umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " "
238✔
1705
      ++ O.list
238✔
1706
           ~sep:(O.cut ++ O.txt " " ++ O.keyword "and" ++ O.txt " ")
238✔
1707
           ~f:(fun x -> O.span (substitution x))
279✔
1708
           subs
1709

1710
    and mty_strengthen expr path =
UNCOV
1711
      umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " "
×
UNCOV
1712
      ++ Link.from_path (path :> Paths.Path.t)
×
1713

1714
    and mty_typeof t_desc =
1715
      match t_desc with
154✔
1716
      | Odoc_model.Lang.ModuleType.ModPath m ->
90✔
1717
          O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
90✔
1718
          ++ O.keyword "of" ++ O.txt " "
90✔
1719
          ++ Link.from_path (m :> Paths.Path.t)
90✔
1720
      | StructInclude m ->
64✔
1721
          O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
64✔
1722
          ++ O.keyword "of" ++ O.txt " " ++ O.keyword "struct" ++ O.txt " "
64✔
1723
          ++ O.keyword "include" ++ O.txt " "
64✔
1724
          ++ Link.from_path (m :> Paths.Path.t)
64✔
1725
          ++ O.txt " " ++ O.keyword "end"
64✔
1726

1727
    and is_elidable_with_u : Odoc_model.Lang.ModuleType.U.expr -> bool =
1728
      function
1729
      | Path _ -> false
213✔
1730
      | Signature _ -> true
5✔
UNCOV
1731
      | With (_, expr) -> is_elidable_with_u expr
×
1732
      | TypeOf _ -> false
25✔
1733
      | Strengthen (expr, _, _) -> is_elidable_with_u expr
×
1734

1735
    and umty : Odoc_model.Lang.ModuleType.U.expr -> text =
1736
     fun m ->
1737
      match m with
527✔
1738
      | Path p -> Link.from_path (p :> Paths.Path.t)
403✔
1739
      | Signature _ ->
9✔
1740
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
9✔
1741
      | With (_, expr) when is_elidable_with_u expr ->
25✔
1742
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
5✔
1743
      | With (subs, expr) -> mty_with subs expr
20✔
1744
      | TypeOf (t_desc, _) -> mty_typeof t_desc
90✔
UNCOV
1745
      | Strengthen (expr, _, _) when is_elidable_with_u expr ->
×
UNCOV
1746
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
×
1747
      | Strengthen (expr, p, _) -> mty_strengthen expr (p :> Paths.Path.t)
×
1748

1749
    and mty : Odoc_model.Lang.ModuleType.expr -> text =
1750
     fun m ->
1751
      if mty_hidden m then
3,416✔
UNCOV
1752
        Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
×
1753
      else
1754
        match m with
3,416✔
1755
        | Path { p_path = mty_path; _ } ->
837✔
1756
            Link.from_path (mty_path :> Paths.Path.t)
UNCOV
1757
        | Functor (Unit, expr) ->
×
UNCOV
1758
            (if Syntax.Mod.functor_keyword then O.keyword "functor" else O.noop)
×
1759
            ++ O.span (O.txt " () " ++ Syntax.Type.arrow)
×
1760
            ++ O.sp ++ mty expr
×
1761
        | Functor (Named arg, expr) ->
48✔
1762
            let arg_expr = arg.expr in
1763
            let stop_before = expansion_of_module_type_expr arg_expr = None in
48✔
1764
            let name =
1765
              let open Odoc_model.Lang.FunctorParameter in
1766
              let name = Paths.Identifier.name arg.id in
1767
              let href =
48✔
1768
                Url.from_identifier ~stop_before (arg.id :> Paths.Identifier.t)
1769
              in
1770
              resolved href [ inline @@ Text name ]
48✔
1771
            in
UNCOV
1772
            (if Syntax.Mod.functor_keyword then O.keyword "functor" else O.noop)
×
1773
            ++ (O.box_hv @@ O.span
48✔
1774
               @@ O.txt " (" ++ name
48✔
1775
                  ++ O.txt Syntax.Type.annotation_separator
48✔
1776
                  ++ mty arg_expr ++ O.txt ")" ++ O.txt " " ++ Syntax.Type.arrow
48✔
1777
               )
1778
            ++ O.sp ++ mty expr
48✔
1779
        | With { w_expr; _ } when is_elidable_with_u w_expr ->
218✔
UNCOV
1780
            Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
×
1781
        | With { w_substitutions; w_expr; _ } ->
218✔
1782
            O.box_hv @@ mty_with w_substitutions w_expr
218✔
1783
        | TypeOf { t_desc; _ } -> mty_typeof t_desc
64✔
1784
        | Signature _ ->
2,249✔
1785
            Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
2,249✔
UNCOV
1786
        | Strengthen { s_expr; _ } when is_elidable_with_u s_expr ->
×
UNCOV
1787
            Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
×
1788
        | Strengthen { s_expr; s_path; _ } ->
×
1789
            O.box_hv @@ mty_strengthen s_expr (s_path :> Paths.Path.t)
×
1790

1791
    and mty_in_decl :
1792
        Paths.Identifier.Signature.t -> Odoc_model.Lang.ModuleType.expr -> text
1793
        =
1794
     fun base -> function
UNCOV
1795
      | (Path _ | Signature _ | With _ | TypeOf _ | Strengthen _) as m ->
×
1796
          O.txt Syntax.Type.annotation_separator ++ O.cut ++ mty m
1,868✔
1797
      | Functor _ as m when not Syntax.Mod.functor_contraction ->
171✔
UNCOV
1798
          O.txt Syntax.Type.annotation_separator ++ O.cut ++ mty m
×
1799
      | Functor (arg, expr) ->
171✔
1800
          let text_arg =
1801
            match arg with
1802
            | Unit -> O.txt "()"
8✔
1803
            | Named arg ->
163✔
1804
                let arg_expr = arg.expr in
1805
                let stop_before =
1806
                  expansion_of_module_type_expr arg_expr = None
163✔
1807
                in
1808
                let name =
1809
                  let open Odoc_model.Lang.FunctorParameter in
1810
                  let name = Paths.Identifier.name arg.id in
1811
                  let href =
163✔
1812
                    Url.from_identifier ~stop_before
1813
                      (arg.id :> Paths.Identifier.t)
1814
                  in
1815
                  resolved href [ inline @@ Text name ]
163✔
1816
                in
1817
                O.box_hv
163✔
1818
                @@ O.txt "(" ++ name
163✔
1819
                   ++ O.txt Syntax.Type.annotation_separator
163✔
1820
                   ++ O.cut ++ mty arg.expr ++ O.txt ")"
163✔
1821
          in
1822
          O.sp ++ text_arg ++ mty_in_decl base expr
171✔
1823

1824
    (* TODO : Centralize the list juggling for type parameters *)
1825
    and type_expr_in_subst td typath =
1826
      let typath = Link.from_fragment typath in
151✔
1827
      match td.Lang.TypeDecl.Equation.params with
151✔
1828
      | [] -> typath
125✔
1829
      | l -> Syntax.Type.handle_substitution_params typath (format_params l)
26✔
1830

1831
    and substitution : Odoc_model.Lang.ModuleType.substitution -> text =
1832
      function
1833
      | ModuleEq (frag_mod, md) ->
56✔
1834
          O.box_hv
1835
          @@ O.keyword "module" ++ O.txt " "
56✔
1836
             ++ Link.from_fragment (frag_mod :> Paths.Fragment.leaf)
56✔
1837
             ++ O.txt " =" ++ O.sp ++ mdexpr md
56✔
1838
      | ModuleTypeEq (frag_mty, md) ->
32✔
1839
          O.box_hv
1840
          @@ O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
32✔
1841
             ++ Link.from_fragment (frag_mty :> Paths.Fragment.leaf)
32✔
1842
             ++ O.txt " =" ++ O.sp ++ mty md
32✔
1843
      | TypeEq (frag_typ, td) ->
104✔
1844
          O.box_hv
1845
          @@ O.keyword "type" ++ O.txt " "
104✔
1846
             ++ type_expr_in_subst td (frag_typ :> Paths.Fragment.leaf)
104✔
1847
             ++ fst (format_manifest td)
104✔
1848
             ++ format_constraints
104✔
1849
                  td.Odoc_model.Lang.TypeDecl.Equation.constraints
1850
      | ModuleSubst (frag_mod, mod_path) ->
24✔
1851
          O.box_hv
1852
          @@ O.keyword "module" ++ O.txt " "
24✔
1853
             ++ Link.from_fragment (frag_mod :> Paths.Fragment.leaf)
24✔
1854
             ++ O.txt " :=" ++ O.sp
24✔
1855
             ++ Link.from_path (mod_path :> Paths.Path.t)
24✔
1856
      | ModuleTypeSubst (frag_mty, md) ->
16✔
1857
          O.box_hv
1858
          @@ O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
16✔
1859
             ++ Link.from_fragment (frag_mty :> Paths.Fragment.leaf)
16✔
1860
             ++ O.txt " :=" ++ O.sp ++ mty md
16✔
1861
      | TypeSubst (frag_typ, td) -> (
47✔
1862
          O.box_hv
1863
          @@ O.keyword "type" ++ O.txt " "
47✔
1864
             ++ type_expr_in_subst td (frag_typ :> Paths.Fragment.leaf)
47✔
1865
             ++ O.txt " :=" ++ O.sp
47✔
1866
             ++
47✔
1867
             match td.Lang.TypeDecl.Equation.manifest with
1868
             | None -> assert false (* cf loader/cmti *)
1869
             | Some te -> type_expr te)
47✔
1870

1871
    and include_ (t : Odoc_model.Lang.Include.t) =
1872
      let decl_hidden =
290✔
1873
        match t.decl with
UNCOV
1874
        | Alias p -> Paths.Path.(is_hidden (p :> t))
×
1875
        | ModuleType mty -> umty_hidden mty
290✔
1876
      in
1877
      let status = if decl_hidden then `Inline else t.status in
1✔
1878

1879
      let _, content = signature t.expansion.content in
1880
      let summary =
290✔
1881
        if decl_hidden then O.render (O.keyword "include" ++ O.txt " ...")
1✔
1882
        else
1883
          let include_decl =
289✔
1884
            match t.decl with
UNCOV
1885
            | Odoc_model.Lang.Include.Alias mod_path ->
×
UNCOV
1886
                Link.from_path (mod_path :> Paths.Path.t)
×
1887
            | ModuleType mt -> umty mt
289✔
1888
          in
1889
          O.render
289✔
1890
            (O.keyword "include" ++ O.txt " " ++ include_decl
289✔
UNCOV
1891
            ++ if Syntax.Mod.include_semicolon then O.keyword ";" else O.noop)
×
1892
      in
1893
      let content = { Include.content; status; summary } in
1894
      let attr = [ "include" ] in
1895
      let anchor = None in
1896
      let doc =
1897
        (* Documentation attached to includes behave differently than other
1898
           declarations, which show only the synopsis. We can't only show the
1899
           synopsis because no page is generated to render it and we'd loose
1900
           the full documentation.
1901
           The documentation from the expansion is not used. *)
1902
        Comment.to_ir t.doc.elements
1903
      in
1904
      Item.Include { attr; anchor; doc; content; source_anchor = None }
290✔
1905
  end
1906

1907
  open Module
1908

1909
  module Page : sig
1910
    val compilation_unit : Lang.Compilation_unit.t -> Document.t
1911

1912
    val page : Lang.Page.t -> Document.t
1913

1914
    val implementation :
1915
      Lang.Implementation.t ->
1916
      Syntax_highlighter.infos ->
1917
      string ->
1918
      Document.t list
1919
  end = struct
1920
    let pack : Lang.Compilation_unit.Packed.t -> Item.t list =
1921
     fun t ->
UNCOV
1922
      let f x =
×
UNCOV
1923
        let id = x.Lang.Compilation_unit.Packed.id in
×
1924
        let modname = Paths.Identifier.name id in
1925
        let md_def =
×
UNCOV
1926
          O.keyword "module" ++ O.txt " " ++ O.txt modname ++ O.txt " = "
×
1927
          ++ Link.from_path (x.path :> Paths.Path.t)
×
1928
        in
1929
        let content = O.documentedSrc md_def in
×
UNCOV
1930
        let anchor =
×
1931
          Some (Url.Anchor.from_identifier (id :> Paths.Identifier.t))
×
1932
        in
1933
        let attr = [ "modules" ] in
1934
        let doc = [] in
1935
        let decl = { Item.anchor; content; attr; doc; source_anchor = None } in
1936
        Item.Declaration decl
1937
      in
1938
      List.map f t
1939

1940
    let compilation_unit (t : Odoc_model.Lang.Compilation_unit.t) =
1941
      let url = Url.Path.from_identifier t.id in
359✔
1942
      let unit_doc, items =
359✔
1943
        match t.content with
1944
        | Module sign -> signature sign
359✔
UNCOV
1945
        | Pack packed -> ([], pack packed)
×
1946
      in
1947
      let source_anchor = source_anchor t.source_loc in
1948
      let page = make_expansion_page ~source_anchor url [ unit_doc ] items in
359✔
1949
      Document.Page page
359✔
1950

1951
    let page (t : Odoc_model.Lang.Page.t) =
1952
      (*let name =
1953
          match t.name.iv with `Page (_, name) | `LeafPage (_, name) -> name
1954
        in*)
1955
      (*let title = Odoc_model.Names.PageName.to_string name in*)
1956
      let url = Url.Path.from_identifier t.name in
69✔
1957
      let preamble, items = Sectioning.docs t.content.elements in
69✔
1958
      let source_anchor = None in
69✔
1959
      Document.Page { Page.preamble; items; url; source_anchor }
1960

1961
    let implementation (v : Odoc_model.Lang.Implementation.t) syntax_info
1962
        source_code =
1963
      match v.id with
28✔
UNCOV
1964
      | None -> []
×
1965
      | Some id ->
28✔
1966
          [
1967
            Document.Source_page
1968
              (Source_page.source id syntax_info v.source_info source_code);
28✔
1969
          ]
1970
  end
1971

1972
  include Page
1973

1974
  let type_expr = type_expr
1975

1976
  let record = record
1977

1978
  let unboxed_record = unboxed_record
1979
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