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

ocaml / odoc / 3250

01 Jul 2026 08:28AM UTC coverage: 70.761% (-0.2%) from 70.942%
3250

Pull #1452

github

web-flow
Merge ca9079a7c into 8f9e1fc78
Pull Request #1452: `include functor` support for OxCaml

10 of 53 new or added lines in 9 files covered. (18.87%)

4 existing lines in 3 files now uncovered.

10455 of 14775 relevant lines covered (70.76%)

5847.33 hits per line

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

78.82
/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 Modalities : sig
308
    val format : Odoc_model.Lang.Modalities.t -> text
309
  end = struct
310
    let format = function
311
      | [] -> O.noop
1,492✔
312
      | mods ->
×
313
          O.txt " " ++ O.txt "@@" ++ O.txt " "
×
314
          ++ O.txt (String.concat ~sep:" " mods)
×
315
  end
316

317
  module Type_expression : sig
318
    val type_expr : ?needs_parentheses:bool -> Lang.TypeExpr.t -> text
319

320
    val format_type_path :
321
      delim:[ `parens | `brackets ] -> Lang.TypeExpr.t list -> text -> text
322

323
    val kind_annotation :
324
      ?needs_parentheses:bool -> Odoc_model.Lang.Kind.t -> text
325

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

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

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

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

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

480
    and with_kind_annotation kind base =
481
      match kind with
648✔
482
      | Odoc_model.Lang.Kind.Default -> base
648✔
483
      | k -> O.txt "(" ++ base ++ O.txt " : " ++ kind_annotation k ++ O.txt ")"
×
484

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

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

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

585
  open Type_expression
586

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

596
    val extension : Lang.Extension.t -> Item.t
597

598
    val record : Lang.TypeDecl.Field.t list -> DocumentedSrc.one list
599

600
    val unboxed_record :
601
      Lang.TypeDecl.UnboxedField.t list -> DocumentedSrc.one list
602

603
    val exn : Lang.Exception.t -> Item.t
604

605
    val format_params :
606
      ?delim:[ `parens | `brackets ] -> Lang.TypeDecl.param list -> text
607

608
    val format_manifest :
609
      ?is_substitution:bool ->
610
      ?compact_variants:bool ->
611
      Lang.TypeDecl.Equation.t ->
612
      text * bool
613

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

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

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

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

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

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

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

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

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

934
    let format_constraints constraints =
935
      O.list constraints ~f:(fun (t1, t2) ->
3,103✔
936
          O.sp
104✔
937
          ++ (O.box_hv
104✔
938
             @@ O.keyword "constraint" ++ O.sp
104✔
939
                ++ O.box_hv_no_indent (type_expr t1)
104✔
940
                ++ O.txt " =" ++ O.sp
104✔
941
                ++ O.box_hv_no_indent (type_expr t2)))
104✔
942

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

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

1050
  open Type_declaration
1051

1052
  module Value : sig
1053
    val value : Lang.Value.t -> Item.t
1054
  end = struct
1055
    let rec arity_of_type_expr = function
1056
      | Lang.TypeExpr.Arrow (_lbl, _arg, curried) ->
×
1057
          1 + arity_of_type_expr curried
×
1058
      | _ -> 0
×
1059

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

1113
  open Value
1114

1115
  (* This chunk of code is responsible for sectioning list of items
1116
     according to headings by extracting headings as Items.
1117

1118
     TODO: This sectioning would be better done as a pass on the model directly.
1119
  *)
1120
  module Sectioning : sig
1121
    open Odoc_model
1122

1123
    val comment_items : Comment.elements -> Item.t list
1124

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

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

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

1180
  module Class : sig
1181
    val class_ : Lang.Class.t -> Item.t
1182

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

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

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

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

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

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

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

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

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

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

1382
  open Class
1383

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

1394
    let internal_type t =
1395
      let open Lang.TypeDecl in
2,969✔
1396
      match t.id.iv with
1397
      | `Type (_, name) when TypeName.is_hidden name -> true
1✔
1398
      | _ -> false
2,968✔
1399

1400
    let internal_value v =
1401
      let open Lang.Value in
1,018✔
1402
      match v.id.iv with
1403
      | `Value (_, name) when ValueName.is_hidden name -> true
106✔
1404
      | _ -> false
912✔
1405

1406
    let internal_module_type t =
1407
      let open Lang.ModuleType in
1,361✔
1408
      match t.id.iv with
1409
      | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true
