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

ocaml / odoc / 3231

09 Jun 2026 12:07PM UTC coverage: 70.941%. First build
3231

Pull #1444

github

web-flow
Merge 994b7436f into 591a8d2e8
Pull Request #1444: Omit displaying zero alloc arity when it matches the function arity

0 of 6 new or added lines in 1 file covered. (0.0%)

10444 of 14722 relevant lines covered (70.94%)

5867.77 hits per line

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

79.26
/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.Kind.t -> text
315

316
    val with_kind_annotation : Odoc_model.Lang.Kind.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

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

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

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

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

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

578
  open Type_expression
579

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

589
    val extension : Lang.Extension.t -> Item.t
590

591
    val record : Lang.TypeDecl.Field.t list -> DocumentedSrc.one list
592

593
    val unboxed_record :
594
      Lang.TypeDecl.UnboxedField.t list -> DocumentedSrc.one list
595

596
    val exn : Lang.Exception.t -> Item.t
597

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1042
  open Type_declaration
1043

1044
  module Value : sig
1045
    val value : Lang.Value.t -> Item.t
1046
  end = struct
1047
    let rec arity_of_type_expr = function
NEW
1048
      | Lang.TypeExpr.Arrow (_lbl, _arg, curried) ->
×
NEW
1049
          1 + arity_of_type_expr curried
×
NEW
1050
      | _ -> 0
×
1051

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

1103
  open Value
1104

1105
  (* This chunk of code is responsible for sectioning list of items
1106
     according to headings by extracting headings as Items.
1107

1108
     TODO: This sectioning would be better done as a pass on the model directly.
1109
  *)
1110
  module Sectioning : sig
1111
    open Odoc_model
1112

1113
    val comment_items : Comment.elements -> Item.t list
1114

1115
    val docs : Comment.elements -> Item.t list * Item.t list
1116
  end = struct
1117
    let take_until_heading_or_end (docs : Odoc_model.Comment.elements) =
1118
      let content, _, rest =
693✔
1119
        Doctree.Take.until docs ~classify:(fun b ->
1120
            match b.Location.value with
1,481✔
1121
            | `Heading _ -> Stop_and_keep
202✔
1122
            | #Odoc_model.Comment.attached_block_element as doc ->
1,279✔
1123
                let content = Comment.attached_block_element doc in
1124
                Accum content)
1,279✔
1125
      in
1126
      (content, rest)
693✔
1127

1128
    let comment_items (input0 : Odoc_model.Comment.elements) =
1129
      let rec loop input_comment acc =
963✔
1130
        match input_comment with
2,493✔
1131
        | [] -> List.rev acc
963✔
1132
        | element :: input_comment -> (
1,530✔
1133
            match element.Location.value with
1134
            | `Heading h ->
837✔
1135
                let item = Comment.heading h in
1136
                loop input_comment (item :: acc)
837✔
1137
            | _ ->
693✔
1138
                let content, input_comment =
1139
                  take_until_heading_or_end (element :: input_comment)
1140
                in
1141
                let item = Item.Text content in
693✔
1142
                loop input_comment (item :: acc))
1143
      in
1144
      loop input0 []
1145

1146
    (* For doc pages, we want the header to contain everything until
1147
       the first heading, then everything before the next heading which
1148
       is either lower, or a section.
1149
    *)
1150
    let docs input_comment =
1151
      let items = comment_items input_comment in
69✔
1152
      let until_first_heading, o, items =
69✔
1153
        Doctree.Take.until items ~classify:(function
1154
          | Item.Heading h as i -> Stop_and_accum ([ i ], Some h.level)
69✔
1155
          | i -> Accum [ i ])
×
1156
      in
1157
      match o with
69✔
1158
      | None -> (until_first_heading, items)
×
1159
      | Some level ->
69✔
1160
          let max_level = if level = 1 then 2 else level in
×
1161
          let before_second_heading, _, items =
1162
            Doctree.Take.until items ~classify:(function
1163
              | Item.Heading h when h.level >= max_level -> Stop_and_keep
13✔
1164
              | i -> Accum [ i ])
38✔
1165
          in
1166
          let header = until_first_heading @ before_second_heading in
69✔
1167
          (header, items)
1168
  end
1169

1170
  module Class : sig
1171
    val class_ : Lang.Class.t -> Item.t
1172

1173
    val class_type : Lang.ClassType.t -> Item.t
1174
  end = struct
1175
    let class_type_expr (cte : Odoc_model.Lang.ClassType.expr) =
1176
      match cte with
249✔
1177
      | Constr (path, args) ->
58✔
1178
          let link = Link.from_path (path :> Paths.Path.t) in
