123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499moduleLocation=Location_moduleAst=Odoc_parser.Asttypeinternal_tags_removed=[`TagofAst.ocamldoc_tag|`HeadingofAst.heading|Ast.nestable_block_element](** {!Ast.block_element} without internal tags. *)type_handle_internal_tags=|Expect_status:[`Default|`Inline|`Open|`Closed]handle_internal_tags|Expect_canonical:[`DotofPaths.Path.Module.t*string]optionhandle_internal_tags|Expect_none:unithandle_internal_tagsletdescribe_internal_tag=function|`Canonical_->"@canonical"|`Inline->"@inline"|`Open->"@open"|`Closed->"@closed"letwarn_unexpected_tag{Location.value;location}=Error.raise_warning@@Error.make"Unexpected tag '%s' at this location."(describe_internal_tagvalue)locationletwarn_root_canonicallocation=Error.raise_warning@@Error.make"Canonical paths must contain a dot, eg. X.Y."locationletrecfind_tagf=function|[]->None|hd::tl->(matchfhd.Location.valuewith|Somex->Some(x,hd.location)|None->warn_unexpected_taghd;find_tagftl)lethandle_internal_tags(typea)tags:ahandle_internal_tags->a=function|Expect_status->(matchfind_tag(function(`Inline|`Open|`Closed)ast->Somet|_->None)tagswith|Some(status,_)->status|None->`Default)|Expect_canonical->(matchfind_tag(function`Canonicalp->Somep|_->None)tagswith|Some(`Root_,location)->warn_root_canonicallocation;None|Some((`Dot_asp),_)->Somep|None->None)|Expect_none->(* Will raise warnings. *)ignore(find_tag(fun_->None)tags);()(* Errors *)letinvalid_raw_markup_target:string->Location.span->Error.t=Error.make~suggestion:"try '{%html:...%}'.""'{%%%s:': bad raw markup target."letdefault_raw_markup_target_not_supported:Location.span->Error.t=Error.make~suggestion:"try '{%html:...%}'.""'{%%...%%}' (raw markup) needs a target language."letheadings_not_allowed:Location.span->Error.t=Error.make"Headings not allowed in this comment."lettitles_not_allowed:Location.span->Error.t=Error.make"Title-level headings {0 ...} are only allowed in pages."letbad_heading_level:int->Location.span->Error.t=Error.make"'%d': bad heading level (0-5 allowed)."letheading_level_should_be_lower_than_top_level:int->int->Location.span->Error.t=funthis_heading_leveltop_heading_level->Error.make"%s: heading level should be lower than top heading level '%d'."(Printf.sprintf"'{%i'"this_heading_level)top_heading_levelletpage_heading_required:string->Error.t=Error.filename_only"Pages (.mld files) should start with a heading."letnot_allowed:?suggestion:string->what:string->in_what:string->Location.span->Error.t=fun?suggestion~what~in_what->Error.make?suggestion"%s is not allowed in %s."(Astring.String.Ascii.capitalizewhat)in_whatletdescribe_element=function|`Reference(`Simple,_,_)->"'{!...}' (cross-reference)"|`Reference(`With_text,_,_)->"'{{!...} ...}' (cross-reference)"|`Link_->"'{{:...} ...}' (external link)"|`Heading(level,_,_)->Printf.sprintf"'{%i ...}' (section heading)"level(* End of errors *)type'awith_location='aLocation.with_locationtypeast_leaf_inline_element=[`Spaceofstring|`Wordofstring|`Code_spanofstring|`Raw_markupofstringoption*string]typesections_allowed=[`All|`No_titles|`None]typestatus={sections_allowed:sections_allowed;parent_of_sections:Paths.Identifier.LabelParent.t;}letleaf_inline_element:ast_leaf_inline_elementwith_location->Comment.leaf_inline_elementwith_location=funelement->matchelementwith|{value=`Word_|`Code_span_;_}aselement->element|{value=`Space_;_}->Location.sameelement`Space|{value=`Raw_markup(target,s);location}->(matchtargetwith|Someinvalid_targetwhenString.triminvalid_target=""||String.containsinvalid_target'%'||String.containsinvalid_target'}'->Error.raise_warning(invalid_raw_markup_targetinvalid_targetlocation);Location.sameelement(`Code_spans)|None->Error.raise_warning(default_raw_markup_target_not_supportedlocation);Location.sameelement(`Code_spans)|Sometarget->Location.sameelement(`Raw_markup(target,s)))typesurrounding=[`Headingofint*stringoption*Odoc_parser.Ast.inline_elementLocation_.with_locationlist|`Linkofstring*Odoc_parser.Ast.inline_elementLocation_.with_locationlist|`Referenceof[`Simple|`With_text]*stringLocation_.with_location*Odoc_parser.Ast.inline_elementLocation_.with_locationlist]letrecnon_link_inline_element:status->surrounding:surrounding->Odoc_parser.Ast.inline_elementwith_location->Comment.non_link_inline_elementwith_location=funstatus~surroundingelement->matchelementwith|{value=#ast_leaf_inline_element;_}aselement->(leaf_inline_elementelement:>Comment.non_link_inline_elementwith_location)|{value=`Styled(style,content);_}->`Styled(style,non_link_inline_elementsstatus~surroundingcontent)|>Location.sameelement|({value=`Reference(_,_,content);_}|{value=`Link(_,content);_})aselement->not_allowed~what:(describe_elementelement.value)~in_what:(describe_elementsurrounding)element.location|>Error.raise_warning;`Styled(`Emphasis,non_link_inline_elementsstatus~surroundingcontent)|>Location.sameelementandnon_link_inline_elementsstatus~surroundingelements=List.map(non_link_inline_elementstatus~surrounding)elementsletrecinline_element:status->Odoc_parser.Ast.inline_elementwith_location->Comment.inline_elementwith_location=funstatuselement->matchelementwith|{value=#ast_leaf_inline_element;_}aselement->(leaf_inline_elementelement:>Comment.inline_elementwith_location)|{value=`Styled(style,content);location}->`Styled(style,inline_elementsstatuscontent)|>Location.atlocation|{value=`Reference(kind,target,content)asvalue;location}->(let{Location.value=target;location=target_location}=targetinmatchError.raise_warnings(Reference.parsetarget_locationtarget)with|Result.Oktarget->letcontent=non_link_inline_elementsstatus~surrounding:valuecontentinLocation.atlocation(`Reference(target,content))|Result.Errorerror->Error.raise_warningerror;letplaceholder=matchkindwith|`Simple->`Code_spantarget|`With_text->`Styled(`Emphasis,content)ininline_elementstatus(Location.atlocationplaceholder))|{value=`Link(target,content)asvalue;location}->`Link(target,non_link_inline_elementsstatus~surrounding:valuecontent)|>Location.atlocationandinline_elementsstatuselements=List.map(inline_elementstatus)elementsletrecnestable_block_element:status->Odoc_parser.Ast.nestable_block_elementwith_location->Comment.nestable_block_elementwith_location=funstatuselement->matchelementwith|{value=`Paragraphcontent;location}->Location.atlocation(`Paragraph(inline_elementsstatuscontent))|{value=`Code_block(_,code);_}->Location.sameelement(`Code_blockcode)|{value=`Verbatim_;_}aselement->element|{value=`Modulesmodules;location}->letmodules=List.fold_left(funacc{Location.value;location}->matchError.raise_warnings(Reference.read_mod_longidentlocationvalue)with|Result.Okr->{Comment.module_reference=r;module_synopsis=None}::acc|Result.Errorerror->Error.raise_warningerror;acc)[]modules|>List.revinLocation.atlocation(`Modulesmodules)|{value=`List(kind,_syntax,items);location}->`List(kind,List.map(nestable_block_elementsstatus)items)|>Location.atlocationandnestable_block_elementsstatuselements=List.map(nestable_block_elementstatus)elementslettag:location:Location.span->status->Ast.ocamldoc_tag->(Comment.block_elementwith_location,internal_tags_removedwith_location)Result.result=fun~locationstatustag->letokt=Result.Ok(Location.atlocation(`Tagt))inmatchtagwith|(`Author_|`Since_|`Version_)astag->oktag|`Deprecatedcontent->ok(`Deprecated(nestable_block_elementsstatuscontent))|`Param(name,content)->ok(`Param(name,nestable_block_elementsstatuscontent))|`Raise(name,content)->ok(`Raise(name,nestable_block_elementsstatuscontent))|`Returncontent->ok(`Return(nestable_block_elementsstatuscontent))|`See(kind,target,content)->ok(`See(kind,target,nestable_block_elementsstatuscontent))|`Before(version,content)->ok(`Before(version,nestable_block_elementsstatuscontent))(* When the user does not give a section heading a label (anchor), we generate
one from the text in the heading. This is the common case. This involves
simply scanning the AST for words, lowercasing them, and joining them with
hyphens.
This must be done in the parser (i.e. early, not at HTML/other output
generation time), so that the cross-referencer can see these anchors. *)letgenerate_heading_label:Comment.link_content->string=funcontent->(* Code spans can contain spaces, so we need to replace them with hyphens. We
also lowercase all the letters, for consistency with the rest of this
procedure. *)letreplace_spaces_with_hyphens_and_lowercases=letresult=Bytes.create(String.lengths)ins|>String.iteri(funindexc->letc=matchcwith|' '|'\t'|'\r'|'\n'->'-'|_->Astring.Char.Ascii.lowercasecinBytes.setresultindexc);Bytes.unsafe_to_stringresultin(* Perhaps this should be done using a [Buffer.t]; we can switch to that as
needed. *)letrecscan_inline_elementsanchor=function|[]->anchor|element::more->letanchor=matchelement.Location.valuewith|`Space->anchor^"-"|`Wordw->anchor^Astring.String.Ascii.lowercasew|`Code_spanc->anchor^replace_spaces_with_hyphens_and_lowercasec|`Raw_markup_->(* TODO Perhaps having raw markup in a section heading should be an
error? *)anchor|`Styled(_,content)->scan_inline_elementsanchorcontentinscan_inline_elementsanchormoreinscan_inline_elements""contentletsection_heading:status->top_heading_level:intoption->Location.span->[`Headingof_]->intoption*Comment.block_elementwith_location=funstatus~top_heading_levellocationheading->let(`Heading(level,label,content))=headinginlettext=non_link_inline_elementsstatus~surrounding:(heading:>surrounding)contentinletheading_label_explicit,label=matchlabelwith|Somelabel->(true,label)|None->(false,generate_heading_labeltext)inletlabel=`Label(status.parent_of_sections,Names.LabelName.make_stdlabel)inletmk_headingheading_level=letattrs={Comment.heading_level;heading_label_explicit}inletelement=Location.atlocation(`Heading(attrs,label,text))inlettop_heading_level=matchtop_heading_levelwithNone->Somelevel|some->somein(top_heading_level,element)inmatch(status.sections_allowed,level)with|`None,_any_level->Error.raise_warning(headings_not_allowedlocation);lettext=(text:>Comment.inline_elementwith_locationlist)inletelement=Location.atlocation(`Paragraph[Location.atlocation(`Styled(`Bold,text))])in(top_heading_level,element)|`No_titles,0->Error.raise_warning(titles_not_allowedlocation);mk_heading`Title|_,level->letlevel'=matchlevelwith|0->`Title|1->`Section|2->`Subsection|3->`Subsubsection|4->`Paragraph|5->`Subparagraph|_->Error.raise_warning(bad_heading_levellevellocation);(* Implicitly promote to level-5. *)`Subparagraphin(matchtop_heading_levelwith|Sometop_levelwhenstatus.sections_allowed=`All&&level<=top_level&&level<=5->Error.raise_warning(heading_level_should_be_lower_than_top_levelleveltop_levellocation)|_->());mk_headinglevel'letvalidate_first_page_headingstatusast_element=matchstatus.parent_of_sectionswith|`Page(_,name)|`LeafPage(_,name)->(matchast_elementwith|{Location.value=`Heading(_,_,_);_}->()|_invalid_ast_element->letfilename=Names.PageName.to_stringname^".mld"inError.raise_warning(page_heading_requiredfilename))|_not_a_page->()lettop_level_block_elementsstatusast_elements=letrectraverse:top_heading_level:intoption->Comment.block_elementwith_locationlist->internal_tags_removedwith_locationlist->Comment.block_elementwith_locationlist=fun~top_heading_levelcomment_elements_accast_elements->matchast_elementswith|[]->List.revcomment_elements_acc|ast_element::ast_elements->((* The first [ast_element] in pages must be a title or section heading. *)ifstatus.sections_allowed=`All&&top_heading_level=Nonethenvalidate_first_page_headingstatusast_element;matchast_elementwith|{value=#Odoc_parser.Ast.nestable_block_element;_}aselement->letelement=nestable_block_elementstatuselementinletelement=(element:>Comment.block_elementwith_location)intraverse~top_heading_level(element::comment_elements_acc)ast_elements|{value=`Tagthe_tag;location}->(matchtag~locationstatusthe_tagwith|Result.Okelement->traverse~top_heading_level(element::comment_elements_acc)ast_elements|Result.Errorplaceholder->traverse~top_heading_levelcomment_elements_acc(placeholder::ast_elements))|{value=`Heading_asheading;_}->lettop_heading_level,element=section_headingstatus~top_heading_levelast_element.Location.locationheadingintraverse~top_heading_level(element::comment_elements_acc)ast_elements)inlettop_heading_level=(* Non-page documents have a generated title. *)matchstatus.parent_of_sectionswith|`Page_|`LeafPage_->None|_parent_with_generated_title->Some0intraverse~top_heading_level[]ast_elementsletstrip_internal_tagsast:internal_tags_removedwith_locationlist*_=letreclooptagsast'=function|({Location.value=`Tag(#Ast.internal_tagastag);_}aswloc)::tl->(letnexttag=loop({wlocwithvalue=tag}::tags)ast'tlinmatchtagwith|(`Inline|`Open|`Closed)astag->nexttag|`Canonical{Location.value=s;location=r_location}->(matchError.raise_warnings(Reference.read_path_longidentr_locations)with|Result.Okpath->next(`Canonicalpath)|Result.Errore->Error.raise_warninge;looptagsast'tl))|({value=`Tag#Ast.ocamldoc_tag|`Heading_|#Ast.nestable_block_element;_;}ashd)::tl->looptags(hd::ast')tl|[]->(List.revast',List.revtags)inloop[][]astletast_to_comment~internal_tags~sections_allowed~parent_of_sectionsast=Error.catch_warnings(fun()->letstatus={sections_allowed;parent_of_sections}inletast,tags=strip_internal_tagsastin(top_level_block_elementsstatusast,handle_internal_tagstagsinternal_tags))letparse_comment~internal_tags~sections_allowed~containing_definition~location~text=Error.catch_warnings(fun()->letast=Odoc_parser.parse_comment~location~text|>Error.raise_parser_warningsinast_to_comment~internal_tags~sections_allowed~parent_of_sections:containing_definitionast|>Error.raise_warnings)letparse_referencetext=letlocation=Location_.{file="";start={line=0;column=0};end_={line=0;column=String.lengthtext};}inReference.parselocationtext