×
1410
      | _ -> false
1,361✔
1411

1412
    let internal_module_substitution t =
1413
      let open Lang.ModuleSubstitution in
8✔
1414
      match t.id.iv with
1415
      | `Module (_, name) when ModuleName.is_hidden name -> true
×
1416
      | _ -> false
8✔
1417

1418
    let internal_module_type_substitution t =
1419
      let open Lang.ModuleTypeSubstitution in
8✔
1420
      match t.id.iv with
1421
      | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true
×
1422
      | _ -> false
8✔
1423

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

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

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

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

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

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

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

1655
    and simple_expansion_in_decl (base : Paths.Identifier.Module.t) se =
1656
      let rec ty_of_se :
102✔
1657
          Lang.ModuleType.simple_expansion -> Lang.ModuleType.expr = function
1658
        | Signature sg -> Signature sg
102✔
1659
        | Functor (arg, sg) -> Functor (arg, ty_of_se sg)
×
1660
      in
1661
      mty_in_decl (base :> Paths.Identifier.Signature.t) (ty_of_se se)
102✔
1662

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

1675
    and mdexpr : Odoc_model.Lang.Module.decl -> text = function
1676
      | Alias (mod_path, _) -> Link.from_path (mod_path :> Paths.Path.t)
242✔
1677
      | ModuleType mt -> mty mt
×
1678

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

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

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

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

1748
    and mty_with subs expr =
1749
      umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " "
238✔
1750
      ++ O.list
238✔
1751
           ~sep:(O.cut ++ O.txt " " ++ O.keyword "and" ++ O.txt " ")
238✔
1752
           ~f:(fun x -> O.span (substitution x))
279✔
1753
           subs
1754

1755
    and mty_strengthen expr path =
1756
      umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " "
×
1757
      ++ Link.from_path (path :> Paths.Path.t)
×
1758

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

1772
    and is_elidable_with_u : Odoc_model.Lang.ModuleType.U.expr -> bool =
1773
      function
1774
      | Path _ -> false
213✔
1775
      | Signature _ -> true
5✔
1776
      | With (_, expr) -> is_elidable_with_u expr
×
1777
      | TypeOf _ -> false
25✔
1778
      | Strengthen (expr, _, _) -> is_elidable_with_u expr
×
1779

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

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

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

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

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

1916
    and include_ (t : Odoc_model.Lang.Include.t) =
1917
      let decl_hidden =
290✔
1918
        match t.decl with
NEW
1919
        | Alias p | Functor (Path p) -> Paths.Path.(is_hidden (p :> t))
×
NEW
1920
        | ModuleType mty | Functor (ModuleType mty) -> umty_hidden mty
×
1921
      in
1922
      let status = if decl_hidden then `Inline else t.status in
1✔
1923

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

1956
  open Module
1957

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

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

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

1989
    let compilation_unit (t : Odoc_model.Lang.Compilation_unit.t) =
1990
      let url = Url.Path.from_identifier t.id in
359✔
1991
      let unit_doc, items =
359✔
1992
        match t.content with
1993
        | Module sign -> signature sign
359✔
1994
        | Pack packed -> ([], pack packed)
×
1995
      in
1996
      let source_anchor = source_anchor t.source_loc in
1997
      let page = make_expansion_page ~source_anchor url [ unit_doc ] items in
359✔
1998
      Document.Page page
359✔
1999

2000
    let page (t : Odoc_model.Lang.Page.t) =
2001
      (*let name =
2002
          match t.name.iv with `Page (_, name) | `LeafPage (_, name) -> name
2003
        in*)
2004
      (*let title = Odoc_model.Names.PageName.to_string name in*)
2005
      let url = Url.Path.from_identifier t.name in
69✔
2006
      let preamble, items = Sectioning.docs t.content.elements in
69✔
2007
      let source_anchor = None in
69✔
2008
      Document.Page { Page.preamble; items; url; source_anchor }
2009

2010
    let implementation (v : Odoc_model.Lang.Implementation.t) syntax_info
2011
        source_code =
2012
      match v.id with
28✔
2013
      | None -> []
×
2014
      | Some id ->
28✔
2015
          [
2016
            Document.Source_page
2017
              (Source_page.source id syntax_info v.source_info source_code);
28✔
2018
          ]
2019
  end
2020

2021
  include Page
2022

2023
  let type_expr = type_expr
2024

2025
  let record = record
2026

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

© 2026 Coveralls, Inc