123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171open!CompatmoduleCode_block=structtypet={location:Odoc_parser.Loc.span;contents:string}endletdrop_lastlst=matchList.revlstwith|[]->None|last::rev_tl->Some(List.revrev_tl,last)(* drop_first_and_last [1; 2; 3; 4] = Some (1, Some ([2; 3], 4)). *)letdrop_first_and_last=function|[]->None|first::tl->Some(first,drop_lasttl)letslicelines~(start:Odoc_parser.Loc.point)~(end_:Odoc_parser.Loc.point)=letlines_to_include =Util.Array.slicelines~from:(start.line-1)~to_:(end_.line-1)|>Array.to_listinmatchdrop_first_and_lastlines_to_includewith|None->""|Some(line,None)->String.sublinestart.column(end_.column-start.column)(* Imagine we were slicing the file from (Line 2, Column 3) to (Line 6, Column 7):
0123456789
1 ----------
2 ---[---
3 ---------
4 --
5 ----------
6 -------]--
7 ----------
8 ----------
The case below handles this multiline case, concatenating the included substrings
from lines 2-6 ([lines_to_include]). *)|Some(first_line,Some(stripped,last_line))->letfirst_line =String.subfirst_linestart.column(String.lengthfirst_line-start.column)inletlast_line=String.sublast_line0end_.columninString.concat"\n"([first_line]@stripped@[last_line])letextract_code_blocks ~(location:Lexing.position)~docstring=letrecaccblocks=List.map(fun block->matchOdoc_parser.Loc.valueblockwith|`Code_block(_metadata,{Odoc_parser.Loc.value =contents;_})->[{Code_block.location=block.location;contents}]|`List(_,_,lists)->List.mapacclists|>List.concat|_->[])blocks|>List.concatinletparsed=Odoc_parser.parse_comment~location~text:docstringinList.iter(funerror->failwith (Odoc_parser.Warning.to_stringerror))(Odoc_parser.warnings parsed);List.map(funelement ->matchelementwith|{Odoc_parser.Loc.value=#Odoc_parser.Ast.nestable_block_element;_}ase->acc[e]|{value=`Tagtag;_}->(matchtagwith|`Deprecatedblocks->accblocks|`Param(_,blocks)->accblocks|`Raise(_,blocks)->accblocks|`Returnblocks->accblocks|`See(_,_,blocks)->accblocks|`Before(_,blocks)->accblocks|_->[])|{value=`Heading_;_}->[])(Odoc_parser.astparsed)|>List.concatletdocstringslexbuf=letreclooplist=matchLexer.token_with_commentslexbufwith|Parser.EOF-> list|Parser.DOCSTRINGdocstring->letdocstring =(Docstrings.docstring_bodydocstring,Docstrings.docstring_locdocstring)inloop (docstring::list)|_->loop listinloop[]|>List.revletconvert_pos (p:Lexing.position)(pt:Odoc_parser.Loc.point)={pwithpos_lnum=pt.line;pos_cnum=pt.column}letconvert_loc(loc:Location.t)(sp:Odoc_parser.Loc.span)=letloc_start=convert_pos loc.loc_start sp.startinletloc_end=convert_posloc.loc_endsp.end_in{locwithloc_start;loc_end}letdocstring_code_blocksstr=Lexer.handle_docstrings:=true;Lexer.init();List.map(fun(docstring,(cmt_loc:Location.t))->letlocation={cmt_loc.loc_startwithpos_cnum=cmt_loc.loc_start.pos_cnum+3}inletblocks=extract_code_blocks~location~docstringinList.map(fun(b:Code_block.t)->(b,convert_loccmt_locb.location))blocks)(docstrings(Lexing.from_stringstr))|>List.concatletparse_mlifile_contents=(* Find 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. *)letcode_blocks=docstring_code_blocksfile_contentsinletcursor=ref{Odoc_parser.Loc.line=1;column=0}inletlines=String.split_on_char'\n'file_contents|>Array.of_listinlettokens=List.map(fun ((code_block:Code_block.t),loc)->letpre_text=Document.Text(slicelines~start:!cursor~end_:code_block.location.start)inletcolumn=code_block.location.start.columninletcontents=Compat.String.split_on_char '\n'code_block.contents inletblock=matchBlock.mk~loc~section:None~labels:[]~header:(SomeOCaml)~contents~legacy_labels:false~errors:[]with|Okblock->Document.Blockblock|Error_->failwith"Error creating block"inlethpad=ifList.length contents=1then""else Astring.String.v~len:column(fun_->' ')incursor:=code_block.location.end_;[pre_text;Text"{[";block;Text(hpad^"]}")])code_blocks|>List.concatinleteof={Odoc_parser.Loc.line=Array.lengthlines;column=String.lengthlines.(Array.lengthlines-1);}inleteof_is_beyond_location(loc:Odoc_parser.Loc.point)=eof.line>loc.line ||(eof.line=loc.line&&eof.column>loc.column)inifeof_is_beyond_location!cursorthenletremainder =slicelines~start:!cursor ~end_:eofinifnot(Compat.String.equalremainder "")thentokens @[Text remainder]elsetokenselsetokensletparse_mlifile_contents=tryResult.Ok(parse_mli file_contents)withexn->Util.Result.errorf"%s"(Printexc.to_string exn)