1179
          format_type_path ~delim:`brackets args link
58✔
1180
      | Signature _ ->
191✔
1181
          Syntax.Class.open_tag ++ O.txt " ... " ++ Syntax.Class.close_tag
191✔
1182

1183
    let method_ (t : Odoc_model.Lang.Method.t) =
1184
      let name = Paths.Identifier.name t.id in
90✔
1185
      let virtual_ =
90✔
1186
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
8✔
1187
      in
1188
      let private_ =
1189
        if t.private_ then O.keyword "private" ++ O.txt " " else O.noop
8✔
1190
      in
1191
      let content =
1192
        O.documentedSrc
1193
          (O.keyword "method" ++ O.txt " " ++ private_ ++ virtual_ ++ O.txt name
90✔
1194
          ++ O.txt Syntax.Type.annotation_separator
90✔
1195
          ++ type_expr t.type_)
90✔
1196
      in
1197
      let attr = [ "method" ] in
90✔
1198
      let anchor = path_to_id t.id in
1199
      let doc = Comment.to_ir t.doc.elements in
90✔
1200
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
90✔
1201

1202
    let instance_variable (t : Odoc_model.Lang.InstanceVariable.t) =
1203
      let name = Paths.Identifier.name t.id in
17✔
1204
      let virtual_ =
17✔
1205
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
8✔
1206
      in
1207
      let mutable_ =
1208
        if t.mutable_ then O.keyword "mutable" ++ O.txt " " else O.noop
8✔
1209
      in
1210
      let content =
1211
        O.documentedSrc
1212
          (O.keyword "val" ++ O.txt " " ++ mutable_ ++ virtual_ ++ O.txt name
17✔
1213
          ++ O.txt Syntax.Type.annotation_separator
17✔
1214
          ++ type_expr t.type_)
17✔
1215
      in
1216
      let attr = [ "value"; "instance-variable" ] in
17✔
1217
      let anchor = path_to_id t.id in
1218
      let doc = Comment.to_ir t.doc.elements in
17✔
1219
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
17✔
1220

1221
    let inherit_ (ih : Lang.ClassSignature.Inherit.t) =
1222
      let cte =
16✔
1223
        match ih.expr with
1224
        | Signature _ -> assert false (* Bold. *)
1225
        | cty -> cty
16✔
1226
      in
1227
      let content =
1228
        O.documentedSrc (O.keyword "inherit" ++ O.txt " " ++ class_type_expr cte)
16✔
1229
      in
1230
      let attr = [ "inherit" ] in
16✔
1231
      let anchor = None in
1232
      let doc = Comment.to_ir ih.doc.elements in
1233
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
16✔
1234

1235
    let constraint_ (cst : Lang.ClassSignature.Constraint.t) =
1236
      let content =
8✔
1237
        O.documentedSrc (format_constraints [ (cst.left, cst.right) ])
8✔
1238
      in
1239
      let attr = [] in
8✔
1240
      let anchor = None in
1241
      let doc = Comment.to_ir cst.doc.elements in
1242
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
8✔
1243

1244
    let class_signature (c : Lang.ClassSignature.t) =
1245
      let rec loop l acc_items =
233✔
1246
        match l with
388✔
1247
        | [] -> List.rev acc_items
233✔
1248
        | item :: rest -> (
155✔
1249
            let continue item = loop rest (item :: acc_items) in
131✔
1250
            match (item : Lang.ClassSignature.item) with
1251
            | Inherit cty -> continue @@ inherit_ cty
16✔
1252
            | Method m -> continue @@ method_ m
90✔
1253
            | InstanceVariable v -> continue @@ instance_variable v
17✔
1254
            | Constraint cst -> continue @@ constraint_ cst
8✔
1255
            | Comment `Stop ->
8✔
1256
                let rest =
1257
                  List.skip_until rest ~p:(function
1258
                    | Lang.ClassSignature.Comment `Stop -> true
8✔
1259
                    | _ -> false)
8✔
1260
                in
1261
                loop rest acc_items
8✔
1262
            | Comment (`Docs c) ->
16✔
1263
                let items = Sectioning.comment_items c.elements in
1264
                loop rest (List.rev_append items acc_items))
16✔
1265
      in
1266
      (* FIXME: use [t.self] *)
1267
      (c.doc.elements, loop c.items [])
233✔
1268

1269
    let rec class_decl (cd : Odoc_model.Lang.Class.decl) =
1270
      match cd with
182✔
1271
      | ClassType expr -> class_type_expr expr
166✔
1272
      (* TODO: factorize the following with [type_expr] *)
1273
      | Arrow (None, src, dst) ->
16✔
1274
          O.span
16✔
1275
            (type_expr ~needs_parentheses:true src
16✔
1276
            ++ O.txt " " ++ Syntax.Type.arrow)
16✔
1277
          ++ O.txt " " ++ class_decl dst
16✔
1278
      | Arrow (Some (RawOptional _ as lbl), _src, dst) ->
×
1279
          O.span
×
1280
            (O.box_hv
×
1281
            @@ label lbl ++ O.txt ":"
×
1282
               ++ tag "error" (O.txt "???")
×
1283
               ++ O.txt " " ++ Syntax.Type.arrow)
