123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355openAst.UtilmoduleRaw=Ast_block.RawmodulePre=structtypecontainer=|Rblockquoteoft|Rlistoflist_type*list_spacing*bool*int*attributesRaw.blocklistlist*t|Rparagraphofstringlist|Rfenced_codeofint*int*Parser.code_block_kind*(string*string)*stringlist*attributes|Rindented_codeofstringlist|RhtmlofParser.html_kind*stringlist|Rdef_listofstring*stringlist|Rtable_headerofStrSlice.tlist*string|Rtableof(string*cell_alignment)list*stringlistlist|Remptyandt={blocks:attributesRaw.blocklist;next:container}letconcatl=String.concat"\n"(List.revl)^"\n"lettrim_lefts=letrecloopi=ifi>=String.lengthsthenielsematchs.[i]with' '|'\t'->loop(succi)|_->iinleti=loop0inifi>0thenString.subsi(String.lengths-i)elsesletlink_reference_definitionss=letdefs,off=Parser.link_reference_definitions(Parser.P.of_strings)inlets=String.subsoff(String.lengths-off)|>String.trimin(defs,s)letreccloselink_defs{blocks;next}=letfinish=finishlink_defsinmatchnextwith|Rblockquotestate->Raw.Blockquote([],finishstate)::blocks|Rlist(ty,sp,_,_,closed_items,state)->List([],ty,sp,List.rev(finishstate::closed_items))::blocks|Rparagraphl->lets=concat(List.maptrim_leftl)inletdefs,off=Parser.link_reference_definitions(Parser.P.of_strings)inlets=String.subsoff(String.lengths-off)|>String.triminlink_defs:=defs@!link_defs;ifs=""thenblockselseParagraph([],s)::blocks|Rfenced_code(_,_,_kind,(label,_other),[],attr)->Code_block(attr,label,"")::blocks|Rfenced_code(_,_,_kind,(label,_other),l,attr)->Code_block(attr,label,concatl)::blocks|Rdef_list(term,defs)->letl,blocks=matchblockswith|Definition_list(_,l)::b->(l,b)|b->([],b)inDefinition_list([],l@[{term;defs=List.revdefs}])::blocks|Rindented_codel->(* TODO: trim from the right *)letrecloop=function""::l->loopl|_asl->linCode_block([],"",concat(loopl))::blocks|Rhtml(_,l)->Html_block([],concatl)::blocks|Rtable_header(_header,line)->(* FIXME: this will only ever get called on the very last
line. Should it do the link definitions? *)closelink_defs{blocks;next=Rparagraph[line]}|Rtable(header,rows)->Table([],header,List.revrows)::blocks|Rempty->blocksandfinishlink_defsstate=List.rev(closelink_defsstate)letempty={blocks=[];next=Rempty}letclassify_lines=Parser.parsesletclassify_delimiters=letleft,s=matchStrSlice.headswith|Some':'->(true,StrSlice.drop1s)|_->(false,s)inletright,s=matchStrSlice.lastswith|Some':'->(true,StrSlice.drop_lasts)|_->(false,s)inifStrSlice.exists(func->c<>'-')sthenNoneelsematch(left,right)with|true,true->SomeCentre|true,false->SomeLeft|false,true->SomeRight|false,false->SomeDefaultletmatch_table_headersheadersdelimiters=letrecloopprocessed=function|[],[]->Some(List.revprocessed)|header::headers,line::delimiters->(matchclassify_delimiterlinewith|None->None|Somealignment->loop((StrSlice.to_stringheader,alignment)::processed)(headers,delimiters))|[],_::_|_::_,[]->Noneinloop[](headers,delimiters)letrecmatch_row_lengthl1l2=match(l1,l2)with|[],_->[]|l1,[]->List.init(List.lengthl1)(fun_->"")|_::l1,x::l2->StrSlice.to_stringx::match_row_lengthl1l2letrecprocesslink_defs{blocks;next}s=letprocess=processlink_defsinletclose=closelink_defsinletfinish=finishlink_defsinmatch(next,classify_lines)with|Rempty,Parser.Lempty->{blocks;next=Rempty}|Rempty,Lblockquotes->{blocks;next=Rblockquote(processemptys)}|Rempty,Lthematic_break->{blocks=Thematic_break[]::blocks;next=Rempty}|Rempty,Lsetext_heading{level=2;len}whenlen>=3->{blocks=Thematic_break[]::blocks;next=Rempty}|Rempty,Latx_heading(level,text,attr)->{blocks=Heading(attr,level,text)::blocks;next=Rempty}|Rempty,Lfenced_code(ind,num,q,info,a)->{blocks;next=Rfenced_code(ind,num,q,info,[],a)}|Rempty,Lhtml(_,kind)->process{blocks;next=Rhtml(kind,[])}s|Rempty,Lindented_codes->{blocks;next=Rindented_code[StrSlice.to_strings]}|Rempty,Llist_item(kind,indent,s)->{blocks;next=Rlist(kind,Tight,false,indent,[],processemptys)}|Rempty,(Lsetext_heading_|Lparagraph|Ldef_list_|Ltable_line[])->{blocks;next=Rparagraph[StrSlice.to_strings]}|Rempty,Ltable_lineitems->{blocks;next=Rtable_header(items,StrSlice.to_strings)}|Rparagraph[h],Ldef_listdef->{blocks;next=Rdef_list(h,[def])}|Rdef_list(term,defs),Ldef_listdef->{blocks;next=Rdef_list(term,def::defs)}|Rparagraph_,Llist_item((Ordered(1,_)|Bullet_),_,s1)whennot(Parser.is_empty(Parser.P.of_string(StrSlice.to_strings1)))->process{blocks=close{blocks;next};next=Rempty}s|(Rparagraph_,(Lempty|Lblockquote_|Lthematic_break|Latx_heading_|Lfenced_code_|Lhtml(true,_)))->process{blocks=close{blocks;next};next=Rempty}s|Rparagraph(_::_aslines),Lsetext_heading{level;_}->lettext=concat(List.maptrim_leftlines)inletdefs,text=link_reference_definitionstextinlink_defs:=defs@!link_defs;iftext=""then(* Happens when there's nothing between the [link reference definition] and the [setext heading].
[foo]: /foo-url
===
[foo]
In that case, there's nothing to make as Heading. We can simply add `===` as Rparagraph
*){blocks;next=Rparagraph[StrSlice.to_strings]}else{blocks=Heading([],level,text)::blocks;next=Rempty}|Rparagraphlines,_->{blocks;next=Rparagraph(StrSlice.to_strings::lines)}|Rfenced_code(_,num,q,_,_,_),Lfenced_code(_,num',q1,("",_),_)whennum'>=num&&q=q1->{blocks=close{blocks;next};next=Rempty}|Rfenced_code(ind,num,q,info,lines,a),_->lets=letind=min(Parser.indents)indinifind>0thenStrSlice.offsetindselsesin{blocks;next=Rfenced_code(ind,num,q,info,StrSlice.to_strings::lines,a)}|Rdef_list(term,d::defs),Lparagraph->{blocks;next=Rdef_list(term,(d^"\n"^StrSlice.to_strings)::defs)}|Rdef_list_,_->process{blocks=close{blocks;next};next=Rempty}s|Rtable_header(headers,line),Ltable_lineitems->(matchmatch_table_headersheadersitemswith|Someheaders->(* Makes sure that there are the same number of delimiters
as headers. See
https://github.github.com/gfm/#example-203 *){blocks;next=Rtable(headers,[])}|None->(* Reinterpret the previous line as the start of a
paragraph. *)process{blocks;next=Rparagraph[line]}s)|Rtable_header(_,line),_->(* If we only have a potential header, and the current line
doesn't look like a table delimiter, then reinterpret the
previous line as the start of a paragraph. *)process{blocks;next=Rparagraph[line]}s|Rtable(header,rows),Ltable_linerow->(* Make sure the number of items in the row is consistent with
the headers and the rest of the rows. See
https://github.github.com/gfm/#example-204 *)letrow=match_row_lengthheaderrowin{blocks;next=Rtable(header,row::rows)}|Rtable(header,rows),(Lparagraph|Lsetext_heading_)->(* Treat a contiguous line after a table as a row, even if it
doesn't contain any '|'
characters. https://github.github.com/gfm/#example-202 *)letrow=match_row_lengthheader[s]in{blocks;next=Rtable(header,row::rows)}|Rtable_,_->process{blocks=close{blocks;next};next=Rempty}s|Rindented_codelines,Lindented_codes->{blocks;next=Rindented_code(StrSlice.to_strings::lines)}|Rindented_codelines,Lempty->letn=min(Parser.indents)4inlets=StrSlice.offsetnsin{blocks;next=Rindented_code(StrSlice.to_strings::lines)}|Rindented_code_,_->process{blocks=close{blocks;next};next=Rempty}s|Rhtml((Hcontainslask),lines),_whenList.exists(funt->StrSlice.containsts)l->{blocks=close{blocks;next=Rhtml(k,StrSlice.to_strings::lines)};next=Rempty}|Rhtml(Hblank,_),Lempty->{blocks=close{blocks;next};next=Rempty}|Rhtml(k,lines),_->{blocks;next=Rhtml(k,StrSlice.to_strings::lines)}|Rblockquotestate,Lblockquotes->{blocks;next=Rblockquote(processstates)}|Rlist(kind,style,_,ind,items,state),Lempty->{blocks;next=Rlist(kind,style,true,ind,items,processstates)}|Rlist(_,_,true,ind,_,{blocks=[];next=Rempty}),_whenParser.indents>=ind->process{blocks=close{blocks;next};next=Rempty}s|Rlist(kind,style,prev_empty,ind,items,state),_whenParser.indents>=ind->lets=StrSlice.offsetindsinletstate=processstatesinletstyle=letrecnew_block=function|Rblockquote{blocks=[];next}|Rlist(_,_,_,_,_,{blocks=[];next})->new_blocknext|Rparagraph[_]|Rfenced_code(_,_,_,_,[],_)|Rindented_code[_]|Rhtml(_,[_])->true|_->falseinifprev_empty&&new_blockstate.nextthenLooseelsestylein{blocks;next=Rlist(kind,style,false,ind,items,state)}|(Rlist(kind,style,prev_empty,_,items,state),Llist_item(kind',ind,s))whensame_block_list_kindkindkind'->letstyle=ifprev_emptythenLooseelsestylein{blocks;next=Rlist(kind,style,false,ind,finishstate::items,processemptys)}|(Rlist_|Rblockquote_),_->(letrecloop=function|Rlist(kind,style,prev_empty,ind,items,{blocks;next})->(matchloopnextwith|Somenext->Some(Rlist(kind,style,prev_empty,ind,items,{blocks;next}))|None->None)|Rblockquote{blocks;next}->(matchloopnextwith|Somenext->Some(Rblockquote{blocks;next})|None->None)|Rparagraph(_::_aslines)->(matchclassify_lineswith|Parser.Lparagraph|Lindented_code_|Lsetext_heading{level=1;_}|Lhtml(false,_)->Some(Rparagraph(StrSlice.to_strings::lines))|_->None)|_->Noneinmatchloopnextwith|Somenext->{blocks;next}|None->process{blocks=close{blocks;next};next=Rempty}s)letprocesslink_defsstates=processlink_defsstate(StrSlice.of_strings)letof_channelic=letlink_defs=ref[]inletrecloopstate=matchinput_lineicwith|s->loop(processlink_defsstates)|exceptionEnd_of_file->letblocks=finishlink_defsstatein(blocks,List.rev!link_defs)inloopemptyletread_linesoff=letbuf=Buffer.create128inletrecloopcr_readoff=ifoff>=String.lengthsthen(Buffer.contentsbuf,None)elsematchs.[off]with|'\n'->(Buffer.contentsbuf,Some(succoff))|'\r'->ifcr_readthenBuffer.add_charbuf'\r';looptrue(succoff)|c->ifcr_readthenBuffer.add_charbuf'\r';Buffer.add_charbufc;loopfalse(succoff)inloopfalseoffletof_strings=letlink_defs=ref[]inletrecloopstate=function|None->letblocks=finishlink_defsstatein(blocks,List.rev!link_defs)|Someoff->lets,off=read_linesoffinloop(processlink_defsstates)offinloopempty(Some0)end