package odoc

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file url.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
open Result
open StdLabels
open Odoc_model.Paths
open Odoc_model.Names

type t = {
  page : string list;
  (* in reverse order! *)

  anchor : string;

  kind : string;
}

let to_string { page; anchor; _ } =
  String.concat ~sep:"/" (List.rev page) ^ "#" ^ anchor

module Error = struct
  type nonrec t =
    | Not_linkable of string
    | Uncaught_exn of string
    (* These should basicaly never happen *)
    | Unexpected_anchor of t * string
    | Missing_anchor of t * string

  let to_string = function
    | Not_linkable s -> Printf.sprintf "Not_linkable %S" s
    | Uncaught_exn s -> Printf.sprintf "Uncaught_exn %S" s
    | Unexpected_anchor (t, s) ->
      Printf.sprintf "Unexpected_anchor %S (parent of %s)" (to_string t) s
    | Missing_anchor (t, s) ->
      Printf.sprintf "Missing_anchor on %S for %S" (to_string t) s
end

(* let (^/) x y = x ^ "/" ^ y *)

let (>>|) x f =
  match x with
  | Ok x -> Ok (f x)
  | Error _ as e -> e

let (>>=) x f =
  match x with
  | Ok x -> f x
  | Error _ as e -> e

let rec from_identifier : stop_before:bool ->
  Identifier.t -> (t, Error.t) result =
  fun ~stop_before ->
    let open Error in
    function
    | `Root (abstr, unit_name) ->
      begin try Ok abstr.package
      with exn -> Error (Uncaught_exn (Printexc.to_string exn))
      end >>| fun pkg_name ->
      let page = [ pkg_name ] in
      let kind = "module" in
      (* FIXME: for the moment we ignore [stop_before] for compilation units. At
         some point we want to change that. *)
      (*
      if stop_before then
        { page; anchor = unit_name; kind }
      else
      *)
      { page = UnitName.to_string unit_name :: page; anchor = ""; kind }
    | `Page (abstr, page_name) ->
      begin try Ok abstr.package
      with exn -> Error (Uncaught_exn (Printexc.to_string exn))
      end >>| fun pkg_name ->
      let page = [ PageName.to_string page_name ^ ".html"; pkg_name ] in
      let kind = "page" in
      { page; anchor = ""; kind }
    | `Module (parent, mod_name) ->
      from_identifier_no_anchor (parent :> Identifier.t) ("module " ^ ModuleName.to_string mod_name)
      >>| fun parent ->
      let kind = "module" in
      if stop_before then
        { page = parent; anchor = Printf.sprintf "%s-%s" kind (ModuleName.to_string mod_name); kind }
      else
        { page = (ModuleName.to_string mod_name) :: parent; anchor = ""; kind }
    | `Argument (functor_id, arg_num, arg_name) ->
      from_identifier_no_anchor (functor_id :> Identifier.t) ("arg " ^ ArgumentName.to_string arg_name)
      >>| fun parent ->
      let kind = "argument" in
      let suffix = Printf.sprintf "%s-%d-%s" kind arg_num (ArgumentName.to_string arg_name) in
      if stop_before then
        { page = parent; anchor = suffix; kind }
      else
        { page = suffix :: parent; anchor = ""; kind }
    | `ModuleType (parent, modt_name) ->
      from_identifier_no_anchor (parent :> Identifier.t) ("module type " ^ ModuleTypeName.to_string modt_name)
      >>| fun parent ->
      let kind = "module-type" in
      let suffix = Printf.sprintf "%s-%s" kind (ModuleTypeName.to_string modt_name) in
      if stop_before then
        { page = parent; anchor = suffix; kind }
      else
        { page = suffix :: parent; anchor = ""; kind }
    | `Type (parent, type_name) ->
      from_identifier_no_anchor (parent :> Identifier.t) ("type " ^ (TypeName.to_string type_name))
      >>| fun page ->
      let kind = "type" in
      { page; anchor = Printf.sprintf "%s-%s" kind (TypeName.to_string type_name); kind }
    | `CoreType ty_name ->
      Error (Not_linkable ("core_type:"^ (TypeName.to_string ty_name)))
    | `Constructor (parent, name) ->
      from_identifier ~stop_before:false (parent :> Identifier.t)
      >>= begin function
      (* FIXME: update doc-ock. *)
