123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191open!BaseincludeSexp_pretty_intfmoduleSexp=Sexplib.SexpmoduleConfig=ConfigopenConfigmoduleW=Sexp.With_layoutmoduleFormat=structincludeCaml.Formatletpp_listisep?(offset=0)?singletonppfmtlist=matchlistwith|[]->()|hd::tl->(matchsingleton,tlwith|Somepp,[]->ppoffsetfmthd|_->ppoffsetfmthd);List.iteritl~f:(funiel->Caml.Format.fprintffmtsep;pp(i+offset+1)fmtel);;letpp_listsep?singletonppfmtlist=letsingleton=Option.mapsingleton~f:(funsingleton_->singleton)inpp_listisep?singleton(fun_->pp)fmtlist;;endtypecomment_kind=|Sexp_comment|Line_commenttypecontent_kind=|Sexp|Commentofcomment_kindtypestate={content_kind:content_kind}letstart_state={content_kind=Sexp}letsplit=lazy(Re.Str.regexp"[ \t]+")letcolor_to_code=function|Black->30|Red->31|Green->32|Yellow->33|Blue->34|Magenta->35|Cyan->36|White->37|Default->39;;letrainbow_open_tagconftag=letargs=Re.Str.split(forcesplit)taginletcolor_count=Array.lengthconf.color_schemeinmatchargswith|["d";n]->leti=Int.of_stringnin"["^Int.to_string(color_to_code(ifi<0||color_count<1thenDefaultelseconf.color_scheme.(i%color_count)))^"m"(* Printing out comments. *)|["c";_]->(matchconf.commentswith|Print(_,Someclr,_)->"["^Int.to_string(color_to_codeclr)^"m"|_->"")|_->tag;;letrainbow_tagsconf={Format.mark_open_stag=(function|Format.String_tagtag->rainbow_open_tagconftag|_->"");Format.mark_close_stag=(fun_->matchconf.commentswith|Print(_,Some_clr,_)->""|_->"");Format.print_open_stag=ignore;Format.print_close_stag=ignore};;(* Opens n parentheses, starting at level depth. *)letopen_parensconfstate~depthfmtn=matchconf.paren_coloring,state.content_kind,conf.commentswith(* Overrides the option not to color parentheses. *)|_,Comment_,Print(_,Some_,_)->fori=depthtodepth+n-1doFormat.fprintffmt"@{<c %d>(@}"idone|true,Sexp,_->fori=depthtodepth+n-1doFormat.fprintffmt"@{<d %d>(@}"idone|_,_,_->for_=depthtodepth+n-1doFormat.fprintffmt"("done;;(* Closes n parentheses, starting at level depth+(n-1) to depth. *)letclose_parensconfstate~depthfmtn=(* Overrides the option not to color parentheses. *)matchconf.paren_coloring,state.content_kind,conf.commentswith|_,Comment_,Print(_,Some_,_)->fori=depth+(n-1)downtodepthdoFormat.fprintffmt"@{<c %d>)@}"idone|true,Sexp,_->fori=depth+(n-1)downtodepthdoFormat.fprintffmt"@{<d %d>)@}"idone|_,_,_->for_=depth+(n-1)downtodepthdoFormat.fprintffmt")"done;;letmust_escape=function|"\\"->false|string->Sexplib.Pre_sexp.must_escapestring;;letminimal_escapingat=letbody=String.concat_mapat~f:(funchar->matchcharwith|'"'|'\\'->String.of_char'\\'^String.of_charchar|' '|'\t'|'\n'->String.of_charchar|_->ifChar.is_printcharthenString.of_charcharelseChar.escapedchar)inString.concat["\"";body;"\""];;letatom_escapeconfat=matchconf.atom_printingwith|Escaped|Interpreted->Sexplib.Pre_sexp.mach_maybe_esc_strat|Minimal_escaping->ifSexplib.Pre_sexp.must_escapeatthenminimal_escapingatelseat;;letatom_printing_lenconfat=lets=atom_escapeconfatinifString.for_alls~f:Char.is_printthenSome(String.lengths)elseNone;;letatom_printing_len_exnconfat=matchatom_printing_lenconfatwith|Somelen->len|None->raise_s(Sexp.List[Atom"Sexp_pretty.atom_printing_len_exn: invalid input";Atomat]);;letpp_atomconfstate~depth~lenindexfmtat=letat=matchstate.content_kindwith|CommentLine_comment->(* we never need to escape a line comment *)at|Sexp|CommentSexp_comment->ifmust_escapeatthen(matchconf.atom_printingwith|Escaped|Interpreted->Sexplib.Pre_sexp.esc_strat|Minimal_escaping->minimal_escapingat)elseatinletshould_be_colored=matchconf.atom_coloringwith|Color_none->false|Color_firstthreshold->Int.equalindex0&&len<=threshold|Color_all->trueinmatchstate.content_kindwith|Comment_->(matchconf.commentswith|Drop->assertfalse|Print(_,Some_,_)->Format.fprintffmt"@{<c %d>%s@}"depthat|Print(_,None,_)->Format.fprintffmt"%s"at)|Sexp->ifshould_be_coloredthenFormat.fprintffmt"@{<d %d>%s@}"depthatelseFormat.fprintffmt"%s"at;;letpp_associated_commentsconf~depthfmtassociated_comments=ifnot(List.is_emptyassociated_comments)then(Format.fprintffmt" ";Format.pp_open_vboxfmt0;List.iteriassociated_comments~f:(funicomment->ifi>0thenFormat.pp_print_breakfmt00;pp_atomconf{content_kind=CommentLine_comment}~depth~len:10fmtcomment);Format.pp_close_boxfmt());;moduleNormalize=structtypet=|Sexpofsexp*stringlist|Commentofcommentandcomment=|Line_commentofstring|Block_commentofint*stringlist|Sexp_commentofcommentlist*sexpandsexp=|Atomofstring|Listoftlistletparse_sexps=Sexp.With_layout.Parser.sexpsSexp.With_layout.Lexer.mainmodulePos=Sexplib.Src_pos.Relativeletblock_comment=lazyRe.(seq[str"#|";group(seq[group(rep(set"\t "));rep(alt[char'\n';any])]);str"|#"]|>compile);;letword_split=lazy(Re.Str.regexp"[ \n\t]+")lettrailing=lazy(Re.Str.regexp"\\(.*\\b\\)[ \t]*$")lettab_size=2typematch_dimension=|Horizontal|Verticalletmatch_block_commentcomment=Re.exec_opt(forceblock_comment)commentletis_block_commentcomment=Option.is_some(match_block_commentcomment)letgrab_commentsposlist=letrecloopdimensionaccpos=function|[]->acc,[]|W.Sexp_::_aslist->acc,list|(W.Comment(W.Plain_comment(cpos,content))ascomment)::rest->if(matchdimensionwith|Horizontal->pos.Pos.row=cpos.Pos.row|Vertical->pos.Pos.col=cpos.Pos.col)&¬(is_block_commentcontent)thenloopVertical(content::acc)cposrestelseacc,comment::rest|W.Comment(W.Sexp_comment_)::_aslist->acc,listinletrev_comments,rest=loopHorizontal[]poslistinList.revrev_comments,rest;;letrecpre_process_atomconfposatom=matchconf.atom_printingwith|Escaped|Minimal_escaping->`Atomatom|Interpreted->Option.value~default:(`Atomatom)(Option.try_with(fun()->matchparse_sexps(Lexing.from_stringatom)with(* Perhaps normalized the atom, but nothing more to do. *)|[W.Sexp(W.Atom(_,_atom_without_spaces,None))]->`Atomatom(* Nested atom, try again. *)|[W.Sexp(W.Atom(_,inner_atom,Somesource))]->ifString.equalinner_atomsourcethen`Atomatom(* avoid an infinite loop of reinterpreting the atom *)else(matchpre_process_atomconfposinner_atomwith|`Atom_->`Atomatom(* original atom is better since it contains original
spacing which will be stripped off by
pre_process_atom *)|`Listlst->`Listlst)(* Parsed one whole sexp, bubble it up. *)|[W.Sexp(W.List(_,list,_))]->`Listlist(* It would cause problems if we parsed a comment in the case the atom is a
commented out sexp. We will be conservative here and we won't parse the
comment.
*)|[W.Comment_]->`Atomatom(* Results in an empty. We keep the original. *)|[]->`Atomatom(* Parsed a list of multiple sexps. It could either be spliced into the current
list, or put into a new Sexp list.
At the moment, they are put into separate lists.
*)(* If needed, we could traverse [sexps] and adjust positions so that they
corespond to the respective positions in the original file. Also, we could
calculate the end position of this list correctly.
*)|sexpswhenList.for_allsexps~f:(function|W.Sexp(W.Atom_)->true|_->false)->(* we parsed a plain string *)`Atomatom|sexps->(* If atom was created by failwiths or structural_sexp, it would looks like
this:
"human-readable message followed by (potentially (long and (ugly sexp)))"
We will try to preserve human-readable part by concatenating all sequences
of top-level atoms into singe atom *)letbreakab=matcha,bwith|W.Sexp(W.Atom_),W.Sexp(W.Atom_)->false|_->trueinletconcatenate_atomslst=List.group~breaklst|>List.map~f:(function|W.Sexp(W.Atom(pos,_,_))::_asatoms->letget_atom_contents=function|W.Sexp(W.Atom(_,a,_))->a|_->assertfalse(* List.group guarantees that we have only Atoms
here *)inletatom_contents=List.map~f:get_atom_contentsatoms|>String.concat~sep:" "inletescaped_atom_contents=Sexplib.Pre_sexp.mach_maybe_esc_stratom_contentsin[W.Sexp(W.Atom(pos,atom_contents,Someescaped_atom_contents))]|W.Sexp(W.List_)::_aslists->lists|W.Comment_::_ascomments->comments|[]->[](* cant really happen *))|>List.concatin`List(concatenate_atomssexps)));;letpre_process_block_commentstylecomment=matchstylewith|Conservative_print->String.splitcomment~on:'\n'|Pretty_print->String.stripcomment|>Re.Str.split(forceword_split)|>List.map~f:(funline->ifRe.Str.string_match(forcetrailing)line0thenRe.Str.matched_group1lineelseline)|>List.filter~f:(funs->String.lengths>0);;letget_sizestring=String.countstring~f:(func->Char.equalc' ')+(String.countstring~f:(func->Char.equalc'\t')*tab_size);;letatom_end_position~(pos:Pos.t)~atom~quoted=matchquotedwith|None->{poswithcol=pos.col+String.lengthatom}|Somequoted_string->String.foldquoted_string~init:pos~f:(funposchar->matchcharwith|'\n'->{row=pos.row+1;col=0}|_->{poswithcol=pos.col+1});;exceptionDrop_exn(* Converts to t, does initial pre-processing - interprets/escapes atoms,
reorders/drops/normalizes comments.
*)letrecof_sexp_or_commentconf:W.t_or_comment->t=function|W.Commentcomment->Comment(of_commentconfcomment)|W.Sexpsexp->Sexp(of_sexpconfsexp,[])andof_sexp(conf:Config.t):W.t->sexp=function|W.Atom(pos,atom,_escaped)->(matchpre_process_atomconfposatomwith|`Atomatom->Atomatom|`Listlist->of_sexp_or_comment_listconflist)|W.List(_,list,_)->of_sexp_or_comment_listconflistandof_sexp_or_comment_list(conf:Config.t):W.t_or_commentlist->sexp=funlist->matchconf.commentswith|Drop->List(List.filter_maplist~f:(funel->matchof_sexp_or_commentconfelwith|t->Somet|exceptionDrop_exn->None))|Print_->(* Re-orders comments to have comment that belong to a sexp before it, not after. If
[conf.sticky_comments = Same_line], it ties the comments to the sexp instead *)letrecreorderacc=function|[]->acc|W.Sexp(W.Atom(pos,atom,quoted)assexp)::rest->reorder_commentsacc(atom_end_position~pos~atom~quoted)sexprest|W.Sexp(W.List(_,_,pos)assexp)::rest->reorder_commentsaccpossexprest|W.Commentcomment::rest->reorder(Comment(of_commentconfcomment)::acc)restandreorder_commentsaccpossexprest=letcomments,rest=grab_commentsposrestinletsexp=of_sexpconfsexpinletwith_commentsinit=List.foldcomments~init~f:(funacccomment->Comment(Line_commentcomment)::acc)inmatchconf.sticky_commentswith|Same_line->reorder(Sexp(sexp,comments)::acc)rest|Before->reorder(Sexp(sexp,[])::with_commentsacc)rest|After->reorder(with_comments(Sexp(sexp,[])::acc))restinList(reorder[]list|>List.rev)andof_comment(conf:Config.t):W.comment->comment=function|W.Plain_comment(_,comment)->(matchconf.commentswith|Drop->raiseDrop_exn|Print(indent,_,style)->(matchmatch_block_commentcommentwith|Somegroup->letindent=matchindentwith|Auto_indent_comment->get_size(Re.Group.getgroup2)+2|Indent_commenti->iinlettext=pre_process_block_commentstyle(Re.Group.getgroup1)inBlock_comment(indent,text)|None->Line_commentcomment))|W.Sexp_comment(_,comment_list,sexp)->(matchconf.commentswith|Drop->raiseDrop_exn|Print_->letcomm_list=List.mapcomment_list~f:(funcomment->of_commentconfcomment)inletsexp=of_sexpconfsexpinSexp_comment(comm_list,sexp));;endmodulePrint=structmoduleN=Normalize(* [associated_comments] are line comments correspond to a sexp that are expected to be
printed on the same line *)typeassociated_comments=stringlisttypeforces_breakline=booltypeopened=|Opened|Closedtype'atree=|Nodeof'atreelist|Leafof'a(* Also contains the first atom list. *)typeshape=(int*string)treetypet=|Sexpofsexp*associated_comments|Commentofcommentandcomment=|Line_commentofstring|Block_commentofint*stringlist(* Does not contain the #| |#*)|Sexp_commentof(commentlist*forces_breakline)*sexpandsexp=|Atomofstring(* With leading atoms. *)|Listofstringlist*t_or_alignedlist*forces_breakline(* Sexp is a tree - List, Aligned, or Singleton *)|Singletonofstringlist*int*sexp*forces_breaklineandt_or_aligned=|Alignedofaligned|Toftandaligned=(shape*associated_comments)*linelistandline=|Atom_lineofstringtree*associated_comments|Comment_lineofcomment(* Unwraps singleton lists. *)letunwrapsexp=letrecinnerlevel=function|N.List[N.Sexp((N.List_assexp_list),[])]->inner(level+1)sexp_list|N.List_assexp_list->level+1,sexp_list|N.Atom_asatom->level,atomininner0sexp;;letmaybe_singletonconf(t_list:Normalize.tlist)=matchconf.singleton_limitwith|Singleton_limit(Atom_thresholdmax_at,Character_thresholdmax_char)->letrecmaybe_singleton_inner~atom_count~char_countacc=function|[]->None|N.Sexp(N.Atomatom,[])::tl->letchar_count=char_count+String.lengthatominifatom_count=max_at||char_count>max_charthenNoneelsemaybe_singleton_inner(atom::acc)tl~atom_count:(atom_count+1)~char_count|[N.Sexp((N.List_aslist),[])]->letlevel,list=unwraplistinSome(List.revacc,level,list)|N.Comment_::_->None|_->Noneinmaybe_singleton_inner~atom_count:0~char_count:0[]t_list;;letforces_breakline_atom~confatom=matchconf.atom_printingwith|Escaped|Interpreted->false|Minimal_escaping->String.mematom'\n';;letforces_breakline_sexp~conf=function|Atomatom->forces_breakline_atom~confatom|List(_,_,forces)->forces|Singleton(_,_,_,forces)->forces;;letforces_breakline_comment~conf=function|Line_comment_->true|Block_comment_->false|Sexp_comment((_,comm_force),sexp)->comm_force||forces_breakline_sexp~confsexp;;letforces_breakline~conf=function|Sexp(sexp,[])->forces_breakline_sexp~confsexp|Sexp(_,_::_)->true|Commentcomment->forces_breakline_comment~confcomment;;letforces_breakline_aligned_or_t~conf=function|Aligned_->true|Tt->forces_breakline~conft;;exceptionCant_align(* Check that the shape is the same and returns a new shape with updated sizes of tabs. *)lettry_check_shapeconfshape=letrectry_check_shape_innershapet=matchshape,twith|Leaf(len,at),N.Sexp(N.Atomat2,[])->(matchatom_printing_lenconfat2with|Someat2_len->Leaf(maxlenat2_len,at),Leafat2|None->raiseCant_align)|Nodeshape_list,N.Sexp(N.Listsexp_list,[])->(matchList.unzip(List.map2_exnshape_listsexp_list~f:try_check_shape_inner)with|shape_list,atom_list->Nodeshape_list,Nodeatom_list|exceptionInvalid_argument_->raiseCant_align)|_,_->raiseCant_aligninfunction|N.Comment(N.Line_commentcomment)->Some(shape,Comment_line(Line_commentcomment))|N.Comment(N.Block_comment(n,list))->Some(shape,Comment_line(Block_comment(n,list)))|N.Comment(N.Sexp_comment_)->None|N.Sexp(sexp,associated_comments)->(matchtry_check_shape_innershape(N.Sexp(sexp,[]))with|shape_list,atom_list->Some(shape_list,Atom_line(atom_list,associated_comments))|exceptionCant_align->None);;letget_shapeconf~atom_thresh~char_thresh~depth_threshlist=letrecget_shape_from_list~depth~atom_count~char_countlist_acc=function|[]->List.revlist_acc,atom_count,char_count|hd::tl->letshape,atom_count,char_count=get_shape_innerhd~depth~atom_count~char_countinget_shape_from_list(shape::list_acc)tl~depth~atom_count~char_countandget_shape_inner~depth~atom_count~char_countt=(* Breached the depth threshold. *)ifdepth>depth_threshthenraiseCant_align;matchtwith|N.Comment_->raiseCant_align|N.Sexp(N.Listlist,[])->letshape_list,atom_count,char_count=get_shape_from_list[]list~depth:(depth+1)~atom_count~char_countinNodeshape_list,atom_count,char_count|N.Sexp(N.Atomatom,[])->(matchatom_printing_lenconfatomwith|Someatom_len->letchar_count=char_count+atom_leninifatom_count<atom_thresh&&char_count<=char_threshthenLeaf(atom_len,atom),atom_count+1,char_count(* Breached the number of atoms threshold or the number of characters threshold. *)elseraiseCant_align|None->raiseCant_align)|N.Sexp(_,_::_)->raiseCant_alignintrymatchget_shape_from_list[]list~depth:1~atom_count:0~char_count:0with|shape_list,_,_->Some(Nodeshape_list)with|Cant_align->None;;letrecshape_size=function|Leaf(len,_)->len|Nodelist->List.fold_leftlist~init:0~f:(funlenshape->len+shape_sizeshape);;letfind_alignableconfshape~char_threshlist=letrecfind_alignableshaperes_acc=function|[]->shape,List.revres_acc,[]|hd::tl->(matchtry_check_shapeconfshapehdwith|None->shape,List.revres_acc,hd::tl|Some(new_shape,res)->ifshape_sizenew_shape<=char_threshthenfind_alignablenew_shape(res::res_acc)tl(* Breached the number of characters threshold. *)elseshape,List.revres_acc,hd::tl)infind_alignableshape[]list;;exceptionToo_many_atomsletget_leading_atomsconf(list:Normalize.tlist)=matchconf.leading_thresholdwith|Atom_thresholdleading_atom_threshold,Character_thresholdleading_char_threshold->letrecget_leading_atoms_inneracc~atom_count~char_count=function|[]->List.revacc,[]|N.Sexp(N.Atomatom,[])::tlaslist->(matchforces_breakline_atom~confatomwith|true->List.revacc,list|false->letchar_count=char_count+String.lengthatominifatom_count=leading_atom_threshold||char_count>leading_char_threshold(* Breached the threshold for number of leading atoms. *)thenraiseToo_many_atomselseget_leading_atoms_inner(atom::acc)tl~atom_count:(atom_count+1)~char_count)|list->List.revacc,listin(tryget_leading_atoms_inner[]~atom_count:0~char_count:0listwith|Too_many_atoms->[],list);;letpreprocessconf(t:Normalize.t):t=letrecpreprocess_t=function|N.Commentcomment->Comment(preprocess_commentcomment)|N.Sexp(sexp,associated_comments)->Sexp(preprocess_sexpsexp,associated_comments)andpreprocess_sexp=function|N.Atomatom->Atomatom|N.Listlist->(matchmaybe_singletonconflistwith|Some(atoms,lvl,sexp)->letproc_sexp=preprocess_sexpsexpinSingleton(atoms,lvl,proc_sexp,forces_breakline_sexp~confproc_sexp)|None->letleading_atoms,rest=get_leading_atomsconflistinletaligned_or_t=matchconf.data_alignmentwith|Data_not_aligned->List.maprest~f:(funel->T(preprocess_tel))|Data_aligned(_,Atom_thresholdatom_thresh,Character_thresholdchar_thresh,Depth_thresholddepth_thresh)->try_alignrest~atom_thresh~char_thresh~depth_threshinList(leading_atoms,aligned_or_t,List.existsaligned_or_t~f:(forces_breakline_aligned_or_t~conf)))andpreprocess_comment=function|N.Line_commentcomment->Line_commentcomment|N.Block_comment(i,comment)->Block_comment(i,comment)|N.Sexp_comment(comment_list,sexp)->letproc_comment_list=List.mapcomment_list~f:preprocess_commentinletproc_sexp=preprocess_sexpsexpinletcomm_force=List.existsproc_comment_list~f:(forces_breakline_comment~conf)inSexp_comment((proc_comment_list,comm_force),proc_sexp)andtry_align~atom_thresh~char_thresh~depth_threshlist=letrectry_align_inneracc=function|[]->List.revacc|[last]->List.rev(T(preprocess_tlast)::acc)|(N.Comment_ascomment)::tl->try_align_inner(T(preprocess_tcomment)::acc)tl|N.Sexp((N.Atom_assexp),associated_comments)::tl->try_align_inner(T(Sexp(preprocess_sexpsexp,associated_comments))::acc)tl|N.Sexp((N.Listlistassexp),associated_comments)::tl->letshape=get_shapeconflist~atom_thresh~char_thresh~depth_threshin(matchshapewith|None->try_align_inner(T(Sexp(preprocess_sexpsexp,associated_comments))::acc)tl|Someshape->letshape,aligned,rest=find_alignableconfshapetl~char_threshinifList.existsaligned~f:(function|Atom_line_->true|_->false)thentry_align_inner(Aligned((shape,associated_comments),aligned)::acc)restelsetry_align_inner(T(Sexp(preprocess_sexpsexp,associated_comments))::acc)tl)intry_align_inner[]listinpreprocess_tt;;letset_up_tabulationconfstateparens_alignedshapedepthfmt=letrecset_up_markers~depth~index:shape->int=function|Leaf(tab,at)->Format.pp_set_tabfmt();pp_atomconfstate~depth~len:1indexfmtat;(* Spaces that should still be printed*)tab-atom_printing_len_exnconfat|Nodeshape_list->Format.pp_set_tabfmt();open_parensconfstate~depth:(depth+1)fmt1;lettrailing_spaces=List.foldishape_list~init:0~f:(funiprevious_spacesel->for_=1toprevious_spacesdoFormat.fprintffmt" "done;ifi>0thenFormat.fprintffmt" ";set_up_markers~depth:(depth+1)~index:iel)inifparens_alignedthen(for_=1totrailing_spacesdoFormat.fprintffmt" "done;Format.pp_set_tabfmt();close_parensconfstate~depth:(depth+1)fmt1;0)else(close_parensconfstate~depth:(depth+1)fmt1;trailing_spaces)inignore(set_up_markers~depth~index:0shape:int);;(* The closing paren goes on a new line, or the last element forces a breakline. *)letnewline_at_endconfsexp=matchconf.closing_parenswith|New_line->true|Same_line->(matchsexpwith|List(_,list,true)->(matchList.lastlistwith|Some(Aligned(_,line_list))->(* Would not create an [Aligned] with an empty [line_list] *)(matchList.last_exnline_listwith|Comment_line(Line_comment_)|Atom_line(_,_::_)->true|Comment_line(Block_comment_|Sexp_comment_)|Atom_line(_,[])->false)|Some(T(Comment(Line_comment_)|Sexp(_,_::_)))->true|Some(T(Comment(Block_comment_|Sexp_comment_)|Sexp(_,[])))|None->false)|List(_,_,false)|Atom_|Singleton_->false);;letrecpp_tconfstate?(opened=Closed)?(len=1)depth?(index=0)fmt=function|Sexp(sexp,associated_comments)->pp_sexpconfstate~openeddepth~index~lenfmtsexp;pp_associated_commentsconf~depthfmtassociated_comments|Commentcomment->pp_commentconfstatedepth~indexfmtcommentandpp_sexpconfstate~opened?(len=1)depth~indexfmt=function|Atomat->pp_atomconfstate~depth~lenindexfmtat|List(leading,list,forces_breakline)assexp_list->letprint_leadinglenfmtleading=Format.fprintffmt"@[<hv>%a@]"(Format.pp_listi"@ "(pp_atomconfstate~depth:(depth+1)~len))leadinginletprint_restofffmtrest=Format.pp_listi"@ "(funifmtel->pp_t_or_alignedconfstate(depth+1)~index:(i+off)~len:(List.lengthrest)fmtel)fmtrestinletprint_openedfmtleadingrest=letleading_len=List.lengthleadinginletleading_is_not_empty=leading_len>0inletrest_is_not_empty=not(List.is_emptyrest)inifleading_is_not_emptythenprint_leadingleading_lenfmtleading;ifleading_is_not_empty&&rest_is_not_emptythenFormat.pp_print_spacefmt();ifrest_is_not_emptythenprint_restleading_lenfmtrestinletprint_closedprintleadingrest=letleading_len=List.lengthleadinginletleading_not_empty=leading_len>0inletrest_not_empty=not(List.is_emptyrest)inletsame_line_rest=matchconf.opening_parenswith|New_line->false|Same_line->rest_not_empty&¬leading_not_emptyinprint(ifsame_line_restthen1elseconf.indent)(funfmt()->open_parensconfstate~depth:(depth+1)fmt1)()(funfmt(leading,rest)->ifleading_not_emptythenprint_leadingleading_lenfmtleading;(* Close the leading atom block. *)Format.pp_close_boxfmt();ifrest_not_emptythenifleading_not_emptythenFormat.pp_print_spacefmt()elseifnotsame_line_restthenFormat.pp_print_cutfmt();ifrest_not_emptythenprint_restleading_lenfmtrest)(leading,rest)(funfmt()->close_parensconfstate~depth:(depth+1)fmt1)()in(matchleading,list,forces_breakline,opened,newline_at_endconfsexp_listwith|[],[],_,Closed,_->open_parensconfstate~depth:(depth+1)fmt1;close_parensconfstate~depth:(depth+1)fmt1|leading,rest,false,Opened,_->Format.pp_open_hvboxfmt0;print_openedfmtleadingrest;Format.pp_close_boxfmt()|leading,rest,true,Opened,_->print_openedfmtleadingrest|leading,rest,true,Closed,true->(* There must be something in the list, if it forces a breakline *)print_closed(Format.fprintffmt"@[<v %d>@[<h>%a%a@]@,%a")leadingrest|leading,rest,true,Closed,false->(* There must be something in the list, if it forces a breakline *)print_closed(Format.fprintffmt"@[<v %d>@[<h>%a%a@]%a")leadingrest|leading,rest,false,Closed,true->print_closed(Format.fprintffmt"@[<h>@[<hv>@[<hv %d>@[<h>%a%a@]@,@]%a@]")leadingrest|leading,rest,false,Closed,false->print_closed(Format.fprintffmt"@[<h>@[<hv>@[<hv %d>@[<h>%a%a@]@]%a@]")leadingrest)|Singleton(atoms,d,sexp,forces_breakline)->letprint_openedprinteratoms=printerconf.indent(Format.pp_listi"@ "(pp_atomconfstate~depth:(depth+1)~len:(List.lengthatoms)))atoms(open_parensconfstate~depth:(depth+2))d(pp_sexpconfstate~opened:Opened(depth+d)~index:0~len:1)sexp(close_parensconfstate~depth:(depth+2))dinletprint_closedprinteratoms=printerconf.indent(open_parensconfstate~depth:(depth+1))1(funfmt->function|[]->()|atoms->Format.pp_listi"@ "(pp_atomconfstate~depth:(depth+1)~len:(List.lengthatoms))fmtatoms;Format.pp_print_spacefmt())atoms(open_parensconfstate~depth:(depth+2))d(pp_sexpconfstate~opened:Opened(depth+d)~len:1~index:0)sexp(close_parensconfstate~depth:(depth+1))(d+1)in(matchatoms,forces_breakline,opened,newline_at_endconfsexpwith|[],_,Opened,_->assertfalse|atoms,true,Closed,true->print_closed(Format.fprintffmt"@[<v %d>@[<h>%a%a%a@]@,%a@]@,%a")atoms|atoms,true,Closed,false->print_closed(Format.fprintffmt"@[<v %d>@[<h>%a%a%a@]@,%a@]%a")atoms|atoms,false,Closed,true->print_closed(Format.fprintffmt"@[<h>@[<hv>@[<hv %d>@[<h>%a%a%a@]@,%a@]@,@]%a@]")atoms|atoms,false,Closed,false->print_closed(Format.fprintffmt"@[<h>@[<hv>@[<hv %d>@[<h>%a%a%a@]@,%a@]@]%a@]")atoms|atoms,true,Opened,true->print_opened(Format.fprintffmt"@[<v %d>@[<h>%a@ %a@]@,%a@]@,%a")atoms|atoms,true,Opened,false->print_opened(Format.fprintffmt"@[<v %d>@[<h>%a@ %a@]@,%a@]%a")atoms|atoms,false,Opened,true->print_opened(Format.fprintffmt"@[<h>@[<hv>@[<hv %d>@[<h>%a@ %a@]@,%a@]@,@]%a@]")atoms|atoms,false,Opened,false->print_opened(Format.fprintffmt"@[<h>@[<hv>@[<hv %d>@[<h>%a@ %a@]@,%a@]@]%a@]")atoms)andpp_t_or_alignedconfstatedepth~len~indexfmt=function|Tt->pp_tconfstate~lendepth~indexfmtt|Aligned((shape,associated_comments),line_list)->pp_alignedconfstatedepthfmtshapeassociated_commentsline_listandpp_commentconfstatedepth~indexfmtcomment=matchconf.commentswith|Drop->assertfalse|_->();(matchcommentwith|Line_commentcomment->pp_atomconf{content_kind=CommentLine_comment}~depth~len:1indexfmtcomment|Block_comment(indent,comment_list)->(matchconf.commentswith|Drop->assertfalse(* Would have dropped the comment at pre-processing. *)|Print(_,color,Conservative_print)->letf=matchcolorwith|Some_->Format.fprintffmt"@{<c %d>@[<h>#|%a|#@]@}"(* This is an ugly hack not to print anything if colors are disabled. The opening
tag works fine, as it checks whether or not anything should be printed. The
closing one doesn't (it can't have any arguments, which is bad).
*)|None->Format.fprintffmt"@{<c %d}@[<h>#|%a|#@]"infdepth(funfmtcomment_list->Format.pp_list"@."(funfmtcomm->Format.fprintffmt"%s"comm)fmtcomment_list)comment_list|Print(_,color,Pretty_print)->letf=matchcolorwith|Some_->Format.fprintffmt"@{<c %d>@[<h>@[<hv>@[<hv %d>#|%a@[<hov>%a@]@]@ @]|#@]@}"|None->Format.fprintffmt"@{<c %d>@[<h>@[<hv>@[<hv %d>#|%a@[<hov>%a@]@]@ @]|#@]"infdepthindent(funfmtspaces->Format.pp_print_breakfmtspaces0)(ifindent>2&¬(List.is_emptycomment_list)thenindent-2else0)(funfmtcomment_list->Format.pp_list"@ "(funfmtcomm->Format.fprintffmt"%s"comm)fmtcomment_list)comment_list)|Sexp_comment((comments,_),sexp)->(matchconf.commentswith|Drop->assertfalse|Print(_,Some_,_)->Format.fprintffmt"@{<c %d>#;@}@ "depth|Print(_,None,_)->Format.fprintffmt"#;@ ");List.itericomments~f:(funicomm->pp_commentconfstatedepth~index:ifmtcomm);ifnot(List.is_emptycomments)thenFormat.pp_print_spacefmt();pp_sexpconf{content_kind=CommentSexp_comment}~opened:Closeddepth~indexfmtsexp)andpp_alignedconfstatedepthfmtshapeassociated_commentsalign_list=letparens_aligned=matchconf.data_alignmentwith|Data_aligned(Parens_alignmenta,_,_,_)->a|_->assertfalseinletrecprint_aligned~depthindex=function|Leafat->Format.pp_print_tabfmt();pp_atomconfstate~depth~len:1indexfmtat|Nodelist->Format.pp_print_tabfmt();open_parensconfstate~depth:(depth+1)fmt1;List.iterilist~f:(print_aligned~depth:(depth+1));ifparens_alignedthenFormat.pp_print_tabfmt();close_parensconfstate~depth:(depth+1)fmt1inletprint_aligned_or_commentindex=function(* Comments on a separate line for now. *)|Comment_linecomm->Format.pp_print_cutfmt();pp_commentconfstatedepth~indexfmtcomm|Atom_line(line,associated_comments)->Format.pp_print_cutfmt();print_aligned~depth0line;pp_associated_commentsconf~depthfmtassociated_commentsinFormat.pp_open_tboxfmt();set_up_tabulationconfstateparens_alignedshapedepthfmt;pp_associated_commentsconf~depthfmtassociated_comments;List.iterialign_list~f:print_aligned_or_comment;Format.pp_close_tboxfmt();;letpp_sexp_rainbow_toplevelconffmtsexp=lett=Normalize.of_sexp_or_commentconfsexpinletaligned=preprocessconftinFormat.fprintffmt"@[<v>%a@]@."(pp_tconfstart_state~opened:Closed0~index:0)aligned;;endletsetupconffmt=Format.pp_set_formatter_stag_functionsfmt(rainbow_tagsconf);Format.pp_set_tagsfmttrue;;letrun~nextconffmt=setupconffmt;letrecloopprints_newline=matchnext()with|None->()|Somet_or_comment->(matchconf.comments,t_or_commentwith|Drop,W.Comment_->loopprints_newline|Print_,W.Comment_->(matchprints_newline,conf.separatorwith|true,Empty_line->Format.pp_print_breakfmt00|false,_|_,No_separator->());Print.pp_sexp_rainbow_toplevelconffmtt_or_comment;loopfalse|_,W.Sexp_->(matchprints_newline,conf.separatorwith|true,Empty_line->Format.pp_print_breakfmt00|false,_|_,No_separator->());Print.pp_sexp_rainbow_toplevelconffmtt_or_comment;looptrue)inFormat.pp_open_vboxfmt0;loopfalse;ifconf.paren_coloringthen(* Reset all formatting *)Format.pp_print_stringfmt"[0m";Format.pp_close_boxfmt();Format.pp_print_flushfmt();;letdummy_pos={Sexplib.Src_pos.Relative.row=0;col=0}letrecsexp_to_sexp_or_comment=function|Sexp.Atomat->letfmt_at=Some(Sexplib.Pre_sexp.mach_maybe_esc_strat)inW.Sexp(W.Atom(dummy_pos,at,fmt_at))|Sexp.Listlist->W.Sexp(W.List(dummy_pos,List.maplist~f:sexp_to_sexp_or_comment,dummy_pos));;moduleMake(M:sigtypetvalto_sexp_or_comment:t->Sexp.With_layout.t_or_commentend):Swithtypesexp:=M.t=structtype'awriter=Config.t->'a->M.t->unitletpp_formatterconffmtsexp=lett_or_comment=M.to_sexp_or_commentsexpinletnext=letstop=reffalseinfun()->if!stopthenNoneelse(stop:=true;Somet_or_comment)inrun~nextconffmt;;letpp_formatter'~nextconffmt=run~next:(fun()->matchnext()with|None->None|Somes->Some(M.to_sexp_or_comments))conffmt;;letpp_bufferconfbuffersexp=pp_formatterconf(Format.formatter_of_bufferbuffer)sexp;;letpp_out_channelconfocsexp=pp_formatterconf(Format.formatter_of_out_channeloc)sexp;;letpp_blitconfblitsexp=letformatter=Format.make_formatter(funbufposlen->blitbuf~pos~len)ignoreinpp_formatterconfformattersexp;;letpretty_stringconfsexp=letbuffer=Buffer.create16inpp_bufferconfbuffersexp;Buffer.contentsbuffer;;letsexp_to_string=letconfig=lazy(Config.create~color:false())infunsexp->pretty_string(Lazy.forceconfig)sexp;;endincludeMake(structtypet=Sexp.tletto_sexp_or_comment=sexp_to_sexp_or_commentend)moduleSexp_with_layout=Make(structtypet=W.t_or_commentletto_sexp_or_comment=Fn.idend)