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

ocaml / odoc / 3201

22 May 2026 01:15PM UTC coverage: 70.966% (-0.05%) from 71.012%
3201

Pull #1422

github

web-flow
Merge 64c61625a into e0285939d
Pull Request #1422: Zero alloc annotation for OxCaml

3 of 13 new or added lines in 1 file covered. (23.08%)

347 existing lines in 2 files now uncovered.

10444 of 14717 relevant lines covered (70.97%)

5869.51 hits per line

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

79.66
/src/document/generator.ml
1
(*
2
 * Copyright (c) 2016 Thomas Refis <trefis@janestreet.com>
3
 *
4
 * Permission to use, copy, modify, and distribute this software for any
5
 * purpose with or without fee is hereby granted, provided that the above
6
 * copyright notice and this permission notice appear in all copies.
7
 *
8
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
 *)
16

17
open Odoc_utils
18
open Odoc_model.Names
19
module Location = Odoc_model.Location_
20
module Paths = Odoc_model.Paths
21
open Types
22
module O = Codefmt
23
open O.Infix
24

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

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

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

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

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

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

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

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

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

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

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

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

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

92
include Generator_signatures
93

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

578
  open Type_expression
579

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1042
  open Type_declaration
1043

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

1094
  open Value
1095

1096
  (* This chunk of code is responsible for sectioning list of items
1097
     according to headings by extracting headings as Items.
1098

1099
     TODO: This sectioning would be better done as a pass on the model directly.
1100
  *)
1101
  module Sectioning : sig
1102
    open Odoc_model
1103

1104
    val comment_items : Comment.elements -> Item.t list
1105

1106
    val docs : Comment.elements -> Item.t list * Item.t list
1107
  end = struct
1108
    let take_until_heading_or_end (docs : Odoc_model.Comment.elements) =
1109
      let content, _, rest =
693✔
1110
        Doctree.Take.until docs ~classify:(fun b ->
1111
            match b.Location.value with
1,481✔
1112
            | `Heading _ -> Stop_and_keep
202✔
1113
            | #Odoc_model.Comment.attached_block_element as doc ->
1,279✔
1114
                let content = Comment.attached_block_element doc in
1115
                Accum content)
1,279✔
1116
      in
1117
      (content, rest)
693✔
1118

1119
    let comment_items (input0 : Odoc_model.Comment.elements) =
1120
      let rec loop input_comment acc =
963✔
1121
        match input_comment with
2,493✔
1122
        | [] -> List.rev acc
963✔
1123
        | element :: input_comment -> (
1,530✔
1124
            match element.Location.value with
1125
            | `Heading h ->
837✔
1126
                let item = Comment.heading h in
1127
                loop input_comment (item :: acc)
837✔
1128
            | _ ->
693✔
1129
                let content, input_comment =
1130
                  take_until_heading_or_end (element :: input_comment)
1131
                in
1132
                let item = Item.Text content in
693✔
1133
                loop input_comment (item :: acc))
1134
      in
1135
      loop input0 []
1136

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

1161
  module Class : sig
1162
    val class_ : Lang.Class.t -> Item.t
1163

1164
    val class_type : Lang.ClassType.t -> Item.t
1165
  end = struct
1166
    let class_type_expr (cte : Odoc_model.Lang.ClassType.expr) =
1167
      match cte with
249✔
1168
      | Constr (path, args) ->
58✔
1169
          let link = Link.from_path (path :> Paths.Path.t) in
