123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976openBaseopenIntopenConfigmoduleSexp=Sexplib.SexpmoduleW=Sexp.With_layoutmoduleFormat=structincludeCaml.Formatletpp_listisep?(offset=0)?singletonppfmtlist=matchlistwith|[]->()|hd::tl->(matchsingleton,tlwithSomepp,[]->ppoffsetfmthd|_->ppoffsetfmthd);List.iteritl~f:(funiel->Caml.Format.fprintffmtsep;pp(i+offset+1)fmtel);;letpp_listsep?singletonppfmtlist=letsingleton=Option.mapsingleton~f:(funsingleton->(fun_->singleton))inpp_listisep?singleton(fun_->pp)fmtlist;;endmoduleConfig=Configtypestate={is_comment:bool}letstart_state={is_comment=false;}letsplit=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->39letrainbow_open_tagconftag=letargs=Re.Str.splitsplittaginletcolor_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"|_->"")|_->tagletrainbow_tagsconf={Format.mark_open_tag=rainbow_open_tagconf;Format.mark_close_tag=(fun_->"[0m");Format.print_open_tag=ignore;Format.print_close_tag=ignore}(* Opens n parentheses, starting at level depth. *)letopen_parensconfstate~depthfmtn=matchconf.paren_coloring,state.is_comment,conf.commentswith(* Overrides the option not to color parentheses. *)|_,true,Print(_,Some_,_)->fori=depthto(depth+n-1)doFormat.fprintffmt"@{<c %d>(@}"idone|true,false,_->fori=depthto(depth+n-1)doFormat.fprintffmt"@{<d %d>(@}"idone|_,_,_->for_=depthto(depth+n-1)doFormat.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.is_comment,conf.commentswith|_,true,Print(_,Some_,_)->fori=depth+(n-1)downtodepthdoFormat.fprintffmt"@{<c %d>)@}"idone|true,false,_->fori=depth+(n-1)downtodepthdoFormat.fprintffmt"@{<d %d>)@}"idone|_,_,_->for_=depth+(n-1)downtodepthdoFormat.fprintffmt")"doneletpp_atomconfstate~depth~lenindexfmtat=letat=ifstate.is_commentthenatelseSexplib.Pre_sexp.mach_maybe_esc_stratinletshould_be_colored=matchconf.atom_coloringwith|Color_none->false|Color_firstthreshold->Int.equalindex0&&len<=threshold|Color_all->trueinifstate.is_commentthenmatchconf.commentswith|Drop->assertfalse|Print(_,Some_,_)->Format.fprintffmt"@{<c %d>%s@}"depthat|Print(_,None,_)->Format.fprintffmt"%s"atelseifshould_be_coloredthenFormat.fprintffmt"@{<d %d>%s@}"depthatelseFormat.fprintffmt"%s"atmoduleNormalize=structtypet=|Sexpofsexp|Commentofcommentandcomment=|Line_commentofstring|Block_commentofint*stringlist|Sexp_commentof(commentlist)*sexpandsexp=|Atomofstring|Listoftlistletparse_sexps=Sexp.With_layout.Parser.sexpsSexp.With_layout.Lexer.mainmodulePos=Sexplib.Src_pos.Relativetypematch_dimension=|Horizontal|Verticalletgrab_commentspos=letrecgrab_commentsdimensionaccpos=function|[]->acc,[]|(W.Sexp_::_)aslist->acc,list|(W.Comment(W.Plain_comment(cpos,_))ascomment)::rest->if(matchdimensionwith|Horizontal->pos.Pos.row=cpos.Pos.row|Vertical->pos.Pos.col=cpos.Pos.col)thengrab_commentsVertical(comment::acc)cposrestelseacc,comment::rest|(W.Comment(W.Sexp_comment_)::_)aslist->acc,listingrab_commentsHorizontal[]pos(* Re-orders comments to have comment that belong to a sexp before it, not after. *)letreorder_commentsconft_or_sexp=letrecreorder_t_or_sexp=function|W.Sexpsexp->W.Sexp(reorder_sexpsexp)|W.Commentcomment->W.Comment(reorder_commentcomment)andreorder_sexp=function|W.Atom_asatom->atom|W.List(pos1,list,pos2)->letlist=List.maplist~f:(funel->reorder_t_or_sexpel)inW.List(pos1,reorder_listlist,pos2)andreorder_comment=function|W.Plain_comment_ascomment->comment|W.Sexp_comment(pos,comment_list,sexp)->W.Sexp_comment(pos,List.mapcomment_list~f:(funel->reorder_commentel),reorder_sexpsexp)andreorder_list=function|[]->[]|(W.Sexp(W.Atom(pos,_,_))assexp)::rest(* Taking the ending position. *)|(W.Sexp(W.List(_,_,pos))assexp)::rest->letcomments,rest=grab_commentsposrestinList.rev_appendcomments(sexp::reorder_listrest)|W.Comment_ascomment::rest->comment::reorder_listrestinifnot(conf.sticky_comments)thent_or_sexpelsereorder_t_or_sexpt_or_sexpletrecpre_process_atomconfposatom=matchconf.atom_printingwith|Escaped->`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,Some_))]->beginmatchpre_process_atomconfposinner_atomwith|`Atom_->`Atomatom(* original atom is better since it contains original
spacing which will be stripped off by
pre_process_atom *)|`Listlst->`Listlstend(* 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->beginletget_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))]end|(W.Sexp(W.List_)::_)aslists->lists|(W.Comment_::_)ascomments->comments|[]->[](* cant really happen *))|>List.concatin`List(concatenate_atomssexps)));;letblock_comment=Re.Str.regexp"#|\\(\\([\t ]*\\)\\(\\(\n\\|.\\)*\\)\\)|#"letline_split=Re.Str.regexp"\n[ \t]*"letword_split=Re.Str.regexp"[ \n\t]+"lettrailing=Re.Str.regexp"\\(.*\\b\\)[ \t]*$"lettab_size=2letpre_process_block_commentstylecomment=(* Split along lines or words. *)letcontents=matchstylewith|Pretty_print->Re.Str.splitword_splitcomment|Conservative_print->Re.Str.splitline_splitcommentin(* Remove trailing spaces. *)letcontents=List.mapcontents~f:(funline->ifRe.Str.string_matchtrailingline0thenRe.Str.matched_group1lineelseline)inList.filtercontents~f:(funs->String.lengths>0)letget_sizestring=String.countstring~f:(func->Char.equalc' ')+String.countstring~f:(func->Char.equalc'\t')*tab_sizeexceptionDrop_exn(* Converts to t, does initial pre-processing - interprets/escapes atoms,
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->Atom(atom)|`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->letlist=List.filter_maplist~f:(funel->trySome(of_sexp_or_commentconfel)withDrop_exn->None)inListlistandof_comment(conf:Config.t):W.comment->comment=function|W.Plain_comment(_,comment)->(matchconf.commentswith|Drop->raiseDrop_exn|Print(indent,_,style)->ifRe.Str.string_matchblock_commentcomment0thenletind=matchindentwith|Auto_indent_comment->get_size(Re.Str.matched_group2comment)+2|Indent_commenti->iinBlock_comment(ind,pre_process_block_commentstyle(Re.Str.matched_group3comment))elseLine_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=Normalizetypeforces_breakline=booltypeopened=|Opened|Closedtype'atree=|Nodeof'atreelist|Leafof'a(* Also contains the first atom list. *)typeshape=(int*string)treetypet=|Sexpofsexp|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*linelistandline=|Atom_lineofstringtree|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,atomininner0sexpletmaybe_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: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_listletforces_breakline_sexp=function|Atom_->false|List(_,_,forces)->forces|Singleton(_,_,_,forces)->forcesletforces_breakline_comment=function|Line_comment_->true|Block_comment_->false|Sexp_comment((_,comm_force),sexp)->comm_force||forces_breakline_sexpsexpletforces_breakline=function|Sexpsexp->forces_breakline_sexpsexp|Commentcomment->forces_breakline_commentcommentletforces_breakline_aligned_or_t=function|Aligned_->true|Tt->forces_breaklinetexceptionCant_align(*Check that the shape is the same and returns a new shape with updated sizes of tabs.*)lettry_check_shapeshape=letrectry_check_shape_innershapesexp=matchshape,sexpwith|Leaf(len,at),N.Sexp(N.Atomat2)->Leaf(maxlen(String.lengthat2),at),Leafat2|Nodeshape_list,N.Sexp(N.Listsexp_list)->(trylet(shape_list,atom_list)=List.unzip(List.map2_exnshape_listsexp_list~f:try_check_shape_inner)inNodeshape_list,Nodeatom_listwith|Invalid_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_assexp->tryletshape_list,atom_list=try_check_shape_innershapesexpinSome(shape_list,Atom_lineatom_list)withCant_align->Noneletget_shape~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->let(shape,atom_count,char_count)=get_shape_innerhd~depth:depth~atom_count:atom_count~char_count:char_countinget_shape_from_list(shape::list_acc)tl~depth:depth~atom_count:atom_count~char_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:atom_count~char_count:char_countinNodeshape_list,atom_count,char_count|N.Sexp(N.Atomatom)->letatom_len=String.lengthatominletchar_count=char_count+atom_leninifatom_count<atom_thresh&&char_count<=char_threshthen(Leaf(atom_len,atom),atom_count+1,char_count)(* Breached the number of atoms threshold or the number of characters threshold. *)elseraiseCant_alignintrymatchget_shape_from_list[]list~depth:1~atom_count:0~char_count:0with|shape_list,_,_->Some(Nodeshape_list)withCant_align->Noneletrecshape_size=function|Leaf(len,_)->len|Nodelist->List.fold_leftlist~init:0~f:(funlenshape->len+shape_sizeshape)letfind_alignableshape~char_threshlist=letrecfind_alignableshaperes_acc=function|[]->shape,List.revres_acc,[]|hd::tl->matchtry_check_shapeshapehdwith|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::tlinfind_alignableshape[]listexceptionToo_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)::tl->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:char_count|list->List.revacc,listintryget_leading_atoms_inner[]~atom_count:0~char_count:0listwith|Too_many_atoms->[],listletpreprocessconf(t:Normalize.t):t=letrecpreprocess_t=function|N.Commentcomment->Comment(preprocess_commentcomment)|N.Sexpsexp->Sexp(preprocess_sexpsexp)andpreprocess_sexp=function|N.Atomatom->Atomatom|N.Listlist->matchmaybe_singletonconflistwith|Some(atoms,lvl,sexp)->letproc_sexp=preprocess_sexpsexpinSingleton(atoms,lvl,proc_sexp,forces_breakline_sexpproc_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:atom_thresh~char_thresh:char_thresh~depth_thresh:depth_threshinList(leading_atoms,aligned_or_t,List.existsaligned_or_t~f:(forces_breakline_aligned_or_t))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_commentinSexp_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.Atomatom)::tl->try_align_inner(T(Sexp(Atomatom))::acc)tl|N.Sexp(N.Listlist)::tl->letshape=get_shapelist~atom_thresh:atom_thresh~char_thresh:char_thresh~depth_thresh:depth_threshin(matchshapewith|None->try_align_inner(T(Sexp(preprocess_sexp(N.Listlist)))::acc)tl|Someshape->letshape,aligned,rest=find_alignableshapetl~char_thresh:char_threshinifList.existsaligned~f:(functionAtom_line_->true|_->false)thentry_align_inner(Aligned(shape,aligned)::acc)restelsetry_align_inner(T(Sexp(preprocess_sexp(N.Listlist)))::acc)tl)intry_align_inner[]listinpreprocess_ttletset_up_tabulationconfstateparens_alignedshapedepthfmt=letrecset_up_markers~depth~index:shape->int=function|Leaf(tab,at)->Format.pp_set_tabfmt();pp_atomconfstate~depth:depth~len:1indexfmtat;(* Spaces that should still be printed*)tab-String.lengthat|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:depth~index:0shape)(* The last element forces a breakline. *)letlast_forces=function|List(_,list,true)->(matchList.lastlistwith|Some(Aligned(_,line_list))->(matchList.lastline_listwith|None->false|Some(Comment_line(Line_comment_))->true(* For now. *)|Some(Atom_line_)->false|_->false)|Some(T(Comment(Line_comment_)))->true|_->false)|_->falseletrecpp_tconfstate?(opened=Closed)?(len=1)depth?(index=0)fmt=function|Sexpsexp->pp_sexpconfstate~opened:openeddepth~index:index~len:lenfmtsexp|Commentcomment->pp_commentconfstatedepth~index:indexfmtcommentandpp_sexpconfstate~opened?(len=1)depth~indexfmt=function|Atomat->pp_atomconfstate~depth:depth~len:lenindexfmtat|List(leading,list,forces_breakline)assexp_list->letprint_leadinglenfmtleading=Format.fprintffmt"@[<hv>%a@]"(Format.pp_listi"@ "(pp_atomconfstate~depth:(depth+1)~len: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=Poly.equalconf.opening_parensSame_line&&rest_not_empty&¬(leading_not_empty)inprint(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()elseifnot(same_line_rest)thenFormat.pp_print_cutfmt();ifrest_not_emptythenprint_restleading_lenfmtrest)(leading,rest)(funfmt()->close_parensconfstate~depth:(depth+1)fmt1)()in(matchleading,list,forces_breakline,opened,Poly.equalconf.closing_parensNew_line||last_forcessexp_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,Poly.equalconf.closing_parensNew_line||last_forcessexpwith|[],_,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~len:lendepth~index:indexfmtt|Aligned(shape,line_list)->pp_alignedconfstatedepthfmtshapeline_listandpp_commentconfstatedepth~indexfmtcomment=matchconf.commentswith|Drop->assertfalse|_->();matchcommentwith|Line_commentcomment->pp_atomconf{is_comment=true}~depth:depth~len:1indexfmtcomment|Block_comment(indent,comment_list)->(matchconf.commentswith|Drop->assertfalse(* Would have dropped the comment at pre-processing. *)|Print(_,Some_,Conservative_print)->Format.fprintffmt"@{<c %d>@[<h>@[<hv>@[<hv %d>#|%a%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).
*)|Print(_,None,Conservative_print)->Format.fprintffmt"@{<c %d}@[<h>@[<hv>@[<hv %d>#|%a%a@]@ @]|#@]"|Print(_,Some_,Pretty_print)->Format.fprintffmt"@{<c %d>@[<h>@[<hv>@[<hv %d>#|%a@[<hov>%a@]@]@ @]|#@]@}"|Print(_,None,Pretty_print)->Format.fprintffmt"@{<c %d>@[<h>@[<hv>@[<hv %d>#|%a@[<hov>%a@]@]@ @]|#@]")depthindent(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{is_comment=true}~opened:Closeddepth~index:indexfmtsexpandpp_alignedconfstatedepthfmtshapealign_list=letparens_aligned=(matchconf.data_alignmentwith|Data_aligned(Parens_alignmenta,_,_,_)->a|_->assertfalse)inletrecprint_aligned~depthindex=function|Leafat->Format.pp_print_tabfmt();pp_atomconfstate~depth: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~index:indexfmtcomm|Atom_lineline->Format.pp_print_cutfmt();print_aligned~depth:depth0lineinFormat.pp_open_tboxfmt();set_up_tabulationconfstateparens_alignedshapedepthfmt;List.iterialign_list~f:print_aligned_or_comment;Format.pp_close_tboxfmt()letpp_sexp_rainbow_toplevelconffmtsexp=letsexp=Normalize.reorder_commentsconfsexpinlett=Normalize.of_sexp_or_commentconfsexpinletaligned=preprocessconftinFormat.fprintffmt"@[<v>%a@]@."(pp_tconfstart_state~opened:Closed(0)~index:0)alignedendletsetupconffmt=Format.pp_set_formatter_tag_functionsfmt(rainbow_tagsconf);Format.pp_set_tagsfmttrueletrun~nextconffmt=setupconffmt;letrecloopprints_newline=matchnext()with|None->()|Somet_or_comment->matchconf.comments,t_or_commentwith|Drop,W.Comment_->loopprints_newline|Print_,W.Comment_->ifprints_newline&&Poly.equalconf.separatorEmpty_linethenFormat.pp_print_breakfmt00;Print.pp_sexp_rainbow_toplevelconffmtt_or_comment;loopfalse|_,W.Sexp_->ifprints_newline&&Poly.equalconf.separatorEmpty_linethenFormat.pp_print_breakfmt00;Print.pp_sexp_rainbow_toplevelconffmtt_or_comment;looptrueinFormat.pp_open_vboxfmt0;loopfalse;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))moduletypeS=sigtypesexptype'awriter=Config.t->'a->sexp->unitvalpp_formatter:Format.formatterwritervalpp_formatter':next:(unit->sexpoption)->Config.t->Caml.Format.formatter->unitvalpp_buffer:Buffer.twritervalpp_out_channel:Caml.out_channelwritervalpp_blit:(string,unit)Blit.subwritervalpretty_string:Config.t->sexp->stringvalsexp_to_string:sexp->stringendmoduleMake(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=reffalsein(fun()->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))conffmtletpp_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)