123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558(*
* Copyright (c) 2016 Thomas Refis <trefis@janestreet.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)openOdoc_document.TypesmoduleHtml=Tyxml.HtmlmoduleDoctree=Odoc_document.DoctreemoduleUrl=Odoc_document.Urltypeany=Html_types.flow5typeitem=Html_types.flow5_without_header_footertypeflow=Html_types.flow5_without_sectioning_heading_header_footertypephrasing=Html_types.phrasingtypenon_link_phrasing=Html_types.phrasing_without_interactiveletmk_anchor_linkid=[Html.a~a:[Html.a_href("#"^id);Html.a_class["anchor"]][]]letmk_anchorconfiganchor=matchanchorwith|None->([],[],[])|_whenConfig.search_resultconfig->(* When displaying for a search result, anchor are not added as it would
make no sense to add them. *)([],[],[])|Some{Url.Anchor.anchor;_}->letlink=mk_anchor_linkanchorinletextra_attr=[Html.a_idanchor]inletextra_class=["anchored"]in(extra_attr,extra_class,link)letmk_link_to_source~config~resolveanchor=matchanchorwith|None->[]|Someurl->lethref=Link.href~config~resolveurlin[Html.a~a:[Html.a_hrefhref;Html.a_class["source_link"]][Html.txt"Source"];]letclass_(l:Class.t)=ifl=[]then[]else[Html.a_classl]letinline_math(s:Math.t)=Html.code~a:[Html.a_class["odoc-katex-math"]][Html.txts]letblock_math(s:Math.t)=Html.pre~a:[Html.a_class["odoc-katex-math";"display"]][Html.txts]andraw_markup(t:Raw_markup.t)=lettarget,content=tinmatchAstring.String.Ascii.lowercasetargetwith|"html"->(* This is OK because we output *textual* HTML.
In theory, we should try to parse the HTML with lambdasoup and rebuild
the HTML tree from there.
*)[Html.Unsafe.datacontent]|_->[]andsourcek?a(t:Source.t)=letrectoken(x:Source.token)=matchxwith|Elti->ki|Tag(None,l)->letcontent=tokenslinifcontent=[]then[]else[Html.spancontent]|Tag(Somes,l)->[Html.span~a:[Html.a_class[s]](tokensl)]andtokenst=Utils.list_concat_mapt~f:tokeninUtils.optional_eltHtml.code?a(tokenst)andstyledstyle~emph_level=matchstylewith|`Emphasis->leta=ifemph_levelmod2=0then[]else[Html.a_class["odd"]]in(emph_level+1,Html.em~a)|`Bold->(emph_level,Html.b~a:[])|`Italic->(emph_level,Html.i~a:[])|`Superscript->(emph_level,Html.sup~a:[])|`Subscript->(emph_level,Html.sub~a:[])letrecinternallink~config~emph_level~resolve?(a=[]){InternalLink.target;content;tooltip}=leta=matchtooltipwithSomes->Html.a_titles::a|None->ainletelt=matchtargetwith|Resolveduri->lethref=Link.href~config~resolveuriinletcontent=inline_nolink~emph_levelcontentinifConfig.search_resultconfigthen(* When displaying for a search result, links are displayed as regular
text. *)Html.span~acontentelseleta=Html.a_hrefhref::(a:>Html_types.a_attribHtml.attriblist)inHtml.a~acontent|Unresolved->(* let title =
* Html.a_title (Printf.sprintf "unresolved reference to %S"
* (ref_to_string ref)
* in *)leta=Html.a_class["xref-unresolved"]::ainHtml.span~a(inline~config~emph_level~resolvecontent)in[(elt:>phrasingHtml.elt)]andinline~config?(emph_level=0)~resolve(l:Inline.t):phrasingHtml.eltlist=letone(t:Inline.one)=leta=class_t.attrinmatcht.descwith|Text""->[]|Texts->ifa=[]then[Html.txts]else[Html.span~a[Html.txts]]|Entitys->ifa=[]then[Html.entitys]else[Html.span~a[Html.entitys]]|Linebreak->[Html.br~a()]|Styled(style,c)->letemph_level,app_style=styledstyle~emph_levelin[app_style@@inline~config~emph_level~resolvec]|Link(_,c)whenConfig.search_resultconfig->(* When displaying for a search result, links are displayed as regular
text. *)letcontent=inline_nolink~emph_levelcin[Html.span~acontent]|Link(href,c)->leta=(a:>Html_types.a_attribHtml.attriblist)inletcontent=inline_nolink~emph_levelcin[Html.a~a:(Html.a_hrefhref::a)content]|InternalLinkc->internallink~config~emph_level~resolve~ac|Sourcec->source(inline~config~emph_level~resolve)~ac|Maths->[inline_maths]|Raw_markupr->raw_markuprinUtils.list_concat_map~f:onelandinline_nolink?(emph_level=0)(l:Inline.t):non_link_phrasingHtml.eltlist=letone(t:Inline.one)=leta=class_t.attrinmatcht.descwith|Text""->[]|Texts->ifa=[]then[Html.txts]else[Html.span~a[Html.txts]]|Entitys->ifa=[]then[Html.entitys]else[Html.span~a[Html.entitys]]|Linebreak->[Html.br~a()]|Styled(style,c)->letemph_level,app_style=styledstyle~emph_levelin[app_style@@inline_nolink~emph_levelc]|Link_->assertfalse|InternalLink_->assertfalse|Sourcec->source(inline_nolink~emph_level)~ac|Maths->[inline_maths]|Raw_markupr->raw_markuprinUtils.list_concat_map~f:onelletheading~config~resolve(h:Heading.t)=leta,anchor=matchh.labelwith|Some_whenConfig.search_resultconfig->(* When displaying for a search result, anchor are not added as it would
make no sense to add them. *)([],[])|Someid->([Html.a_idid],mk_anchor_linkid)|None->([],[])inletcontent=inline~config~resolveh.titleinletsource_link=mk_link_to_source~config~resolveh.source_anchorinletmk=matchh.levelwith|0->Html.h1|1->Html.h2|2->Html.h3|3->Html.h4|4->Html.h5|_->Html.h6inmk~a(anchor@content@source_link)lettext_align=function|Table.Left->[Html.a_style"text-align:left"]|Center->[Html.a_style"text-align:center"]|Right->[Html.a_style"text-align:right"]|Default->[]letcell_kind=function`Header->Html.th|`Data->Html.tdletrecblock~config~resolve(l:Block.t):flowHtml.eltlist=letas_flowx=(x:phrasingHtml.eltlist:>flowHtml.eltlist)inletone(t:Block.one)=letmk_block?(extra_class=[])mkcontent=leta=Some(class_(extra_class@t.attr))in[mk?acontent]inmatcht.descwith|Inlinei->ift.attr=[]thenas_flow@@inline~config~resolveielsemk_blockHtml.span(inline~config~resolvei)|Paragraphi->mk_blockHtml.p(inline~config~resolvei)|List(typ,l)->letmk=matchtypwithOrdered->Html.ol|Unordered->Html.ulinmk_blockmk(List.map(funx->Html.li(block~config~resolvex))l)|Tablet->mk_block~extra_class:["odoc-table"](fun?ax->Html.table?ax)(mk_rows~config~resolvet)|Descriptionl->letitemi=leta=class_i.Description.attrinletterm=(inline~config~resolvei.Description.key:phrasingHtml.eltlist:>flowHtml.eltlist)inletdef=block~config~resolvei.Description.definitioninHtml.li~a(term@(Html.txt" "::def))inmk_blockHtml.ul(List.mapiteml)|Raw_markupr->raw_markupr|Verbatims->mk_blockHtml.pre[Html.txts]|Source(lang_tag,c)->letextra_class=["language-"^lang_tag]inmk_block~extra_classHtml.pre(source(inline~config~resolve)c)|Maths->mk_blockHtml.div[block_maths]inUtils.list_concat_mapl~f:oneandmk_rows~config~resolve{align;data}=letmk_rowrow=letmk_cell~align(x,h)=leta=text_alignalignincell_kind~ah(block~config~resolvex)inletalignmentalign=matchalignwithalign::q->(align,q)|[]->(Table.Default,[])(* Second case is for recovering from a too short alignment list. A
warning should have been raised when loading the doc-comment. *)inletacc,_align=List.fold_left(fun(acc,aligns)(x,h)->letalign,aligns=alignmentalignsinletcell=mk_cell~align(x,h)in(cell::acc,aligns))([],align)rowinHtml.tr(List.revacc)inList.mapmk_rowdata(* This coercion is actually sound, but is not currently accepted by Tyxml.
See https://github.com/ocsigen/tyxml/pull/265 for details
Can be replaced by a simple type coercion once this is fixed
*)letflow_to_item:flowHtml.eltlist->itemHtml.eltlist=funx->Html.totl@@Html.toeltlxletdiv:([<Html_types.div_attrib],[<item],[>Html_types.div])Html.star=Html.Unsafe.node"div"letspec_classattr=class_("spec"::attr)letspec_doc_div~config~resolve=function|[]->[]|docs->leta=[Html.a_class["spec-doc"]]in[div~a(flow_to_item@@block~config~resolvedocs)]letrecdocumentedSrc~config~resolve(t:DocumentedSrc.t):itemHtml.eltlist=letopenDocumentedSrcinlettake_codel=Doctree.Take.untill~classify:(function|Codecode->Accumcode|Alternative(Expansion{summary;_})->Accumsummary|_->Stop_and_keep)inlettake_descrl=Doctree.Take.untill~classify:(function|Documented{attrs;anchor;code;doc;markers}->Accum[{DocumentedSrc.attrs;anchor;code=`Dcode;doc;markers}]|Nested{attrs;anchor;code;doc;markers}->Accum[{DocumentedSrc.attrs;anchor;code=`Ncode;doc;markers}]|_->Stop_and_keep)inletrecto_htmlt:itemHtml.eltlist=matchtwith|[]->[]|(Code_|Alternative_)::_->letcode,_,rest=take_codetinsource(inline~config~resolve)code@to_htmlrest|Subpagesubp::_->subpage~config~resolvesubp|(Documented_|Nested_)::_->letl,_,rest=take_descrtinletone{DocumentedSrc.attrs;anchor;code;doc;markers}=letcontent=matchcodewith|`Dcode->(inline~config~resolvecode:>itemHtml.eltlist)|`Nn->to_htmlninletdoc=matchdocwith|[]->[]|doc->letopening,closing=markersinletdelims=[Html.span~a:(class_["comment-delim"])[Html.txts]]in[Html.div~a:(class_["def-doc"])(delimopening@block~config~resolvedoc@delimclosing);]inletextra_attr,extra_class,link=mk_anchorconfiganchorinletcontent=(content:>anyHtml.eltlist)inHtml.li~a:(extra_attr@class_(attrs@extra_class))(link@content@doc)inHtml.ol(List.maponel)::to_htmlrestinto_htmltandsubpage~config~resolve(subp:Subpage.t):itemHtml.eltlist=items~config~resolvesubp.content.itemsanditems~config~resolvel:itemHtml.eltlist=letrecwalk_itemsacc(t:Item.tlist):itemHtml.eltlist=letcontinue_withrestelts=(walk_items[@tailcall])(List.rev_appendeltsacc)restinmatchtwith|[]->List.revacc|Text_::_ast->lettext,_,rest=Doctree.Take.untilt~classify:(function|Item.Texttext->Accumtext|_->Stop_and_keep)inletcontent=flow_to_item@@block~config~resolvetextin(continue_with[@tailcall])restcontent|Headingh::rest->(continue_with[@tailcall])rest[heading~config~resolveh]|Include{attr;anchor;source_anchor;doc;content={summary;status;content};}::rest->letdoc=spec_doc_div~config~resolvedocinletincluded_html=(itemscontent:>itemHtml.eltlist)inleta_class=ifList.lengthcontent=0then["odoc-include";"shadowed-include"]else["odoc-include"]inletcontent:itemHtml.eltlist=letdetails~open'=letopen'=ifopen'then[Html.a_open()]else[]inletsummary=letextra_attr,extra_class,anchor_link=mk_anchorconfiganchorinletlink_to_source=mk_link_to_source~config~resolvesource_anchorinleta=spec_class(attr@extra_class)@extra_attrinHtml.summary~a@@anchor_link@link_to_source@source(inline~config~resolve)summaryinletinner=[Html.details~a:open'summary(included_html:>anyHtml.eltlist);]in[Html.div~a:[Html.a_classa_class](doc@inner)]inmatchstatuswith|`Inline->doc@included_html|`Closed->details~open':false|`Open->details~open':true|`Default->details~open':(Config.open_detailsconfig)in(continue_with[@tailcall])restcontent|Declaration{Item.attr;anchor;source_anchor;content;doc}::rest->letextra_attr,extra_class,anchor_link=mk_anchorconfiganchorinletlink_to_source=mk_link_to_source~config~resolvesource_anchorinleta=spec_class(attr@extra_class)@extra_attrinletcontent=anchor_link@link_to_source@documentedSrc~config~resolvecontentinletspec=letdoc=spec_doc_div~config~resolvedocin[div~a:[Html.a_class["odoc-spec"]](div~acontent::doc)]in(continue_with[@tailcall])restspecanditemsl=walk_items[]linitemslmoduleToc=structopenOdoc_document.DoctreeopenTypesleton_sub:Subpage.status->bool=function|`Closed|`Open|`Default->false|`Inline->trueletgen_toc~config~resolve~pathi=lettoc=Toc.computepath~on_subiinletrecsection{Toc.url;text;children}=lettext=inline_nolinktextinlettitle=(text:non_link_phrasingHtml.eltlist:>Html_types.flow5_without_interactiveHtml.eltlist)inlettitle_str=List.map(Format.asprintf"%a"(Tyxml.Html.pp_elt()))text|>String.concat""inlethref=Link.href~config~resolveurlin{title;title_str;href;children=List.mapsectionchildren}inList.mapsectiontocendmoduleBreadcrumbs=structopenTypesletgen_breadcrumbs~config~url=letrecget_parent_pathsx=matchxwith|[]->[]|x::xs->(matchOdoc_document.Url.Path.of_list(List.rev(x::xs))with|Somex->x::get_parent_pathsxs|None->get_parent_pathsxs)inletto_breadcrumbpath=lethref=Link.href~config~resolve:(Currenturl)(Odoc_document.Url.from_pathpath)in{href;name=path.name;kind=path.kind}inget_parent_paths(List.rev(Odoc_document.Url.Path.to_listurl))|>List.rev|>List.mapto_breadcrumbendmodulePage=structleton_sub=function|`Page_->None|`Includex->(matchx.Include.statuswith|`Closed|`Open|`Default->None|`Inline->Some0)letrecinclude_~config{Subpage.content;_}=page~configcontentandsubpages~configsubpages=List.map(include_~config)subpagesandpage~configp:Odoc_document.Renderer.page=let{Page.preamble;items=i;url;source_anchor}=Doctree.Labels.disambiguate_page~enter_subpages:falsepinletsubpages=subpages~config@@Doctree.Subpages.computepinletresolve=Link.Currenturlinleti=Doctree.Shift.compute~on_subiinletuses_katex=Doctree.Math.has_math_elementspinlettoc=Toc.gen_toc~config~resolve~path:urliinletbreadcrumbs=Breadcrumbs.gen_breadcrumbs~config~urlinletcontent=(items~config~resolvei:>anyHtml.eltlist)inifConfig.as_jsonconfigthenletsource_anchor=matchsource_anchorwith|Someurl->Some(Link.href~config~resolveurl)|None->NoneinHtml_fragment_json.make~config~preamble:(items~config~resolvepreamble:>anyHtml.eltlist)~breadcrumbs~toc~url~uses_katex~source_anchorcontentsubpageselseletheader=items~config~resolve(Doctree.PageTitle.render_title?source_anchorp@preamble)inHtml_page.make~config~header~toc~breadcrumbs~url~uses_katexcontentsubpagesandsource_page~configsp=let{Source_page.url;contents}=spinletresolve=Link.Currentsp.urlinlettitle=url.Url.Path.nameanddoc=Html_source.html_of_doc~config~resolvecontentsinletbreadcrumbs=Breadcrumbs.gen_breadcrumbs~config~urlinletheader=items~config~resolve(Doctree.PageTitle.render_src_titlesp)inifConfig.as_jsonconfigthenHtml_fragment_json.make_src~config~url~breadcrumbs[doc]elseHtml_page.make_src~breadcrumbs~header~config~urltitle[doc]letasset~config{Asset.url;src}=letfilename=Link.Path.as_filename~is_flat:(Config.flatconfig)urlinletcontentppf=letic=open_in_bin(Fpath.to_stringsrc)inletlen=1024inletbuf=Bytes.createleninletrecloop()=letread=inputicbuf0leninifread=lenthen(Format.fprintfppf"%s"(Bytes.to_stringbuf);loop())elseiflen>0thenletbuf=Bytes.subbuf0readinFormat.fprintfppf"%s"(Bytes.to_stringbuf)inloop();close_inicin{Odoc_document.Renderer.filename;content;children=[]}endletrender~config=function|Document.Pagepage->[Page.page~configpage]|Source_pagesrc->[Page.source_page~configsrc]|Assetasset->[Page.asset~configasset]letdoc~config~xref_base_urib=letresolve=Link.Basexref_base_uriinblock~config~resolveb