×
1284
          ++ O.sp ++ class_decl dst
×
1285
      | Arrow (Some lbl, src, dst) ->
×
1286
          O.span
×
1287
            (label lbl ++ O.txt ":"
×
1288
            ++ type_expr ~needs_parentheses:true src
×
1289
            ++ O.txt " " ++ Syntax.Type.arrow)
×
1290
          ++ O.txt " " ++ class_decl dst
×
1291

1292
    let class_ (t : Odoc_model.Lang.Class.t) =
1293
      let name = Paths.Identifier.name t.id in
166✔
1294
      let params =
166✔
1295
        match t.params with
1296
        | [] -> O.noop
142✔
1297
        | _ :: _ as params -> format_params ~delim:`brackets params ++ O.txt " "
24✔
1298
      in
1299
      let virtual_ =
1300
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
24✔
1301
      in
1302

1303
      let source_anchor = source_anchor t.source_loc in
1304
      let cname, expansion, expansion_doc =
166✔
1305
        match t.expansion with
1306
        | None -> (O.documentedSrc @@ O.txt name, None, None)
×
1307
        | Some csig ->
166✔
1308
            let expansion_doc, items = class_signature csig in
1309
            let url = Url.Path.from_identifier t.id in
166✔
1310
            let page =
166✔
1311
              make_expansion_page ~source_anchor url
1312
                [ t.doc.elements; expansion_doc ]
1313
                items
1314
            in
1315
            ( O.documentedSrc @@ path url [ inline @@ Text name ],
166✔
1316
              Some page,
1317
              Some expansion_doc )
1318
      in
1319
      let summary =
1320
        O.txt Syntax.Type.annotation_separator ++ class_decl t.type_
166✔
1321
      in
1322
      let cd =
166✔
1323
        attach_expansion
1324
          (Syntax.Type.annotation_separator, "object", "end")
1325
          expansion summary
1326
      in
1327
      let content =
166✔
1328
        O.documentedSrc (O.keyword "class" ++ O.txt " " ++ virtual_ ++ params)
166✔
1329
        @ cname @ cd
1330
      in
1331
      let attr = [ "class" ] in
1332
      let anchor = path_to_id t.id in
1333
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
166✔
1334
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1335

1336
    let class_type (t : Odoc_model.Lang.ClassType.t) =
1337
      let name = Paths.Identifier.name t.id in
67✔
1338
      let params = format_params ~delim:`brackets t.params in
67✔
1339
      let virtual_ =
67✔
1340
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
8✔
1341
      in
1342
      let source_anchor = source_anchor t.source_loc in
1343
      let cname, expansion, expansion_doc =
67✔
1344
        match t.expansion with
1345
        | None -> (O.documentedSrc @@ O.txt name, None, None)
×
1346
        | Some csig ->
67✔
1347
            let url = Url.Path.from_identifier t.id in
1348
            let expansion_doc, items = class_signature csig in
67✔
1349
            let page =
67✔
1350
              make_expansion_page ~source_anchor url
1351
                [ t.doc.elements; expansion_doc ]
1352
                items
1353
            in
1354
            ( O.documentedSrc @@ path url [ inline @@ Text name ],
67✔
1355
              Some page,
1356
              Some expansion_doc )
1357
      in
1358
      let summary = O.txt " = " ++ class_type_expr t.expr in
67✔
1359
      let expr = attach_expansion (" = ", "object", "end") expansion summary in
67✔
1360
      let content =
67✔
1361
        O.documentedSrc
67✔
1362
          (O.keyword "class" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
67✔
1363
         ++ virtual_ ++ params ++ O.txt " ")
67✔
1364
        @ cname @ expr
1365
      in
1366
      let attr = [ "class-type" ] in
1367
      let anchor = path_to_id t.id in
1368
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
67✔
1369
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1370
  end
1371

1372
  open Class
1373

1374
  module Module : sig
1375
    val signature : Lang.Signature.t -> Comment.Comment.elements * Item.t list
1376
    (** Returns [header_doc, content]. *)
1377
  end = struct
1378
    let internal_module m =
1379
      let open Lang.Module in
1,924✔
1380
      match m.id.iv with
1381
      | `Module (_, name) when ModuleName.is_hidden name -> true
81✔
1382
      | _ -> false
1,843✔
1383

1384
    let internal_type t =
1385
      let open Lang.TypeDecl in
2,969✔
1386
      match t.id.iv with
1387
      | `Type (_, name) when TypeName.is_hidden name -> true
1✔
1388
      | _ -> false
2,968✔
1389

1390
    let internal_value v =
1391
      let open Lang.Value in
1,018✔
1392
      match v.id.iv with
