1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759(**************************************************************************)(* *)(* 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=ifconfig.i_match_tail_cascade&&(matchblock.pathwith|{kind=KArrow(KMatch|KTry)}::_->true|_->false)thenPath.maptop(funn->{nwithpad=0})block.pathelsefold_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
- inside a [struct]
- after [type = |]
*)(matchblock.pathwith|{kind=KExpri}::pwheni=prio_max->appendKLetL(unwind_topp)|[]|{kind=KCodeInComment|KBarKType|KStruct}::_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|KLet)|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=KBody(KLet|KLetIn);line=letline}::_whennext_offsettokstream=None&&line=current_line&&line<>letline->(* Special indentation of [fun] inside a [let]. *)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::_(* spurious warning 57 here *)|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 =
*)