123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489(* Escape: Low-level ANSI escape sequence builders *)typewriter=Writer.ttypet=writer->unittypeterminator=[`Bel|`St](* ASCII character code constants *)letchar_0=Char.code'0'(* 48 *)letchar_a_hex=Char.code'a'-10(* 87: for hex digits a-f *)(* Writer primitives — delegated to Writer module *)letmake=Writer.makeletlen=Writer.lenletreset_pos=Writer.reset_posletslice=Writer.slice(* Low-level writing — delegated to Writer module *)letwrite_char=Writer.write_charletwrite_string=Writer.write_stringletwrite_subbytes=Writer.write_subbytes(* Integer writing - optimized for zero stack allocation *)let[@inline]digit_charn=Char.unsafe_chr(char_0+n)(* Powers of 10 lookup table sized for the host int range. *)letpow10=letrecmax_digitspd=ifp>max_int/10thendelsemax_digits(p*10)(d+1)inletlen=max_digits11inlettbl=Array.makelen1infori=1tolen-1dotbl.(i)<-tbl.(i-1)*10done;tbl(* Write unsigned integer - fully iterative, no stack allocation *)letadd_uintwn=ifn=0thenwrite_charw'0'elseletdigits=ref1inletmax_digits=Array.lengthpow10inwhile!digits<max_digits&&n>=pow10.(!digits)doincrdigitsdone;fori=!digits-1downto0dowrite_charw(digit_char(n/pow10.(i)mod10))doneletadd_intwn=ifn>=0thenadd_uintwnelse((* Work in negative space to handle Int.min_int without overflow. For
negative n: q = n / 10 is negative or zero, r = n - q*10 is in [-9, 0] *)write_charw'-';letrecemit_negativen=letq=n/10inletr=n-(q*10)inifq<>0thenemit_negativeq;write_charw(digit_char(-r))inemit_negativen)(* Write a two-digit lowercase hex value for a byte [0,255] *)let[@inline]hex_digitn=Char.unsafe_chr(ifn<10thenchar_0+nelsechar_a_hex+n)letadd_hex2wv=letv=max0(min255v)inwrite_charw(hex_digit(vlsr4));write_charw(hex_digit(vland0xF))letwrite_terminatorw=function|`Bel->write_charw'\007'|`St->write_charw'\027';write_charw'\\'(* Combinators *)letempty_=()letliteralsw=write_stringwsletcharcw=write_charwcletconcatabw=aw;bwletseqtsw=letrecloop=function|[]->()|t::rest->tw;looprestinlooptsletemittw=twletbytesb~off~lenw=write_subbyteswbofflenletutf8cpw=letcp=(* Clamp invalid unicode scalars or surrogates to replacement char U+FFFD *)ifcp<0||cp>0x10FFFF||(cp>=0xD800&&cp<=0xDFFF)then0xFFFDelsecpinifcp<0x80thenwrite_charw(Char.chrcp)elseifcp<0x800then(write_charw(Char.chr(0xC0lor(cplsr6)));write_charw(Char.chr(0x80lor(cpland0x3F))))elseifcp<0x10000then(write_charw(Char.chr(0xE0lor(cplsr12)));write_charw(Char.chr(0x80lor((cplsr6)land0x3F)));write_charw(Char.chr(0x80lor(cpland0x3F))))else(write_charw(Char.chr(0xF0lor(cplsr18)));write_charw(Char.chr(0x80lor((cplsr12)land0x3F)));write_charw(Char.chr(0x80lor((cplsr6)land0x3F)));write_charw(Char.chr(0x80lor(cpland0x3F))))(* IO Utilities *)letto_string(t:t)=(* Pass 1: counting *)letcounter=Writer.make_counting()intcounter;letn=Writer.lencounterin(* Fast path: return empty string literal without allocation *)ifn=0then""else(* Pass 2: actual write *)letbytes=Bytes.createninletw=Writer.makebytesintw;letactual=Writer.lenwinifactual<>ntheninvalid_arg(Printf.sprintf"Escape.to_string: stateful sequence detected (expected %d bytes, \
wrote %d)"nactual);Bytes.unsafe_to_stringbytes(* to_buffer: convenience wrapper via to_string (allocates intermediate
string) *)letto_buffertbuf=Buffer.add_stringbuf(to_stringt)(* CSI / SGR helpers *)letescbodyw=write_stringw"\027[";write_stringwbodyletcsi~params~commandw=write_stringw"\027[";write_stringwparams;write_charwcommandletsgrcodesw=matchcodeswith|[]->()|first::rest->write_stringw"\027[";add_intwfirst;letrecloop=function|[]->()|code::rest->write_charw';';add_intwcode;looprestinlooprest;write_charw'm'(* sgr_direct uses writer position to track separator state without ref
allocation *)letsgr_directwrite_codesw=write_stringw"\027[";letstart_pos=Writer.poswinwrite_codes(funcode->ifWriter.posw>start_posthenwrite_charw';';add_intwcode);write_charw'm'(* Low-level SGR building primitives for zero-allocation emission *)let[@inline]sgr_openw=write_stringw"\027["let[@inline]sgr_codewn=add_intwnlet[@inline]sgr_sepw=write_charw';'let[@inline]sgr_closew=write_charw'm'letreset:t=sgr[0]letclamp_nonnegv=max0vletclamp_posv=max1vletclamp_bytev=max0(min255v)(* CSI sequence helpers to reduce code duplication *)(* CSI with single param, returns empty if n=0 *)let[@inline]csi_n_optcmdn=letn=clamp_nonnegninifn=0thenemptyelsefunw->write_stringw"\027[";add_intwn;write_charwcmd(* CSI with single param, always emits *)let[@inline]csi_ncmdnw=write_stringw"\027[";add_intwn;write_charwcmd(* CSI with two params *)let[@inline]csi_nncmdabw=write_stringw"\027[";add_intwa;write_charw';';add_intwb;write_charwcmd(* Cursor Control *)letcursor_up~n=csi_n_opt'A'nletcursor_down~n=csi_n_opt'B'nletcursor_forward~n=csi_n_opt'C'nletcursor_back~n=csi_n_opt'D'nletcursor_next_line~n=csi_n_opt'E'nletcursor_previous_line~n=csi_n_opt'F'nletcursor_horizontal_absolutecolw=letcol=clamp_poscolincsi_n'G'colwletcursor_vertical_absoluteroww=letrow=clamp_posrowincsi_n'd'rowwletcursor_position~row~colw=letrow=clamp_posrowinletcol=clamp_poscolincsi_nn'H'rowcolwletcursor_save:t=literal"\027[s"letcursor_restore:t=literal"\027[u"(* Cursor Appearance *)typecursor_shape=[`Default|`Blinking_block|`Block|`Blinking_underline|`Underline|`Blinking_bar|`Bar]letcursor_shape_to_int=function|`Default->0|`Blinking_block->1|`Block->2|`Blinking_underline->3|`Underline->4|`Blinking_bar->5|`Bar->6letcursor_style~shapew=write_stringw"\027[";add_intw(cursor_shape_to_intshape);write_stringw" q"letcursor_color~r~g~bw=write_stringw"\027]12;#";add_hex2w(clamp_byter);add_hex2w(clamp_byteg);add_hex2w(clamp_byteb);write_terminatorw`Belletreset_cursor_color:t=literal"\027]112\007"letreset_cursor_color_fallback:t=literal"\027]12;default\007"(* Screen Control *)letclear:t=literal"\027[2J"lethome:t=literal"\027[H"letclear_and_home:t=literal"\027[H\027[2J"typeerase_display_mode=[`Below|`Above|`All|`Scrollback]leterase_display_mode_to_int=function|`Below->0|`Above->1|`All->2|`Scrollback->3leterase_display~modew=csi_n'J'(erase_display_mode_to_intmode)wleterase_below_cursor:t=literal"\027[J"typeerase_line_mode=[`Right|`Left|`All]leterase_line_mode_to_int=function`Right->0|`Left->1|`All->2leterase_line~modew=csi_n'K'(erase_line_mode_to_intmode)wletinsert_lines~n=csi_n_opt'L'nletdelete_lines~n=csi_n_opt'M'nletscroll_up~n=csi_n_opt'S'nletscroll_down~n=csi_n_opt'T'nletset_scrolling_region~top~bottomw=iftop<1||bottom<=toptheninvalid_arg"Escape.set_scrolling_region: invalid bounds";csi_nn'r'topbottomwletreset_scrolling_region:t=literal"\027[r"(* Colors and Attributes *)(* RGB color helper - combines consecutive writes *)let[@inline]csi_rgbprefixrgbw=letr=clamp_byterinletg=clamp_byteginletb=clamp_bytebinwrite_stringwprefix;add_intwr;write_charw';';add_intwg;write_charw';';add_intwb;write_charw'm'letset_foreground~r~g~bw=csi_rgb"\027[38;2;"rgbwletset_background~r~g~bw=csi_rgb"\027[48;2;"rgbwletreset_background:t=literal"\027[49m"letreset_foreground:t=literal"\027[39m"(* Terminal Properties *)letset_title~titlew=write_stringw"\027]0;";write_stringwtitle;write_terminatorw`Stlet[@inline]write_explicit_width_prefixwwidth=write_stringw"\027]66;w=";add_intwwidth;write_charw';'letexplicit_width~width~textw=write_explicit_width_prefixwwidth;write_stringwtext;write_terminatorw`Stletexplicit_width_bytes~width~bytes~off~lenw=write_explicit_width_prefixwwidth;write_subbyteswbytesofflen;write_terminatorw`St(* OSC helpers *)letosc?(terminator=`St)~payloadw=write_stringw"\027]";write_stringwpayload;write_terminatorwterminator(* Hyperlinks (OSC 8) *)lethyperlink_start?(params="")~urlw=write_stringw"\027]8;";ifparams<>""thenwrite_stringwparams;write_charw';';write_stringwurl;write_terminatorw`Stlethyperlink_end:t=osc~terminator:`St~payload:"8;;"lethyperlink?(params="")~url~textw=hyperlink_start~params~urlw;write_stringwtext;hyperlink_endwlet[@inline]hyperlink_openwurl=write_stringw"\027]8;;";write_stringwurl;write_terminatorw`Stlet[@inline]hyperlink_closew=write_stringw"\027]8;;";write_terminatorw`St(* Terminal Modes *)typemode=|Cursor_visible|Mouse_tracking|Mouse_button_tracking|Mouse_motion|Mouse_sgr|Mouse_sgr_pixel|Mouse_x10|Urxvt_mouse|Alternate_screen|Focus_tracking|Bracketed_paste|Sync_output|Unicode|Color_schemeletmode_code=function|Cursor_visible->25|Mouse_tracking->1000|Mouse_button_tracking->1002|Mouse_motion->1003|Mouse_sgr->1006|Mouse_sgr_pixel->1016|Mouse_x10->9|Urxvt_mouse->1015|Alternate_screen->1049|Focus_tracking->1004|Bracketed_paste->2004|Sync_output->2026|Unicode->2027|Color_scheme->2031letenablemodew=write_stringw"\027[?";add_intw(mode_codemode);write_charw'h'letdisablemodew=write_stringw"\027[?";add_intw(mode_codemode);write_charw'l'(* Key Encoding *)letcsi_u_on:t=literal"\027[>1u"letcsi_u_off:t=literal"\027[<1u"letcsi_u_push~flagsw=write_stringw"\027[>";add_intwflags;write_charw'u'letcsi_u_pop:t=literal"\027[<u"letmodify_other_keys_on:t=literal"\027[>4;1m"letmodify_other_keys_off:t=literal"\027[>4;0m"(* Device and Capability Queries *)typequery=|Cursor_position|Pixel_size|Device_attributes|Tertiary_attributes|Terminal_identity|Device_status|Csi_u_support|Kitty_graphics|Sixel_geometry|Explicit_width_support|Scaled_text_support|Color_scheme_query|Focus_mode|Sgr_pixels_mode|Bracketed_paste_mode|Sync_mode|Unicode_mode|Color_scheme_modeletquery=function|Cursor_position->literal"\027[6n"|Pixel_size->literal"\027[14t"|Device_attributes->literal"\027[c"|Tertiary_attributes->literal"\027[=c"|Terminal_identity->literal"\027[>0q"|Device_status->literal"\027[5n"|Csi_u_support->literal"\027[?u"|Kitty_graphics->literal"\027_Gi=31337,s=1,v=1,a=q,t=d,f=24;AAAA\027\\\027[c"|Sixel_geometry->literal"\027[?2;1;0S"|Explicit_width_support->literal"\027]66;w=1; \027\\"|Scaled_text_support->literal"\027]66;s=2; \027\\"|Color_scheme_query->literal"\027[?996n"|Focus_mode->literal"\027[?1004$p"|Sgr_pixels_mode->literal"\027[?1016$p"|Bracketed_paste_mode->literal"\027[?2004$p"|Sync_mode->literal"\027[?2026$p"|Unicode_mode->literal"\027[?2027$p"|Color_scheme_mode->literal"\027[?2031$p"