From bbd52d4891c55e98f51ce138f0d54d67da0f8be8 Mon Sep 17 00:00:00 2001 From: Paul-Elliot Date: Tue, 18 Feb 2025 16:34:58 +0100 Subject: [PATCH 1/2] Json output: add header --- src/document/doctree.ml | 32 ++++++++++++------- src/html/generator.ml | 21 ++++++------ src/html/html_fragment_json.ml | 6 ++-- src/html/html_fragment_json.mli | 2 ++ src/latex/generator.ml | 5 +-- src/manpage/generator.ml | 5 ++- test/integration/html_opts.t/run.t | 12 ++++++- test/integration/json_expansion.t/run.t | 6 ++-- .../json_expansion_with_sources.t/run.t | 8 ++--- 9 files changed, 61 insertions(+), 36 deletions(-) diff --git a/src/document/doctree.ml b/src/document/doctree.ml index cba5dc056d..d48ff41bb6 100644 --- a/src/document/doctree.ml +++ b/src/document/doctree.ml @@ -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 } ] @@ -323,13 +326,17 @@ 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 @@ -337,10 +344,13 @@ end = struct | 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 diff --git a/src/html/generator.ml b/src/html/generator.ml index d35baf2991..6a30ffca6a 100644 --- a/src/html/generator.ml +++ b/src/html/generator.ml @@ -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 @@ -666,6 +666,9 @@ 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 @@ -673,15 +676,12 @@ module Page = struct | 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 @@ -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 ] diff --git a/src/html/html_fragment_json.ml b/src/html/html_fragment_json.ml index 7fac891bb6..c0ea7c5a1f 100644 --- a/src/html/html_fragment_json.ml +++ b/src/html/html_fragment_json.ml @@ -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 @@ -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); @@ -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 @@ -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:"" diff --git a/src/html/html_fragment_json.mli b/src/html/html_fragment_json.mli index 363f83e838..91f133072a 100644 --- a/src/html/html_fragment_json.mli +++ b/src/html/html_fragment_json.mli @@ -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 @@ -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 diff --git a/src/latex/generator.ml b/src/latex/generator.ml index 48808b8168..5e20a7fc2c 100644 --- a/src/latex/generator.ml +++ b/src/latex/generator.ml @@ -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 diff --git a/src/manpage/generator.ml b/src/manpage/generator.ml index ea39f2fd41..2853760ee8 100644 --- a/src/manpage/generator.ml +++ b/src/manpage/generator.ml @@ -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" diff --git a/test/integration/html_opts.t/run.t b/test/integration/html_opts.t/run.t index e86c949a0f..d2fd139119 100644 --- a/test/integration/html_opts.t/run.t +++ b/test/integration/html_opts.t/run.t @@ -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 @@ -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":"

Test

","content":"

Section 1

\u000A
\u000A \u000A type t\u000A
\u000A

Section 2

\u000A
\u000A \u000A type u\u000A
\u000A
"} + {"header":"

Module Test

","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":"

Test

","content":"

Section 1

\u000A
\u000A \u000A type t\u000A
\u000A

Section 2

\u000A
\u000A \u000A type u\u000A
\u000A
"} $ 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":"

The title

","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 diff --git a/test/integration/json_expansion.t/run.t b/test/integration/json_expansion.t/run.t index 46d24005b8..be7933038b 100644 --- a/test/integration/json_expansion.t/run.t +++ b/test/integration/json_expansion.t/run.t @@ -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":"
module A : sig ... end
"} + {"header":"

Module Main

","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":"
module A : sig ... end
"} $ 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":"
module B : sig ... end
"} + {"header":"

Module Main.A

","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":"
module B : sig ... end
"} $ 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":"

Module A.B

","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":""} diff --git a/test/integration/json_expansion_with_sources.t/run.t b/test/integration/json_expansion_with_sources.t/run.t index 8a7fc73abd..ffc54de878 100644 --- a/test/integration/json_expansion_with_sources.t/run.t +++ b/test/integration/json_expansion_with_sources.t/run.t @@ -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":"
Sourcemodule A : sig ... end
"} + {"header":"

Module MainSource

","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":"
Sourcemodule A : sig ... end
"} $ 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":"
Sourcemodule B : sig ... end
"} + {"header":"

Module Main.ASource

","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":"
Sourcemodule B : sig ... end
"} $ 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":"

Module A.BSource

","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":"
1\u000Amodule B = struct end\u000A
"} + {"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":"

Source file a.ml

","content":"
1\u000Amodule B = struct end\u000A
"} From e19a70b55e1d6e19c31b5eb1c776888d8e091757 Mon Sep 17 00:00:00 2001 From: Paul-Elliot Date: Tue, 18 Feb 2025 16:38:28 +0100 Subject: [PATCH 2/2] Changelog entry for #1314 --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index f714a3a2eb..be550bc8c6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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