1393
      | `Value (_, name) when ValueName.is_hidden name -> true
106✔
1394
      | _ -> false
912✔
1395

1396
    let internal_module_type t =
1397
      let open Lang.ModuleType in
1,361✔
1398
      match t.id.iv with
1399
      | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true
×
1400
      | _ -> false
1,361✔
1401

1402
    let internal_module_substitution t =
1403
      let open Lang.ModuleSubstitution in
8✔
1404
      match t.id.iv with
1405
      | `Module (_, name) when ModuleName.is_hidden name -> true
×
1406
      | _ -> false
8✔
1407

1408
    let internal_module_type_substitution t =
1409
      let open Lang.ModuleTypeSubstitution in
8✔
1410
      match t.id.iv with
1411
      | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true
×
1412
      | _ -> false
8✔
1413

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

1459
    and functor_parameter :
1460
        Odoc_model.Lang.FunctorParameter.parameter -> DocumentedSrc.t =
1461
     fun arg ->
1462
      let open Odoc_model.Lang.FunctorParameter in
211✔
1463
      let name = Paths.Identifier.name arg.id in
1464
      let render_ty = arg.expr in
211✔
1465
      let modtyp =
1466
        mty_in_decl (arg.id :> Paths.Identifier.Signature.t) render_ty
1467
      in
1468
      let modname, mod_decl =
211✔
1469
        match expansion_of_module_type_expr arg.expr with
1470
        | None ->
×
1471
            let modname = O.txt (Paths.Identifier.name arg.id) in
×
1472
            (modname, O.documentedSrc modtyp)
×
1473
        | Some (expansion_doc, items) ->
211✔
1474
            let url = Url.Path.from_identifier arg.id in
1475
            let modname = path url [ inline @@ Text name ] in
211✔
1476
            let type_with_expansion =
211✔
1477
              let content =
1478
                make_expansion_page ~source_anchor:None url [ expansion_doc ]
1479
                  items
1480
              in
1481
              let summary = O.render modtyp in
211✔
1482
              let status = `Default in
211✔
1483
              let expansion =
1484
                O.documentedSrc
211✔
1485
                  (O.txt Syntax.Type.annotation_separator ++ O.keyword "sig")
211✔
1486
                @ DocumentedSrc.[ Subpage { content; status } ]
1487
                @ O.documentedSrc (O.keyword "end")
211✔
1488
              in
1489
              DocumentedSrc.
1490
                [
1491
                  Alternative
1492
                    (Expansion { status = `Default; summary; url; expansion });
1493
                ]
1494
            in
1495
            (modname, type_with_expansion)
1496
      in
1497
      O.documentedSrc (O.keyword "module" ++ O.txt " ")
211✔
1498
      @ O.documentedSrc modname @ mod_decl
211✔
1499

1500
    and module_substitution (t : Odoc_model.Lang.ModuleSubstitution.t) =
1501
      let name = Paths.Identifier.name t.id in
8✔
1502
      let path = Link.from_path (t.manifest :> Paths.Path.t) in
8✔
1503
      let content =
8✔
1504
        O.documentedSrc
1505
          (O.keyword "module" ++ O.txt " " ++ O.txt name ++ O.txt " :=" ++ O.sp
8✔
1506
         ++ path)
8✔
1507
      in
1508
      let attr = [ "module-substitution" ] in
8✔
1509
      let anchor = path_to_id t.id in
1510
      let doc = Comment.to_ir t.doc.elements in
8✔
1511
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
8✔
1512

1513
    and module_type_substitution (t : Odoc_model.Lang.ModuleTypeSubstitution.t)
1514
        =
1515
      let prefix =
8✔
1516
        O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
8✔
1517
      in
1518
      let source_anchor = None in
8✔
1519
      let modname = Paths.Identifier.name t.id in
1520
      let modname, expansion_doc, mty =
8✔
1521
        module_type_manifest ~subst:true ~source_anchor modname t.id
1522
          t.doc.elements (Some t.manifest) prefix
1523
      in
1524
      let content =
8✔
1525
        O.documentedSrc (prefix ++ modname)
8✔
1526
        @ mty
1527
        @ O.documentedSrc
8✔
1528
            (if Syntax.Mod.close_tag_semicolon then O.txt ";" else O.noop)
×
1529
      in
1530
      let attr = [ "module-type" ] in
1531
      let anchor = path_to_id t.id in
1532
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
8✔
1533
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1534

1535
    and simple_expansion :
1536
        Odoc_model.Lang.ModuleType.simple_expansion ->
1537
        Comment.Comment.elements * Item.t list =
1538
     fun t ->
1539
      let rec extract_functor_params
2,933✔
1540
          (f : Odoc_model.Lang.ModuleType.simple_expansion) =
1541
        match f with
3,152✔
1542
        | Signature sg -> (None, sg)
2,933✔
1543
        | Functor (p, expansion) ->
219✔
1544
            let add_to params =
1545
              match p with Unit -> params | Named p -> p :: params
8✔
1546
            in