1170
          format_type_path ~delim:`brackets args link
58✔
1171
      | Signature _ ->
191✔
1172
          Syntax.Class.open_tag ++ O.txt " ... " ++ Syntax.Class.close_tag
191✔
1173

1174
    let method_ (t : Odoc_model.Lang.Method.t) =
1175
      let name = Paths.Identifier.name t.id in
90✔
1176
      let virtual_ =
90✔
1177
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
8✔
1178
      in
1179
      let private_ =
1180
        if t.private_ then O.keyword "private" ++ O.txt " " else O.noop
8✔
1181
      in
1182
      let content =
1183
        O.documentedSrc
1184
          (O.keyword "method" ++ O.txt " " ++ private_ ++ virtual_ ++ O.txt name
90✔
1185
          ++ O.txt Syntax.Type.annotation_separator
90✔
1186
          ++ type_expr t.type_)
90✔
1187
      in
1188
      let attr = [ "method" ] in
90✔
1189
      let anchor = path_to_id t.id in
1190
      let doc = Comment.to_ir t.doc.elements in
90✔
1191
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
90✔
1192

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

1212
    let inherit_ (ih : Lang.ClassSignature.Inherit.t) =
1213
      let cte =
16✔
1214
        match ih.expr with
1215
        | Signature _ -> assert false (* Bold. *)
1216
        | cty -> cty
16✔
1217
      in
1218
      let content =
1219
        O.documentedSrc (O.keyword "inherit" ++ O.txt " " ++ class_type_expr cte)
16✔
1220
      in
1221
      let attr = [ "inherit" ] in
16✔
1222
      let anchor = None in
1223
      let doc = Comment.to_ir ih.doc.elements in
1224
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
16✔
1225

1226
    let constraint_ (cst : Lang.ClassSignature.Constraint.t) =
1227
      let content =
8✔
1228
        O.documentedSrc (format_constraints [ (cst.left, cst.right) ])
8✔
1229
      in
1230
      let attr = [] in
8✔
1231
      let anchor = None in
1232
      let doc = Comment.to_ir cst.doc.elements in
1233
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
8✔
1234

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

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

1283
    let class_ (t : Odoc_model.Lang.Class.t) =
1284
      let name = Paths.Identifier.name t.id in
166✔
1285
      let params =
166✔
1286
        match t.params with
1287
        | [] -> O.noop
142✔
1288
        | _ :: _ as params -> format_params ~delim:`brackets params ++ O.txt " "
24✔
1289
      in
1290
      let virtual_ =
1291
        if t.virtual_ then O.keyword "virtual" ++ O.txt " " else O.noop
24✔
1292
      in
1293

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

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

1363
  open Class
1364

1365
  module Module : sig
1366
    val signature : Lang.Signature.t -> Comment.Comment.elements * Item.t list
1367
    (** Returns [header_doc, content]. *)
1368
  end = struct
1369
    let internal_module m =
1370
      let open Lang.Module in
1,924✔
1371
      match m.id.iv with
1372
      | `Module (_, name) when ModuleName.is_hidden name -> true
81✔
1373
      | _ -> false
1,843✔
1374

1375
    let internal_type t =
1376
      let open Lang.TypeDecl in
2,969✔
1377
      match t.id.iv with
1378
      | `Type (_, name) when TypeName.is_hidden name -> true
1✔
1379
      | _ -> false
2,968✔
1380

1381
    let internal_value v =
1382
      let open Lang.Value in
1,018✔
1383
      match v.id.iv with
1384
      | `Value (_, name) when ValueName.is_hidden name -> true
106✔
1385
      | _ -> false
912✔
1386

1387
    let internal_module_type t =
1388
      let open Lang.ModuleType in
1,361✔
1389
      match t.id.iv with
1390
      | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true
×
1391
      | _ -> false
1,361✔
1392

1393
    let internal_module_substitution t =
1394
      let open Lang.ModuleSubstitution in
8✔
1395
      match t.id.iv with
1396
      | `Module (_, name) when ModuleName.is_hidden name -> true
×
1397
      | _ -> false
8✔
1398

1399
    let internal_module_type_substitution t =
1400
      let open Lang.ModuleTypeSubstitution in
8✔
1401
      match t.id.iv with
