12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048(**************************************************************************)(* *)(* Copyright 2011 Jun Furuse *)(* Copyright 2012,2015 OCamlPro *)(* *)(* All rights reserved.This file is distributed under the terms of the *)(* GNU Lesser General Public License version 3.0 with linking *)(* exception. *)(* *)(* TypeRex is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* Lesser GNU General Public License for more details. *)(* *)(**************************************************************************)openNstreamopenApprox_lexeropenUtilmoduleNode=struct(* Node kind *)typekind=|KParen|KBrace|KBracket|KBracketBar|KLet|KAndofkind|KLetIn|KIn|KExprofint(* actually handles also patterns / types / ... *)(* Parameter:Priority - next expression is deindented if the op has
lower priority *)|KBodyofkind|KArrowofkind|KColon|KType|KException|KOpen|KInclude|KVal|KBarofkind|KUnknown|KStruct|KSig|KModule|KBegin|KObject|KMatch|KTry|KWithofkind|KLoop|KIf|KThen|KElse|KDo|KFun|KWhen|KExternal|KExtendedExprofstringlist|KExtendedItemofstringlist|KAttrIdofstringlist*bool|KComment(* Complete comment *)|KOCamldocCode(* Complete OCamldoc code *)(* Stores the original token and line offset for alignment of
comment continuations *)|KInCommentofNstream.token*int*bool(* no indent *)*boolref(* aligned stars at bol *)|KInOCamldocVerbatim|KInOCamldocCode|KInStringofbool(* do indent *)|KInQuotation|KInStringIndent|KInQuotationIndent|KInCommentIndent(* Priority of open expression constructs (see below for operators) *)letprio=function|KIn|KArrow_->0|KThen|KElse->10|KExpri->i|_->-10letprio_max=200letprio_dot=160letprio_apply=140letexpr_atom=KExprprio_maxletexpr_apply=KExpr140(* Special operators that should break arrow indentation have this prio
(eg monad operators, >>=) *)letprio_flatop=59letprio_semi=5letrecfollow=function|KAndk|KBodyk|KWithk->followk|k->kletrecstring_of_kind=function|KExpri->Printf.sprintf"KExpr(%d)"i|KParen->"KParen"|KBrace->"KBrace"|KBracket->"KBracket"|KBracketBar->"KBracketBar"(* | KField -> "KField" *)|KLet->"KLet"|KIn->"KIn"|KAndk->aux"KAnd"k|KLetIn->"KLetIn"|KBodyk->aux"KBody"k|KArrowk->aux"KArrow"k|KColon->"KColon"|KVal->"KVal"|KBark->aux"KBar"k|KOpen->"KOpen"|KInclude->"KInclude"|KUnknown->"KUnknown"|KType->"Ktype"|KException->"KException"|KStruct->"KStruct"|KSig->"KSig"|KModule->"KModule"|KBegin->"KBegin"|KObject->"KObject"|KMatch->"KMatch"|KTry->"KTry"|KWithk->aux"KWith"k|KLoop->"KLoop"|KIf->"KIf"|KThen->"Kthen"|KElse->"KElse"|KDo->"KDo"|KFun->"KFun"|KWhen->"KWhen"|KExternal->"KExternal"|KExtendedExprname->Printf.sprintf"KExtendedExpr(%s)"(String.concat"."(List.revname))|KExtendedItemname->Printf.sprintf"KExtendedItem(%s)"(String.concat"."(List.revname))|KAttrId(name,dotted)->Printf.sprintf"KAttrId(%s,%B)"(String.concat"."(List.revname))dotted|KComment->"KComment"|KOCamldocCode->"KOCamldocCode"|KInComment(_,_,b1,b2)->Printf.sprintf"KInComment(%B, %B)"b1!b2|KInOCamldocVerbatim->"KInOCamldocVerbatim"|KInOCamldocCode->"KInOCamldocCode"|KInStringb->Printf.sprintf"KInString(%b)"b|KInQuotation->"KInQuotation"|KInStringIndent->"KInStringIndent"|KInQuotationIndent->"KInQuotationIndent"|KInCommentIndent->"KInCommentIndent"andauxstrk=Printf.sprintf"%s(%s)"str(string_of_kindk)(* A node:
- has a kind
- has the current line offset [indent]
- has the current token offset [column]
- has a inner padding [pad]
- has a line count [count]
XXX XXX XXX [
XXX
]
XXX XXX XXX [
XXX
]
<indent>
<----------x-------->
<-pad->
<-pad->
*)typet={kind:kind;indent:int;(* expression starting column *)column:int;(* starting column of the token *)pad:int;(* padding: how much children should be indented from
current line *)line_indent:int;(* starting column of the current line *)line:int;(* starting line of the expression *)}letto_stringit=Printf.sprintf"%s%s %d|%d-%d-%d(%d)"(String.makei' ')(string_of_kindt.kind)t.linet.line_indentt.indentt.columnt.padletdefault={kind=KUnknown;indent=0;column=0;pad=0;line=0;line_indent=0;}letshiftnoden=letn=maxn(-node.indent)in{nodewithindent=node.indent+n;column=node.column+n}endmodulePath=structopenNodetypet=Node.tlistletto_stringt=String.concat" \027[35m/\027[m "(List.map(funn->Node.to_string0n)(List.revt))lettop=function|[]->Node.default|t::_->tletindent=function|[]->0|t::_->t.indentletpad=function|[]->0|t::_->t.padletmaptopf=function|[]|{kind=KInOCamldocCode}::_asl->l|t::l->ft::lletshiftpathn=maptop(funt->Node.shifttn)pathletin_string=function|{kind=KInString_}::_->true|{kind=KInStringIndent}::{kind=KInString_}::_->true|_->falseletrecis_indented_string=function|{kind=(KInStringindent)}::_->indent|{kind=(KInStringIndent)}::path->is_indented_stringpath|_->falseletin_quotation=function|{kind=(KInQuotation|KInQuotationIndent)}::_->true|_->falseletin_comment=function|{kind=(KInComment_|KInCommentIndent)}::_->true|_->falseletin_ocamldoc_verbatim=function|{kind=KInOCamldocVerbatim}::_->true|_->falseletin_non_indented_comment=function|{kind=KInComment(_,_,b,_)}::_->b|{kind=KInOCamldocVerbatim}::_->true|_->falseendopenNode(* A block is: *)typet={path:Path.t;(* a node path to go to this block *)last:Nstream.tokenlist;(* the last token of this block
(when a comment, it is stacked to keep the
last meaningful token)
Excludes EOL and ESCAPED_EOL. *)toff:int;(* the last token offset *)orig:int;(* the original starting column for this block *)newlines:int;(* how many consecutive EOL in the previous tokens ?
Special case: -1, means lat token = "ESCAPED_EOL" *)starts_line:bool;(* was the previous token preceded by EOL ? *)pp_stack:Path.tlist;}letshifttn={twithpath=Path.shiftt.pathn}letto_stringt=Path.to_stringt.pathletempty={path=[];last=[];toff=0;orig=0;newlines=1;starts_line=false;pp_stack=[];}(*
(* Does the token close a top LET construct ? *)
(* NB: we do this with another way below, but this one might be more robust *)
let rec close_top_let = function
| None -> true
| Some t ->
match t.token with
| COMMENT _ -> assert false (* COMMENT must be skipped *)
(* Tokens that allow a let-in after them *)
| AMPERSAND | AMPERAMPER | BARBAR | BEGIN | COLONCOLON | COLONEQUAL
| COMMA | DO | DOWNTO | ELSE | EQUAL | GREATER | IF | IN
| INFIXOP0 _ | INFIXOP1 _ | INFIXOP2 _ | INFIXOP3 _ | INFIXOP4 _
| LBRACE | LBRACELESS
| LBRACKET | LBRACKETBAR | LBRACKETLESS | LBRACKETGREATER
| LESS | LESSMINUS | LPAREN | MATCH | MINUS | MINUSDOT | MINUSGREATER | OR
| PLUS | PLUSDOT | QUESTION | QUESTIONQUESTION | SEMI | STAR | THEN
| TO | TRY | WHEN | WHILE
| TILDE -> false
| _ -> true
*)(* Go back to the node path until [f] holds *)letrecunwindfpath=matchpathwith|{kind}::_whenfkind->path|{kind=KAttrId_}::{kind}::_whenfkind->path(* never remove the KattrId following a KExtendedItem *)|{kind=KInOCamldocCode}::_->path|_::path->unwindfpath|[]->[](* Unwinds the path while [f] holds,
returning the last step for which it does *)letunwind_whilefpath=letrecauxacc=function|{kind}ash::pwhenfkind->auxhp|p->acc::pinmatchpathwith|{kind=KInOCamldocCode}::_->None|{kind}ash::pwhenfkind->Some(auxhp)|_->Nonelettop_kind=function|KStruct|KSig|KParen|KBegin|KObject|KExtendedItem_->true|_->falseletstritem_kind=function|KModule|KVal|KLet|KExternal|KType|KException|KOpen|KInclude->true|_->false(* Unwind the struct/sig top *)letunwind_top=unwindtop_kind(* Get the parent node *)letparent=function|[]|{kind=KInOCamldocCode}::_ast->t|_::t->tletrecskip_commentstream=matchNstream.nextstreamwith|None->stream|Some(token,stream)->matchtoken.tokenwith|COMMENT_CONTENT|EOL->skip_commentstream|COMMENT_CLOSE->stream|STRING_OPEN->letstream=skip_stringstreaminskip_commentstream|PPX_QUOTATION_OPEN->letstream=skip_ppx_quotationstreaminskip_commentstream|COMMENT_CODE_OPEN->letstream=skip_ocamldoc_codestreaminskip_commentstream|COMMENT_VERB_OPEN->letstream=skip_ocamldoc_verbatimstreaminskip_commentstream|EOF->stream|_->Printf.eprintf"Unexpected token: %s\n%!"(Approx_tokens.string_of_toktoken.token);assertfalseandskip_stringstream=matchNstream.nextstreamwith|None->stream|Some(token,stream)->matchtoken.tokenwith|STRING_CONTENT|EOL|ESCAPED_EOL->skip_stringstream|STRING_CLOSE->stream|EOF->stream|_->Printf.eprintf"Unexpected token: %s\n%!"(Approx_tokens.string_of_toktoken.token);assertfalseandskip_ppx_quotationstream=matchNstream.nextstreamwith|None->stream|Some(token,stream)->matchtoken.tokenwith|PPX_QUOTATION_CONTENT|EOL->skip_ppx_quotationstream|PPX_QUOTATION_CLOSE->stream|EOF->stream|_->Printf.eprintf"Unexpected token: %s\n%!"(Approx_tokens.string_of_toktoken.token);assertfalseandskip_p4_quotationstream=matchNstream.nextstreamwith|None->stream|Some(token,stream)->matchtoken.tokenwith|P4_QUOTATION_CONTENT|EOL->skip_p4_quotationstream|P4_QUOTATION_CLOSE->stream|EOF->stream|_->Printf.eprintf"Unexpected token: %s\n%!"(Approx_tokens.string_of_toktoken.token);assertfalseandskip_ocamldoc_codestream=matchNstream.nextstreamwith|None->stream|Some(token,stream)->matchtoken.tokenwith|COMMENT_CODE_CLOSE->stream|COMMENT_OPEN_CLOSE->skip_ocamldoc_codestream|COMMENT_OPEN|COMMENT_OPEN_EOL->letstream=skip_commentstreaminskip_ocamldoc_codestream|STRING_OPEN->letstream=skip_stringstreaminskip_ocamldoc_codestream|PPX_QUOTATION_OPEN->letstream=skip_ppx_quotationstreaminskip_ocamldoc_codestream|P4_QUOTATION_OPEN->letstream=skip_p4_quotationstreaminskip_ocamldoc_codestream|EOF->stream|_->skip_ocamldoc_codestreamandskip_ocamldoc_verbatimstream=matchNstream.nextstreamwith|None->stream|Some(token,stream)->matchtoken.tokenwith|COMMENT_VERB_CLOSE->stream|COMMENT_CODE_OPEN->letstream=skip_commentstreaminskip_ocamldoc_verbatimstream|STRING_OPEN->letstream=skip_stringstreaminskip_ocamldoc_verbatimstream|PPX_QUOTATION_OPEN->letstream=skip_ppx_quotationstreaminskip_ocamldoc_verbatimstream|COMMENT_CONTENT|EOL->skip_ocamldoc_verbatimstream|EOF->stream|_->Printf.eprintf"Unexpected token: %s\n%!"(Approx_tokens.string_of_toktoken.token);assertfalse(* Get the next token, skipping comments (and in-comment tokens) *)letrecnext_token_full?(newlines=0)stream=matchNstream.nextstreamwith|None->None|Some({token=COMMENT_OPEN_CLOSE},stream)->next_token_fullstream|Some({token=(COMMENT_OPEN|COMMENT_OPEN_EOL)},stream)->next_token_full(skip_commentstream)|Some({token=EOL},stream)->next_token_full~newlines:(newlines+1)stream|Some(tok,stream)->Some(tok,newlines,stream)letnext_tokenstream=matchnext_token_fullstreamwith|None->None|Some(t,_,_)->Somet.tokenletlast_tokent=letrecloop=function|[]->None|{token=COMMENT_CLOSE}::tokens->looptokens|t::_->Somet.tokeninloopt.lastletrecskip_string_contentstream=matchNstream.nextstreamwith|Some({token=STRING_CONTENT},stream)->skip_string_contentstream|_->stream(* a more efficient way to do this would be to store a
"context-type" in the stack *)letrecis_inside_typepath=matchunwind(function|KParen|KBegin|KBracket|KBrace|KBracketBar|KVal|KLet|KLetIn|KBody(KVal|KLet|KLetIn)|KBody(KType|KExternal)|KColon|KStruct|KSig|KObject->true|_->false)pathwith|{kind=KBody(KVal|KType|KExternal)|KColon}::_->true|{kind=KParen|KBegin|KBracket|KBrace}::p->is_inside_typep|_->false(* Returns None if the current token ends a line, the offset of
the next token otherwise *)letnext_offsettokstream=matchnext_token_fullstreamwith|None->None|Some(next,_,_)->ifRegion.end_linetok.region<Region.start_linenext.regionthenNoneelseSomenext.offsetletreset_padding?(pad=0)path=Path.maptop(funn->{nwithpad})pathletreset_line_indentconfigcurrent_linepath=letlimit_overindent=matchconfig.IndentConfig.i_max_indentwith|Somem->letm=max0(m-config.IndentConfig.i_base)infuni->minim|None->funi->iinletrecauxacc=function|{line}ast::rwhenline=current_line->aux(t::acc)r|p->letp,acc,extra=matchaccwith|{kind=KParen|KBracket|KBrace|KBracketBar}asacc1::accwhenacc1.line_indent=acc1.column->(* ignore those if at start of line *)acc1::p,acc,acc1.pad|_->p,acc,0inList.fold_left(funpt->{twithindent=t.line_indent+limit_overindent(t.indent-t.line_indent)+extra}::p)paccinaux[]pathletdumpt=Printf.eprintf"\027[35m# \027[32m%d%8S\027[m %d; %s\n%!"(matcht.lastwithtok::_->(String.lengthtok.between)|_->0)(matcht.lastwithtok::_->shorten_string30tok.substr|_->"")t.newlines(to_stringt)(* different kinds of position:
[T]: token aligned: the child is aligned with the token position
[L]: line aligned: the child is aligned with the begining of line
[A]: absolute position *)typepos=L|T|Aofint(* position *)(* indent configuration of the infix operators *)letop_prio_align_indentconfig=letopenIndentConfiginletalign,indent=matchconfig.i_align_opswith|true->T,0|false->L,config.i_baseinletis_monadops=matchString.subs0(min2(String.lengths))with|">>"|">|"|"@@"|"@>"->true|_->falseinletis_monadops=is_monadops(* "*>>=", "+>>>", "/>>|", etc. *)||(String.lengths>3&&is_monadop(String.subs12))infunction(* anything else : -10 *)(* in -> : 0 *)|SEMI->prio_semi,L,-2(* special negative indent is only honored at beginning of line *)(* then else : 10 *)|BAR->10,T,-2|OF->20,L,2|LESSMINUS|COLONEQUAL->20,L,config.i_base|COMMA->30,align,-2|MINUSGREATER->32,L,0(* is an operator only in types *)|COLON->35,T,config.i_base|COLONGREATER->35,L,config.i_base|OR|BARBAR->40,T,0|AMPERSAND|AMPERAMPER->50,T,0|(INFIXOP0s|INFIXOP1s|INFIXOP2s|INFIXOP3s|INFIXOP4s)(* these should deindent fun -> *)whenis_monadops->prio_flatop,L,0|INFIXOP0s->(matchString.subs0(min2(String.lengths))with|"|!"|"|>"->prio_flatop,T,0|_->60,align,indent)|EQUAL|LESS|GREATER->60,align,0|INFIXOP1_->70,align,indent|COLONCOLON->80,align,indent|INFIXOP2_|PLUSDOT|PLUS|MINUSDOT|MINUS->90,align,indent|INFIXOP3_|STAR->100,align,indent|INFIXOP4_->110,align,indent(* apply: 140 *)|AS->prio_apply,L,0|TILDE|QUESTION->prio_apply,L,config.i_base|LABEL_|OPTLABEL_->ifconfig.i_align_params=Alwaysthen145,T,config.i_baseelse145,L,config.i_base|SHARP->150,align,config.i_base|DOT->prio_dot,align,config.i_base|token->Printf.eprintf"Unexpected token: %s\n%!"(Approx_tokens.string_of_toktoken);assertfalselethandle_dottedblocktok=letstarts_line=block.newlines<>0inletcurrent_line=Region.start_linetok.regioninletis_attr_id=function|{kind=KAttrId(_,dotted)}::_->notdotted|_->falseinletmake_dotted_attr_id=function|{kind=KAttrId(names,_)}asnode::({kind=(KExtendedItem[]|KExtendedExpr[])}::_aspath)->{nodewithkind=KAttrId(names,true)}::path|_->assertfalseinletis_dotted_attr_id=function|{kind=KExtendedExpr[]}::_->true|{kind=KExtendedItem[]}::_->true|{kind=KAttrId(_,dotted)}::_->dotted|_->falseinletmake_attr_idname=function|({kind=(KExtendedItem[]|KExtendedExpr[]);indent;pad;}::_aspath)->letindent=ifstarts_linethenindent+padelseindent+pad+String.lengthtok.between-1inletcolumn=ifstarts_linethenindentelseblock.toff+tok.offsetin{kind=(KAttrId([name],false));indent;line_indent=indent;column;line=current_line;pad=0}::path|({kind=KAttrId(names,_)}asnode)::path->{nodewithkind=KAttrId(name::names,false);}::path|_->assertfalseinifis_dotted_attr_idblock.paththenmatchtok.tokenwith|LIDENTs|UIDENTs->Some(make_attr_idsblock.path)|AND|AS|ASSERT|BEGIN|CLASS|CONSTRAINT|DO|DONE|DOWNTO|ELSE|END|EXCEPTION|EXTERNAL|FALSE|FOR|FUN|FUNCTION|FUNCTOR|IF|IN|INCLUDE|INHERIT|INITIALIZER|LAZY|LET|MATCH|METHOD|MODULE|MUTABLE|NEW|OBJECT|OF|OPEN|OR|PRIVATE|REC|SIG|STRUCT|THEN|TO|TRUE|TRY|TYPE|VAL|VIRTUAL|WHEN|WHILE|WITH->Some(make_attr_idtok.substrblock.path)|_->Noneelseifis_attr_idblock.paththenmatchtok.tokenwith|DOT->Some(make_dotted_attr_idblock.path)|_->NoneelseNone(* Take a block, a token stream and a token.
Return the new block stack. *)letrecupdate_pathconfigblockstreamtok=letopenIndentConfiginletstarts_line=block.newlines<>0inletcurrent_line=Region.start_linetok.regioninletnodereplacekindpospadpath=letparent=Path.toppathinifstarts_linethenletindent=matchposwith|Ap->p|L->parent.indent+ifreplacethen0elseparent.pad|T->parent.column+ifreplacethen0elseparent.padin{kind;indent;line_indent=indent;column=indent;pad;line=current_line}elseletcolumn=block.toff+tok.offsetin{kind;indent=parent.indent;line_indent=parent.line_indent;column;pad;line=current_line}in(* Add a new child block *)letappendkindpos?(pad=config.i_base)=function|{kind=KAttrId(names,_)}::({kind=KExtendedItem[]|KExtendedExpr[]}asn)::path->letn={nwithkind=matchn.kindwith|KExtendedItem[]->KExtendedItem(List.revnames)|KExtendedExpr[]->KExtendedExpr(List.revnames)|_->assertfalse}inletpath={nwithpad=config.i_ppx_stritem_ext}::pathinnodefalsekindpospadpath::path|path->nodefalsekindpospadpath::pathin(* replace the current block with a new one *)letreplacekindpos?(pad=config.i_base)path=matchpathwith|[]|{kind=KInOCamldocCode}::_->nodetruekindpospadpath::path|_::t->nodetruekindpospadpath::tin(* Used when expressions are merged together (for example in "3 +" the "+"
extends the lower-priority expression "3") *)letextendkindpos?(pad=config.i_base)=function|[]|{kind=KInOCamldocCode}::_aspath->nodetruekindpospadpath::path|h::p->letnegative_indent()=(* Special negative indent: relative, only at beginning of line,
and when prio is changed or there is a paren to back-align to *)ifpad>=0||notstarts_linethenNoneelsematchpwith|{kind=KParen|KBracket|KBracketBar|KBrace|KBar_|KWithKBrace|KBody_}asparen::_whenparen.line=h.line->letparen_len=matchparen.kindwith|KParen|KBracket|KBrace|KBar_|KBody_->1|KBracketBar->2|KWithKBrace->4|_->assertfalseinletindent=paren.column+paren_len+1(* usually 1 space *)+padinSome({hwithkind;indent;column=indent;line_indent=indent-pad;pad=maxh.pad(h.indent-indent)}::p)|_->matchkind,h.kindwith|KExprpk,KExprphwhenph=pk->(* respect the indent of the above same-priority term, we
assume it was already back-indented *)Some({hwithkind;indent=h.column;column=h.column;line_indent=h.column;pad=h.pad}::p)|_->letindent=h.column+padinifindent<0thenNoneelseSome({hwithkind;indent;column=indent;line_indent=indent;pad=-pad}::p)inmatchnegative_indent()with|Somep->p|None->(* normal case *)(* change indent to set the starting column of the expression *)letpad=max0padinletindent,pad=ifpos=Tthenh.column,padelse(* set indent of the whole expr accoring to its parent *)Path.indentp+Path.padp,padinletline_indent=ifstarts_linethenindentelseh.line_indentin{hwithkind;indent;line_indent;pad}::pin(* use before appending a new expr_atom: checks if that may cause an
apply and folds parent exprs accordingly *)letfold_exprpath=matchpathwith|{kind=KExpr_}ase::({kind=KFun}asfn)::p->{fnwithline_indent=e.line_indent}::p|{kind=KExpri}ase::_wheni=prio_max->(* we are appending two expr_atom next to each other:
this is an apply. *)(* this "folds" the left-side of the apply *)letp=matchunwind_while(funkind->priokind>=prio_apply)pathwith|Some({kind=KExpri}ase1::p)wheni=prio_apply->{e1withline_indent=e.line_indent}::p|Some({kind=KExpr_;line}::{kind=KModule|KInclude|KOpen|KBodyKModule}::_asp)->(* ignore align_params for functor application *)extend(KExprprio_apply)L(reset_line_indentconfiglinep)|Some({kind=KExpr_;line}::{kind=KArrow(KMatch|KTry)|KTry|KMatch;line=arrow_line}::_asp)whenconfig.i_align_params=Auto&&line=arrow_line->(* Special case: switch to token-aligned (see test js-args) *)extend(KExprprio_apply)Tp|Somep->extend(KExprprio_apply)(ifconfig.i_align_params=AlwaysthenTelseL)p|None->assertfalseinp|_->pathinletbefore_append_atom=function|{kind=KWith(KTry|KMatchasm)}::parentaspath->(* Special case: 'match with' and no bar for the 1st case:
we append a virtual bar for alignment *)letpath=matchparentwith|{kind=KExpri}::_wheni=prio_flatop->reset_paddingpath|_->pathinletp=append(KBarm)L~pad:2pathinifnotstarts_linethenletcolumn=max0(block.toff+tok.offset-2)inPath.maptop(funh->{hwithcolumn})pelsep|path->fold_exprpathinletatompath=letpath=before_append_atompathinletpad=matchpathwith{kind=KExpr_;pad}::_->pad|_->config.i_baseinappendexpr_atomL~padpathinletopen_parenkindpath=letpath=before_append_atompathinletpath=matchnext_offsettokstreamwith|None(* EOL *)->reset_line_indentconfigcurrent_linepath|Some_->pathinletp=appendkindLpathinletp=matchpwith(* Special case: paren after arrow has extra indent
(see test js-begin) *)|{kind=KParen|KBegin|KBracket|KBracketBar|KBrace}::{kind=KArrow_}::_whennotstarts_line->Path.shiftpconfig.i_base|p->pinmatchpwith|[]->[]|h::paspath->matchkindwith|KBegin->path|KParenwhenifnotconfig.i_align_opsthennotstarts_lineelsematchnext_tokenstreamwith|Some(SIG|STRUCT|OBJECT)->true|_->false->path|_->(* set alignment for next lines relative to [ *)(matchnext_offsettokstreamwith|Somepad->letindent=ifstarts_linethenh.indentelseblock.toff+tok.offsetin{hwithindent;column=indent;pad}::p|None->ifstarts_linethenpathelse{hwithcolumn=h.indent+h.pad}::p)inletclosefpath=(* Remove the padding for the closing brace/bracket/paren/etc. *)Path.maptop(funh->{hwithkind=expr_atom;pad=0})(unwindfpath)inletmake_infixtokpath=letop_prio,align,indent=op_prio_align_indentconfigtok.tokeninletin_record=matchunwind_while(funkind->priokind>=op_prio)pathwith|Some({kind=KExpr_}::{kind=KBrace}::_)->true|_->falsein(* special cases *)letindent=(* don't back-indent operators when alone on their line
(except BAR because that would disrupt typing) *)ifindent<0&&tok.token<>BAR&¬(tok.token=SEMI&&in_record)&&next_offsettokstream=Nonethen0elseindentinmatchpathwith|{kind=KExprprio}::_whenprio>=op_prio&&prio<prio_max->(* we are just after another operator (should be an atom).
handle as unary (eg. x + -y) : indented but no effect
on following expressions *)(* append KUnknown L path *)append(KExprprio)L~pad:(max0indent)path|_->matchunwind_while(funkind->priokind>=op_prio)pathwith|Somep->extend(KExprop_prio)align~pad:indentp|None->(* used as prefix ? Don't apply T indent *)append(KExprop_prio)L~pad:(max0indent)pathin(* KComment/KUnknown nodes correspond to comments or top-level stuff, they
shouldn't be taken into account when indenting the next token *)letblock0=blockinletblock=matchblock.pathwith|{kind=KUnknown}::path|{kind=KInStringIndent}::path|{kind=KInQuotationIndent}::path|{kind=KInCommentIndent}::path|{kind=KOCamldocCode}::path|{kind=KComment}::path->{blockwithpath}|_->blockinletcompute_string_indenttok=ifPath.is_indented_stringblock.path&&block.newlines<0then(* Previous line finished with an '\'. *)iftok.token=STRING_CLOSE||(String.lengthtok.substr>=2&&tok.substr.[0]='\\'&&tok.substr.[1]=' ')thenA(Path.topblock.path).indentelseLelseA(String.lengthtok.between)inlet(>>!)optf=matchoptwithSomex->x|None->f()inhandle_dottedblocktok>>!fun()->matchtok.tokenwith(* Comments *)|COMMENT_OPEN_EOL|COMMENT_OPEN|COMMENT_OPEN_CLOSE->beginletno_indent=tok.token=COMMENT_OPEN_EOL&¬config.i_strict_commentsinletnodecol=iftok.token=COMMENT_OPEN_CLOSEthenKCommentelseKInComment(tok,col,no_indent,reftrue)inlets=tok.substrinletpad=ifno_indentthen0elseletlen=String.lengthsinleti=ref2inwhile!i<len&&s.[!i]='*'doincridone;while!i<len&&s.[!i]=' 'doincridone;iftok.token=COMMENT_OPEN_EOLthen3else!iinifnotstarts_linethenletcol=block.toff+tok.offsetinPath.maptop(funn->{nwithindent=col})(append(nodecol)L~padblock.path)elsematchblock.pathwith|{kind=KExpri}::_wheni=prio_max->beginletblocklevel()=letp=unwind_topblock.pathinletcol=Path.indentp+Path.padpinappend(nodecol)(Acol)~padblock.pathinletstream=iftok.token=COMMENT_OPEN_CLOSEthenstreamelseskip_commentstreaminmatchnext_token_fullstreamwith|None->blocklevel()|Some(* full block-closing tokens + newline *)({token=SEMISEMI|DONE|END|GREATERRBRACE|GREATERRBRACKET|RBRACE|RBRACKET|RPAREN},_,_)whenblock.newlines>1->blocklevel()|Some(* semi block-closing tokens *)({token=SEMISEMI|DONE|END|GREATERRBRACE|GREATERRBRACKET|RBRACE|RBRACKET|RPAREN|THEN|ELSE|IN|EQUAL},_,_)whenblock.newlines<=1->(* indent as above *)letcol=(Path.topblock0.path).line_indentinappend(nodecol)(Acol)~padblock.path|next->(* indent like next token, _unless_ we are directly after a
case in a sum-type *)letalign_bar=ifblock.newlines>1||not(is_inside_typeblock.path)thenNoneelseletfind_bar=unwind_while(functionKBar_|KExpr_->true|_->false)block0.pathinmatchfind_barwith|Some({kind=KBar_;column}::_)->Somecolumn|_->Noneinmatchalign_barwith|Someindent->append(nodeindent)(Aindent)~padblock.path|None->(* recursive call to indent like next line *)letcol=matchnextwith|Some({token=EOF},_,_)|None->Path.indent[]|Some(next,newlines,stream)->letnewlines=newlines+block.newlinesinletpath=update_pathconfig{blockwithnewlines}streamnextinifnext.token=COMMENT_CODE_CLOSEthenPath.indentpath+Path.padpathelsePath.indentpathinappend(nodecol)(Acol)~padblock.pathend|_->letcol=Path.indentblock.path+Path.padblock.pathinappend(nodecol)(Acol)~padblock.pathend|COMMENT_CONTENT|STRING_OPEN|STRING_CONTENT|STRING_CLOSE|PPX_QUOTATION_OPEN|PPX_QUOTATION_CONTENT|PPX_QUOTATION_CLOSE|P4_QUOTATION_OPEN|P4_QUOTATION_CONTENT|P4_QUOTATION_CLOSEwhen(Path.in_commentblock.path||Path.in_ocamldoc_verbatimblock.path)&&block.newlines=0->block.path|COMMENT_CONTENT|STRING_OPEN|STRING_CONTENT|STRING_CLOSE|PPX_QUOTATION_OPEN|PPX_QUOTATION_CONTENT|PPX_QUOTATION_CLOSE|P4_QUOTATION_OPEN|P4_QUOTATION_CONTENT|P4_QUOTATION_CLOSEwhenPath.in_non_indented_commentblock.path->letcol=String.lengthtok.betweeninappendKInCommentIndent(Acol)~pad:0block.path|COMMENT_CONTENT|STRING_OPEN|STRING_CONTENT|STRING_CLOSE|PPX_QUOTATION_OPEN|PPX_QUOTATION_CONTENT|PPX_QUOTATION_CLOSE|P4_QUOTATION_OPEN|P4_QUOTATION_CONTENT|P4_QUOTATION_CLOSEwhenPath.in_ocamldoc_verbatimblock.path->letcol=String.lengthtok.betweeninappendKInCommentIndent(Acol)~pad:0block.path|COMMENT_CONTENT|STRING_OPEN|STRING_CONTENT|STRING_CLOSE|PPX_QUOTATION_OPEN|PPX_QUOTATION_CONTENT|PPX_QUOTATION_CLOSE|P4_QUOTATION_OPEN|P4_QUOTATION_CONTENT|P4_QUOTATION_CLOSEwhenPath.in_commentblock.path->beginmatchblock.pathwith|{kind=KInComment({region},_,false,aligned_star);indent;pad;column}::_->letorig_col=Region.start_columnregioninletcol=String.lengthtok.betweeninletrelative_col=col-(orig_col+pad)inletappend_indent()=letcol=ifrelative_col>0&¬config.i_strict_commentsthenindent+pad+relative_colelsecolumn+padinaligned_star:=false;appendKInCommentIndent(Acol)block.pathinifnotstarts_linethenblock.pathelseiftok.substr<>""thenappend_indent()elsebeginmatchNstream.nextstreamwith|None->block.path|Some({token=COMMENT_CONTENT;substr="*"},_)when!aligned_star->appendKInCommentIndent(A(indent+1))block.path|Some({token=COMMENT_VERB_OPEN},_)->aligned_star:=false;appendKInCommentIndentT~pad:0block.path|Some({token=EOL},_)->aligned_star:=false;appendKInCommentIndent(A0)block.path|Some_->append_indent()end|_->Printf.eprintf"Unexpected stack: %s\n%!"(Path.to_stringblock.path);assertfalseend|COMMENT_CONTENT->Printf.eprintf"Unexpected stack: %s\n%!"(Path.to_stringblock.path);assertfalse|COMMENT_VERB_OPEN->beginmatchblock.pathwith|{kind=KInComment(tok,_,_,_);indent;pad}::_->{kind=KInOCamldocVerbatim;line=Region.start_linetok.region;indent=indent+pad;line_indent=indent+pad;column=indent+pad;pad=0}::block.path|_->Printf.eprintf"Unexpected stack: %s\n%!"(Path.to_stringblock.path);assertfalseend|COMMENT_VERB_CLOSE->assert(Path.in_ocamldoc_verbatimblock.path);List.tlblock.path|COMMENT_CODE_OPEN->letindent=ifstarts_linethenPath.indentblock0.path+Path.padblock0.pathelsePath.indentblock0.pathinletpath={kind=KInOCamldocCode;line=Region.start_linetok.region;indent=indent;line_indent=indent;column=indent;pad=config.i_base}::block.pathinpath|COMMENT_CODE_CLOSE->beginmatchunwind(fun_->false)block.pathwith|{kind=KInOCamldocCode}::pathaspath0->nodetrueKOCamldocCodeTconfig.i_basepath0::path|_->assertfalseend|COMMENT_CLOSEwhenblock.newlines>=1&&Path.in_non_indented_commentblock.path->letcol=String.lengthtok.betweeninreplaceKComment~pad:0(Acol)block.path|COMMENT_CLOSE->ifnot(Path.in_commentblock.path)thenbeginPrintf.eprintf"Unexpected stack: %s\n%!"(Path.to_stringblock.path);assertfalseend;(* TODO config for pad ?? *)replaceKComment~pad:0Lblock.path|_whenPath.in_commentblock.path->Printf.eprintf"Unexpected token: %s\n%!"(Approx_tokens.string_of_toktok.token);assertfalse(* Strings *)|STRING_OPEN->letindent=matchNstream.nextstreamwith|Some({token=ESCAPED_EOL}astok,_)->String.lengthtok.between<>0|Some({token=STRING_CONTENT},stream)->beginletstream=skip_string_contentstreaminmatchNstream.nextstreamwith|Some({token=ESCAPED_EOL},_)->true|_->falseend|_->falseinletpath=before_append_atomblock.pathinappend~pad:1(KInStringindent)Lpath|STRING_CONTENT->assert(Path.in_stringblock.path);ifstarts_linethenletkind=compute_string_indenttokinappendKInStringIndentkindblock.pathelseblock.path|STRING_CLOSE->beginassert(Path.in_stringblock.path);letpad=matchblock.pathwith|_::{kind=KExpr_;pad}::_->pad|_->config.i_baseinletpath=matchreplaceexpr_atomT~padblock.pathwith|[]->assertfalse|node::path->(* Revert node's column to the one of "STRING_OPEN". *){nodewithcolumn=(Path.topblock.path).column}::pathinifstarts_linethenletkind=compute_string_indenttokinappendKInStringIndentkindpathelsepathend|_whenPath.in_stringblock.path->Printf.eprintf"Unexpected token: %s\n%!"(Approx_tokens.string_of_toktok.token);assertfalse(* Quotations *)|PPX_QUOTATION_OPEN|P4_QUOTATION_OPEN->letpath=before_append_atomblock.pathinappendKInQuotationLpath|PPX_QUOTATION_CONTENT|P4_QUOTATION_CONTENT->assert(Path.in_quotationblock.path);ifblock.newlines=0thenblock.pathelseletkind=ifblock.newlines<0thenTelseA(String.lengthtok.between)inappendKInQuotationIndentkindblock.path|PPX_QUOTATION_CLOSE|P4_QUOTATION_CLOSE->assert(Path.in_quotationblock.path);letpad=matchblock.pathwith|_::{kind=KExpr_;pad}::_->pad|_->config.i_baseinreplaceexpr_atomL~padblock.path|_whenPath.in_quotationblock.path->Printf.eprintf"Unexpected token: %s\n%!"(Approx_tokens.string_of_toktok.token);assertfalse(* General cases *)|SEMISEMI->appendKUnknownL~pad:0(unwind_topblock.path)|INCLUDE->appendKIncludeL(unwind_topblock.path)|EXCEPTION->(matchlast_tokenblockwith|SomeLET->appendKUnknownLblock.path(* let exception *)|_->letp=unwind(functionKExpr_->false|_->true)block.pathin(matchpwith|{kind=KWithKMatch|KBarKMatch}::_->appendexpr_atomLblock.path|_->appendKExceptionL(unwind_topblock.path)))|BEGIN->open_parenKBeginblock.path|OBJECT->appendKObjectLblock.path|VAL->appendKValL(unwind_topblock.path)|MATCH->letp=fold_exprblock.pathinifstarts_linethenappendKMatchLpelseletenforce_strict=config.i_strict_with=Always||config.i_strict_with=Auto&&matchpwith|{kind=KBegin;indent;column}::_->column=indent|_->falseinletp,pad=ifenforce_strictthenletp=reset_line_indentconfigcurrent_linepinreset_paddingp,config.i_baseelsep,Path.padp+config.i_baseinappendKMatchL~padp|TRY->letp=fold_exprblock.pathinifstarts_linethenappendKTryLpelseletenforce_strict=config.i_strict_with=Always||config.i_strict_with=Auto&&matchpwith|{kind=KBegin;indent;column}::_->column=indent|_->falseinletp,pad=ifenforce_strictthenletp=reset_line_indentconfigcurrent_linepinreset_paddingp,config.i_baseelsep,Path.padp+config.i_baseinappendKTryL~padp|LPAREN->open_parenKParenblock.path|LBRACKET|LBRACKETGREATER|LBRACKETLESS->open_parenKBracketblock.path|LBRACKETPERCENT|LBRACKETAT->letpath=before_append_atomblock.pathinappend~pad:4(KExtendedExpr[])Lpath|LBRACKETATAT->letpath=(unwind(functionKBodyk|k->top_kindk||stritem_kindk)block.path)inletpath=matchpathwith|{kind=KBodyk|k}::p->iftop_kindkthenpathelsep|[]->[]inappend~pad:4(KExtendedItem[])Lpath|LBRACKETPERCENTPERCENT|LBRACKETATATAT->append~pad:4(KExtendedItem[])L(unwind_topblock.path)|LBRACKETBAR->open_parenKBracketBarblock.path|LBRACE|LBRACELESS->open_parenKBraceblock.path|FUNCTION->(matchfold_exprblock.pathwith|l::_aspwhennotstarts_line&&l.kind<>KExpr0&&(config.i_strict_with=Never||config.i_strict_with=Auto&&l.kind<>KBegin)->letp=reset_line_indentconfigcurrent_linepinappend(KWithKMatch)L~pad:(max(maxl.padconfig.i_base)config.i_with)p|p->letp=reset_line_indentconfigcurrent_linepinappend(KWithKMatch)L~pad:config.i_withp)|FUN|FUNCTOR->(matchblock.pathwith|{kind=KArrowKFun}::path->letpath=unwind(functionKFun->true|_->false)pathin(matchpathwith|{line;column;line_indent}::_whenline=current_line||column=line_indent->replaceKFunLpath|_->appendKFunLblock.path)|p->appendKFunL(fold_exprp))|STRUCT|SIG->letk=matchtok.tokenwith|STRUCT->KStruct|SIG->KSig|_->assertfalseinletexpr_start=unwind(functionKParen|KLet|KLetIn|KBody_->true|_->false)block.pathinletindent=matchexpr_startwith|{kind=KParen}::{kind=KExprprio;line;indent}::_whenprio=prio_apply&&line=current_line->indent|_->Path.indentblock.pathinPath.maptop(funn->{nwithindent})(appendkL(reset_paddingblock.path))|WHEN->appendKWhenL~pad:(config.i_base+ifstarts_linethen0else2)(unwind(function|KWith(KTry|KMatch)|KBar(KTry|KMatch)|KFun->true|_->false)block.path)|OPEN->iflast_tokenblock=SomeLETthenappendKOpenLblock.pathelseappendKOpenL(unwind_topblock.path)|LET->(* Two ways to detect let vs letin ;
both seem to work, but need to check which one
is the most robust (for example w.r.t. unfinished expressions) *)(* - it's a top Let if it is after a closed expression *)(matchblock.pathwith|{kind=KExpri}::pwheni=prio_max->appendKLetL(unwind_topp)|[]|{kind=KInOCamldocCode}::_asp->appendKLetL(unwind_topp)|_->appendKLetInL(fold_exprblock.path))(* - or if after a specific token *)(* if close_top_let block.last then *)(* append KLet L config.i_base (unwind_top block.path) *)(* else *)(* append KLetIn L config.i_base (fold_expr block.path) *)|CLASS->appendKLetL(unwind_topblock.path)|METHOD->appendKLetL(unwind_topblock.path)|INITIALIZER->append(KBodyKLet)L(unwind_topblock.path)|CONSTRAINT->letpath=unwind(functionKType|KBodyKType|KObject->true|_->false)block.pathinappendKLetLpath|AND->letunwind_to=function|KLet|KLetIn|KType|KModule->true|_->falseinletpath=unwind(unwind_to@*follow)block.pathin(matchpathwith|[]|{kind=KInOCamldocCode}::_->append(KAndKUnknown)Lpath|{kind=KType|KModule|KBody(KType|KModule)}::({kind=KWith_}asm)::p->(* hack to align "and" with the 'i' of "with": consider "with" was
1 column further to the right *)letm=ifstarts_linethen{mwithcolumn=m.column+1}elseminreplace(KAndm.kind)T~pad:0(m::p)|{kind=KType|KModule|KBody(KType|KModule)}::({kind=KAnd(KWith_)}asm)::p->replacem.kindT~pad:0(m::p)|h::_->append(KAnd(followh.kind))L(parentpath))|IN->letpath=unwind((functionKLetIn|KLet->true|_->false)@*follow)block.pathinletpad=matchnext_tokenstreamwith|SomeLET->0|_->config.i_inin(matchunwind_while((=)KIn)(parentpath)with|Somep->extendKInL~padp|None->extendKInL~padpath)|TYPE->(matchlast_tokenblockwith|Some(MODULE|CLASS)->appendKUnknownLblock.path(* module type *)|Some(WITH|AND)|SomeCOLON(* 'type' inside type decl, for GADTs *)->appendKTypeLblock.path|_->appendKTypeL(unwind_topblock.path))|MODULE->(matchlast_tokenblockwith|SomeLET->appendKUnknownLblock.path(* let module *)|SomeCOLON|SomeEQUALwhennext_tokenstream=SomeTYPE->appendKUnknownLblock.path(* : module type of *)|Some(WITH|AND)->appendKTypeLblock.path|_->appendKModuleL(unwind_topblock.path))|END->close(functionKStruct|KSig|KBegin|KObject->true|_->false)block.path|WITH->(matchnext_token_fullstreamwith|Some({token=TYPE|MODULEastm},_,_)->letpath=unwind(function|KModule|KOpen|KInclude|KParen|KBegin|KColon|KBodyKModule->true|_->false)block.pathinletkind=matchtmwithTYPE->KType|MODULE->KModule|_->assertfalseinappend(KWithkind)Lpath|next->letpath=unwind(function|KTry|KMatch|KVal|KType|KBodyKType|KException(* type-conv *)|KColon|KBrace->true|KWithKTry->(* useful for lwt's try-finally *)tok.substr="finally"|_->false)block.pathinmatchpathwith|{kind=KBrace;pad}::_->(matchnextwith|Some(next,_,_)whenRegion.start_linenext.region=Region.end_linetok.region->Path.maptop(funn->{nwithindent=n.column})(append(KWithKBrace)L~pad:next.offsetpath)|_->append(KWithKBrace)L~pad:(pad+config.i_with)path)|{kind=KVal|KType|KExceptionaskind}::_->replace(KWithkind)Lpath|{kind=KTry|KMatchaskind}asn::parent::_whenn.line=current_line&&n.column<>n.line_indent&&config.i_strict_with<>Always->letpath,pad=ifparent.line_indent=parent.columnthenpath,maxparent.padconfig.i_withelsereset_line_indentconfign.linepath,maxconfig.i_with(ifparent.pad>0thenconfig.i_baseelse0)inreplace(KWithkind)L~padpath|{kind=(KTry|KMatchaskind)}::p->ifstarts_linethenappend(KWithkind)L~pad:config.i_withpelsereplace(KWithkind)L~pad:config.i_with(reset_line_indentconfigcurrent_linepath)|{kind=KColon}::_asp->(* may happen with sexp extension, 'with default' *)appendexpr_atomLp|_->path)|IF->(matchlast_tokenblockwith|SomeELSE->replaceKIfLblock.path|_->appendKIfL(fold_exprblock.path))|THEN->extendKThenL(unwind((=)KIf)block.path)|ELSE->letpad=matchconfig.i_strict_elsewith|Always->config.i_base|Never->ifnext_offsettokstream<>Nonethenconfig.i_baseelse0|Auto->ifnext_offsettokstream<>Nonethenconfig.i_baseelsematchnext_tokenstreamwith|Some(LET|MATCH|TRY|FUN|FUNCTION)->0|_->config.i_baseinextendKElseL~pad(unwind((=)KThen)block.path)|WHILE|FOR->appendKLoopL(fold_exprblock.path)|TO|DOWNTO->letp=Path.maptop(funn->{nwithindent=n.indent+config.i_base})(unwind((=)KLoop)block.path)inreplaceKLoopLp|DO->extendKDoL(unwind((=)KLoop)block.path)|DONE->close((=)KDo)block.path|BARRBRACKET->close((=)KBracketBar)block.path|RPAREN->close((=)KParen)block.path|RBRACE|GREATERRBRACE->close((=)KBrace)block.path|RBRACKET->close(function|KBracket|KExtendedItem_|KExtendedExpr_->true|_->false)block.path|GREATERRBRACKET->close((=)KBracket)block.path|BAR->letpath=unwind(function|KParen|KBegin|KBracket|KBrace|KBracketBar|KWith(KMatch|KTry)|KBar(KMatch|KTry)|KArrow(KMatch|KTry)|KLet|KLetIn|KBody(KType)->true|_->false)block.pathin(matchpathwith|{kind=KWithm}::{kind=KExpri}::_wheni=prio_flatop->append(KBarm)L(reset_paddingpath)|{kind=KWithm}::_->append(KBarm)Lpath|{kind=KArrow(KMatch|KTryasm)}::({kind=KBar_}ash::_asp)->Path.maptop(funx->{xwithcolumn=h.column})(replace(KBarm)(Ah.column)p)|{kind=KArrowm}::p->append(KBarm)Lp|_->matchblock.pathwith|{kind=KExpr_}::_->make_infixtokblock.path|_->append(KBarKType)Lblock.path)|MINUSGREATER->letrecfind_parentpath=letpath=unwind(function|KParen|KBegin|KBracket|KBrace|KBracketBar|KWith(KMatch|KTry)|KBar(KMatch|KTry)|KArrow(KMatch|KTry)|KFun|KBody(KType|KExternal)|KColon|KStruct|KSig|KObject->true|_->false)pathinmatchpathwith|{kind=KFun}::({kind=KExpri}ase)::pathwheni=prio_flatop->(* eg '>>= fun x ->': indent like the top of the expression *){ewithkind=KExpr0}::path|{kind=KFun;line}::_whennext_offsettokstream=None&&line=current_line->(* Special case: [fun ->] at eol, this should be strictly indented
wrt the above line, independently of the structure *)append(KArrowKFun)L(reset_line_indentconfiglinepath)|{kind=KFun}::_->append(KArrowKFun)Lpath|{kind=KBarm}::{kind=KWith_;line}::_whenline=current_line->(* Special case: don't respect match_clause when 'with X ->' is on
a single line *)letpad=ifnext_offsettokstream<>Nonethenconfig.i_baseelsematchnext_tokenstreamwith|Some(MATCH|TRY|FUN|FUNCTION)->0|_->config.i_baseinappend(KArrowm)L~pad(reset_line_indentconfiglinepath)|{kind=KWithm|KBarm}::_->letpad=config.i_match_clause-ifstarts_linethenconfig.i_baseelse0inappend(KArrowm)L~padpath|{kind=KArrow(KMatch|KTry)}::p->(* might happen if doing 'when match' for example *)(matchunwind(function|KParen|KBegin|KBracket|KBrace|KBracketBar|KWith(KMatch|KTry)|KFun|KBody(KType|KExternal)|KColon|KStruct|KSig|KObject->true|_->false)pwith|{kind=KWith(_)}::p->find_parentp|_->make_infixtokblock.path)|_->make_infixtokblock.pathinfind_parentblock.path|EQUAL->letunwind_to=function|KParen|KBegin|KBrace|KBracket|KBracketBar|KBody_|KExternal|KModule|KType|KLet|KLetIn|KException|KVal|KBarKType|KStruct|KSig|KObject|KAnd(KModule|KType|KLet|KLetIn)->true|_->falseinletrecfind_parentpath=letpath=unwindunwind_topathin(matchpathwith|[]|{kind=KInOCamldocCode}::_->make_infixtokblock.path|{kind=KBodyKType}::p->(* type t = t' = ... *)(matchpwith|{kind=KWith(KType|KModule)|KAndKWith(KType|KModule)}::_->find_parentp|_->replace(KBodyKType)L~pad:config.i_typepath)|{kind=KBrace}::_->(matchunwind_while(funkind->priokind>prio_semi)block.pathwith|Some({kind=KExprprio}::_)whenprio=prio_semi+1->(* already after a field binding: this '=' must be
the normal operator *)make_infixtokblock.path|Somep->extend(KExpr(prio_semi+1))T~pad:config.i_basep|None->make_infixtokblock.path)|{kind=KParen|KBegin|KBracket|KBracketBar|KBody_|KBarKType}::_->make_infixtokblock.path|{kind=KAndkind|kind}ash::p->letindent=matchnext_tokenstream,kindwith|Some(STRUCT|SIG),_->0|_,(KType|KBodyKType)->config.i_type|_->config.i_baseinifstarts_linethenleth={hwithindent=h.indent+indent;pad=0}inreplace(KBodykind)L~pad:0(h::p)elseleth={hwithindent=h.column}inreplace(KBodykind)T~pad:indent(h::p))infind_parentblock.path|COLONEQUAL|INFIXOP2"+="->(matchunwind_while(functionKExpr_|KType->true|_->false)block.pathwith|Some({kind=KType}::_asp)->(* type t := t' *)replace(KBodyKType)Lp|_->make_infixtokblock.path)|COLON->letpath=unwind(function|KParen|KBegin|KBrace|KBracket|KBracketBar|KBody_|KModule|KLet|KLetIn|KExternal|KVal|KAnd(KModule|KLet|KLetIn)->true|_->false)block.pathin(matchpathwith|{kind=KModule|KLet|KLetIn|KExternal|KAnd(KModule|KLet|KLetIn|KExternal)}::_->appendKColonLpath|{kind=KVal}::{kind=KObject}::_->make_infixtokpath|{kind=KVal}ash::p->letindent=config.i_baseinifstarts_linethenleth={hwithindent=h.indent+indent;pad=0}inreplace(KBodyh.kind)L~pad:0(h::p)elsereplace(KBodyh.kind)L~pad:indent(h::p)|{kind=KBrace}::_->(* record type *)(matchblock.pathwith|{kind=KExpri}::{kind=KBrace}::_aspwheni=prio_max->extendKColonLp|{kind=KExpri}::({kind=KExprj}::{kind=KBrace}::_asp)wheni=prio_max&&j=prio_apply->(* "mutable" *)extendKColonLp|_->make_infixtokblock.path)|_->make_infixtokblock.path)|SEMI->(matchunwind(funkind->priokind<prio_semi)block.pathwith|{kind=KColon}::({kind=KBrace}::_asp)->p|_->make_infixtokblock.path)(* Some commom preprocessor directives *)|UIDENT("INCLUDE"|"IFDEF"|"THEN"|"ELSE"|"ENDIF"|"TEST"|"TEST_UNIT"|"TEST_MODULE"|"BENCH"|"BENCH_FUN"|"BENCH_MODULE"|"BENCH_INDEXED"ass)whenstarts_line->ifString.subs04="TEST"||(String.lengths>4&&String.subs05="BENCH")thenappendKLetL~pad:(2*config.i_base)(unwind_topblock.path)elsereplaceKUnknownL(unwind_topblock.path)|EXTERNAL->appendKExternalL(unwind_topblock.path)|DOT->letlast_expr=unwind_while(functionKExpr_->true|_->false)block.pathin(matchlast_exprwith|Some({kind=KExpr_}::{kind=KType}::({kind=KColon}::_asp))->(* let f: type t. t -> t = ... *)p|Some({kind=KExpri}::({kind=KBrace|KWithKBrace}ash::p))when(i=prio_max||i=prio_dot)&&next_offsettokstream=None->(* special case: distributive { Module. field; field } *){hwithpad=config.i_base}::p|_->make_infixtokblock.path)|AMPERAMPER|BARBAR->(* back-indented when after if or when and not alone *)letop_prio,_align,_indent=op_prio_align_indentconfigtok.tokenin(matchunwind_while(funkind->priokind>=op_prio)block.pathwith|Some({kind=KExpr_;line}::{kind=KWhen|KIf;line=line_if}::_asp)whenline=line_if&&next_offsettokstream<>None->extend(KExprop_prio)T~pad:(-3)p|_->make_infixtokblock.path)|LESS->ifis_inside_typeblock.paththen(* object type *)open_parenKBraceblock.pathelsemake_infixtokblock.path|GREATER->ifis_inside_typeblock.paththenmatchunwind(function|KParen|KBegin|KBracket|KBrace|KBracketBar|KBody(KType|KExternal)->true|_->false)block.pathwith|{kind=KBrace}::_asp->close(fun_->true)p|_->appendexpr_applyL(fold_exprblock.path)elsemake_infixtokblock.path|LESSMINUS|COMMA|OR|AMPERSAND|INFIXOP0_|INFIXOP1_|COLONCOLON|INFIXOP2_|PLUSDOT|PLUS|MINUSDOT|MINUS|INFIXOP3_|STAR|INFIXOP4_|SHARP|AS|COLONGREATER|OF->make_infixtokblock.path|LABEL_|OPTLABEL_->(matchunwind_while(function|KExpr_|KLet|KLetIn|KFun|KAnd(KLet|KLetIn)->true|_->false)block.pathwith|Some({kind=KExpr_}::_)|None->(* considered as infix, but forcing function application *)make_infixtok(fold_exprblock.path)|_->(* in function definition *)atomblock.path)|UIDENT_->(matchblock.pathwith|{kind=KBodyKType}::_whenstarts_line&&next_tokenstream<>SomeDOT->(* type =\nA\n| B : append a virtual bar before A for alignment *)letpath=append(KBarKType)L~pad:2block.pathinatompath|{kind=KBracket}asbr::({kind=KBodyKType;line}::_asp)whenstarts_line->(* type = [\n`A\n| `B ]: append a virtual bar before `A *)letpath=ifbr.line>linethen{brwithpad=0}::pelseblock.pathinletpath=append(KBarKType)L~pad:2pathinatompath|{kind=KModule|KInclude|KOpen}::_whennotstarts_line->(* indent functor parameters as if indent was flushed (like after a
newline) *)Path.maptop(funn->letindent=n.indent+n.padin{nwithindent;line_indent=indent;pad=config.i_base})(atomblock.path)|_->atomblock.path)|INT64_|INT32_|INT_|LIDENT_|FLOAT_|CHAR_|TYPEVAR|TRUE|FALSE|NATIVEINT_|UNDERSCORE|TILDE|QUESTION|QUOTE->atomblock.path|PREFIXOP_|BANG|QUESTIONQUESTION->(* FIXME: should be highest priority, > atom
( append is not right for atoms ) *)atomblock.path|ASSERT|LAZY|NEW|MUTABLE->appendexpr_applyL(before_append_atomblock.path)|INHERIT->append(KExpr0)L(unwind_topblock.path)|DOTDOT->(matchblock.pathwith|{kind=KBodyKType}::_->atomblock.path|_->appendKUnknownLblock.path)|VIRTUAL|REC|PRIVATE|EOF|BACKQUOTE|ILLEGAL_CHAR_|LINE_DIRECTIVE->(* indent the token, but otherwise ignored *)appendKUnknownLblock.path|EOL|ESCAPED_EOL->assertfalse|SPACES->assertfalseletupdateconfigblockstreamtok=letstarts_line=block.newlines<>0inletblock=matchblock.lastwith|{token=(COMMENT_CLOSE|COMMENT_CODE_CLOSE)}::last->{blockwithlast}|_->blockinmatchtok.token,block.pathwith(* String and quotation *)|(EOL|ESCAPED_EOL),((({kind=(KInString_|KInQuotation)}asnode)::path)|({kind=(KInStringIndent|KInQuotationIndent)}::({kind=_}asnode)::path))->letpath=ifstarts_line&&tok.token=ESCAPED_EOL&&Path.is_indented_stringblock.paththenletindent=node.indent+node.padin{kind=KInStringIndent;indent;line_indent=indent;column=indent;pad=0;line=node.line}::node::pathelse{nodewithindent=node.column;line_indent=node.column}::pathinletlast=block.lastinlettoff=0inletorig=Region.start_columntok.regioninletnewlines=iftok.token=ESCAPED_EOLthen-1else1inletpp_stack=block.pp_stackin{path;last;toff;orig;newlines;starts_line;pp_stack}|ESCAPED_EOL,{kind=(KInComment_|KInCommentIndent)}::_|EOL,_->{blockwithnewlines=block.newlines+1;starts_line}|_->letpath=update_pathconfigblockstreamtokinletlast=matchtok.token,block.lastwith|(COMMENT_OPEN|COMMENT_CODE_OPEN),last->tok::last|_,_::last->tok::last|_,[]->[tok]inlettoff=ifblock.newlines<>0thenPath.indentpathelseblock.toff+tok.offsetinletorig=Region.start_columntok.regioninletnewlines=matchtok.tokenwith|COMMENT_OPEN_EOL->1|_->0inletpp_stack=block.pp_stackin{path;last;toff;orig;newlines;starts_line;pp_stack}letindentt=Path.indentt.pathletpaddingt=Path.padt.pathletset_columntcol={twithpath=Path.maptop(funn->{nwithindent=col})t.path;toff=col}letreverset=letcol=t.originletexpected=t.toffinifcol=expectedthentelsematcht.lastwith|_::_whent.starts_line->letdiff=col-expectedinletpath=matcht.pathwith|_whenPath.in_stringt.path&&last_tokent<>SomeSTRING_OPEN->(* Do not reverse in string except for the opening quote *)t.path|n::[]->{nwithindent=col;column=col}::[]|({kind=KInComment(tok,_,b1,b2)}asn)::r->{nwithkind=KInComment(tok,col,b1,b2);indent=col;column=col}::r|({kind=KInOCamldocVerbatim}asn)::r->{nwithkind=KInOCamldocVerbatim;indent=col;column=col}::r|n1::n2::p->{n1withindent=col;column=col}::{n2withpad=n2.pad+diff}::p|[]->[]in{twithpath;toff=col}|_->{twithtoff=col}letguess_indentt=letpath=unwind(functionKUnknown->false|_->true)t.pathinmatchpathwith|{kind=KExpri}::pwheni=prio_max&&t.newlines>2->(* closed expr and newline: we probably want a toplevel block *)letp=unwind_toppinPath.indentp+Path.padp|path->(* we probably want to write a child of the current node *)letpath=matchunwind_while(functionKExprp->p>=prio_apply|_->false)pathwithSomep->p|None->pathinmatchpathwith|{indent;pad}::_->indent+pad|[]->0letis_at_topt=matcht.pathwith|[]->true|[{kind}]->stritem_kindkind|_->falseletis_declarationt=matcht.pathwith|[]->true|{kind=KStruct|KSig|KBegin|KObject}::_->true|_->falseletis_in_commentt=Path.in_commentt.path||Path.in_ocamldoc_verbatimt.pathletis_in_stringt=Path.in_stringt.pathletstarts_linet=t.starts_line