diff --git a/src/document/renderer.ml b/src/document/renderer.ml
index cee635dab6..f69847f67a 100644
--- a/src/document/renderer.ml
+++ b/src/document/renderer.ml
@@ -17,14 +17,14 @@ let traverse ~f t =
in
List.iter aux t
+type input =
+ | CU of Odoc_model.Lang.Compilation_unit.t
+ | Page of Odoc_model.Lang.Page.t
+
type 'a t = {
name : string;
render : 'a -> Types.Document.t -> page list;
- extra_documents :
- 'a ->
- Odoc_model.Lang.Compilation_unit.t ->
- syntax:syntax ->
- Types.Document.t list;
+ extra_documents : 'a -> input -> syntax:syntax -> Types.Document.t list;
}
let document_of_page ~syntax v =
diff --git a/src/document/types.ml b/src/document/types.ml
index a40c78b087..2053ce902e 100644
--- a/src/document/types.ml
+++ b/src/document/types.ml
@@ -192,8 +192,13 @@ and Source_page : sig
end =
Source_page
+and Asset : sig
+ type t = { url : Url.Path.t; src : Fpath.t }
+end =
+ Asset
+
module Document = struct
- type t = Page of Page.t | Source_page of Source_page.t
+ type t = Page of Page.t | Source_page of Source_page.t | Asset of Asset.t
end
let inline ?(attr = []) desc = Inline.{ attr; desc }
diff --git a/src/document/url.ml b/src/document/url.ml
index 3be65243d3..fdd135a486 100644
--- a/src/document/url.ml
+++ b/src/document/url.ml
@@ -87,10 +87,13 @@ module Path = struct
| Identifier.Signature.t_pv
| Identifier.ClassSignature.t_pv ]
- type source_pv =
- [ nonsrc_pv | Identifier.SourcePage.t_pv | Identifier.SourceDir.t_pv ]
+ type any_pv =
+ [ nonsrc_pv
+ | Identifier.SourcePage.t_pv
+ | Identifier.SourceDir.t_pv
+ | Identifier.AssetFile.t_pv ]
- and source = source_pv Odoc_model.Paths.Identifier.id
+ and any = any_pv Odoc_model.Paths.Identifier.id
type kind =
[ `Module
@@ -120,13 +123,13 @@ module Path = struct
let mk ?parent kind name = { kind; parent; name }
- let rec from_identifier : source -> t =
+ let rec from_identifier : any -> t =
fun x ->
match x with
| { iv = `Root (parent, unit_name); _ } ->
let parent =
match parent with
- | Some p -> Some (from_identifier (p :> source))
+ | Some p -> Some (from_identifier (p :> any))
| None -> None
in
let kind = `Module in
@@ -135,7 +138,7 @@ module Path = struct
| { iv = `Page (parent, page_name); _ } ->
let parent =
match parent with
- | Some p -> Some (from_identifier (p :> source))
+ | Some p -> Some (from_identifier (p :> any))
| None -> None
in
let kind = `Page in
@@ -144,48 +147,51 @@ module Path = struct
| { iv = `LeafPage (parent, page_name); _ } ->
let parent =
match parent with
- | Some p -> Some (from_identifier (p :> source))
+ | Some p -> Some (from_identifier (p :> any))
| None -> None
in
let kind = `LeafPage in
let name = PageName.to_string page_name in
mk ?parent kind name
| { iv = `Module (parent, mod_name); _ } ->
- let parent = from_identifier (parent :> source) in
+ let parent = from_identifier (parent :> any) in
let kind = `Module in
let name = ModuleName.to_string mod_name in
mk ~parent kind name
| { iv = `Parameter (functor_id, arg_name); _ } as p ->
- let parent = from_identifier (functor_id :> source) in
+ let parent = from_identifier (functor_id :> any) in
let arg_num = functor_arg_pos p in
let kind = `Parameter arg_num in
let name = ModuleName.to_string arg_name in
mk ~parent kind name
| { iv = `ModuleType (parent, modt_name); _ } ->
- let parent = from_identifier (parent :> source) in
+ let parent = from_identifier (parent :> any) in
let kind = `ModuleType in
let name = ModuleTypeName.to_string modt_name in
mk ~parent kind name
| { iv = `Class (parent, name); _ } ->
- let parent = from_identifier (parent :> source) in
+ let parent = from_identifier (parent :> any) in
let kind = `Class in
let name = ClassName.to_string name in
mk ~parent kind name
| { iv = `ClassType (parent, name); _ } ->
- let parent = from_identifier (parent :> source) in
+ let parent = from_identifier (parent :> any) in
let kind = `ClassType in
let name = ClassTypeName.to_string name in
mk ~parent kind name
- | { iv = `Result p; _ } -> from_identifier (p :> source)
+ | { iv = `Result p; _ } -> from_identifier (p :> any)
| { iv = `SourceDir (parent, name); _ }
| { iv = `SourcePage (parent, name); _ } ->
- let parent = from_identifier (parent :> source) in
+ let parent = from_identifier (parent :> any) in
let kind = `Page in
mk ~parent kind name
+ | { iv = `AssetFile (parent, name); _ } ->
+ let parent = from_identifier (parent :> any) in
+ let kind = `File in
+ mk ~parent kind name
let from_identifier p =
- from_identifier
- (p : [< source_pv ] Odoc_model.Paths.Identifier.id :> source)
+ from_identifier (p : [< any_pv ] Odoc_model.Paths.Identifier.id :> any)
let to_list url =
let rec loop acc { parent; name; kind } =
@@ -266,7 +272,7 @@ module Anchor = struct
let open Error in
function
| { iv = `Module (parent, mod_name); _ } ->
- let parent = Path.from_identifier (parent :> Path.source) in
+ let parent = Path.from_identifier (parent :> Path.any) in
let kind = `Module in
let anchor =
Printf.sprintf "%s-%s" (Path.string_of_kind kind)
@@ -274,13 +280,13 @@ module Anchor = struct
in
Ok { page = parent; anchor; kind }
| { iv = `Root _; _ } as p ->
- let page = Path.from_identifier (p :> Path.source) in
+ let page = Path.from_identifier (p :> Path.any) in
Ok { page; kind = `Module; anchor = "" }
| { iv = `Page _; _ } as p ->
- let page = Path.from_identifier (p :> Path.source) in
+ let page = Path.from_identifier (p :> Path.any) in
Ok { page; kind = `Page; anchor = "" }
| { iv = `LeafPage _; _ } as p ->
- let page = Path.from_identifier (p :> Path.source) in
+ let page = Path.from_identifier (p :> Path.any) in
Ok { page; kind = `LeafPage; anchor = "" }
(* For all these identifiers, page names and anchors are the same *)
| {
@@ -289,7 +295,7 @@ module Anchor = struct
} as p ->
Ok (anchorify_path @@ Path.from_identifier p)
| { iv = `Type (parent, type_name); _ } ->
- let page = Path.from_identifier (parent :> Path.source) in
+ let page = Path.from_identifier (parent :> Path.any) in
let kind = `Type in
Ok
{
@@ -302,7 +308,7 @@ module Anchor = struct
| { iv = `CoreType ty_name; _ } ->
Error (Not_linkable ("core_type:" ^ TypeName.to_string ty_name))
| { iv = `Extension (parent, name); _ } ->
- let page = Path.from_identifier (parent :> Path.source) in
+ let page = Path.from_identifier (parent :> Path.any) in
let kind = `Extension in
Ok
{
@@ -313,7 +319,7 @@ module Anchor = struct
kind;
}
| { iv = `Exception (parent, name); _ } ->
- let page = Path.from_identifier (parent :> Path.source) in
+ let page = Path.from_identifier (parent :> Path.any) in
let kind = `Exception in
Ok
{
@@ -326,7 +332,7 @@ module Anchor = struct
| { iv = `CoreException name; _ } ->
Error (Not_linkable ("core_exception:" ^ ExceptionName.to_string name))
| { iv = `Value (parent, name); _ } ->
- let page = Path.from_identifier (parent :> Path.source) in
+ let page = Path.from_identifier (parent :> Path.any) in
let kind = `Val in
Ok
{
@@ -337,13 +343,13 @@ module Anchor = struct
}
| { iv = `Method (parent, name); _ } ->
let str_name = MethodName.to_string name in
- let page = Path.from_identifier (parent :> Path.source) in
+ let page = Path.from_identifier (parent :> Path.any) in
let kind = `Method in
Ok
{ page; anchor = Format.asprintf "%a-%s" pp_kind kind str_name; kind }
| { iv = `InstanceVariable (parent, name); _ } ->
let str_name = InstanceVariableName.to_string name in
- let page = Path.from_identifier (parent :> Path.source) in
+ let page = Path.from_identifier (parent :> Path.any) in
let kind = `Val in
Ok
{ page; anchor = Format.asprintf "%a-%s" pp_kind kind str_name; kind }
@@ -367,16 +373,19 @@ module Anchor = struct
Error (Unexpected_anchor "core_type label parent")
| { iv = `Type (gp, _); _ } -> mk ~kind:`Section gp str_name
| { iv = #Path.nonsrc_pv; _ } as p ->
- mk ~kind:`Section (p :> Path.source) str_name)
+ mk ~kind:`Section (p :> Path.any) str_name)
| { iv = `SourceLocation (parent, loc); _ } ->
- let page = Path.from_identifier (parent :> Path.source) in
+ let page = Path.from_identifier (parent :> Path.any) in
Ok { page; kind = `SourceAnchor; anchor = DefName.to_string loc }
| { iv = `SourceLocationMod parent; _ } ->
- let page = Path.from_identifier (parent :> Path.source) in
+ let page = Path.from_identifier (parent :> Path.any) in
Ok { page; kind = `SourceAnchor; anchor = "" }
- | { iv = `SourcePage (p, _name); _ } | { iv = `SourceDir (p, _name); _ } ->
- let page = Path.from_identifier (p :> Path.source) in
+ | { iv = `SourcePage _ | `SourceDir _; _ } as p ->
+ let page = Path.from_identifier (p :> Path.any) in
Ok { page; kind = `Page; anchor = "" }
+ | { iv = `AssetFile _; _ } as p ->
+ let page = Path.from_identifier p in
+ Ok { page; kind = `File; anchor = "" }
let polymorphic_variant ~type_ident elt =
let name_of_type_constr te =
@@ -403,7 +412,7 @@ module Anchor = struct
(** The anchor looks like
[extension-decl-"Path.target_type"-FirstConstructor]. *)
let extension_decl (decl : Odoc_model.Lang.Extension.t) =
- let page = Path.from_identifier (decl.parent :> Path.source) in
+ let page = Path.from_identifier (decl.parent :> Path.any) in
let kind = `ExtensionDecl in
let first_cons = Identifier.name (List.hd decl.constructors).id in
let anchor = Format.asprintf "%a-%s" pp_kind kind first_cons in
@@ -420,7 +429,7 @@ let from_path page =
{ Anchor.page; anchor = ""; kind = (page.kind :> Anchor.kind) }
let from_identifier ~stop_before = function
- | { Odoc_model.Paths.Identifier.iv = #Path.source_pv; _ } as p
+ | { Odoc_model.Paths.Identifier.iv = #Path.any_pv; _ } as p
when not stop_before ->
Ok (from_path @@ Path.from_identifier p)
| p -> Anchor.from_identifier p
diff --git a/src/document/url.mli b/src/document/url.mli
index 10bc88fa32..539da979d6 100644
--- a/src/document/url.mli
+++ b/src/document/url.mli
@@ -29,17 +29,17 @@ module Path : sig
type t = { kind : kind; parent : t option; name : string }
- type nonsrc_pv =
+ type any_pv =
[ Identifier.Page.t_pv
| Identifier.Signature.t_pv
- | Identifier.ClassSignature.t_pv ]
+ | Identifier.ClassSignature.t_pv
+ | Identifier.SourcePage.t_pv
+ | Identifier.SourceDir.t_pv
+ | Identifier.AssetFile.t_pv ]
- type source_pv =
- [ nonsrc_pv | Identifier.SourcePage.t_pv | Identifier.SourceDir.t_pv ]
+ and any = any_pv Odoc_model.Paths.Identifier.id
- and source = source_pv Odoc_model.Paths.Identifier.id
-
- val from_identifier : [< source_pv ] Odoc_model.Paths.Identifier.id -> t
+ val from_identifier : [< any_pv ] Odoc_model.Paths.Identifier.id -> t
val to_list : t -> (kind * string) list
diff --git a/src/html/generator.ml b/src/html/generator.ml
index 215bb22cd2..aca2d5d401 100644
--- a/src/html/generator.ml
+++ b/src/html/generator.ml
@@ -503,11 +503,32 @@ module Page = struct
if Config.as_json config then
Html_fragment_json.make_src ~config ~url ~breadcrumbs [ doc ]
else Html_page.make_src ~breadcrumbs ~header ~config ~url title [ doc ]
+
+ let asset ~config { Asset.url; src } =
+ let filename = Link.Path.as_filename ~is_flat:(Config.flat config) url in
+ let content ppf =
+ let ic = open_in_bin (Fpath.to_string src) in
+ let len = 1024 in
+ let buf = Bytes.create len in
+ let rec loop () =
+ let read = input ic buf 0 len in
+ if read = len then (
+ Format.fprintf ppf "%s" (Bytes.to_string buf);
+ loop ())
+ else if len > 0 then
+ let buf = Bytes.sub buf 0 read in
+ Format.fprintf ppf "%s" (Bytes.to_string buf)
+ in
+ loop ();
+ close_in ic
+ in
+ { Odoc_document.Renderer.filename; content; children = [] }
end
let render ~config = function
| Document.Page page -> [ Page.page ~config page ]
| Source_page src -> [ Page.source_page ~config src ]
+ | Asset asset -> [ Page.asset ~config asset ]
let doc ~config ~xref_base_uri b =
let resolve = Link.Base xref_base_uri in
diff --git a/src/latex/generator.ml b/src/latex/generator.ml
index 83c00a9587..db5d7a3030 100644
--- a/src/latex/generator.ml
+++ b/src/latex/generator.ml
@@ -489,4 +489,4 @@ end
let render ~with_children = function
| Document.Page page -> [ Page.page ~with_children page ]
- | Source_page _ -> []
+ | Source_page _ | Asset _ -> []
diff --git a/src/manpage/generator.ml b/src/manpage/generator.ml
index c23979863a..ccb98f618e 100644
--- a/src/manpage/generator.ml
+++ b/src/manpage/generator.ml
@@ -562,4 +562,4 @@ and render_page (p : Page.t) =
let render = function
| Document.Page page -> [ render_page page ]
- | Source_page _ -> []
+ | Source_page _ | Asset _ -> []
diff --git a/src/model/lang.ml b/src/model/lang.ml
index 7b6b1e1345..962de3df42 100644
--- a/src/model/lang.ml
+++ b/src/model/lang.ml
@@ -497,6 +497,7 @@ module rec Page : sig
| Page_child of string
| Module_child of string
| Source_tree_child of string
+ | Asset_child of string
type t = {
name : Identifier.Page.t;
diff --git a/src/model/paths.ml b/src/model/paths.ml
index 555fa3454a..2c55144609 100644
--- a/src/model/paths.ml
+++ b/src/model/paths.ml
@@ -55,6 +55,7 @@ module Identifier = struct
| `SourceDir (_, name) -> name
| `SourceLocation (_, anchor) -> DefName.to_string anchor
| `SourceLocationMod x -> name_aux (x :> t)
+ | `AssetFile (_, name) -> name
let name : [< t_pv ] id -> string = fun n -> name_aux (n :> t)
@@ -282,6 +283,11 @@ module Identifier = struct
type t_pv = Paths_types.Identifier.source_location_pv
end
+ module AssetFile = struct
+ type t = Id.asset_file
+ type t_pv = Id.asset_file_pv
+ end
+
module OdocId = struct
type t = Id.odoc_id
type t_pv = Id.odoc_id_pv
@@ -372,6 +378,9 @@ module Identifier = struct
[> `LeafPage of ContainerPage.t option * PageName.t ] id =
mk_parent_opt PageName.to_string "lp" (fun (p, n) -> `LeafPage (p, n))
+ let asset_file : Page.t * string -> AssetFile.t =
+ mk_parent (fun k -> k) "asset" (fun (p, n) -> `AssetFile (p, n))
+
let source_page (container_page, path) =
let rec source_dir dir =
match dir with
diff --git a/src/model/paths.mli b/src/model/paths.mli
index d301110433..92368846f9 100644
--- a/src/model/paths.mli
+++ b/src/model/paths.mli
@@ -143,6 +143,11 @@ module Identifier : sig
type t_pv = Id.source_location_pv
end
+ module AssetFile : sig
+ type t = Id.asset_file
+ type t_pv = Id.asset_file_pv
+ end
+
module OdocId : sig
type t = Id.odoc_id
type t_pv = Id.odoc_id_pv
@@ -219,6 +224,8 @@ module Identifier : sig
val source_page : ContainerPage.t * string list -> SourcePage.t
+ val asset_file : Page.t * string -> AssetFile.t
+
val root :
ContainerPage.t option * ModuleName.t ->
[> `Root of ContainerPage.t option * ModuleName.t ] id
diff --git a/src/model/paths_types.ml b/src/model/paths_types.ml
index e1579c8956..026605b6d8 100644
--- a/src/model/paths_types.ml
+++ b/src/model/paths_types.ml
@@ -32,6 +32,14 @@ module Identifier = struct
type source_page = source_page_pv id
(** @canonical Odoc_model.Paths.Identifier.SourcePage.t *)
+ type asset_file_pv = [ `AssetFile of page * string ]
+ (** The second argument is the filename.
+
+ @canonical Odoc_model.Paths.Identifier.AssetFile.t_pv *)
+
+ type asset_file = asset_file_pv id
+ (** @canonical Odoc_model.Paths.Identifier.AssetFile.t *)
+
type source_location_pv =
[ `SourceLocationMod of source_page
| `SourceLocation of source_page * DefName.t ]
@@ -214,7 +222,11 @@ module Identifier = struct
(** @canonical Odoc_model.Paths.Identifier.NonSrc.t *)
type any_pv =
- [ non_src_pv | source_page_pv | source_dir_pv | source_location_pv ]
+ [ non_src_pv
+ | source_page_pv
+ | source_dir_pv
+ | source_location_pv
+ | asset_file_pv ]
(** @canonical Odoc_model.Paths.Identifier.t_pv *)
and any = any_pv id
diff --git a/src/model_desc/paths_desc.ml b/src/model_desc/paths_desc.ml
index fc8cd70c9c..0719ca910a 100644
--- a/src/model_desc/paths_desc.ml
+++ b/src/model_desc/paths_desc.ml
@@ -74,6 +74,8 @@ module General_paths = struct
( "`LeafPage",
((parent :> id_t option), name),
Pair (Option identifier, Names.pagename) )
+ | `AssetFile (parent, name) ->
+ C ("`AssetFile", ((parent :> id_t), name), Pair (identifier, string))
| `Root (parent, name) ->
C
( "`Root",
diff --git a/src/odoc/bin/main.ml b/src/odoc/bin/main.ml
index 657e607006..0631d87ae4 100644
--- a/src/odoc/bin/main.ml
+++ b/src/odoc/bin/main.ml
@@ -706,19 +706,27 @@ module Odoc_html_args = struct
& opt (some convert_fpath) None
& info [ "source" ] ~doc ~docv:"file.ml")
+ let assets =
+ let doc =
+ "Assets files. These must match the assets listed as children during the \
+ compile phase."
+ in
+ Arg.(
+ value & opt_all convert_fpath [] & info [ "asset" ] ~doc ~docv:"file.ext")
+
let extra_args =
let config semantic_uris closed_details indent theme_uri support_uri flat
- as_json source_file =
+ as_json source_file assets =
let open_details = not closed_details in
let html_config =
Odoc_html.Config.v ~theme_uri ~support_uri ~semantic_uris ~indent ~flat
~open_details ~as_json ()
in
- { Html_page.html_config; source_file }
+ { Html_page.html_config; source_file; assets }
in
Term.(
const config $ semantic_uris $ closed_details $ indent $ theme_uri
- $ support_uri $ flat $ as_json $ source_file)
+ $ support_uri $ flat $ as_json $ source_file $ assets)
end
module Odoc_html = Make_renderer (Odoc_html_args)
diff --git a/src/odoc/compile.ml b/src/odoc/compile.ml
index 198382101d..faed378614 100644
--- a/src/odoc/compile.ml
+++ b/src/odoc/compile.ml
@@ -50,9 +50,11 @@ let is_module_name n = String.length n > 0 && Char.Ascii.is_upper n.[0]
(** Accepted child references:
- - [page-foo] child is a container or leaf page.
+ - [asset-foo] child is an arbitrary asset
- [module-Foo] child is a module.
- [module-foo], [Foo] child is a module, for backward compatibility.
+ - [page-foo] child is a container or leaf page.
+ - [src-foo] child is a source tree
Parses [...-"foo"] as [...-foo] for backward compatibility. *)
let parse_parent_child_reference s =
@@ -65,6 +67,7 @@ let parse_parent_child_reference s =
match String.cut ~sep:"-" s with
| Some ("page", n) -> Ok (Lang.Page.Page_child (unquote n))
| Some ("src", n) -> Ok (Source_tree_child (unquote n))
+ | Some ("asset", n) -> Ok (Asset_child (unquote n))
| Some ("module", n) ->
Ok (Module_child (unquote (String.Ascii.capitalize n)))
| Some (k, _) -> Error (`Msg ("Unrecognized kind: " ^ k))
@@ -76,7 +79,7 @@ let resolve_parent_page resolver f =
match Resolver.lookup_page resolver p with
| Some r -> Ok r
| None -> Error (`Msg "Couldn't find specified parent page"))
- | Source_tree_child _ | Module_child _ ->
+ | Source_tree_child _ | Module_child _ | Asset_child _ ->
Error (`Msg "Expecting page as parent")
in
let extract_parent = function
@@ -190,7 +193,7 @@ let root_of_compilation_unit ~parent_spec ~hidden ~output ~module_name ~digest =
let check_child = function
| Lang.Page.Module_child n ->
String.Ascii.(uncapitalize n = uncapitalize filename)
- | Source_tree_child _ | Page_child _ -> false
+ | Asset_child _ | Source_tree_child _ | Page_child _ -> false
in
match parent_spec with
| Noparent -> result None
@@ -237,7 +240,7 @@ let mld ~parent_spec ~output ~children ~warnings_options input =
let page_name = PageName.make_std root_name in
let check_child = function
| Lang.Page.Page_child n -> root_name = n
- | Source_tree_child _ | Module_child _ -> false
+ | Asset_child _ | Source_tree_child _ | Module_child _ -> false
in
(if children = [] then
(* No children, this is a leaf page. *)
diff --git a/src/odoc/html_page.ml b/src/odoc/html_page.ml
index 25a95c08da..29a58b822a 100644
--- a/src/odoc/html_page.ml
+++ b/src/odoc/html_page.ml
@@ -16,13 +16,17 @@
open Odoc_model
-type args = { html_config : Odoc_html.Config.t; source_file : Fpath.t option }
+type args = {
+ html_config : Odoc_html.Config.t;
+ source_file : Fpath.t option;
+ assets : Fpath.t list;
+}
-let render { html_config; source_file = _ } page =
+let render { html_config; source_file = _; assets = _ } page =
Odoc_html.Generator.render ~config:html_config page
-let extra_documents args unit ~syntax =
- match (unit.Lang.Compilation_unit.source_info, args.source_file) with
+let source_documents source_info source_file ~syntax =
+ match (source_info, source_file) with
| Some { Lang.Source_info.id; infos }, Some src -> (
match Fs.File.read src with
| Error (`Msg msg) ->
@@ -54,4 +58,59 @@ let extra_documents args unit ~syntax =
[]
| None, None -> []
+let list_filter_map f lst =
+ List.rev
+ @@ List.fold_left
+ (fun acc x -> match f x with None -> acc | Some x -> x :: acc)
+ [] lst
+
+let asset_documents parent_id children asset_paths =
+ let asset_names =
+ list_filter_map
+ (function Lang.Page.Asset_child name -> Some name | _ -> None)
+ children
+ in
+ let rec extract paths name =
+ match paths with
+ | [] -> (paths, (name, None))
+ | x :: xs when Fpath.basename x = name -> (xs, (name, Some x))
+ | x :: xs ->
+ let rest, elt = extract xs name in
+ (x :: rest, elt)
+ in
+ let unmatched, paired_or_missing =
+ let rec foldmap paths paired = function
+ | [] -> (paths, paired)
+ | name :: names ->
+ let paths, pair = extract paths name in
+ foldmap paths (pair :: paired) names
+ in
+ foldmap asset_paths [] asset_names
+ in
+ List.iter
+ (fun asset ->
+ Error.raise_warning
+ (Error.filename_only "this asset was not declared as a child of %s"
+ (Paths.Identifier.name parent_id)
+ (Fs.File.to_string asset)))
+ unmatched;
+ list_filter_map
+ (fun (name, path) ->
+ match path with
+ | None ->
+ Error.raise_warning (Error.filename_only "asset is missing." name);
+ None
+ | Some path ->
+ let asset_id = Paths.Identifier.Mk.asset_file (parent_id, name) in
+ let url = Odoc_document.Url.Path.from_identifier asset_id in
+ Some (Odoc_document.Types.Document.Asset { url; src = path }))
+ paired_or_missing
+
+let extra_documents args input ~syntax =
+ match input with
+ | Odoc_document.Renderer.CU unit ->
+ source_documents unit.Lang.Compilation_unit.source_info args.source_file
+ ~syntax
+ | Page page -> asset_documents page.Lang.Page.name page.children args.assets
+
let renderer = { Odoc_document.Renderer.name = "html"; render; extra_documents }
diff --git a/src/odoc/html_page.mli b/src/odoc/html_page.mli
index 77a399d7a8..c2747cfb61 100644
--- a/src/odoc/html_page.mli
+++ b/src/odoc/html_page.mli
@@ -16,6 +16,10 @@
open Odoc_document
-type args = { html_config : Odoc_html.Config.t; source_file : Fpath.t option }
+type args = {
+ html_config : Odoc_html.Config.t;
+ source_file : Fpath.t option;
+ assets : Fpath.t list;
+}
val renderer : args Renderer.t
diff --git a/src/odoc/rendering.ml b/src/odoc/rendering.ml
index 9ae308620d..6325a63f2e 100644
--- a/src/odoc/rendering.ml
+++ b/src/odoc/rendering.ml
@@ -3,20 +3,24 @@ open Or_error
let documents_of_unit ~warnings_options ~syntax ~renderer ~extra unit =
Odoc_model.Error.catch_warnings (fun () ->
- renderer.Renderer.extra_documents ~syntax extra unit)
+ renderer.Renderer.extra_documents ~syntax extra (CU unit))
|> Odoc_model.Error.handle_warnings ~warnings_options
>>= fun extra_docs ->
- let main_doc =
- if unit.hidden then []
- else [ Renderer.document_of_compilation_unit ~syntax unit ]
- in
- Ok (main_doc @ extra_docs)
+ Ok
+ (if unit.hidden then extra_docs
+ else Renderer.document_of_compilation_unit ~syntax unit :: extra_docs)
+
+let documents_of_page ~warnings_options ~syntax ~renderer ~extra page =
+ Odoc_model.Error.catch_warnings (fun () ->
+ renderer.Renderer.extra_documents ~syntax extra (Page page))
+ |> Odoc_model.Error.handle_warnings ~warnings_options
+ >>= fun extra_docs -> Ok (Renderer.document_of_page ~syntax page :: extra_docs)
let documents_of_odocl ~warnings_options ~renderer ~extra ~syntax input =
Odoc_file.load input >>= fun unit ->
match unit.content with
| Odoc_file.Page_content odoctree ->
- Ok [ Renderer.document_of_page ~syntax odoctree ]
+ documents_of_page ~warnings_options ~syntax ~renderer ~extra odoctree
| Source_tree_content srctree ->
Ok (Renderer.documents_of_source_tree ~syntax srctree)
| Unit_content (odoctree, _) ->
diff --git a/src/odoc/source_tree.ml b/src/odoc/source_tree.ml
index dbf736ee52..7682cea9b6 100644
--- a/src/odoc/source_tree.ml
+++ b/src/odoc/source_tree.ml
@@ -7,7 +7,7 @@ module Id = Paths.Identifier
let check_is_child_of_parent siblings root_name =
let check_child = function
| Lang.Page.Source_tree_child n -> root_name = n
- | Page_child _ | Module_child _ -> false
+ | Page_child _ | Asset_child _ | Module_child _ -> false
in
if List.exists check_child siblings then Ok ()
else Error (`Msg "Specified parent is not a parent of this file")
diff --git a/src/xref2/component.ml b/src/xref2/component.ml
index 5034b92835..0cb73ac0f4 100644
--- a/src/xref2/component.ml
+++ b/src/xref2/component.ml
@@ -1258,6 +1258,10 @@ module Fmt = struct
| `SourceLocationMod p ->
Format.fprintf ppf "%a#" model_identifier
(p :> Odoc_model.Paths.Identifier.t)
+ | `AssetFile (p, name) ->
+ Format.fprintf ppf "%a/%s" model_identifier
+ (p :> Odoc_model.Paths.Identifier.t)
+ name
and model_fragment ppf (f : Odoc_model.Paths.Fragment.t) =
match f with
diff --git a/src/xref2/link.ml b/src/xref2/link.ml
index dffdd2fc26..1a229af651 100644
--- a/src/xref2/link.ml
+++ b/src/xref2/link.ml
@@ -1046,7 +1046,7 @@ let page env page =
| None -> Errors.report ~what `Lookup
in
match child with
- | Page.Source_tree_child _ -> ()
+ | Page.Asset_child _ | Page.Source_tree_child _ -> ()
| Page.Page_child page ->
check_resolves ~what:(`Child_page page) Env.lookup_page page
| Page.Module_child mod_ ->
diff --git a/test/pages/assets.t/index.mld b/test/pages/assets.t/index.mld
new file mode 100644
index 0000000000..4e01eac22f
--- /dev/null
+++ b/test/pages/assets.t/index.mld
@@ -0,0 +1,4 @@
+{0 Package page}
+
+Some image:
+{%html:
%}
diff --git a/test/pages/assets.t/run.t b/test/pages/assets.t/run.t
new file mode 100644
index 0000000000..89439be4cb
--- /dev/null
+++ b/test/pages/assets.t/run.t
@@ -0,0 +1,92 @@
+Blablabla
+
+ $ cat index.mld
+ {0 Package page}
+
+ Some image:
+ {%html:
%}
+
+And we'll have a module that we'll put underneath this package page.
+
+ $ cat test.mli
+ (** Humpf, let's try accessing the asset:
+ {%html:
%}
+ *)
+
+ (** Nevermind *)
+ type t
+
+
+Compile the module first
+
+ $ ocamlc -c -bin-annot test.mli
+
+Then we need to odoc-compile the package mld file, listing its children
+
+ $ odoc compile index.mld --child module-test --child asset-img.jpg
+
+This will have produced a file called 'page-index.odoc'.
+Now we can odoc-compile the module odoc file passing that file as parent.
+
+ $ odoc compile test.cmti -I . --parent index
+
+Link and generate the HTML (forgetting the asset!):
+
+ $ for i in *.odoc; do odoc link -I . $i; done
+ $ for i in *.odocl; do odoc html-generate $i -o html; done
+ File "img.jpg":
+ Warning: asset is missing.
+
+Note that the html was generated despite the missing asset (there might be dead refs!)
+
+ $ find html -type f | sort
+ html/index/Test/index.html
+ html/index/index.html
+
+Which matches the output of the targets command (which emits no warning):
+
+ $ odoc html-targets page-index.odocl -o html
+ html/index/index.html
+
+Trying to pass an asset which doesn't exist:
+(also: some sed magic due to cmdliner output changing based on the version)
+
+ $ odoc html-generate page-index.odocl --asset img.jpg -o html 2>&1 | \
+ > sed 's/…/.../' | sed "s/\`/'/g"
+ odoc: option '--asset': no 'img.jpg' file or directory
+ Usage: odoc html-generate [OPTION]... FILE.odocl
+ Try 'odoc html-generate --help' or 'odoc --help' for more information.
+
+Creating then passing the asset alongside an incorrect one:
+
+ $ touch img.jpg
+ $ odoc html-generate page-index.odocl --asset img.jpg --asset test.mli -o html
+ File "test.mli":
+ Warning: this asset was not declared as a child of index
+
+This time, the asset should have been copied at the right place:
+
+ $ find html -type f | sort
+ html/index/Test/index.html
+ html/index/img.jpg
+ html/index/index.html
+
+Which once again matches the output of the targets command (still no warning!):
+
+ $ odoc html-targets page-index.odocl --asset img.jpg --asset test.mli -o html
+ html/index/index.html
+ html/index/img.jpg
+
+Let's make sure the manpage and latex renderers "work" too
+
+ $ for i in *.odocl; do odoc man-generate $i -o man; odoc latex-generate $i -o latex; done
+
+ $ find man -type f | sort
+ man/index.3o
+ man/index/Test.3o
+
+ $ find latex -type f | sort
+ latex/index.tex
+ latex/index/Test.tex
+
+Notice that the assets are *not* there. This should probably be fixed for the latex backend.
diff --git a/test/pages/assets.t/test.mli b/test/pages/assets.t/test.mli
new file mode 100644
index 0000000000..d329f104f8
--- /dev/null
+++ b/test/pages/assets.t/test.mli
@@ -0,0 +1,7 @@
+(** Humpf, let's try accessing the asset:
+ {%html:
%}
+ *)
+
+(** Nevermind *)
+type t
+