1402
      | `ModuleType (_, name) when ModuleTypeName.is_hidden name -> true
×
1403
      | _ -> false
8✔
1404

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

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

1491
    and module_substitution (t : Odoc_model.Lang.ModuleSubstitution.t) =
1492
      let name = Paths.Identifier.name t.id in
8✔
1493
      let path = Link.from_path (t.manifest :> Paths.Path.t) in
8✔
1494
      let content =
8✔
1495
        O.documentedSrc
1496
          (O.keyword "module" ++ O.txt " " ++ O.txt name ++ O.txt " :=" ++ O.sp
8✔
1497
         ++ path)
8✔
1498
      in
1499
      let attr = [ "module-substitution" ] in
8✔
1500
      let anchor = path_to_id t.id in
1501
      let doc = Comment.to_ir t.doc.elements in
8✔
1502
      Item.Declaration { attr; anchor; doc; content; source_anchor = None }
8✔
1503

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

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

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

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

1636
    and simple_expansion_in_decl (base : Paths.Identifier.Module.t) se =
1637
      let rec ty_of_se :
102✔
1638
          Lang.ModuleType.simple_expansion -> Lang.ModuleType.expr = function
1639
        | Signature sg -> Signature sg
102✔
1640
        | Functor (arg, sg) -> Functor (arg, ty_of_se sg)
×
1641
      in
1642
      mty_in_decl (base :> Paths.Identifier.Signature.t) (ty_of_se se)
102✔
1643

1644
    and mdexpr_in_decl (base : Paths.Identifier.Module.t) md =
1645
      let sig_dotdotdot =
1,843✔
1646
        O.txt Syntax.Type.annotation_separator
1,843✔
1647
        ++ O.cut ++ Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
1,843✔
1648
      in
1649
      match md with
1,843✔
1650
      | Alias (_, Some se) -> simple_expansion_in_decl base se
102✔
1651
      | Alias (p, _) when not Paths.Path.(is_hidden (p :> t)) ->
186✔
1652
          O.txt " =" ++ O.sp ++ mdexpr md
186✔
1653
      | Alias _ -> sig_dotdotdot
×
1654
      | ModuleType mt -> mty_in_decl (base :> Paths.Identifier.Signature.t) mt
1,555✔
1655

1656
    and mdexpr : Odoc_model.Lang.Module.decl -> text = function
1657
      | Alias (mod_path, _) -> Link.from_path (mod_path :> Paths.Path.t)
242✔
1658
      | ModuleType mt -> mty mt
×
1659

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

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

1712
    and umty_hidden : Odoc_model.Lang.ModuleType.U.expr -> bool = function
1713
      | Path p -> Paths.Path.(is_hidden (p :> t))
403✔
1714
      | With (_, expr) -> umty_hidden expr
25✔
1715
      | TypeOf (ModPath m, _) | TypeOf (StructInclude m, _) ->
42✔
1716
          Paths.Path.(is_hidden (m :> t))
1717
      | Signature _ -> false
14✔
1718
      | Strengthen (expr, p, _) ->
×
1719
          umty_hidden expr || Paths.Path.(is_hidden (p :> t))
×
1720

1721
    and mty_hidden : Odoc_model.Lang.ModuleType.expr -> bool = function
1722
      | Path { p_path = mty_path; _ } -> Paths.Path.(is_hidden (mty_path :> t))
837✔
1723
      | With { w_expr; _ } -> umty_hidden w_expr
218✔
1724
      | TypeOf { t_desc = ModPath m; _ }
48✔
1725
      | TypeOf { t_desc = StructInclude m; _ } ->
16✔
1726
          Paths.Path.(is_hidden (m :> t))
1727
      | _ -> false
2,297✔
1728

1729
    and mty_with subs expr =
1730
      umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " "
238✔
1731
      ++ O.list
238✔
1732
           ~sep:(O.cut ++ O.txt " " ++ O.keyword "and" ++ O.txt " ")
238✔
1733
           ~f:(fun x -> O.span (substitution x))
279✔
1734
           subs
1735

1736
    and mty_strengthen expr path =
1737
      umty expr ++ O.sp ++ O.keyword "with" ++ O.txt " "
×
1738
      ++ Link.from_path (path :> Paths.Path.t)
×
1739

1740
    and mty_typeof t_desc =
1741
      match t_desc with
154✔
1742
      | Odoc_model.Lang.ModuleType.ModPath m ->
90✔
1743
          O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
90✔
1744
          ++ O.keyword "of" ++ O.txt " "
90✔
1745
          ++ Link.from_path (m :> Paths.Path.t)
90✔
1746
      | StructInclude m ->
64✔
1747
          O.keyword "module" ++ O.txt " " ++ O.keyword "type" ++ O.txt " "
64✔
1748
          ++ O.keyword "of" ++ O.txt " " ++ O.keyword "struct" ++ O.txt " "
64✔
1749
          ++ O.keyword "include" ++ O.txt " "
64✔
1750
          ++ Link.from_path (m :> Paths.Path.t)
64✔
1751
          ++ O.txt " " ++ O.keyword "end"
64✔
1752

1753
    and is_elidable_with_u : Odoc_model.Lang.ModuleType.U.expr -> bool =
1754
      function
1755
      | Path _ -> false
213✔
1756
      | Signature _ -> true
5✔
1757
      | With (_, expr) -> is_elidable_with_u expr
×
1758
      | TypeOf _ -> false
25✔
1759
      | Strengthen (expr, _, _) -> is_elidable_with_u expr
×
1760

1761
    and umty : Odoc_model.Lang.ModuleType.U.expr -> text =
1762
     fun m ->
1763
      match m with
527✔
1764
      | Path p -> Link.from_path (p :> Paths.Path.t)
403✔
1765
      | Signature _ ->
9✔
1766
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
9✔
1767
      | With (_, expr) when is_elidable_with_u expr ->
25✔
1768
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
5✔
1769
      | With (subs, expr) -> mty_with subs expr
20✔
1770
      | TypeOf (t_desc, _) -> mty_typeof t_desc
90✔
1771
      | Strengthen (expr, _, _) when is_elidable_with_u expr ->
×
1772
          Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
×
1773
      | Strengthen (expr, p, _) -> mty_strengthen expr (p :> Paths.Path.t)
×
1774

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

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

1850
    (* TODO : Centralize the list juggling for type parameters *)
1851
    and type_expr_in_subst td typath =
1852
      let typath = Link.from_fragment typath in
151✔
1853
      match td.Lang.TypeDecl.Equation.params with
151✔
1854
      | [] -> typath
125✔
1855
      | l -> Syntax.Type.handle_substitution_params typath (format_params l)
26✔
1856

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

1897
    and include_ (t : Odoc_model.Lang.Include.t) =
1898
      let decl_hidden =
290✔
1899
        match t.decl with
1900
        | Alias p -> Paths.Path.(is_hidden (p :> t))
×
1901
        | ModuleType mty -> umty_hidden mty
290✔
1902
      in
1903
      let status = if decl_hidden then `Inline else t.status in