1547
            let params, sg = extract_functor_params expansion in
1548
            let params = match params with None -> [] | Some p -> p in
36✔
1549
            (Some (add_to params), sg)
219✔
1550
      in
1551
      match extract_functor_params t with
1552
      | None, sg -> signature sg
2,750✔
1553
      | Some params, sg ->
183✔
1554
          let sg_doc, content = signature sg in
1555
          let params =
183✔
1556
            let decl_of_arg arg =
1557
              let content = functor_parameter arg in
211✔
1558
              let attr = [ "parameter" ] in
211✔
1559
              let anchor =
1560
                Some (Url.Anchor.from_identifier (arg.id :> Paths.Identifier.t))
211✔
1561
              in
1562
              let doc = [] in
1563
              [
1564
                Item.Declaration
1565
                  { content; anchor; attr; doc; source_anchor = None };
1566
              ]
1567
            in
1568
            List.concat_map decl_of_arg params
183✔
1569
          in
1570
          let prelude = mk_heading ~label:"parameters" "Parameters" :: params
183✔
1571
          and content = mk_heading ~label:"signature" "Signature" :: content in
183✔
1572
          (sg_doc, prelude @ content)
1573

1574
    and expansion_of_module_type_expr :
1575
        Odoc_model.Lang.ModuleType.expr ->
1576
        (Comment.Comment.elements * Item.t list) option =
1577
     fun t ->
1578
      let rec simple_expansion_of (t : Odoc_model.Lang.ModuleType.expr) =
3,218✔
1579
        match t with
3,437✔
1580
        | Path { p_expansion = None; _ }
379✔
1581
        | TypeOf { t_expansion = None; _ }
8✔
1582
        | With { w_expansion = None; _ }
×
1583
        | Strengthen { s_expansion = None; _ } ->
×
1584
            None
1585
        | Path { p_expansion = Some e; _ }
426✔
1586
        | TypeOf { t_expansion = Some e; _ }
56✔
1587
        | With { w_expansion = Some e; _ }
218✔
1588
        | Strengthen { s_expansion = Some e; _ } ->
×
1589
            Some e
1590
        | Signature sg -> Some (Signature sg)
2,131✔
1591
        | Functor (f_parameter, e) -> (
219✔
1592
            match simple_expansion_of e with
1593
            | Some e -> Some (Functor (f_parameter, e))
211✔
1594
            | None -> None)
8✔
1595
      in
1596
      match simple_expansion_of t with
1597
      | None -> None
387✔
1598
      | Some e -> Some (simple_expansion e)
2,831✔
1599

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

1645
    and simple_expansion_in_decl (base : Paths.Identifier.Module.t) se =
1646
      let rec ty_of_se :
102✔
1647
          Lang.ModuleType.simple_expansion -> Lang.ModuleType.expr = function
1648
        | Signature sg -> Signature sg
102✔
1649
        | Functor (arg, sg) -> Functor (arg, ty_of_se sg)
×
1650
      in
1651
      mty_in_decl (base :> Paths.Identifier.Signature.t) (ty_of_se se)
102✔
1652

1653
    and mdexpr_in_decl (base : Paths.Identifier.Module.t) md =
1654
      let sig_dotdotdot =
1,843✔
1655
        O.txt Syntax.Type.annotation_separator
1,843✔
1656
        ++ O.cut ++ Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
1,843✔
1657
      in
1658
      match md with
1,843✔
1659
      | Alias (_, Some se) -> simple_expansion_in_decl base se
102✔
1660
      | Alias (p, _) when not Paths.Path.(is_hidden (p :> t)) ->
186✔
1661
          O.txt " =" ++ O.sp ++ mdexpr md
186✔
1662
      | Alias _ -> sig_dotdotdot
×
1663
      | ModuleType mt -> mty_in_decl (base :> Paths.Identifier.Signature.t) mt
1,555✔
1664

1665
    and mdexpr : Odoc_model.Lang.Module.decl -> text = function
1666
      | Alias (mod_path, _) -> Link.from_path (mod_path :> Paths.Path.t)
242✔
1667
      | ModuleType mt -> mty mt
×
1668

1669
    and module_type_manifest ~subst ~source_anchor modname id doc manifest
1670
        prefix =
1671
      let expansion =
1,369✔
1672
        match manifest with
1673
        | None -> None
128✔
1674
        | Some e -> expansion_of_module_type_expr e
1,241✔
1675
      in
1676
      let modname, expansion, expansion_doc =
1677
        match expansion with
1678
        | None -> (O.txt modname, None, None)
379✔
1679
        | Some (expansion_doc, items) ->
990✔
1680
            let url = Url.Path.from_identifier id in
1681
            let link = path url [ inline @@ Text modname ] in
990✔
1682
            let page =
990✔
1683
              make_expansion_page ~source_anchor url [ doc; expansion_doc ]
1684
                items
1685
            in
1686
            (link, Some page, Some expansion_doc)
