123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425(*
* Copyright (c) 2016, 2017 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.
*)moduleComment=Odoc_model.CommentmoduleHtml=Tyxml.HtmlopenOdoc_model.Namestypeflow=Html_types.flow5_without_header_footertypephrasing=Html_types.phrasingtypenon_link_phrasing=Html_types.phrasing_without_interactivemoduleReference=structmoduleId=Tree.Relative_link.IdopenOdoc_model.Pathsletrecrender_resolved:Reference.Resolved.t->string=funr->letopenReference.Resolvedinmatchrwith|`Identifierid->Identifier.nameid|`SubstAlias(_,r)->render_resolved(r:>t)|`Module(r,s)->render_resolved(r:>t)^"."^(ModuleName.to_strings)|`Canonical(_,`Resolvedr)->render_resolved(r:>t)|`Canonical(p,_)->render_resolved(p:>t)|`ModuleType(r,s)->render_resolved(r:>t)^"."^(ModuleTypeName.to_strings)|`Type(r,s)->render_resolved(r:>t)^"."^(TypeName.to_strings)|`Constructor(r,s)->render_resolved(r:>t)^"."^(ConstructorName.to_strings)|`Field(r,s)->render_resolved(r:>t)^"."^(FieldName.to_strings)|`Extension(r,s)->render_resolved(r:>t)^"."^(ExtensionName.to_strings)|`Exception(r,s)->render_resolved(r:>t)^"."^(ExceptionName.to_strings)|`Value(r,s)->render_resolved(r:>t)^"."^(ValueName.to_strings)|`Class(r,s)->render_resolved(r:>t)^"."^(ClassName.to_strings)|`ClassType(r,s)->render_resolved(r:>t)^"."^(ClassTypeName.to_strings)|`Method(r,s)->(* CR trefis: do we really want to print anything more than [s] here? *)render_resolved(r:>t)^"."^(MethodName.to_strings)|`InstanceVariable(r,s)->(* CR trefis: the following makes no sense to me... *)render_resolved(r:>t)^"."^(InstanceVariableName.to_strings)|`Label(r,s)->render_resolved(r:>t)^":"^(LabelName.to_strings)letrecref_to_string:Reference.t->string=letopenReferenceinfunction|`Root(s,_)->UnitName.to_strings|`Dot(parent,s)->ref_to_string(parent:>t)^"."^s|`Module(parent,s)->ref_to_string(parent:>t)^"."^(ModuleName.to_strings)|`ModuleType(parent,s)->ref_to_string(parent:>t)^"."^(ModuleTypeName.to_strings)|`Type(parent,s)->ref_to_string(parent:>t)^"."^(TypeName.to_strings)|`Constructor(parent,s)->ref_to_string(parent:>t)^"."^(ConstructorName.to_strings)|`Field(parent,s)->ref_to_string(parent:>t)^"."^(FieldName.to_strings)|`Extension(parent,s)->ref_to_string(parent:>t)^"."^(ExtensionName.to_strings)|`Exception(parent,s)->ref_to_string(parent:>t)^"."^(ExceptionName.to_strings)|`Value(parent,s)->ref_to_string(parent:>t)^"."^(ValueName.to_strings)|`Class(parent,s)->ref_to_string(parent:>t)^"."^(ClassName.to_strings)|`ClassType(parent,s)->ref_to_string(parent:>t)^"."^(ClassTypeName.to_strings)|`Method(parent,s)->ref_to_string(parent:>t)^"."^(MethodName.to_strings)|`InstanceVariable(parent,s)->ref_to_string(parent:>t)^"."^(InstanceVariableName.to_strings)|`Label(parent,s)->ref_to_string(parent:>t)^"."^(LabelName.to_strings)|`Resolvedr->render_resolvedr(* This is the entry point. stop_before is false on entry, true on recursive
call. *)letrecto_html:?text:(non_link_phrasingHtml.elt)->?xref_base_uri:string->stop_before:bool->Reference.t->phrasingHtml.elt=fun?text?xref_base_uri~stop_beforeref->letspan'(txt:phrasingHtml.eltlist):phrasingHtml.elt=Html.spantxt~a:[Html.a_class["xref-unresolved"];Html.a_title(Printf.sprintf"unresolved reference to %S"(ref_to_stringref))]inletopenReferenceinmatchrefwith|`Root(s,_)->beginmatchtextwith|None->Html.code[Html.txt(Odoc_model.Names.UnitName.to_strings)]|Somes->(span'[(s:>phrasingHtml.elt)]:>phrasingHtml.elt)end|`Dot(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)s|`Module(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(ModuleName.to_strings)|`ModuleType(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(ModuleTypeName.to_strings)|`Type(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(TypeName.to_strings)|`Constructor(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(ConstructorName.to_strings)|`Field(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(FieldName.to_strings)|`Extension(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(ExtensionName.to_strings)|`Exception(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(ExceptionName.to_strings)|`Value(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(ValueName.to_strings)|`Class(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(ClassName.to_strings)|`ClassType(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(ClassTypeName.to_strings)|`Method(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(MethodName.to_strings)|`InstanceVariable(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(InstanceVariableName.to_strings)|`Label(parent,s)->unresolved_parts_to_html?xref_base_uri?textspan'(parent:>t)(LabelName.to_strings)|`Resolvedr->(* IDENTIFIER MUST BE RENAMED TO DEFINITION. *)letid=Reference.Resolved.identifierrinlettxt:non_link_phrasingHtml.elt=matchtextwith|None->Html.code[Html.txt(render_resolvedr)]|Somes->sinbeginmatchId.href?xref_base_uri~stop_beforeidwith|exceptionId.Not_linkable->(txt:>phrasingHtml.elt)|exceptionexn->(* FIXME: better error message *)Printf.eprintf"Id.href failed: %S\n%!"(Printexc.to_stringexn);(txt:>phrasingHtml.elt)|href->Html.a~a:[Html.a_hrefhref][txt]endandunresolved_parts_to_html:?text:(non_link_phrasingHtml.elt)->?xref_base_uri:string->((phrasingHtml.eltlist)->(phrasingHtml.elt))->Reference.t->string->(phrasingHtml.elt)=fun?text?xref_base_urispan'parents->matchtextwith|Somes->(span'[(s:>phrasingHtml.elt)]:>phrasingHtml.elt)|None->lettail=[Html.txt("."^s)]inspan'(matchto_html?xref_base_uri~stop_before:trueparentwith|content->content::tail)endletlocation_to_syntax(loc:Odoc_model.Location_.span)=ifFilename.check_suffixloc.file".rei"thenTree.ReasonelseTree.OCamlletstyle_to_combinator=function|`Bold->Html.b|`Italic->Html.i|`Emphasis->Html.em|`Superscript->Html.sup|`Subscript->Html.subletleaf_inline_element:Comment.leaf_inline_element->([>non_link_phrasing]Html.elt)option=function|`Space->Some(Html.txt" ")|`Words->Some(Html.txts)|`Code_spans->Some(Html.code[Html.txts])|`Raw_markup(`Html,s)->Some(Html.Unsafe.datas)letrecnon_link_inline_element:'a.Comment.non_link_inline_element->(([>non_link_phrasing]as'a)Html.elt)option=function|#Comment.leaf_inline_elementase->leaf_inline_elemente|`Styled(style,content)->Some((style_to_combinatorstyle)(non_link_inline_element_listcontent))andnon_link_inline_element_list:'a._->([>non_link_phrasing]as'a)Html.eltlist=funelements->List.fold_left(funhtml_elementsast_element->matchnon_link_inline_elementast_element.Odoc_model.Location_.valuewith|None->html_elements|Somee->e::html_elements)[]elements|>List.revletlink_content_to_html=non_link_inline_element_listletrecinline_element?xref_base_uri:Comment.inline_element->(phrasingHtml.elt)option=function|#Comment.leaf_inline_elementase->(leaf_inline_elemente:>(phrasingHtml.elt)option)|`Styled(style,content)->Some((style_to_combinatorstyle)(inline_element_list?xref_base_uricontent))|`Reference(path,content)->(* TODO Rework that ugly function. *)(* TODO References should be set in code style, if they are to code
elements. *)letcontent=matchcontentwith|[]->None|_->Some(Html.span(non_link_inline_element_listcontent))inSome(Reference.to_html?text:content?xref_base_uri~stop_before:falsepath)|`Link(target,content)->letcontent=matchcontentwith|[]->[Html.txttarget]|_->non_link_inline_element_listcontentinSome(Html.a~a:[Html.a_hreftarget]content)andinline_element_list?xref_base_urielements=List.fold_left(funhtml_elementsast_element->matchinline_element?xref_base_uriast_element.Odoc_model.Location_.valuewith|None->html_elements|Somee->e::html_elements)[]elements|>List.revletrecnestable_block_element:'a.?xref_base_uri:string->to_syntax:Tree.syntax->from_syntax:Tree.syntax->Comment.nestable_block_element->([>flow]as'a)Html.elt=fun?xref_base_uri~to_syntax~from_syntax->function|`Paragraph[{value=`Raw_markup(`Html,s);_}]->Html.Unsafe.datas|`Paragraphcontent->Html.p(inline_element_list?xref_base_uricontent)|`Code_blocks->letopenTreein(*
TODO: This will probably be replaced by a proper plugin / PPX system.
See: https://discuss.ocaml.org/t/combining-ocamlformat-refmt/2316/10
let transform fn = try (fn s, string_of_syntax to_syntax) with
| Reason_syntax_util.Error(_loc, _err) ->
(s, string_of_syntax from_syntax)
| Syntaxerr.Error(_err) ->
(* TODO: Properly report warnings *)
(* Syntaxerr.report_error Format.std_formatter err; *)
(s, string_of_syntax from_syntax)
in
let (code, classname) = match (from_syntax, to_syntax) with
| (OCaml, OCaml) -> (s, string_of_syntax OCaml)
| (Reason, Reason) -> (s, string_of_syntax Reason)
| (Reason, OCaml) -> transform Utils.ocaml_from_reason
| (OCaml, Reason) -> transform Utils.reason_from_ocaml
in
*)letcode=sinletclassname=string_of_syntaxfrom_syntaxinHtml.pre[Html.code~a:[Html.a_class[classname]][Html.txtcode]]|`Verbatims->Html.pre[Html.txts]|`Modulesms->letitems=List.map(Reference.to_html?xref_base_uri~stop_before:false)(ms:>Odoc_model.Paths.Reference.tlist)inletitems=(items:>(Html_types.li_contentHtml.elt)list)inletitems=List.map(fune->Html.li[e])itemsinHtml.ul~a:[Html.a_class["modules"]]items|`List(kind,items)->letitems=items|>List.mapbeginfunction|[{Odoc_model.Location_.value=`Paragraphcontent;_}]->(inline_element_list?xref_base_uricontent:>(Html_types.li_contentHtml.elt)list)|item->nested_block_element_list?xref_base_uri~to_syntax~from_syntaxitemendinletitems=List.mapHtml.liitemsinmatchkindwith|`Unordered->Html.ulitems|`Ordered->Html.olitemsandnestable_block_element_list?xref_base_uri~to_syntax~from_syntaxelements=elements|>List.mapOdoc_model.Location_.value|>List.map(nestable_block_element?xref_base_uri~to_syntax~from_syntax)andnested_block_element_list?xref_base_uri~to_syntax~from_syntaxelements=(nestable_block_element_list?xref_base_uri~to_syntax~from_syntaxelements:>(Html_types.flow5Html.elt)list)lettag:?xref_base_uri:string->to_syntax:Tree.syntax->from_syntax:Tree.syntax->Comment.tag->([>flow]Html.elt)option=fun?xref_base_uri~to_syntax~from_syntaxt->matchtwith|`Authors->Some(Html.(dl[dt[txt"author"];dd[txts]]))|`Deprecatedcontent->Some(Html.(dl[dt[txt"deprecated"];dd(nested_block_element_list?xref_base_uri~to_syntax~from_syntaxcontent)]))|`Param(name,content)->Some(Html.(dl[dt[txt"parameter ";txtname];dd(nested_block_element_list?xref_base_uri~to_syntax~from_syntaxcontent)]))|`Raise(name,content)->Some(Html.(dl[dt[txt"raises ";txtname];dd(nested_block_element_list?xref_base_uri~to_syntax~from_syntaxcontent)]))|`Returncontent->Some(Html.(dl[dt[txt"returns"];dd(nested_block_element_list?xref_base_uri~to_syntax~from_syntaxcontent)]))|`See(kind,target,content)->lettarget=matchkindwith|`Url->Html.a~a:[Html.a_hreftarget][Html.txttarget]|`File->Html.code[Html.txttarget]|`Document->Html.txttargetinSome(Html.(dl[dt[txt"see ";target];dd(nested_block_element_list?xref_base_uri~to_syntax~from_syntaxcontent)]))|`Sinces->Some(Html.(dl[dt[txt"since"];dd[txts]]))|`Before(version,content)->Some(Html.(dl[dt[txt"before ";txtversion];dd(nested_block_element_list?xref_base_uri~to_syntax~from_syntaxcontent)]))|`Versions->Some(Html.(dl[dt[txt"version"];dd[txts]]))|`Canonical_|`Inline|`Open|`Closed->Noneletblock_element:'a.?xref_base_uri:string->to_syntax:Tree.syntax->from_syntax:Tree.syntax->Comment.block_element->(([>flow]as'a)Html.elt)option=fun?xref_base_uri~to_syntax~from_syntax->function|#Comment.nestable_block_elementase->Some(nestable_block_element?xref_base_uri~to_syntax~from_syntaxe)|`Heading(level,label,content)->(* TODO Simplify the id/label formatting. *)letattributes=let`Label(_,label)=labelin[Html.a_id(Odoc_model.Names.LabelName.to_stringlabel)]inleta=attributesinletcontent=(non_link_inline_element_listcontent:>(phrasingHtml.elt)list)inletcontent=let`Label(_,label)=labelinletanchor=Html.a~a:[Html.a_href("#"^(Odoc_model.Names.LabelName.to_stringlabel));Html.a_class["anchor"]][]inanchor::contentinletelement=matchlevelwith|`Title->Html.h1~acontent|`Section->Html.h2~acontent|`Subsection->Html.h3~acontent|`Subsubsection->Html.h4~acontent|`Paragraph->Html.h5~acontent|`Subparagraph->Html.h6~acontentinSomeelement|`Tagt->tag?xref_base_uri~to_syntax~from_syntaxtletblock_element_list?xref_base_uri~to_syntaxelements=List.fold_left(funhtml_elements(from_syntax,block)->matchblock_element?xref_base_uri~to_syntax~from_syntaxblockwith|Somee->e::html_elements|None->html_elements)[]elements|>List.revletfirst_to_html?xref_base_uri?syntax:(to_syntax=Tree.OCaml)=function|{Odoc_model.Location_.value=`Paragraph_asfirst_paragraph;location}::_->beginmatchblock_element?xref_base_uri~to_syntax~from_syntax:(location_to_syntaxlocation)first_paragraphwith|Someelement->[element]|None->[]end|_->[]letto_html?xref_base_uri?syntax:(to_syntax=Tree.OCaml)docs=block_element_list?xref_base_uri~to_syntax(List.map(funel->Odoc_model.Location_.((locationel|>location_to_syntax,valueel)))docs)lethas_docdocs=docs<>[]