package odig
Lookup documentation of installed OCaml packages
Install
dune-project
Dependency
erratique.ch
Readme
Changelog
ISC; LicenseRef-ParaType-Free-Font-License; LicenseRef-DejaVu-fonts License
Edit opam file
Versions (2)
Authors
Maintainers
Sources
odig-0.1.0.tbz
sha512=acabe752f5010900e9fe024321d6e34cc07f395ebd18f015a00b88c4c6482645b591a83a3f088f110adaf83e062551feb800a1dbca7646cce305eb3f542cfa41
doc/src/odig_support/odig_odoc_page.ml.html
Source file odig_odoc_page.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 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
(*--------------------------------------------------------------------------- Copyright (c) 2018 The odig programmers. All rights reserved. SPDX-License-Identifier: ISC ---------------------------------------------------------------------------*) open Odig_support open B0_std open B0_html let anchor_href aid = At.href (Fmt.str "#%s" aid) let anchor_a aid = El.a ~at:At.[anchor_href aid; class' "anchor"; v "aria-hidden" "true"] [] let a_to_utf_8_txt ~href:h txt = El.a ~at:At.[type' "text/plain; charset=UTF-8"; href h] [El.txt txt] let doc_dir_link ?txt f = let fname = Fpath.basename (Fpath.v f) in let txt = match txt with None -> fname | Some txt -> txt in a_to_utf_8_txt ~href:(Fmt.str "_doc-dir/%s" fname) txt (* Package index.mld file *) let pkg_title pkg pkg_info = let short_info = let get_first f el = match Pkg_info.get f pkg_info with | [] -> El.txt "" | v :: _ -> el v in let version = get_first `Version @@ fun version -> El.span ~at:At.[class' "version"] [El.txt version] in let changes_link = get_first `Changes_files @@ fun c -> doc_dir_link ~txt:"changes" c in let issues_link = get_first `Issues @@ fun issues -> El.a ~at:At.[href issues] [El.txt "issues"] in let info_link = El.a ~at:At.[href "#package_info"] [El.txt "more…"] in let sp = El.txt " " in [version; sp; El.nav [changes_link; sp; issues_link; sp; info_link]] in Fmt.str "{0:package-%s Package %s {%%html:%s%%}}" (Pkg.name pkg) (Pkg.name pkg) (El.to_string ~doctype:false (El.splice short_info)) let ocaml_pkg_module_indexes pkg_info = (* Too much noise to use regular index generation. We cook up something manually. People should simply stop vomiting in the ocaml directory. The day upstream wants control over it can provide an `index.mld` file at the right place. It will override this. *) let dirid f = Fpath.basename (Fpath.parent f) in let add_cobj acc cobj = let dirid = dirid (Doc_cobj.path cobj) in match String.Map.find dirid acc with | exception Not_found -> String.Map.add dirid [cobj] acc | cobjs -> String.Map.add dirid (cobj :: cobjs) acc in let cobjs = Pkg_info.doc_cobjs pkg_info in let bydir = List.fold_left add_cobj String.Map.empty cobjs in let dirmods ~lib = let mods = match String.Map.find lib bydir with | cobjs -> let cobjs = List.filter (Fun.negate Doc_cobj.don't_list) cobjs in List.rev_map Doc_cobj.modname cobjs | exception Not_found -> if lib = "unix" then (* < OCaml 5 *) ["Unix"; "UnixLabels"] else [] in if mods = [] then "" else let mods = List.sort String.compare mods in let libid = String.map (function '-' -> '_' | c -> c) lib in Fmt.str "{1:%s Library [%s]}\n{!modules: %s}\n" libid lib (String.concat " " mods) in let stdlib_mods = match String.Map.find "ocaml" bydir with | exception Not_found -> "" | cobjs -> let stdlib_mod cobj = let modn = Doc_cobj.modname cobj in let prefix = "Stdlib__" in if not (String.starts_with ~prefix modn) then None else Some (String.replace_all ~sub:"__" ~by:"." modn) in let mods = List.filter_map stdlib_mod cobjs in let mods = List.sort String.compare mods in String.concat " " mods in Fmt.str "{1:library_stdlib Library [stdlib]}\n\ {!modules: Stdlib %s}\n\ %s%s%s%s%s%s%s" stdlib_mods (dirmods ~lib:"unix") (dirmods ~lib:"dynlink") (dirmods ~lib:"runtime_events") (dirmods ~lib:"str") (dirmods ~lib:"threads") (dirmods ~lib:"ocamldoc") (dirmods ~lib:"compiler-libs") let pkg_index pkg pkg_info ~user_index = let drop_section_0 s = match String.split_first ~sep:"{0" s with | Some (t, r) when String.trim t = "" -> (* can break but should be mostly ok *) let max = String.length r in let rec loop c i max = match i > max with | true -> s | false -> match r.[i] with | '{' -> loop (c + 1) (i + 1) max | '}' when c = 1 -> String.drop_first (i + 1) r | '}' -> loop (c - 1) (i + 1) max | _ -> loop c (i + 1) max in loop 1 0 max | _ -> s in match user_index with | Some user_index -> drop_section_0 user_index | None when Pkg.name pkg = "ocaml" -> ocaml_pkg_module_indexes pkg_info | None -> let cobjs = Pkg_info.doc_cobjs pkg_info in let cobjs = List.filter (fun c -> not (Doc_cobj.don't_list c)) cobjs in let mods = List.rev_map Doc_cobj.modname cobjs in let mods = List.sort String.compare mods in Fmt.str "{!modules: %s}" (String.concat " " mods) let pkg_info_section pkg pkg_info ~with_tag_links = let def_values field fname fval i =match Pkg_info.get field i with | [] -> El.txt "" | vs -> let fid = Fmt.str "info-%s" fname in let vs = List.sort compare vs in let vs = List.map (fun v -> El.li (fval v)) vs in El.tr ~at:At.[id fid] El.[td [(anchor_a fid); txt fname]; td [ul vs]] in let defs pkg pkg_info = let string_val str = [El.txt str] in let uri_val uri = [El.a ~at:At.[href uri] [El.txt uri]] in let file_val f = [doc_dir_link f] in let pkg_val pkg = [El.a ~at:At.[href (Fmt.str "../%s/index.html" pkg)] [El.txt pkg]] in let tag_val t = match with_tag_links with | true -> [El.a ~at:At.[href (Fmt.str "../index.html#tag-%s" t)] [El.txt t]] | false -> [El.txt t] in [ def_values `Authors "authors" string_val pkg_info; def_values `Changes_files "changes-files" file_val pkg_info; def_values `Depends "depends" pkg_val pkg_info; def_values `Homepage "homepage" uri_val pkg_info; def_values `Issues "issues" uri_val pkg_info; def_values `License "license" string_val pkg_info; def_values `License_files "license-files" file_val pkg_info; def_values `Maintainers "maintainers" string_val pkg_info; def_values `Online_doc "online-doc" uri_val pkg_info; def_values `Readme_files "readme-files" file_val pkg_info; def_values `Repo "repo" string_val pkg_info; def_values `Tags "tags" tag_val pkg_info; def_values `Version "version" string_val pkg_info ] in Fmt.str "{1:package_info Package info}\n {%%html:%s%%}" @@ El.to_string ~doctype:false (El.table ~at:At.[class' "package"; class' "info"] (defs pkg pkg_info)) let index_mld conf pkg pkg_info ~user_index ~with_tag_links = Fmt.str "%s\n%s\n%s" (pkg_title pkg pkg_info) (pkg_index pkg pkg_info ~user_index) (pkg_info_section pkg pkg_info ~with_tag_links) (* Package list *) let pkg_li conf ~pid pkg = let info = try Pkg.Map.find pkg (Conf.pkg_infos conf) with | Not_found -> assert false (* formally, could be racy *) in let name = Pkg.name pkg in let version = String.concat " " (Pkg_info.get `Version info) in let synopsis = String.concat " " (Pkg_info.get `Synopsis info) in let index = Fmt.str "%s/index.html" name in let pid = pid name in El.li ~at:At.[id pid] [ anchor_a pid; El.a ~at:At.[href index] [El.txt name]; El.txt " "; El.span ~at:At.[class' "version"] [El.txt version]; El.txt " "; El.span ~at:At.[class' "synopsis"] [El.txt synopsis]; ] let pkg_list conf pkgs = let letter_id l = Fmt.str "name-%s" l in let letter_link (l, _) = El.(a ~at:At.[anchor_href (letter_id l)] [txt l]) in let letter_section (l, pkgs) = let pkgs = List.sort Pkg.compare_by_caseless_name pkgs in let letter_id = letter_id l in let pid = Fmt.str "package-%s" in El.splice @@ [El.h3 ~at:At.[id letter_id] [anchor_a letter_id; El.txt l]; El.ol ~at:At.[class' "packages"] (List.map (pkg_li conf ~pid) pkgs)] in let by_name = "by-name" in let classes p = [String.of_char (Char.Ascii.lowercase (Pkg.name p).[0])] in let classes = List.classify ~cmp_elts:Pkg.compare ~classes pkgs in [ El.h2 ~at:At.[id by_name] [anchor_a by_name; El.txt "Packages by name"]; El.div ~at:At.[class' by_name] [ El.nav (List.map letter_link classes); El.splice (List.map letter_section classes) ]] let tag_list conf pkgs = let tag_id t = Fmt.str "tag-%s" t in let tag_links = let tag_li (t, _) = El.(li [a ~at:At.[anchor_href (tag_id t)] [txt t]]) in let (letter, ) = let lid = Fmt.str "tags-%s" letter in El.(tr ~at:At.[id lid] [td [anchor_a lid; txt letter]; td [ol ~at:At.[class' "tags"] (List.map tag_li tags)]]) in let classes (t, _) = [String.of_char (Char.Ascii.lowercase t.[0])] in let cmp_elts (t, _) (t', _) = String.compare t t' in let tag_classes = List.classify ~cmp_elts ~classes tags in El.table (List.map tags_by_letter tag_classes) in let tag_section (t, pkgs) = let pkgs = List.sort Pkg.compare_by_caseless_name pkgs in let tag_id = tag_id t in let pid = Fmt.str "tag-%s-package-%s" t in El.splice @@ [El.h3 ~at:At.[id tag_id] [anchor_a tag_id; El.span [El.txt t]]; El.ol ~at:At.[class' "packages"] (List.map (pkg_li conf ~pid) pkgs)] in let by_tag = "by-tag" in let pkg_infos = Conf.pkg_infos conf in let classes p = try Pkg_info.get `Tags (Pkg.Map.find p pkg_infos) with | Not_found -> assert false in let classes = List.classify ~cmp_elts:Pkg.compare ~classes pkgs in [ El.h2 ~at:At.[id by_tag] [anchor_a by_tag; El.txt "Packages by tag"]; El.div ~at:At.[class' by_tag] [ El.nav [tag_links classes]; El.splice (List.map tag_section classes)]] let manual_reference conf ~ocaml_manual_uri = let manual_online = "https://ocaml.org/manual/" in let uri, suff = match ocaml_manual_uri with | None -> manual_online, El.txt " (online, latest version)" | Some href -> href, El.txt "" in El.splice @@ [El.a ~at:At.[href uri] [El.txt "OCaml manual"]; suff], uri let stdlib_link conf = "ocaml/index.html#library_stdlib" let pkgs_with_html_docs conf = let by_names = Pkg.by_names (Conf.pkgs conf) in let add_pkg _ name dir acc = let exists = Os.File.exists Fpath.(dir / "index.html") in match exists |> Log.if_error ~level:Log.Warning ~use:false with | false -> acc | true -> match String.Map.find name by_names with | exception Not_found -> acc | pkg -> pkg :: acc in let pkgs = Os.Dir.fold_dirs ~recurse:false add_pkg (Conf.html_dir conf) [] in let pkgs = pkgs |> Log.if_error ~level:Log.Warning ~use:[] in List.sort Pkg.compare pkgs let pkg_list conf ~index_title ~raw_index_intro ~raw_index_toc ~tag_index ~ocaml_manual_uri pkgs = (* XXX for now it's easier to do it this way. In the future we should rather use the ocamldoc language. Either by using https://github.com/ocaml/odoc/issues/94 or `--fragment`. So that we don't have to guess the way package links are formed. *) let doc_head ~style_href page_title = (* a basic head *) El.head [ El.meta ~at:At.[charset "utf-8"] (); El.meta ~at:At.[name "generator"; content "odig v0.1.0"] (); El.meta ~at:At.[name "viewport"; content "width=device-width, initial-scale=1.0"] (); El.link ~at:At.[rel "stylesheet"; type' "text/css"; media "screen, print"; href style_href; ] (); El.title [El.txt page_title]] in let stdlib_link = stdlib_link conf in let manual_markup, manual_href = manual_reference conf ~ocaml_manual_uri in let doc_header = let comma = El.txt ", " in let contents = match raw_index_intro with | Some h -> [El.unsafe_raw h] | None -> let browse_by_tag = match tag_index with | true -> El.(splice [a ~at:At.[href "#by-tag"] [txt "by tag"]; comma]) | false -> El.splice [] in [ El.h1 [El.txt "OCaml package documentation"]; El.p [El.txt "Browse "; El.a ~at:At.[href "#by-name"] [El.txt "by name"]; comma; browse_by_tag; El.txt " the "; El.a ~at:At.[href stdlib_link] [El.txt "standard library"]; El.txt " and the "; manual_markup; El.txt ".";]; El.p [El.small [El.txt "Generated for "; El.code [El.txt (Fpath.to_string (Conf.lib_dir conf))]]];] in El.header ~at:At.[class' "odoc-preamble"] contents in let toc = let contents = match raw_index_toc with | Some toc -> El.unsafe_raw toc | None -> let packages_by_tag_li = match tag_index with | false -> El.splice [] | true -> El.li [El.a ~at:At.[href "#by-tag"] [El.txt "Packages by tag"]] in El.ul [ El.li [El.a ~at:At.[href stdlib_link] [El.txt "OCaml standard library"]]; El.li [El.a ~at:At.[href manual_href] [El.txt "OCaml manual"]]; El.li [El.a ~at:At.[href "#by-name"] [El.txt "Packages by name"]]; packages_by_tag_li; ] in El.div ~at:At.[class' "odoc-tocs"] [El.nav ~at:At.[class' "odoc-toc"] [contents]] in let style_href = "_odoc-theme/odoc.css" in let page_title = match index_title with | None -> Fpath.(basename @@ parent (Conf.lib_dir conf)) | Some t -> t in El.to_string ~doctype:true @@ El.html [ doc_head ~style_href page_title; El.body ~at:At.[class' "odoc"] [ El.nav ~at:At.[class' "odoc-nav"] [El.txt "\xF0\x9F\x90\xAB"]; doc_header; toc; El.div ~at:At.[class' "odoc-content"] [ El.splice (pkg_list conf pkgs); El.splice (if tag_index then tag_list conf pkgs else [])]]]
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>