990✔
1687
      in
1688
      let summary =
1689
        match manifest with
1690
        | None -> O.noop
128✔
1691
        | Some expr ->
1,241✔
1692
            O.ignore (prefix ++ modname)
1,241✔
1693
            ++ (if subst then O.txt " :=" ++ O.sp else O.txt " =" ++ O.sp)
8✔
1694
            ++ mty expr
1,241✔
1695
      in
1696
      ( modname,
1697
        expansion_doc,
1698
        attach_expansion (" = ", "sig", "end") expansion summary )
1,369✔
1699

1700
    and module_type (t : Odoc_model.Lang.ModuleType.t) =
1701
      let prefix =
1,361✔
1702
        O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
1,361✔
1703
      in
1704
      let modname = Paths.Identifier.name t.id in
1,361✔
1705
      let source_anchor = source_anchor t.source_loc in
1,361✔
1706
      let modname, expansion_doc, mty =
1,361✔
1707
        module_type_manifest ~subst:false ~source_anchor modname t.id
1708
          t.doc.elements t.expr prefix
1709
      in
1710
      let content =
1,361✔
1711
        O.documentedSrc (prefix ++ modname)
1,361✔
1712
        @ mty
1713
        @ O.documentedSrc
1,361✔
1714
            (if Syntax.Mod.close_tag_semicolon then O.txt ";" else O.noop)
×
1715
      in
1716
      let attr = [ "module-type" ] in
1717
      let anchor = path_to_id t.id in
1718
      let doc = Comment.synopsis ~decl_doc:t.doc.elements ~expansion_doc in
1,361✔
1719
      Item.Declaration { attr; anchor; doc; content; source_anchor }
1720

1721
    and umty_hidden : Odoc_model.Lang.ModuleType.U.expr -> bool = function
1722
      | Path p -> Paths.Path.(is_hidden (p :> t))
403✔
1723
      | With (_, expr) -> umty_hidden expr
25✔
1724
      | TypeOf (ModPath m, _) | TypeOf (StructInclude m, _) ->
42✔
1725
          Paths.Path.(is_hidden (m :> t))
1726
      | Signature _ -> false
14✔
1727
      | Strengthen (expr, p, _) ->
×
1728
          umty_hidden expr || Paths.Path.(is_hidden (p :> t))
×
1729

1730
    and mty_hidden : Odoc_model.Lang.ModuleType.expr -> bool = function
1731
      | Path { p_path = mty_path; _ } -> Paths.Path.(is_hidden (mty_path :> t))
837✔
1732
      | With { w_expr; _ } -> umty_hidden w_expr
218✔
1733
      | TypeOf { t_desc = ModPath m; _ }
48✔
1734
      | TypeOf { t_desc = StructInclude m; _ } ->
16✔
1735
          Paths.Path.(is_hidden (m :> t))
1736
      | _ -> false
2,297✔
1737

1738
    and mty_with subs expr =
1739
      umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " "
238✔
1740
      ++ O.list
238✔
1741
           ~sep:(O.cut ++ O.txt " " ++ O.keyword "and" ++ O.txt " ")
238✔
1742
           ~f:(fun x -> O.span (substitution x))
279✔
1743
           subs
1744

1745
    and mty_strengthen expr path =
1746
      umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " "
×
1747
      ++ Link.from_path (path :> Paths.Path.t)
×
1748

1749
    and mty_typeof t_desc =
1750
      match t_desc with
154✔
1751
      | Odoc_model.Lang.ModuleType.ModPath m ->
90✔
1752
          O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
90✔
1753
          ++ O.keyword "of" ++ O.txt " "
90✔
1754
          ++ Link.from_path (m :> Paths.Path.t)
90✔
1755
      | StructInclude m ->
64✔
1756
          O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
64✔
1757
          ++ O.keyword "of" ++ O.txt " " ++ O.keyword "struct" ++ O.txt " "
64✔
1758
          ++ O.keyword "include" ++ O.txt " "
64✔
1759
          ++ Link.from_path (m :> Paths.Path.t)
64✔
1760
          ++ O.txt " " ++ O.keyword "end"
64✔
1761

1762
    and is_elidable_with_u : Odoc_model.Lang.ModuleType.U.expr -> bool =
1763
      function
1764
      | Path _ -> false
213✔
1765
      | Signature _ -> true
5✔
1766
      | With (_, expr) -> is_elidable_with_u expr
×
1767
      | TypeOf _ -> false
25✔
1768
      | Strengthen (expr, _, _) -> is_elidable_with_u expr
×
1769

1770
    and umty : Odoc_model.Lang.ModuleType.U.expr -> text =
1771
     fun m ->
1772
      match m with
527✔
1773
      | Path p -> Link.from_path (p :> Paths.Path.t)
403✔
1774
      | Signature _ ->
9✔
1775
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
9✔
1776
      | With (_, expr) when is_elidable_with_u expr ->
