123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197moduleStyle=structtypet=stringletfg_black="30"letfg_red="31"letfg_green="32"letfg_yellow="33"letfg_blue="34"letfg_magenta="35"letfg_cyan="36"letfg_white="37"letfg_default="39"letfg_bright_black="90"letfg_bright_red="91"letfg_bright_green="92"letfg_bright_yellow="93"letfg_bright_blue="94"letfg_bright_magenta="95"letfg_bright_cyan="96"letfg_bright_white="97"letbg_black="40"letbg_red="41"letbg_green="42"letbg_yellow="43"letbg_blue="44"letbg_magenta="45"letbg_cyan="46"letbg_white="47"letbg_default="49"letbg_bright_black="100"letbg_bright_red="101"letbg_bright_green="102"letbg_bright_yellow="103"letbg_bright_blue="104"letbg_bright_magenta="105"letbg_bright_cyan="106"letbg_bright_white="107"letbold="1"letdim="2"letunderlined="4"letescape_sequencel=letl="0"::linPrintf.sprintf"\027[%sm"(String.concatl~sep:";")letescape_sequence_no_resetl=Printf.sprintf"\027[%sm"(String.concatl~sep:";")endletterm_supports_color=lazy(matchStdlib.Sys.getenv"TERM"with|exceptionNot_found->false|"dumb"->false|_->true)letstdout_supports_color=lazy(Lazy.forceterm_supports_color&&Unix.isattyUnix.stdout)letstderr_supports_color=lazy(Lazy.forceterm_supports_color&&Unix.isattyUnix.stderr)letrectag_handlercurrent_stylesppfstylespp=Format.pp_print_asppf0(Style.escape_sequence_no_resetstyles);Pp.renderppfpp~tag_handler:(tag_handler(current_styles@styles));Format.pp_print_asppf0(Style.escape_sequencecurrent_styles)letmake_printersupports_colorppf=letf=lazy(ifLazy.forcesupports_colorthenPp.renderppf~tag_handler:(tag_handler[])elsePp.render_ignore_tagsppf)inStaged.stage(funpp->Lazy.forcefpp;Format.pp_print_flushppf())letprint=Staged.unstage(make_printerstdout_supports_colorFormat.std_formatter)letprerr=Staged.unstage(make_printerstderr_supports_colorFormat.err_formatter)letstripstr=letlen=String.lengthstrinletbuf=Buffer.createleninletrecloopi=ifi=lenthenBuffer.contentsbufelsematchstr.[i]with|'\027'->skip(i+1)|c->Buffer.add_charbufc;loop(i+1)andskipi=ifi=lenthenBuffer.contentsbufelsematchstr.[i]with|'m'->loop(i+1)|_->skip(i+1)inloop0letparse_linestrstyles=letlen=String.lengthstrinletadd_chunkacc~styles~pos~len=iflen=0thenaccelselets=Pp.verbatim(String.substr~pos~len)inlets=matchstyleswith|[]->s|_->Pp.tagstylessinPp.seqaccsinletrecloopstylesiacc=matchString.index_fromstri'\027'with|None->(styles,add_chunkacc~styles~pos:i~len:(len-i))|Someseq_start->(letacc=add_chunkacc~styles~pos:i~len:(seq_start-i)in(* Skip the "\027[" *)letseq_start=seq_start+2inifseq_start>=len||str.[seq_start-1]<>'['then(styles,acc)elsematchString.index_fromstrseq_start'm'with|None->(styles,acc)|Someseq_end->letstyles=ifseq_start=seq_endthen(* Some commands output "\027[m", which seems to be interpreted
the same as "\027[0m" by terminals *)[]elseString.substr~pos:seq_start~len:(seq_end-seq_start)|>String.split~on:';'|>List.fold_left~init:(List.revstyles)~f:(funstyless->matchswith|"0"->[]|_->s::styles)|>List.revinloopstyles(seq_end+1)acc)inloopstyles0Pp.nopletparse=letrecloopstyleslinesacc=matchlineswith|[]->Pp.vbox(Pp.concat~sep:Pp.cut(List.revacc))|line::lines->letstyles,pp=parse_linelinestylesinloopstyleslines(pp::acc)infunstr->loop[](String.split_linesstr)[]