1✔
1904

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

1933
  open Module
1934

1935
  module Page : sig
1936
    val compilation_unit : Lang.Compilation_unit.t -> Document.t
1937

1938
    val page : Lang.Page.t -> Document.t
1939

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

1966
    let compilation_unit (t : Odoc_model.Lang.Compilation_unit.t) =
1967
      let url = Url.Path.from_identifier t.id in
359✔
1968
      let unit_doc, items =
359✔
1969
        match t.content with
1970
        | Module sign -> signature sign
359✔
1971
        | Pack packed -> ([], pack packed)
×
1972
      in
1973
      let source_anchor = source_anchor t.source_loc in
1974
      let page = make_expansion_page ~source_anchor url [ unit_doc ] items in
359✔
1975
      Document.Page page
359✔
1976

1977
    let page (t : Odoc_model.Lang.Page.t) =
1978
      (*let name =
1979
          match t.name.iv with `Page (_, name) | `LeafPage (_, name) -> name
1980
        in*)
1981
      (*let title = Odoc_model.Names.PageName.to_string name in*)
1982
      let url = Url.Path.from_identifier t.name in
69✔
1983
      let preamble, items = Sectioning.docs t.content.elements in
69✔
1984
      let source_anchor = None in
69✔
1985
      Document.Page { Page.preamble; items; url; source_anchor }
1986

1987
    let implementation (v : Odoc_model.Lang.Implementation.t) syntax_info
1988
        source_code =
1989
      match v.id with
28✔
1990
      | None -> []
×
1991
      | Some id ->
28✔
1992
          [
1993
            Document.Source_page
1994
              (Source_page.source id syntax_info v.source_info source_code);
28✔
1995
          ]
1996
  end
1997

1998
  include Page
1999

2000
  let type_expr = type_expr
2001

2002
  let record = record
2003

2004
  let unboxed_record = unboxed_record
2005
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