Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add header field to the json output #1314

Merged
merged 2 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### Added

- Added support for (local) images in the latex backend (@Octachron, #1297)
- Added a `header` field to the json output (@panglesd, #1314)

### Changed

Expand Down
32 changes: 21 additions & 11 deletions src/document/doctree.ml
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,13 @@ end = struct
end

module PageTitle : sig
val render_title : ?source_anchor:Url.t -> Page.t -> Item.t list
val render_title : ?source_anchor:Url.t -> Page.t -> Item.t list * Item.t list
(** Also returns the "new" preamble, since in the case of pages, the title may
be extracted from the preamle *)

val render_src_title : Source_page.t -> Item.t list
end = struct
let format_title ~source_anchor kind name =
let format_title ~source_anchor kind name preamble =
let mk title =
let level = 0 and label = None in
[ Types.Item.Heading { level; label; title; source_anchor } ]
Expand All @@ -323,24 +326,31 @@ end = struct
mk (Types.inline (Text (s ^ " ")) :: Codefmt.code (Codefmt.txt name))
in
match kind with
| `Module -> prefix "Module"
| `Parameter _ -> prefix "Parameter"
| `ModuleType -> prefix "Module type"
| `ClassType -> prefix "Class type"
| `Class -> prefix "Class"
| `SourcePage -> prefix "Source file"
| `Page | `LeafPage | `File -> []
| `Module -> (prefix "Module", preamble)
| `Parameter _ -> (prefix "Parameter", preamble)
| `ModuleType -> (prefix "Module type", preamble)
| `ClassType -> (prefix "Class type", preamble)
| `Class -> (prefix "Class", preamble)
| `SourcePage -> (prefix "Source file", preamble)
| `File -> ([], preamble)
| `Page | `LeafPage -> (
match preamble with
| (Item.Heading _ as h) :: rest -> ([ h ], rest)
| _ -> ([], preamble))

let make_name_from_path { Url.Path.name; parent; _ } =
match parent with
| None | Some { kind = `Page; _ } -> name
| Some p -> Printf.sprintf "%s.%s" p.name name

let render_title ?source_anchor (p : Page.t) =
format_title ~source_anchor p.url.kind (make_name_from_path p.url)
format_title ~source_anchor p.url.kind
(make_name_from_path p.url)
p.preamble

let render_src_title (p : Source_page.t) =
format_title ~source_anchor:None p.url.kind (make_name_from_path p.url)
format_title ~source_anchor:None p.url.kind (make_name_from_path p.url) []
|> fst
end

module Math : sig
Expand Down
21 changes: 11 additions & 10 deletions src/html/generator.ml
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ module Page = struct
List.map (include_ ~config ~sidebar) subpages

and page ~config ~sidebar p : Odoc_document.Renderer.page =
let { Page.preamble; items = i; url; source_anchor } =
let { Page.preamble = _; items = i; url; source_anchor } =
Doctree.Labels.disambiguate_page ~enter_subpages:false p
in
let subpages = subpages ~config ~sidebar @@ Doctree.Subpages.compute p in
Expand All @@ -666,22 +666,22 @@ module Page = struct
let uses_katex = Doctree.Math.has_math_elements p in
let toc = Toc.gen_toc ~config ~resolve ~path:url i in
let content = (items ~config ~resolve i :> any Html.elt list) in
let header, preamble = Doctree.PageTitle.render_title ?source_anchor p in
let header = items ~config ~resolve header in
let preamble = items ~config ~resolve preamble in
if Config.as_json config then
let source_anchor =
match source_anchor with
| Some url -> Some (Link.href ~config ~resolve url)
| None -> None
in
Html_fragment_json.make ~config
~preamble:(items ~config ~resolve preamble :> any Html.elt list)
~breadcrumbs ~toc ~url ~uses_katex ~source_anchor content subpages
~preamble:(preamble :> any Html.elt list)
~header ~breadcrumbs ~toc ~url ~uses_katex ~source_anchor content
subpages
else
let header =
items ~config ~resolve
(Doctree.PageTitle.render_title ?source_anchor p @ preamble)
in
Html_page.make ~sidebar ~config ~header ~toc ~breadcrumbs ~url ~uses_katex
content subpages
Html_page.make ~sidebar ~config ~header:(header @ preamble) ~toc
~breadcrumbs ~url ~uses_katex content subpages

and source_page ~config ~sidebar sp =
let { Source_page.url; contents } = sp in
Expand All @@ -700,7 +700,8 @@ module Page = struct
items ~config ~resolve (Doctree.PageTitle.render_src_title sp)
in
if Config.as_json config then
Html_fragment_json.make_src ~config ~url ~breadcrumbs ~sidebar [ doc ]
Html_fragment_json.make_src ~config ~url ~breadcrumbs ~sidebar ~header
[ doc ]
else
Html_page.make_src ~breadcrumbs ~header ~config ~url ~sidebar title
[ doc ]
Expand Down
6 changes: 4 additions & 2 deletions src/html/html_fragment_json.ml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ let json_of_sidebar config sidebar =
| Some sidebar -> `String (json_of_html config sidebar)

let make ~config ~preamble ~url ~breadcrumbs ~toc ~uses_katex ~source_anchor
content children =
~header content children =
let filename = Link.Path.as_filename ~config url in
let filename = Fpath.add_ext ".json" filename in
let json_to_string json = Json.to_string json in
Expand All @@ -54,6 +54,7 @@ let make ~config ~preamble ~url ~breadcrumbs ~toc ~uses_katex ~source_anchor
(json_to_string
(`Object
[
("header", `String (json_of_html config header));
("type", `String "documentation");
("uses_katex", `Bool uses_katex);
("breadcrumbs", json_of_breadcrumbs config breadcrumbs);
Expand All @@ -65,7 +66,7 @@ let make ~config ~preamble ~url ~breadcrumbs ~toc ~uses_katex ~source_anchor
in
{ Odoc_document.Renderer.filename; content; children; path = url }

let make_src ~config ~url ~breadcrumbs ~sidebar content =
let make_src ~config ~url ~breadcrumbs ~sidebar ~header content =
let filename = Link.Path.as_filename ~config url in
let filename = Fpath.add_ext ".json" filename in
let htmlpp = Html.pp_elt ~indent:(Config.indent config) () in
Expand All @@ -79,6 +80,7 @@ let make_src ~config ~url ~breadcrumbs ~sidebar content =
("type", `String "source");
("breadcrumbs", json_of_breadcrumbs config breadcrumbs);
("global_toc", global_toc);
("header", `String (json_of_html config header));
( "content",
`String
(String.concat ~sep:""
Expand Down
2 changes: 2 additions & 0 deletions src/html/html_fragment_json.mli
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ val make :
toc:Types.toc list ->
uses_katex:bool ->
source_anchor:string option ->
header:Html_types.flow5_without_header_footer Html.elt list ->
Html_types.div_content Html.elt list ->
Odoc_document.Renderer.page list ->
Odoc_document.Renderer.page
Expand All @@ -17,5 +18,6 @@ val make_src :
url:Odoc_document.Url.Path.t ->
breadcrumbs:Types.breadcrumbs ->
sidebar:Html_types.div_content Html.elt list option ->
header:Html_types.flow5_without_header_footer Html.elt list ->
Html_types.div_content Html.elt list ->
Odoc_document.Renderer.page
5 changes: 3 additions & 2 deletions src/latex/generator.ml
Original file line number Diff line number Diff line change
Expand Up @@ -487,11 +487,12 @@ module Page = struct
List.flatten @@ List.map (subpage ~with_children) subpages

and page ~with_children p =
let { Page.preamble; items = i; url; _ } =
let { Page.items = i; url; _ } =
Doctree.Labels.disambiguate_page ~enter_subpages:true p
and subpages = subpages ~with_children @@ Doctree.Subpages.compute p in
let i = Doctree.Shift.compute ~on_sub i in
let header = items (Doctree.PageTitle.render_title p @ preamble) in
let header, preamble = Doctree.PageTitle.render_title p in
let header = items (header @ preamble) in
let content = items i in
let page = Doc.make ~with_children url (header @ content) subpages in
page
Expand Down
5 changes: 2 additions & 3 deletions src/manpage/generator.ml
Original file line number Diff line number Diff line change
Expand Up @@ -542,9 +542,8 @@ let on_sub subp =

let page p =
reset_heading ();
let header =
Doctree.PageTitle.render_title p @ Shift.compute ~on_sub p.preamble
in
let header, preamble = Doctree.PageTitle.render_title p in
let header = header @ Shift.compute ~on_sub preamble in
let i = Shift.compute ~on_sub p.items in
macro "TH" {|%s 3 "" "Odoc" "OCaml Library"|} p.url.name
++ macro "SH" "Name"
Expand Down
12 changes: 11 additions & 1 deletion test/integration/html_opts.t/run.t
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
$ ocamlc -c -bin-annot test.mli
$ ocamlc -c -bin-annot test2.mli
$ printf "{0 The title}\n" > page.mld
$ odoc compile --package test test.cmti
$ odoc compile --package test -I . test2.cmti
$ odoc compile --package test -I . page.mld
$ odoc link test.odoc
$ odoc link test2.odoc
$ odoc link page-page.odoc
$ odoc html-generate test.odocl -o html --indent

$ odoc html-targets test.odocl -o html --indent
Expand All @@ -22,11 +25,18 @@ Generate --as-json embeddable HTML fragment output:

$ odoc html-generate test.odocl -o html --as-json --indent
$ cat html/test/Test/index.html.json
{"type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../../index.html","kind":"leaf-page"},{"name":"test","href":"../index.html","kind":"page"},{"name":"Test","href":"#","kind":"module"}],"toc":[{"title":"Section 1","href":"#section-1","children":[]},{"title":"Section 2","href":"#section-2","children":[]}],"source_anchor":null,"preamble":"<p>Test</p>","content":"<h2 id=\"section-1\"><a href=\"#section-1\" class=\"anchor\"></a>Section 1</h2><div class=\"odoc-spec\">\u000A <div class=\"spec type anchored\" id=\"type-t\">\u000A <a href=\"#type-t\" class=\"anchor\"></a>\u000A <code><span><span class=\"keyword\">type</span> t</span></code>\u000A </div>\u000A</div><h2 id=\"section-2\"><a href=\"#section-2\" class=\"anchor\"></a>Section 2</h2><div class=\"odoc-spec\">\u000A <div class=\"spec type anchored\" id=\"type-u\">\u000A <a href=\"#type-u\" class=\"anchor\"></a>\u000A <code><span><span class=\"keyword\">type</span> u</span></code>\u000A </div>\u000A</div>"}
{"header":"<h1>Module <code><span>Test</span></code></h1>","type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../../index.html","kind":"leaf-page"},{"name":"test","href":"../index.html","kind":"page"},{"name":"Test","href":"#","kind":"module"}],"toc":[{"title":"Section 1","href":"#section-1","children":[]},{"title":"Section 2","href":"#section-2","children":[]}],"source_anchor":null,"preamble":"<p>Test</p>","content":"<h2 id=\"section-1\"><a href=\"#section-1\" class=\"anchor\"></a>Section 1</h2><div class=\"odoc-spec\">\u000A <div class=\"spec type anchored\" id=\"type-t\">\u000A <a href=\"#type-t\" class=\"anchor\"></a>\u000A <code><span><span class=\"keyword\">type</span> t</span></code>\u000A </div>\u000A</div><h2 id=\"section-2\"><a href=\"#section-2\" class=\"anchor\"></a>Section 2</h2><div class=\"odoc-spec\">\u000A <div class=\"spec type anchored\" id=\"type-u\">\u000A <a href=\"#type-u\" class=\"anchor\"></a>\u000A <code><span><span class=\"keyword\">type</span> u</span></code>\u000A </div>\u000A</div>"}

$ odoc html-targets test.odocl -o html --as-json --indent
html/test/Test/index.html.json

Also for pages

$ odoc html-generate -o html --as-json page-page.odocl
$ cat html/test/page.html.json
{"header":"<h1 id=\"the-title\"><a href=\"#the-title\" class=\"anchor\"></a>The title</h1>","type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../index.html","kind":"leaf-page"},{"name":"test","href":"index.html","kind":"page"},{"name":"page","href":"#","kind":"leaf-page"}],"toc":[],"source_anchor":null,"preamble":"","content":""}


Check semantic_uris:
$ odoc html-generate test2.odocl -o html --indent
$ grep Test.t html/test/Test2/index.html
Expand Down
6 changes: 3 additions & 3 deletions test/integration/json_expansion.t/run.t
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ Test the JSON output in the presence of expanded modules.
$ odoc html-generate --as-json -o html main.odocl

$ cat html/Main/index.html.json
{"type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../index.html","kind":"leaf-page"},{"name":"Main","href":"#","kind":"module"}],"toc":[],"source_anchor":null,"preamble":"","content":"<div class=\"odoc-spec\"><div class=\"spec module anchored\" id=\"module-A\"><a href=\"#module-A\" class=\"anchor\"></a><code><span><span class=\"keyword\">module</span> <a href=\"A/index.html\">A</a></span><span> : <span class=\"keyword\">sig</span> ... <span class=\"keyword\">end</span></span></code></div></div>"}
{"header":"<h1>Module <code><span>Main</span></code></h1>","type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../index.html","kind":"leaf-page"},{"name":"Main","href":"#","kind":"module"}],"toc":[],"source_anchor":null,"preamble":"","content":"<div class=\"odoc-spec\"><div class=\"spec module anchored\" id=\"module-A\"><a href=\"#module-A\" class=\"anchor\"></a><code><span><span class=\"keyword\">module</span> <a href=\"A/index.html\">A</a></span><span> : <span class=\"keyword\">sig</span> ... <span class=\"keyword\">end</span></span></code></div></div>"}

$ cat html/Main/A/index.html.json
{"type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../../index.html","kind":"leaf-page"},{"name":"Main","href":"../index.html","kind":"module"},{"name":"A","href":"#","kind":"module"}],"toc":[],"source_anchor":null,"preamble":"","content":"<div class=\"odoc-spec\"><div class=\"spec module anchored\" id=\"module-B\"><a href=\"#module-B\" class=\"anchor\"></a><code><span><span class=\"keyword\">module</span> <a href=\"B/index.html\">B</a></span><span> : <span class=\"keyword\">sig</span> ... <span class=\"keyword\">end</span></span></code></div></div>"}
{"header":"<h1>Module <code><span>Main.A</span></code></h1>","type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../../index.html","kind":"leaf-page"},{"name":"Main","href":"../index.html","kind":"module"},{"name":"A","href":"#","kind":"module"}],"toc":[],"source_anchor":null,"preamble":"","content":"<div class=\"odoc-spec\"><div class=\"spec module anchored\" id=\"module-B\"><a href=\"#module-B\" class=\"anchor\"></a><code><span><span class=\"keyword\">module</span> <a href=\"B/index.html\">B</a></span><span> : <span class=\"keyword\">sig</span> ... <span class=\"keyword\">end</span></span></code></div></div>"}

$ cat html/Main/A/B/index.html.json
{"type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../../../index.html","kind":"leaf-page"},{"name":"Main","href":"../../index.html","kind":"module"},{"name":"A","href":"../index.html","kind":"module"},{"name":"B","href":"#","kind":"module"}],"toc":[],"source_anchor":null,"preamble":"","content":""}
{"header":"<h1>Module <code><span>A.B</span></code></h1>","type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../../../index.html","kind":"leaf-page"},{"name":"Main","href":"../../index.html","kind":"module"},{"name":"A","href":"../index.html","kind":"module"},{"name":"B","href":"#","kind":"module"}],"toc":[],"source_anchor":null,"preamble":"","content":""}
8 changes: 4 additions & 4 deletions test/integration/json_expansion_with_sources.t/run.t
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ Test the JSON output in the presence of expanded modules.
$ odoc html-generate --as-json -o html main.odocl

$ cat html/Main/index.html.json
{"type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../index.html","kind":"leaf-page"},{"name":"Main","href":"#","kind":"module"}],"toc":[],"source_anchor":"../src/main.ml.html","preamble":"","content":"<div class=\"odoc-spec\"><div class=\"spec module anchored\" id=\"module-A\"><a href=\"#module-A\" class=\"anchor\"></a><a href=\"../src/a.ml.html\" class=\"source_link\">Source</a><code><span><span class=\"keyword\">module</span> <a href=\"A/index.html\">A</a></span><span> : <span class=\"keyword\">sig</span> ... <span class=\"keyword\">end</span></span></code></div></div>"}
{"header":"<h1>Module <code><span>Main</span></code><a href=\"../src/main.ml.html\" class=\"source_link\">Source</a></h1>","type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../index.html","kind":"leaf-page"},{"name":"Main","href":"#","kind":"module"}],"toc":[],"source_anchor":"../src/main.ml.html","preamble":"","content":"<div class=\"odoc-spec\"><div class=\"spec module anchored\" id=\"module-A\"><a href=\"#module-A\" class=\"anchor\"></a><a href=\"../src/a.ml.html\" class=\"source_link\">Source</a><code><span><span class=\"keyword\">module</span> <a href=\"A/index.html\">A</a></span><span> : <span class=\"keyword\">sig</span> ... <span class=\"keyword\">end</span></span></code></div></div>"}

$ cat html/Main/A/index.html.json
{"type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../../index.html","kind":"leaf-page"},{"name":"Main","href":"../index.html","kind":"module"},{"name":"A","href":"#","kind":"module"}],"toc":[],"source_anchor":"../../src/a.ml.html","preamble":"","content":"<div class=\"odoc-spec\"><div class=\"spec module anchored\" id=\"module-B\"><a href=\"#module-B\" class=\"anchor\"></a><a href=\"../../src/a.ml.html#module-B\" class=\"source_link\">Source</a><code><span><span class=\"keyword\">module</span> <a href=\"B/index.html\">B</a></span><span> : <span class=\"keyword\">sig</span> ... <span class=\"keyword\">end</span></span></code></div></div>"}
{"header":"<h1>Module <code><span>Main.A</span></code><a href=\"../../src/a.ml.html\" class=\"source_link\">Source</a></h1>","type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../../index.html","kind":"leaf-page"},{"name":"Main","href":"../index.html","kind":"module"},{"name":"A","href":"#","kind":"module"}],"toc":[],"source_anchor":"../../src/a.ml.html","preamble":"","content":"<div class=\"odoc-spec\"><div class=\"spec module anchored\" id=\"module-B\"><a href=\"#module-B\" class=\"anchor\"></a><a href=\"../../src/a.ml.html#module-B\" class=\"source_link\">Source</a><code><span><span class=\"keyword\">module</span> <a href=\"B/index.html\">B</a></span><span> : <span class=\"keyword\">sig</span> ... <span class=\"keyword\">end</span></span></code></div></div>"}

$ cat html/Main/A/B/index.html.json
{"type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../../../index.html","kind":"leaf-page"},{"name":"Main","href":"../../index.html","kind":"module"},{"name":"A","href":"../index.html","kind":"module"},{"name":"B","href":"#","kind":"module"}],"toc":[],"source_anchor":"../../../src/a.ml.html#module-B","preamble":"","content":""}
{"header":"<h1>Module <code><span>A.B</span></code><a href=\"../../../src/a.ml.html#module-B\" class=\"source_link\">Source</a></h1>","type":"documentation","uses_katex":false,"breadcrumbs":[{"name":"Index","href":"../../../index.html","kind":"leaf-page"},{"name":"Main","href":"../../index.html","kind":"module"},{"name":"A","href":"../index.html","kind":"module"},{"name":"B","href":"#","kind":"module"}],"toc":[],"source_anchor":"../../../src/a.ml.html#module-B","preamble":"","content":""}

$ cat html/src/a.ml.html.json
{"type":"source","breadcrumbs":[{"name":"Index","href":"../index.html","kind":"leaf-page"},{"name":"src","href":"index.html","kind":"page"},{"name":"a.ml","href":"#","kind":"source"}],"global_toc":null,"content":"<pre class=\"source_container\"><code class=\"source_line_column\"><a id=\"L1\" class=\"source_line\" href=\"#L1\">1</a>\u000A</code><code class=\"source_code\"><span><span id=\"module-B\"><span class=\"MODULE\">module</span> <span class=\"UIDENT\">B</span> <span class=\"EQUAL\">=</span> <span class=\"STRUCT\">struct</span> <span class=\"END\">end</span></span><span class=\"EOL\">\u000A</span></span></code></pre>"}
{"type":"source","breadcrumbs":[{"name":"Index","href":"../index.html","kind":"leaf-page"},{"name":"src","href":"index.html","kind":"page"},{"name":"a.ml","href":"#","kind":"source"}],"global_toc":null,"header":"<h1>Source file <code><span>a.ml</span></code></h1>","content":"<pre class=\"source_container\"><code class=\"source_line_column\"><a id=\"L1\" class=\"source_line\" href=\"#L1\">1</a>\u000A</code><code class=\"source_code\"><span><span id=\"module-B\"><span class=\"MODULE\">module</span> <span class=\"UIDENT\">B</span> <span class=\"EQUAL\">=</span> <span class=\"STRUCT\">struct</span> <span class=\"END\">end</span></span><span class=\"EOL\">\u000A</span></span></code></pre>"}