25✔
1777
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
5✔
1778
      | With (subs, expr) -> mty_with subs expr
20✔
1779
      | TypeOf (t_desc, _) -> mty_typeof t_desc
90✔
1780
      | Strengthen (expr, _, _) when is_elidable_with_u expr ->
×
1781
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
×
1782
      | Strengthen (expr, p, _) -> mty_strengthen expr (p :> Paths.Path.t)
×
1783

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

1826
    and mty_in_decl :
1827
        Paths.Identifier.Signature.t -> Odoc_model.Lang.ModuleType.expr -> text
1828
        =
1829
     fun base -> function
1830
      | (Path _ | Signature _ | With _ | TypeOf _ | Strengthen _) as m ->
×
1831
          O.txt Syntax.Type.annotation_separator ++ O.cut ++ mty m
1,868✔
1832
      | Functor _ as m when not Syntax.Mod.functor_contraction ->
171✔
1833
          O.txt Syntax.Type.annotation_separator ++ O.cut ++ mty m
×
1834
      | Functor (arg, expr) ->
171✔
1835
          let text_arg =
1836
            match arg with
1837
            | Unit -> O.txt "()"
8✔
1838
            | Named arg ->
163✔
1839
                let arg_expr = arg.expr in
1840
                let stop_before =
1841
                  expansion_of_module_type_expr arg_expr = None
163✔
1842
                in
1843
                let name =
1844
                  let open Odoc_model.Lang.FunctorParameter in
1845
                  let name = Paths.Identifier.name arg.id in
1846
                  let href =
163✔
1847
                    Url.from_identifier ~stop_before
1848
                      (arg.id :> Paths.Identifier.t)
1849
                  in
1850
                  resolved href [ inline @@ Text name ]
163✔
1851
                in
1852
                O.box_hv
163✔
1853
                @@ O.txt "(" ++ name
163✔
1854
                   ++ O.txt Syntax.Type.annotation_separator
163✔
1855
                   ++ O.cut ++ mty arg.expr ++ O.txt ")"
163✔
1856
          in
1857
          O.sp ++ text_arg ++ mty_in_decl base expr
171✔
1858

1859
    (* TODO : Centralize the list juggling for type parameters *)
1860
    and type_expr_in_subst td typath =
1861
      let typath = Link.from_fragment typath in
151✔
1862
      match td.Lang.TypeDecl.Equation.params with
151✔
1863
      | [] -> typath
125✔
1864
      | l -> Syntax.Type.handle_substitution_params typath (format_params l)
26✔
1865

1866
    and substitution : Odoc_model.Lang.ModuleType.substitution -> text =
1867
      function
1868
      | ModuleEq (frag_mod, md) ->
56✔
1869
          O.box_hv
1870
          @@ O.keyword "module" ++ O.txt " "
56✔
1871
             ++ Link.from_fragment (frag_mod :> Paths.Fragment.leaf)
56✔
1872
             ++ O.txt " =" ++ O.sp ++ mdexpr md
56✔
1873
      | ModuleTypeEq (frag_mty, md) ->
32✔
1874
          O.box_hv
1875
          @@ O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
32✔
1876
             ++ Link.from_fragment (frag_mty :> Paths.Fragment.leaf)
32✔
1877
             ++ O.txt " =" ++ O.sp ++ mty md
32✔
1878
      | TypeEq (frag_typ, td) ->
104✔
1879
          O.box_hv
1880
          @@ O.keyword "type" ++ O.txt " "
104✔
1881
             ++ type_expr_in_subst td (frag_typ :> Paths.Fragment.leaf)
104✔
1882
             ++ fst (format_manifest td)
104✔
1883
             ++ format_constraints
104✔
1884
                  td.Odoc_model.Lang.TypeDecl.Equation.constraints
1885
      | ModuleSubst (frag_mod, mod_path) ->
24✔
1886
          O.box_hv
1887
          @@ O.keyword "module" ++ O.txt " "
24✔
1888
             ++ Link.from_fragment (frag_mod :> Paths.Fragment.leaf)
24✔
1889
             ++ O.txt " :=" ++ O.sp
24✔
1890
             ++ Link.from_path (mod_path :> Paths.Path.t)
24✔
1891
      | ModuleTypeSubst (frag_mty, md) ->
16✔
1892
          O.box_hv
1893
          @@ O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
16✔
1894
             ++ Link.from_fragment (frag_mty :> Paths.Fragment.leaf)
16✔
1895
             ++ O.txt " :=" ++ O.sp ++ mty md
16✔
1896
      | TypeSubst (frag_typ, td) -> (
47✔
1897
          O.box_hv
1898
          @@ O.keyword "type" ++ O.txt " "
47✔
1899
             ++ type_expr_in_subst td (frag_typ :> Paths.Fragment.leaf)
47✔
1900
             ++ O.txt " :=" ++ O.sp
47✔
1901
             ++
47✔
1902
             match td.Lang.TypeDecl.Equation.manifest with
1903
             | None -> assert false (* cf loader/cmti *)
1904
             | Some te -> type_expr te)