(*       | { anchor = ""; _ } as t -> Error (Missing_anchor (t, name)) *)
      | { page; anchor; _ } ->
        let kind = "constructor" in
        Ok { page; anchor = anchor ^ "." ^ (ConstructorName.to_string name); kind }
      end
    | `Field (parent, name) ->
      from_identifier ~stop_before:false (parent :> Identifier.t)
      >>= begin function
      (* FIXME: update doc-ock. *)
(*       | { anchor = ""; _ } as t -> Error (Missing_anchor (t, name)) *)
      | { page; anchor; _ } ->
        let kind = "field" in
        Ok { page; anchor = anchor ^ "." ^ (FieldName.to_string name); kind }
      end
    | `Extension (parent, name) ->
      from_identifier_no_anchor (parent :> Identifier.t) ("extension " ^ (ExtensionName.to_string name))
      >>| fun parent ->
      let kind = "extension" in
      { page = parent; anchor = Printf.sprintf "%s-%s" kind (ExtensionName.to_string name); kind }
    | `Exception (parent, name) ->
      from_identifier_no_anchor (parent :> Identifier.t) ("exception " ^ (ExceptionName.to_string name))
      >>| fun parent ->
      let kind = "exception" in
      { page = parent; anchor = Printf.sprintf "%s-%s" kind (ExceptionName.to_string name); kind }
    | `CoreException name ->
      Error (Not_linkable ("core_exception:" ^ (ExceptionName.to_string name)))
    | `Value (parent, name) ->
      from_identifier_no_anchor (parent :> Identifier.t) ("val " ^ (ValueName.to_string name))
      >>| fun parent ->
      let kind = "val" in
      { page = parent; anchor = Printf.sprintf "%s-%s" kind (ValueName.to_string name); kind }
    | `Class (parent, name) ->
      from_identifier_no_anchor (parent :> Identifier.t) ("class " ^ (ClassName.to_string name))
      >>| fun parent ->
      let kind = "class" in
      let suffix = Printf.sprintf "%s-%s" kind (ClassName.to_string name) in
      if stop_before then
        { page = parent; anchor = suffix; kind }
      else
        { page = suffix :: parent; anchor = ""; kind }
    | `ClassType (parent, name) ->
      from_identifier_no_anchor (parent :> Identifier.t) ("class type " ^ (ClassTypeName.to_string name))
      >>| fun parent ->
      let kind = "class-type" in
      let suffix = Printf.sprintf "%s-%s" kind (ClassTypeName.to_string name) in
      if stop_before then
        { page = parent; anchor = suffix; kind }
      else
        { page = suffix :: parent; anchor = ""; kind }
    | `Method (parent, name) ->
      let str_name = MethodName.to_string name in
      from_identifier_no_anchor (parent :> Identifier.t) ("method " ^ str_name)
      >>| fun page ->
      let kind = "method" in
      { page; anchor = Printf.sprintf "%s-%s" kind str_name; kind }
    | `InstanceVariable (parent, name) ->
      let str_name = InstanceVariableName.to_string name in
      from_identifier_no_anchor (parent :> Identifier.t) ("val " ^ str_name)
      >>| fun page ->
      let kind = "val" in
      { page; anchor = Printf.sprintf "%s-%s" kind str_name; kind }
    | `Label (parent, anchor') ->
      let anchor = LabelName.to_string anchor' in
      from_identifier ~stop_before:false (parent :> Identifier.t)
      >>= function
      | { page; anchor = ""; kind } ->
        (* Really ad-hoc and shitty, but it works. *)
        if kind = "page" then Ok { page; anchor; kind }
        else Ok {page; anchor; kind = "" }
      | otherwise ->
        Error (Unexpected_anchor (otherwise, "label " ^ anchor))

and from_identifier_no_anchor :
  Identifier.t -> string -> (string list, Error.t) result =
  fun id child ->
    from_identifier ~stop_before:false id
    >>= function
    | { page; anchor = ""; _ } -> Ok page
    | otherwise -> Error (Unexpected_anchor (otherwise, child))

let anchor_of_id_exn id =
  match from_identifier ~stop_before:true id with
  | Error e -> failwith (Error.to_string e)
  | Ok { anchor; _ } -> anchor

let kind_of_id_exn id =
  match from_identifier ~stop_before:true id with
  | Error e -> failwith (Error.to_string e)
  | Ok { kind; _ } -> kind

let render_path : Odoc_model.Paths.Path.t -> string =
  let open Odoc_model.Paths.Path in
  let rec render_resolved : Odoc_model.Paths.Path.Resolved.t -> string =
    let open Resolved in
    function
    | `Identifier id -> Identifier.name id
    | `Subst (_, p) -> render_resolved (p :> t)
    | `SubstAlias (_, p) -> render_resolved (p :> t)
    | `Hidden p -> render_resolved (p :> t)
    | `Module (p, s) -> render_resolved (p :> t) ^ "." ^ (ModuleName.to_string s)
    | `Canonical (_, `Resolved p) -> render_resolved (p :> t)
    | `Canonical (p, _) -> render_resolved (p :> t)
    | `Apply (rp, p) -> render_resolved (rp :> t) ^ "(" ^ render_path (p :> Odoc_model.Paths.Path.t) ^ ")"
    | `ModuleType (p, s) -> render_resolved (p :> t) ^ "." ^ (ModuleTypeName.to_string s)
    | `Type (p, s) -> render_resolved (p :> t) ^ "." ^ (TypeName.to_string s)
    | `Class (p, s) -> render_resolved (p :> t) ^ "." ^ (ClassName.to_string s)
    | `ClassType (p, s) -> render_resolved (p :> t) ^ "." ^ (ClassTypeName.to_string s)
  and render_path : Odoc_model.Paths.Path.t -> string =
    function
    | `Root root -> root
    | `Forward root -> root
    | `Dot (prefix, suffix) -> render_path (prefix :> t) ^ "." ^ suffix
    | `Apply (p1, p2) -> render_path (p1 :> t) ^ "(" ^ render_path (p2 :> t) ^ ")"
    | `Resolved rp -> render_resolved rp
  in
  render_path

module Anchor = struct
  type t = {
    kind : string;
    name : string;
  }

  module Polymorphic_variant_decl = struct
    let name_of_type_constr te =
      match te with
      | Odoc_model.Lang.TypeExpr.Constr (path, _) -> render_path (path :> Odoc_model.Paths.Path.t)
      | _ ->
        invalid_arg "DocOckHtml.Url.Polymorphic_variant_decl.name_of_type_constr"

    let from_element ~type_ident elt =
      match from_identifier ~stop_before:true type_ident with
      | Error e -> failwith (Error.to_string e)
      | Ok { anchor; _ } ->
        match elt with
        | Odoc_model.Lang.TypeExpr.Polymorphic_variant.Type te ->
          { kind = "type"
          ; name = Printf.sprintf "%s.%s" anchor (name_of_type_constr te) }
        | Constructor {name; _} ->
          { kind = "constructor"
          ; name = Printf.sprintf "%s.%s" anchor name }
  end

  module Module_listing = struct
    module Reference = Odoc_model.Paths.Reference

    (* TODO: better error message. *)
    let fail () = failwith "Only modules allowed inside {!modules: ...}"

    let rec from_reference : Reference.t -> t = function
      | `Root (name, _) -> { kind = "xref-unresolved"; name = Odoc_model.Names.UnitName.to_string name }
      | `Dot (parent, suffix) ->
        let { name; _ } = from_reference (parent :> Reference.t) in
        { kind = "xref-unresolved"; name = Printf.sprintf "%s.%s" name suffix }
      | `Module (parent, suffix) ->
        let { name; _ } = from_reference (parent :> Reference.t) in
        { kind = "xref-unresolved"; name = Printf.sprintf "%s.%s" name (Odoc_model.Names.ModuleName.to_string suffix) }
      | `ModuleType (parent, suffix) ->
        let { name; _ } = from_reference (parent :> Reference.t) in
        { kind = "xref-unresolved"; name = Printf.sprintf "%s.%s" name (Odoc_model.Names.ModuleTypeName.to_string suffix) }
      | `Resolved r ->
        from_resolved r
      | _ ->
        fail ()

    and from_resolved : Reference.Resolved.t -> t =
      function
      | `Identifier id ->
        let name = Identifier.name id in
        let kind =
          match from_identifier ~stop_before:false id with
          | Ok { kind; _ } -> kind
          | Error _ -> fail ()
        in
        { name; kind }
      | `Module (parent, s) ->
        let { name; _ } = from_resolved (parent :> Reference.Resolved.t) in
        { kind = "module"; name = Printf.sprintf "%s.%s" name (Odoc_model.Names.ModuleName.to_string s) }
      | `ModuleType (parent, s) ->
        let { name; _ } = from_resolved (parent :> Reference.Resolved.t) in
        { kind = "module-type"; name = Printf.sprintf "%s.%s" name (Odoc_model.Names.ModuleTypeName.to_string s) }
      | _ ->
        fail ()
  end
end
OCaml

Innovation. Community. Security.