Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file mli_parser.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197moduleCode_block=structtypemetadata={language_tag:string;labels:stringoption}typet={metadata :metadataoption;content:Location.t;(* Location of the content *)code_block:Location.t;(* Location of the enclosing code block *)}end(* Parse and extract code block metadata from an odoc formatted docstring.
Code blocks are the only thing we're interested in. This function parses
the given text and extracts the metadata and enough location information
from the code blocks be able to String.sub them out of the original text.
[location] is the location of this docstring within the original file
(ie, the location of the contents of the documentation comment). This is
required so we can splice out the code blocks from the original file.
The results are prepended in reverse order onto [acc]. *)letextract_code_block_infoacc~(location:Lexing.position)~docstring=letmoduleO=Odoc_parser inletparsed=O.parse_comment~location ~text:docstringin(* If odoc-parser produced any warnings, we raise them as errors here *)List.iter(funerror->failwith (O.Warning.to_stringerror))(O.warningsparsed);(* Extract the useful info from what odoc has given us.
Note, we don't use the contents of the code block that odoc has handed us
as that has been stripped and we need all the relevant whitespace.
Fortunately the location info give us enough info to be able to extract
the code from the original text, whitespace and all.
*)lethandle_code_block:O.Loc.span->_->Code_block.t=letconvert_loc(sp:O.Loc.span)=Location.{loc_start =O.position_of_pointparsedsp.start;loc_end =O.position_of_pointparsedsp.end_;loc_ghost =false;}infunlocation(metadata,{O.Loc.location =span;_})->letmetadata=Option.map(fun(lang,labels)->letlanguage_tag=O.Loc.valuelanginletlabels=Option.mapO.Loc.valuelabelsinCode_block.{language_tag;labels})metadatainletcontent =convert_locspaninletcode_block=convert_loclocationin{metadata;content;code_block}in(* Fold over the results from odoc-parser, recurse where necessary
and extract the code block metadata *)letrecfold_fnacc(elt:O.Ast.block_element O.Loc.with_location)=matcheltwith|{O.Loc.value=`Code_blockc;location}->handle_code_blocklocationc::acc|{O.Loc.value=`List(_,_,lists);_}->List.fold_left(List.fold_leftfold_fn)acc(lists:>O.Ast.tlist)|{O.Loc.value=`Tagtag;_}->(matchtagwith|`Deprecatedblocks|`Param(_,blocks)|`Raise(_,blocks)|`Returnblocks|`See(_,_,blocks)|`Before(_,blocks)->List.fold_leftfold_fnacc(blocks:>O.Ast.t)|_->acc)|_->accinList.fold_leftfold_fnacc(O.astparsed)(* This function handles string containing ocaml code. It parses it as ocaml
via compiler-libs, then for each odoc-formatted comment it then parses
that via odoc-parser. The end result is a list of metadata about the code
blocks within the comments. The result is given as an in-order list of
[Code_block.t] values. *)letdocstring_code_blocksstr=letinitial_handle_docstrings=!Lexer.handle_docstringsinFun.protect~finally:(fun()->Lexer.handle_docstrings:=initial_handle_docstrings)(fun()->Lexer.handle_docstrings:=true;Lexer.init ();letlexbuf=Lexing.from_stringstrinletreclooplist=matchLexer.token_with_commentslexbufwith|Parser.EOF->list|Parser.DOCSTRINGdocstring->letbody=Docstrings.docstring_bodydocstringinletloc=Docstrings.docstring_locdocstringin(* odoc-parser adjusts for the initial [** *)letadjustment=3(* String.length "(**" *)inletlocation={loc.loc_startwithpos_cnum=loc.loc_start.pos_cnum+adjustment;}inloop(extract_code_block_infolist~location~docstring:body)|_->looplistinloop[]|>List.rev)(* Given code block metadata and the original file, this function splices the
contents of the code block from the original text and creates an Mdx
Block.t, or reports the error (e.g., from invalid tags) *)letmake_blockcode_blockfile_contents=lethandle_header =function|SomeCode_block.{language_tag;labels}->letopenUtil.Result.Infixinletheader=Block.Header.of_stringlanguage_taginlet*labels=matchlabels with|None-> Ok[]|Somelabels->(match Label.of_string(String.trimlabels)with|Ok labels->Oklabels|Errormsgs->Error (List.hdmsgs)(* TODO: Report precise location *))inletlanguage_label=Label.Language_taglanguage_taginOk(header,language_label::labels)|None->(* If not specified, blocks are run as ocaml blocks *)Ok(SomeOCaml,[])inmatchhandle_headercode_block.Code_block.metadata with|Error_ase->e|Ok(header,labels)->letslice (loc :Location.t)=letstart=loc.loc_start.pos_cnuminletlen=loc.loc_end.pos_cnum-startinString.subfile_contentsstartleninletcontents=slicecode_block.content|>String.split_on_char '\n'inBlock.mk~loc:code_block.code_block ~section:None~labels~header~contents~legacy_labels:false~errors:[](* Given the locations of the code blocks within [file_contents], then slice it up into
[Text] and [Block] parts by using the starts and ends of those blocks as
boundaries. *)letextract_blockscode_blocksfile_contents=letcursor,tokens=List.fold_left(fun(cursor,code_blocks)(code_block:Code_block.t)->letpre_text=Document.Text(String.subfile_contentscursor(code_block.code_block.loc_start.pos_cnum-cursor))inlet block=matchmake_blockcode_blockfile_contentswith|Okblock ->Document.Blockblock|Error(`Msgmsg)->Fmt.failwith"Error creating block: %s"msgin(* append them in reverse order, since this is a fold_left *)letcode_blocks=block::pre_text::code_blocksin(code_block.code_block.loc_end.pos_cnum,code_blocks))(0,[])code_blocksinlettokens=List.revtokensinifcursor<String.lengthfile_contentsthenletremainder=String.subfile_contentscursor(String.lengthfile_contents -cursor)inifnot(String.equalremainder"")thentokens@[Textremainder]elsetokenselsetokensletparse_mlifile_contents=tryletcode_blocks =docstring_code_blocksfile_contentsinOk(extract_blocks code_blocksfile_contents)withexn->Error[`Msg(Printexc.to_stringexn)]letparse_mld?(filename="_none_")file_contents=letlocation=Lexing.{pos_bol=0;pos_lnum=1;pos_cnum=0;pos_fname=filename}inletcode_blocks=extract_code_block_info[]~location~docstring:file_contents|>List.revinOk(extract_blockscode_blocksfile_contents)