47✔
1905

1906
    and include_ (t : Odoc_model.Lang.Include.t) =
1907
      let decl_hidden =
290✔
1908
        match t.decl with
1909
        | Alias p -> Paths.Path.(is_hidden (p :> t))
×
1910
        | ModuleType mty -> umty_hidden mty
290✔
1911
      in
1912
      let status = if decl_hidden then `Inline else t.status in
1✔
1913

1914
      let _, content = signature t.expansion.content in
1915
      let summary =
290✔
1916
        if decl_hidden then O.render (O.keyword "include" ++ O.txt " ...")
1✔
1917
        else
1918
          let include_decl =
289✔
1919
            match t.decl with
1920
            | Odoc_model.Lang.Include.Alias mod_path ->
×
1921
                Link.from_path (mod_path :> Paths.Path.t)
×
1922
            | ModuleType mt -> umty mt
289✔
1923
          in
1924
          O.render
289✔
1925
            (O.keyword "include" ++ O.txt " " ++ include_decl
289✔
1926
            ++ if Syntax.Mod.include_semicolon then O.keyword ";" else O.noop)
×
1927
      in
1928
      let content = { Include.content; status; summary } in
1929
      let attr = [ "include" ] in
1930
      let anchor = None in
1931
      let doc =
1932
        (* Documentation attached to includes behave differently than other
1933
           declarations, which show only the synopsis. We can't only show the
1934
           synopsis because no page is generated to render it and we'd loose
1935
           the full documentation.
1936
           The documentation from the expansion is not used. *)
1937
        Comment.to_ir t.doc.elements
1938
      in
1939
      Item.Include { attr; anchor; doc; content; source_anchor = None }
290✔
1940
  end
1941

1942
  open Module
1943

1944
  module Page : sig
1945
    val compilation_unit : Lang.Compilation_unit.t -> Document.t
1946

1947
    val page : Lang.Page.t -> Document.t
1948

1949
    val implementation :
1950
      Lang.Implementation.t ->
1951
      Syntax_highlighter.infos ->
1952
      string ->
1953
      Document.t list
1954
  end = struct
1955
    let pack : Lang.Compilation_unit.Packed.t -> Item.t list =
1956
     fun t ->
1957
      let f x =
×
1958
        let id = x.Lang.Compilation_unit.Packed.id in
×
1959
        let modname = Paths.Identifier.name id in
1960
        let md_def =
×
1961
          O.keyword "module" ++ O.txt " " ++ O.txt modname ++ O.txt " = "
×
1962
          ++ Link.from_path (x.path :> Paths.Path.t)
×
1963
        in
1964
        let content = O.documentedSrc md_def in
×
1965
        let anchor =
×
1966
          Some (Url.Anchor.from_identifier (id :> Paths.Identifier.t))
×
1967
        in
1968
        let attr = [ "modules" ] in
1969
        let doc = [] in
1970
        let decl = { Item.anchor; content; attr; doc; source_anchor = None } in
1971
        Item.Declaration decl
1972
      in
1973
      List.map f t
1974

1975
    let compilation_unit (t : Odoc_model.Lang.Compilation_unit.t) =
1976
      let url = Url.Path.from_identifier t.id in
359✔
1977
      let unit_doc, items =
359✔
1978
        match t.content with
1979
        | Module sign -> signature sign
359✔
1980
        | Pack packed -> ([], pack packed)
×
1981
      in
1982
      let source_anchor = source_anchor t.source_loc in
1983
      let page = make_expansion_page ~source_anchor url [ unit_doc ] items in
359✔
1984
      Document.Page page
359✔
1985

1986
    let page (t : Odoc_model.Lang.Page.t) =
1987
      (*let name =
1988
          match t.name.iv with `Page (_, name) | `LeafPage (_, name) -> name
1989
        in*)
1990
      (*let title = Odoc_model.Names.PageName.to_string name in*)
1991
      let url = Url.Path.from_identifier t.name in
69✔
1992
      let preamble, items = Sectioning.docs t.content.elements in
69✔
1993
      let source_anchor = None in
69✔
1994
      Document.Page { Page.preamble; items; url; source_anchor }
1995

1996
    let implementation (v : Odoc_model.Lang.Implementation.t) syntax_info
1997
        source_code =
1998
      match v.id with
28✔
1999
      | None -> []
×
2000
      | Some id ->
28✔
2001
          [
2002
            Document.Source_page
2003
              (Source_page.source id syntax_info v.source_info source_code);
28✔
2004
          ]
2005
  end
2006

2007
  include Page
2008

2009
  let type_expr = type_expr
2010

2011
  let record = record
2012

2013
  let unboxed_record = unboxed_record
2014
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