12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745(**************************************************************************)(* *)(* 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 2.1 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. *)(* *)(**************************************************************************)openPosopenNstreamopenApprox_lexeropenUtilmoduleNode=structtypeextension_kind=ExtNode|Attr(* 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|KConstraint|KException|KOpen|KInclude|KVal|KBarofkind(* Stores the original token and line offset for alignment of
comment continuations *)|KCommentofNstream.token*int(* ocamldoc verbatim block *)|KVerbatimofNstream.token*int|KUnknown|KStruct|KSig|KModule|KBegin|KObject|KMatch|KTry|KWithofkind|KLoop|KIf|KThen|KElse|KDo|KFun|KWhen|KExternal|KCodeInComment|KExtendedExprofstringlist*extension_kind|KExtendedItemofstringlist*extension_kind|KAttrIdofstringlist*bool(* 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=KExpr140letprio_lbracketat=30(* Special operators that should break arrow indentation have this prio
(eg monad operators, >>=) *)letprio_flatop=59letprio_colon=35letprio_arrow=32letprio_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"|KComment_->"KComment"|KVerbatim_->"KVerbatim"|KUnknown->"KUnknown"|KType->"Ktype"|KConstraint->"KConstraint"|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"|KCodeInComment->"KCodeInComment"|KExtendedExpr(name,kind)->Printf.sprintf"KExtendedExpr(%s,%s)"(String.concat"."(List.revname))(matchkindwithExtNode->"node"|Attr->"attr")|KExtendedItem(name,kind)->Printf.sprintf"KExtendedItem(%s,%s)"(String.concat"."(List.revname))(matchkindwithExtNode->"node"|Attr->"attr")|KAttrId(name,dotted)->Printf.sprintf"KAttrId(%s,%B)"(String.concat"."(List.revname))dottedandauxstrk=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=KCodeInComment}::_asl->l|t::l->ft::lletshiftpathn=maptop(funt->Node.shifttn)pathendopenNode(* A block is:
- a node path to go to this block
- the last token of this block (when a comment, it is stacked to keep the
last meaningful token)
- the last token offset
- the original starting column for this block *)typet={path:Path.t;last:Nstream.tokenlist;toff:int;orig:int;}letshifttn={twithpath=Path.shiftt.pathn}letto_stringt=Path.to_stringt.path(* Printf.sprintf "%s\n%d %b" (Path.to_string t.path) t.toff *)letempty={path=[];last=[];toff=0;orig=0;}(*
(* 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 path until [f] holds *)letrecunwindfpath=matchpathwith|{kind}::_whenfkind->path|{kind=KCodeInComment}::_->path|_::path->unwindfpath|[]->[](* Unwinds the path while [f] holds, returning the last step for which it does *)letunwind_whilefpath=letrecauxacc=function|{kind=KCodeInComment}::_asp->acc::p|{kind}ash::pwhenfkind->auxhp|p->acc::pinmatchpathwith|{kind=KCodeInComment}::_->None|{kind}ash::pwhenfkind->Some(auxhp)|_->Nonelettop_kind=function|KStruct|KSig|KParen|KBegin|KObject|KExtendedItem_|KAttrId_|KExtendedExpr_->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=KCodeInComment}::_ast->t|_::t->t(* Get the next token, skipping comments (and in-comment tokens) *)letnext_token_full=letrecskipdepthstream=matchNstream.nextstreamwith|None->None|Some(tok,stream)->matchtok.tokenwith|COMMENT->skipdepthstream|OCAMLDOC_VERB|OCAMLDOC_CODE->skip(depth+1)stream|COMMENTCONT->ifdepth=0thenNoneelseskip(depth-1)stream|_whendepth=0->Some(tok,stream)|_->skipdepthstreaminskip0letnext_tokenstream=matchnext_token_fullstreamwith|None->None|Some(t,_)->Somet.tokenletnext_2_tokensstream=matchnext_token_fullstreamwith|None->None|Some(t1,s)->matchnext_tokenswith|None->None|Somet2->Some(t1.token,t2)letlast_tokent=letrecaux=function|[]->None|{token=COMMENT|COMMENTCONT}::r->auxr|t::_->Somet.tokeninauxt.last(* 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%8s\027[m %s\n%!"(matcht.lastwithtok::_->shorten_string30(Lazy.forcetok.substr)|_->"")(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->prio_arrow,L,0(* is an operator only in types *)|COLON->prio_colon,T,config.i_base|COLONGREATER->prio_colon,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|LBRACKETAT->prio_lbracketat,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|_->assertfalselethandle_dottedblocktok=letstarts_line=tok.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.length(Lazy.forcetok.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_id(Lazy.forcetok.substr)block.path)|_->Noneelseifis_attr_idblock.paththenmatchtok.tokenwith|DOT->Some(make_dotted_attr_idblock.path)|_->NoneelseNoneletext_kind=function|LBRACKETPERCENT|LBRACKETPERCENTPERCENT->ExtNode|LBRACKETAT|LBRACKETATAT|LBRACKETATATAT->Attr|_->invalid_arg"ext_kind"(* Take a block, a token stream and a token.
Return the new block stack. *)letrecupdate_pathconfigblockstreamtok=letopenIndentConfiginletstarts_line=tok.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([],k)->KExtendedItem(List.revnames,k)|KExtendedExpr([],k)->KExtendedExpr(List.revnames,k)|_->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=KCodeInComment}::_->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=KCodeInComment}::_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=-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=ifconfig.i_align_params=Never||next_offsettokstream=Nonethenreset_line_indentconfigcurrent_linepathelsepathinletp=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->ifconfig.i_align_params=Neverthenpathelsematchkindwith|KBegin->path|KParenwhenifnotconfig.i_align_opsthennotstarts_lineelsematchnext_token_fullstreamwith|Some({token=SIG|STRUCT|OBJECT},_)->true|Some({token=MODULE},stream)whennext_tokenstream=SomeSTRUCT->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_delim_block()=matchunwind_while(funkind->priokind>=op_prio)pathwith|Some({kind=KExpr_;line}::{kind=(KBrace|KParen|KBracket|KBracketBar);line=bline}::_)->line=bline|_->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_delim_block())&&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=KComment_|KVerbatim_|KUnknown}::path->{blockwithpath}|_->blockinlet(>>!)optf=matchoptwithSomex->x|None->f()inhandle_dottedblocktok>>!fun()->matchtok.tokenwith|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|TRY->letk=matchtok.tokenwith|MATCH->KMatch|TRY->KTry|_->assertfalseinletp=fold_exprblock.pathinifstarts_linethenappendkLpelseletenforce_strict=config.i_strict_with=Always||config.i_strict_with=Auto&&matchpwith|{kind=KBegin;indent;column}::_->column=indent|_->falseinifenforce_strictthenletp=reset_line_indentconfigcurrent_linepinappendkL(reset_paddingp)elseappendkL~pad:(Path.padp+config.i_base)p|LPAREN->open_parenKParenblock.path|LBRACKET|LBRACKETGREATER|LBRACKETLESS->open_parenKBracketblock.path|LBRACKETPERCENT->letpath=before_append_atomblock.pathinappend~pad:4(KExtendedExpr([],ExtNode))Lpath|LBRACKETAT->letp=matchblock.pathwith|{kind=KExpr_}::_asp->make_infixtokp|p->pinappend~pad:4(KExtendedExpr([],Attr))Lp|LBRACKETATAT->(* Indented as below parent, but we actually keep the stack
(this is turned into a KUnknown when closed, causing the next token to
be indented as if it was absent) *)letparent_path=unwind(functionKBodyKLetIn|KLetIn->true|KBodyk|k->top_kindk||stritem_kindk)block.pathinnodefalse(KExtendedItem([],ext_kindtok.token))L4(parentparent_path)::block.path|LBRACKETPERCENTPERCENT|LBRACKETATATAT->append~pad:4(KExtendedItem([],ext_kindtok.token))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(function|KParen|KBegin|KLet|KLetIn|KBody_|KInclude|KOpen->true|_->false)block.pathinletindent,path=matchexpr_startwith|{kind=KParen|KBegin}::{kind=KExprprio}::{kind=KBodyKLet;line;indent;pad}::_whenprio=prio_apply&&line=current_line->(* reset indent due to align_params for functor application within
[let module in] *)indent+pad,reset_paddingblock.path|{kind=KParen|KBegin}::{kind=KExprprio;line;indent}::_whenprio=prio_apply&&line=current_line->indent,reset_paddingblock.path|{kind=KInclude;line;indent;pad}::_whenline<current_line->indent+pad,block.path|_->Path.indentblock.path,reset_paddingblock.pathinPath.maptop(funn->{nwithindent})(appendkLpath)|WHEN->appendKWhenL~pad:(config.i_base+ifstarts_linethen0else2)(unwind(function|KWith(KTry|KMatch)|KBar(KTry|KMatch)|KFun|KExtendedExpr_->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=KCodeInComment}::_|{kind=KBarKType}::_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.pathinappendKConstraintLpath|AND->letunwind_to=function|KLet|KLetIn|KType|KModule|KParen->true|_->falseinletpath=unwind(unwind_to@*follow)block.pathin(matchpathwith|[]|{kind=KCodeInComment}::_->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)|{kind=KParen}::_->(* e.g. let (and+) = ... *)appendexpr_atomLpath|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 *)|Some(COLON|EQUAL|INCLUDE)whennext_2_tokensstream=Some(TYPE,OF)->appendKUnknownLblock.path(* : module type of *)|Some(WITH|AND)->appendKTypeLblock.path|SomeINCLUDE->appendKModuleL(reset_paddingblock.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 *)Lazy.forcetok.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->letp=unwind(function|KBracket|KExtendedItem_|KExtendedExpr_->true|_->false)block.pathin(matchpwith|{kind=KExtendedExpr(_,Attr)}::({kind=KExpr_}::_asp)->extendexpr_atomL~pad:0p|{kind=KExtendedItem(_,Attr)|KExtendedExpr(_,Attr)}::_->extendKUnknownL~pad:0p|p->close(fun_->true)p)|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|KExtendedItem_|KExtendedExpr_->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}::{kind=KBodyKLet;line=letline}::_whennext_offsettokstream=None&&line=current_line&&line<>letline->append(KArrowKFun)L~pad:0(reset_line_indentconfiglinepath)|{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)|KConstraint|KExtendedItem_|KExtendedExpr_->true|_->falseinletrecfind_parentpath=letpath=unwindunwind_topathin(matchpathwith|[]|{kind=KCodeInComment|KExtendedItem_|KExtendedExpr_}::_->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->kind=KColon||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|SomeBAR,KTypewhenconfig.i_strict_with=Always->config.i_with|_,(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|KColon|KAnd(KModule|KLet|KLetIn)|KBarKType->true|_->false)block.pathin(matchpathwith|{kind=KBody(KVal|KType|KExternal)|KColon}::_->(matchunwind_while(funkind->priokind>prio_arrow)block.pathwith|Somepath->extend(KExprprio_colon)(ifconfig.i_align_params=NeverthenLelseT)path|None->make_infixtokblock.path)|{kind=KModule|KLet|KLetIn|KAnd(KModule|KLet|KLetIn)}::_->appendKColonLpath|{kind=KExternal}::_aspath->appendKColonL~pad:(ifstarts_linethen0elseconfig.i_base)path|{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)|{kind=KBarKType}::_->make_infix{tokwithtoken=OF}block.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->(matchblock.pathwith|{kind=KArrowKMatch}::_->appendexpr_atomLblock.path|_->letlast_expr=unwind_while(functionKExpr_->true|_->false)block.pathinmatchlast_exprwith|Some({kind=KExpr_}::{kind=KType}::({kind=KColon}::_asp))->(* let f: type t. t -> t = ... *)p|Some({kind=KExpr200}::({kind=KColon}::{kind=KLet|KLetIn}::_asp))->(* method m : 'x 'y . ... = (KLet is actually "method") *)(* let m : 'x 'y . ... = (in) *)(matchlast_tokenblockwith|Some(UIDENT_)->make_infixtokblock.path|_->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|OF->(matchlast_tokenblockwith|SomeTYPE->appendKUnknownLblock.path|_->make_infixtokblock.path)|LESSMINUS|COMMA|OR|AMPERSAND|INFIXOP0_|INFIXOP1_|COLONCOLON|INFIXOP2_|PLUSDOT|PLUS|MINUSDOT|MINUS|INFIXOP3_|STAR|INFIXOP4_|SHARP|AS|COLONGREATER->make_infixtokblock.path|LABEL_|OPTLABEL_->(matchunwind_while(function|KExpr_|KLet|KLetIn|KFun|KAnd(KLet|KLetIn)->true|_->false)block.pathwith|Some((* (opt)labels in types *){kind=KExpr32(* prio_arrow *)}::({kind=KBody(KVal|KType|KExternal)|KColon}::_)|({kind=KBody(KVal|KType|KExternal)|KColon}::_))->(* this is for the case [?foo:], parsed as OPTLABEL, but make sure we
are consistent with [foo:] or [? foo:], which are parsed as 2 or 3
tokens *)extend(KExprprio_colon)(ifconfig.i_align_params=NeverthenLelseT)(appendexpr_atomLblock.path)|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)|LIDENTswhenString.lengths>0&&s.[0]='\''->append(KExprprio_max)L~pad:0block.path|INT64_|INT32_|INT_|LIDENT_|FLOAT_|CHAR_|STRING_|TRUE|FALSE|NATIVEINT_|UNDERSCORE|TILDE|QUESTION|QUOTE|QUOTATION_->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)|OCAMLDOC_CODE->letindent=Path.indentblock0.path+ifLazy.forcetok.substr="$"then0(* cinaps comment (*$ code *) *)elsePath.padblock0.pathin{kind=KCodeInComment;line=Region.start_linetok.region;indent=indent;line_indent=indent;column=indent;pad=config.i_base}::block0.path|OCAMLDOC_VERB->(matchblock0.pathwith|{kind=KComment(tok,toff);indent;pad}::_->{kind=KVerbatim(tok,toff);line=Region.start_linetok.region;indent=indent+pad;line_indent=indent+pad;column=indent+pad;pad=0}::block0.path|_->dumpblock0;assertfalse)|COMMENTCONT->(matchunwind(functionKCodeInComment|KVerbatim_->true|_->false)block0.pathwith|{kind=KCodeInComment|KVerbatim_}::p->p|_->block.path)|COMMENT->lets=Lazy.forcetok.substrinletpad=letlen=String.lengthsinleti=ref2inwhile!i<len&&s.[!i]='*'doincridone;while!i<len&&s.[!i]=' 'doincridone;if!i>=len||s.[!i]='\n'||s.[!i]='\r'then3else!iinifnotstarts_linethenletcol=block.toff+tok.offsetinPath.maptop(funn->{nwithindent=col})(append(KComment(tok,col))L~padblock.path)else(matchblock.pathwith|{kind=KExpri}::_wheni=prio_max->letblocklevel()=letp=unwind_topblock.pathinletcol=Path.indentp+Path.padpinappend(KComment(tok,col))(Acol)~padblock.pathin(* if we are directly after a case in a sum-type, use that for
alignment *)letalign_bar=iftok.newlines>1||not(is_inside_typeblock0.path)thenNoneelseletfind_bar=unwind_while(functionKBar_|KExpr_->true|_->false)block0.pathinmatchfind_barwith|Some({kind=KBar_;column}::_)->Somecolumn|_->Nonein(* after a closed expr: look-ahead *)(matchnext_token_fullstream,align_barwith|None,None->blocklevel()|Some((* full block-closing tokens + newline *){token=SEMISEMI|DONE|END|GREATERRBRACE|GREATERRBRACKET|RBRACE|RBRACKET|RPAREN},_),_whentok.newlines>1->blocklevel()|Some((* semi block-closing tokens *){token=SEMISEMI|DONE|END|GREATERRBRACE|GREATERRBRACKET|RBRACE|RBRACKET|RPAREN|THEN|ELSE|IN|EQUAL},_),Nonewhentok.newlines<=1->(* indent as above *)letcol=(Path.topblock0.path).line_indentinappend(KComment(tok,col))(Acol)~padblock.path|_,Someindent->append(KComment(tok,indent))(Aindent)~padblock.path|next,None->(* recursive call to indent like next line *)letpath=matchnextwith|Some({token=EOF},_)|None->[]|Some(next,stream)->update_pathconfigblockstream{nextwithnewlines=tok.newlines}inletcol=Path.indentpathinappend(KComment(tok,col))(Acol)~padblock.path)|_->letcol=Path.indentblock.path+Path.padblock.pathinappend(KComment(tok,col))(Acol)~padblock.path)|DOTDOT->(matchblock.pathwith|{kind=KBodyKType}::p->p|_->appendKUnknownLblock.path)|VIRTUAL|REC|PRIVATE|EOF|BACKQUOTE|ILLEGAL_CHAR_->(* indent the token, but otherwise ignored *)appendKUnknownLblock.path|LINE_DIRECTIVE->appendKUnknown(A0)~pad:0block.path|EOL|SPACES->assertfalseletupdateconfigblockstreamtok=letpath=update_pathconfigblockstreamtokinletlast=matchtok.tokenwith|COMMENT|COMMENTCONT|OCAMLDOC_VERB|EOF->tok::block.last|_->[tok]inlettoff=iftok.newlines>0thenPath.indentpathelseblock.toff+tok.offsetinletorig=Region.start_columntok.regionin{path;last;toff;orig}letindentt=Path.indentt.pathletoriginal_columnt=matcht.pathwith|{kind=KComment(tok,_)|KVerbatim(tok,_)}::_->Region.start_columntok.region|_->t.origletoffsett=matcht.pathwith|{kind=KComment(_,toff)|KVerbatim(_,toff)}::_->toff|_->t.toffletpaddingt=Path.padt.pathletset_columntcol={twithpath=Path.maptop(funn->{nwithindent=col})t.path;toff=col}letreverset=letcol=t.originletexpected=t.toffinifcol=expectedthentelsematcht.lastwith|{token=COMMENTCONT}::_->(* don't adapt indent on the ']}' because there is a hack with its
padding *)t|tok::_whentok.newlines>0->letdiff=col-expectedinletpath=matcht.pathwith|n::[]->{nwithindent=col;column=col}::[]|({kind=KComment(tok,_)}asn)::r->{nwithkind=KComment(tok,col);indent=col;column=col}::r|({kind=KVerbatim(tok,_)}asn)::r->{nwithkind=KVerbatim(tok,col);indent=col;column=col}::r|n1::n2::p->{n1withindent=col;column=col}::{n2withindent=n2.indent+diff}::p|[]->[]in{twithpath;toff=col}|_->{twithtoff=col}letguess_indentlinet=letpath=unwind(functionKUnknown|KComment_|KVerbatim_->false|_->true)t.pathinmatchpath,t.lastwith|_,({token=COMMENT|COMMENTCONT}astok::_)whenline<=Region.end_linetok.region->(* Inside comment *)Path.indentt.path+Path.padt.path|{kind=KExpri}::p,({token=EOF}::tok::_|tok::_)wheni=prio_max&&line>Region.end_linetok.region+1->(* 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_cleant=List.for_all(funnode->matchnode.kindwith|KCodeInComment->false|KVerbatim_->false|KComment_->false(* we need the next token to decide, because that may be "(* *)"
but also "(* {[". In the last case, it will be followed by
OCAMLDOC_* or COMMENTCONT, and until then the lexer stores a
state *)(* **tuareg hack** "*)" (who says we want ocp-indent to handle coloration
too ?) *)|_->true)t.pathletis_at_topt=matcht.pathwith|[]->true|[{kind}]->stritem_kindkind|_->falseletno_parentst=matcht.pathwith|[_]->true|_->falseletis_declarationt=is_cleant&&matcht.pathwith|[]->true|{kind=KStruct|KSig|KBegin|KObject}::_->true|_->falseletis_in_commentt=matcht.pathwith|{kind=KComment_|KVerbatim_}::_->true|p->List.exists(funn->n.kind=KCodeInComment)p(*
(* for syntax highlighting: returns kind of construct at point *)
type construct_kind =
| CK_paren (* parens and begin/end *)
| CK_block (* struct/end sig/end etc. *)
| CK_toplevel
let construct_kind t token =
*)