123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159(** Odoc Extension Registry
This module provides a minimal registry for odoc tag extensions.
It is kept separate to avoid circular dependencies between
odoc_document and odoc_extension_api.
*)moduleComment=Odoc_model.CommentmoduleLocation_=Odoc_model.Location_(** Resources that can be injected into the page (HTML only) *)typeresource=|Js_urlofstring|Css_urlofstring|Js_inlineofstring|Css_inlineofstring(** Support files that extensions want to output *)typesupport_file={filename:string;(** Relative path, e.g., "extensions/admonition.css" *)content:string;(** File content *)}(** Binary asset generated by an extension (e.g., rendered PNG) *)typeasset={asset_filename:string;(** Filename for the asset, e.g., "diagram-1.png" *)asset_content:bytes;(** Binary content *)}(** Documentation for an extension option *)typeoption_doc={opt_name:string;(** Option name, e.g., "width" *)opt_description:string;(** What the option does *)opt_default:stringoption;(** Default value if any *)}(** Documentation/metadata for an extension *)typeextension_info={info_kind:[`Tag|`Code_block];(** Type of extension *)info_prefix:string;(** The prefix this extension handles *)info_description:string;(** Short description of what it does *)info_options:option_doclist;(** Supported options *)info_example:stringoption;(** Example usage *)}(** Result of processing a custom tag.
We use a record with a polymorphic content type that gets
instantiated with the actual Block.t by odoc_document. *)type'blockextension_result={content:'block;overrides:(string*string)list;resources:resourcelist;assets:assetlist;(** Binary assets to write alongside the HTML output.
Use [__ODOC_ASSET__filename__] placeholder in content to reference. *)}(** Type of handler functions stored in the registry.
The handler takes a tag name and content, returns an optional result.
If None, the tag is handled by the default mechanism. *)type'blockhandler=string->(* tag name *)Comment.nestable_block_elementLocation_.with_locationlist->(* content *)'blockextension_resultoption(** The registry stores handlers indexed by prefix *)lethandlers:(string,Obj.t)Hashtbl.t=Hashtbl.create16(** Registered prefixes for listing *)letprefixes:(string,unit)Hashtbl.t=Hashtbl.create16(** Support files registered by extensions *)letsupport_files:(string,support_file)Hashtbl.t=Hashtbl.create16letregister_handler~prefix(handler:'blockhandler)=Hashtbl.replacehandlersprefix(Obj.reprhandler);Hashtbl.replaceprefixesprefix()letregister_support_file~prefix(file:support_file)=letkey=prefix^":"^file.filenameinHashtbl.replacesupport_fileskeyfileletfind_handler(typeblock)~prefix:blockhandleroption=matchHashtbl.find_opthandlersprefixwith|None->None|Someh->Some(Obj.objh)letlist_prefixes()=Hashtbl.fold(funprefix()acc->prefix::acc)prefixes[]|>List.sortString.compareletlist_support_files()=Hashtbl.fold(fun_fileacc->file::acc)support_files[](** Extract the prefix from a tag name (part before the first dot) *)letprefix_of_tagtag=matchString.index_opttag'.'with|None->tag|Somei->String.subtag0i(** {1 Code Block Handlers}
Similar to custom tag handlers, but for code blocks like [{@dot[...]}].
Handlers can transform code blocks based on language and metadata.
*)(** Metadata for code blocks, extracted from parser AST *)typecode_block_meta={language:string;tags:Odoc_parser.Ast.code_block_taglist;}(** Type of code block handler functions.
Takes metadata and code content, returns optional transformed result. *)type'blockcode_block_handler=code_block_meta->(* language + metadata tags *)string->(* code content *)'blockextension_resultoption(** Registry for code block handlers, indexed by language prefix *)letcode_block_handlers:(string,Obj.t)Hashtbl.t=Hashtbl.create16(** Registered code block prefixes *)letcode_block_prefixes:(string,unit)Hashtbl.t=Hashtbl.create16letregister_code_block_handler~prefix(handler:'blockcode_block_handler)=Hashtbl.replacecode_block_handlersprefix(Obj.reprhandler);Hashtbl.replacecode_block_prefixesprefix()letfind_code_block_handler(typeblock)~prefix:blockcode_block_handleroption=matchHashtbl.find_optcode_block_handlersprefixwith|None->None|Someh->Some(Obj.objh)letlist_code_block_prefixes()=Hashtbl.fold(funprefix()acc->prefix::acc)code_block_prefixes[]|>List.sortString.compare(** Extract the prefix from a language tag (part before the first dot) *)letprefix_of_language=prefix_of_tag(** {1 Extension Documentation}
Extensions can register documentation that describes their options
and usage. This is displayed by [odoc extensions]. *)(** Registry for extension documentation *)letextension_infos:(string,extension_info)Hashtbl.t=Hashtbl.create16letregister_extension_info(info:extension_info)=letkey=matchinfo.info_kindwith|`Tag->"tag:"^info.info_prefix|`Code_block->"code:"^info.info_prefixinHashtbl.replaceextension_infoskeyinfoletlist_extension_infos()=Hashtbl.fold(fun_infoacc->info::acc)extension_infos[]|>List.sort(funab->String.comparea.info_prefixb.info_prefix)