1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024(*
* zed_rope.ml
* -----------
* Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
* Copyright : (c) 2019, ZAN DoYe <zandoye@gmail.com>
* Licence : BSD3
*
* This file is a part of Zed, an editor engine.
*)(* Maximum length of a leaf *)letmax_leaf_size=256exceptionOut_of_bounds(* +-----------------------------------------------------------------+
| Ropes representation |
+-----------------------------------------------------------------+ *)typet=(* the size is the number of Uchar.t in the rope *)|LeafofZed_string.t*(int*int)(* [Leaf(str, (len, size))] *)|Nodeofint*(int*int)*t*(int*int)*t(* [Node(depth, (length_left, size_left), left, (length_right, size_right), right)] *)typerope=tletempty()=Leaf(Zed_string.empty(),(0,0))(* +-----------------------------------------------------------------+
| Basic operations |
+-----------------------------------------------------------------+ *)letlength=function|Leaf(_,(len,_))->len|Node(_,(len_l,_),_,(len_r,_),_)->len_l+len_rletsize=function|Leaf(_,(_,size))->size|Node(_,(_,size_l),_,(_,size_r),_)->size_l+size_rletdepth=function|Leaf_->0|Node(d,_,_,_,_)->dletis_empty=function|Leaf(_,(0,0))->true|_->falseletrectrim_hdt=matchtwith|Leaf(str,(l,_))->lethd,_=Zed_string.extract_nextstr0inlethd=hd|>Zed_char.to_utf8|>Zed_string.unsafe_of_utf8inletafter=Zed_string.afterstr1inletsize=Zed_string.sizeafterin(Leaf(after,(l-1,size)),hd)|Node(d,(ll,_sl),l,(lr,sr),r)->lett,hd=trim_hdlinletsize=sizetin(Node(d,(ll-1,size),t,(lr,sr),r),hd)letappend_cmtcm=letsize=Zed_string.sizecminletrecappend_cmt=matchtwith|Leaf(str,(l,s))->Leaf(Zed_string.appendstrcm,(l,s+size))|Node(d,(ll,sl),l,(lr,sr),r)->Node(d,(ll,sl),l,(lr,sr+size),append_cmr)inappend_cmt(* +-----------------------------------------------------------------+
| Balancing |
+-----------------------------------------------------------------+ *)letrecmake_fiboaccab=letc=a+binifc<bthen(* overflow *)accelsemake_fibo(c::acc)bcletfibo=letl=make_fibo[1;1;0]11inletn=List.lengthlinletfibo=Array.maken0inletrecloopi=function|[]->fibo|x::l->fibo.(i)<-x;loop(i-1)linloop(n-1)lletmax_depth=Array.lengthfiboletunsafe_concatrope1rope2=matchrope1,rope2with|Leaf(_,(0,_)),_->rope2|_,Leaf(_,(0,_))->rope1|_->Node(1+max(depthrope1)(depthrope2),(lengthrope1,sizerope1),rope1,(lengthrope2,sizerope2),rope2)letrecinsert_to_forestforestaccidx=letacc=unsafe_concatforest.(idx)acciniflengthacc<fibo.(idx+1)thenforest.(idx)<-accelsebeginforest.(idx)<-empty();insert_to_forestforestacc(idx+1)endletrecconcat_forest_untilforestaccidxrope=iflengthrope<fibo.(idx+1)theninsert_to_forestforest(unsafe_concataccrope)idxelsebeginletacc=unsafe_concatforest.(idx)accinforest.(idx)<-empty();concat_forest_untilforestacc(idx+1)ropeendletrecbalance_recforestrope=matchropewith|Leaf_->concat_forest_untilforest(empty())2rope|Node(_depth,_len_l,rope_l,_len_r,rope_r)->balance_recforestrope_l;balance_recforestrope_rletrecconcat_forestforestaccidx=ifidx=max_depththenaccelseconcat_forestforest(unsafe_concatforest.(idx)acc)(idx+1)letbalancerope=matchlengthropewith|0|1->rope|lenwhenlen>=fibo.(depthrope+2)->rope|_len->letforest=Array.makemax_depth(empty())inbalance_recforestrope;concat_forestforest(empty())2(* +-----------------------------------------------------------------+
| Leaf operations |
+-----------------------------------------------------------------+ *)letrecunsafe_getidxrope=matchropewith|Leaf(text,_)->Zed_string.gettextidx|Node(_,(len_l,_),rope_l,_len_r,rope_r)->ifidx<len_lthenunsafe_getidxrope_lelseunsafe_get(idx-len_l)rope_rletgetropeidx=ifidx<0||idx>=lengthropethenraiseOut_of_boundselseunsafe_getidxropeletrecunsafe_get_rawidxrope=matchropewith|Leaf(text,_)->Zed_string.get_rawtextidx|Node(_,(_,size_l),rope_l,_len_r,rope_r)->ifidx<size_lthenunsafe_get_rawidxrope_lelseunsafe_get_raw(idx-size_l)rope_rletget_rawropeidx=ifidx<0||idx>=sizeropethenraiseOut_of_boundselseunsafe_get_rawidxropeletappendrope1rope2=letlen_12_comb=iflengthrope1>0&&lengthrope2>0thenZed_char.is_combining_mark(Zed_char.core(getrope20))elsefalseinletlen12l1l2=iflen_12_combthenl1+l2-1elsel1+l2inmatchrope1,rope2with|Leaf(_,(0,_)),_->rope2|_,Leaf(_,(0,_))->rope1|Leaf(text1,(len1,size1)),Leaf(text2,(len2,size2))whenlen12len1len2<=max_leaf_size->Leaf(Zed_string.appendtext1text2,(len12len1len2,size1+size2))|Node(d,len_l,rope_l,_,Leaf(text1,(len1,size1))),Leaf(text2,(len2,size2))whenlen12len1len2<=max_leaf_size->letls=len12len1len2,size1+size2inNode(d,len_l,rope_l,ls,Leaf(Zed_string.appendtext1text2,ls))|Leaf(text1,(len1,size1)),Node(d,_,Leaf(text2,(len2,size2)),len_r,rope_r)whenlen12len1len2<=max_leaf_size->letls=len12len1len2,size1+size2inNode(d,ls,Leaf(Zed_string.appendtext1text2,ls),len_r,rope_r)|_->letrope1,rope2=iflengthrope1>0&&lengthrope2>0thenifZed_char.is_combining_mark(Zed_char.core(getrope20))thenletr2,hd=trim_hdrope2inletr1=append_cmrope1hdinr1,r2elserope1,rope2elserope1,rope2inbalance(Node(1+max(depthrope1)(depthrope2),(lengthrope1,sizerope1),rope1,(lengthrope2,sizerope2),rope2))letconcatsepl=letrecloopacc=function|[]->acc|x::l->loop(append(appendaccsep)x)linmatchlwith|[]->empty()|x::l->loopxlletrecunsafe_subropeidxlen=matchropewith|Leaf(text,_)->letstr=Zed_string.sub~pos:idx~lentextinletsize=Zed_string.sizestrinLeaf(str,(len,size))|Node(_,(len_l,_),rope_l,(len_r,_),rope_r)->iflen=len_l+len_rthenropeelseifidx>=len_lthenunsafe_subrope_r(idx-len_l)lenelseifidx+len<=len_lthenunsafe_subrope_lidxlenelseappend(unsafe_subrope_lidx(len_l-idx))(unsafe_subrope_r0(len-len_l+idx))letsubropeidxlen=ifidx<0||len<0||idx+len>lengthropethenraiseOut_of_boundselseunsafe_subropeidxlenletmakelengthchar=iflength<max_leaf_sizethenLeaf(Zed_string.makelengthchar,(length,length))elsebeginlettext=Zed_string.makemax_leaf_sizecharinletchunk=Leaf(text,(max_leaf_size,max_leaf_size))inletrecloopaccn=ifn=0thenaccelseifn<max_leaf_sizethenletstr=Zed_string.sub~pos:0~len:ntextinletsize=Zed_string.sizestrinappendacc(Leaf(str,(n,size)))elseloop(appendaccchunk)(n-max_leaf_size)inloop(empty())lengthendletsingletonch=Leaf(Zed_string.make1ch,(1,1))letbreakropepos=letlen=lengthropeinifpos<0||pos>lenthenraiseOut_of_bounds;(unsafe_subrope0pos,unsafe_subropepos(len-pos))letbeforeropepos=subrope0posletafterropepos=subropepos(lengthrope-pos)letinsertropepossub=letbefore,after=breakropeposinappendbefore(appendsubafter)letremoveropeposlen=append(subrope0pos)(subrope(pos+len)(lengthrope-pos-len))letreplaceropeposlenrepl=append(subrope0pos)(appendrepl(subrope(pos+len)(lengthrope-pos-len)))letinsert_uCharropeposch=ifUchar.to_intch=0thenropeelseifZed_char.is_combining_markchtheniflengthrope=0thenfailwith"inserting an individual combining mark"elseifpos=0thenfailwith"inserting an individual combining mark"elseletpos=ifpos>0thenpos-1elseposinletglyph=getropeposinifZed_char.is_printable_core(Zed_char.coreglyph)thenletglyph=Zed_char.appendglyphchinreplaceropepos1(Leaf(Zed_string.implode[glyph],(1,1)))elsefailwith"inserting an individual combining mark"elseletsub=Leaf(Zed_string.implode[Zed_char.unsafe_of_uCharch],(1,1))ininsertropepossubletlchop=function|Leaf(_,(0,_))->empty()|rope->subrope1(lengthrope-1)letrchop=function|Leaf(_,(0,_))->empty()|rope->subrope0(lengthrope-1)(* +-----------------------------------------------------------------+
| Iterating, folding and mapping |
+-----------------------------------------------------------------+ *)letreciterf=function|Leaf(text,_)->Zed_string.iterftext|Node(_,_,rope_l,_,rope_r)->iterfrope_l;iterfrope_rletrecrev_iterf=function|Leaf(text,_)->Zed_string.rev_iterftext|Node(_,_,rope_l,_,rope_r)->rev_iterfrope_r;rev_iterfrope_lletrecfoldfropeacc=matchropewith|Leaf(text,_)->Zed_string.foldftextacc|Node(_,_,rope_l,_,rope_r)->foldfrope_r(foldfrope_lacc)letrecrev_foldfropeacc=matchropewith|Leaf(text,_)->Zed_string.rev_foldftextacc|Node(_,_,rope_l,_,rope_r)->rev_foldfrope_l(rev_foldfrope_racc)letrecmapf=function|Leaf(txt,len)->Leaf(Zed_string.mapftxt,len)|Node(depth,length_l,rope_l,length_r,rope_r)->letrope_l'=mapfrope_linletrope_r'=mapfrope_rinNode(depth,length_l,rope_l',length_r,rope_r')letrecrev_mapf=function|Leaf(txt,len)->Leaf(Zed_string.rev_mapftxt,len)|Node(depth,length_l,rope_l,length_r,rope_r)->letrope_l'=rev_mapfrope_linletrope_r'=rev_mapfrope_rinNode(depth,length_r,rope_r',length_l,rope_l')letreciter_leaff=function|Leaf(text,_)->ftext|Node(_,_,rope_l,_,rope_r)->iter_leaffrope_l;iter_leaffrope_rletrecrev_iter_leaff=function|Leaf(text,_)->ftext|Node(_,_,rope_l,_,rope_r)->rev_iter_leaffrope_r;rev_iter_leaffrope_lletrecfold_leaffropeacc=matchropewith|Leaf(text,_)->ftextacc|Node(_,_,rope_l,_,rope_r)->fold_leaffrope_r(fold_leaffrope_lacc)letrecrev_fold_leaffropeacc=matchropewith|Leaf(text,_)->ftextacc|Node(_,_,rope_l,_,rope_r)->rev_fold_leaffrope_l(rev_fold_leaffrope_racc)(* +-----------------------------------------------------------------+
| Comparison |
+-----------------------------------------------------------------+ *)letreccmp_loopstr1ofs1str2ofs2rest1rest2=ifofs1=Zed_string.bytesstr1thenmatchrest1with|[]->ifofs2=Zed_string.lengthstr2&&rest2=[]then0else-1|rope1::rest1->cmp_search1rope1str2ofs2rest1rest2elseifofs2=Zed_string.bytesstr2thenmatchrest2with|[]->1|rope2::rest2->cmp_search2rope2str1ofs1rest1rest2elseletchr1,ofs1=Zed_string.extract_nextstr1ofs1andchr2,ofs2=Zed_string.extract_nextstr2ofs2inletd=Zed_char.compare_rawchr1chr2inifd=0thencmp_loopstr1ofs1str2ofs2rest1rest2elsedandcmp_search1rope1str2ofs2rest1rest2=matchrope1with|Leaf(str1,_)->cmp_loopstr10str2ofs2rest1rest2|Node(_,_,rope1_l,_,rope1_r)->cmp_search1rope1_lstr2ofs2(rope1_r::rest1)rest2andcmp_search2rope2str1ofs1rest1rest2=matchrope2with|Leaf(str2,_)->cmp_loopstr1ofs1str20rest1rest2|Node(_,_,rope2_l,_,rope2_r)->cmp_search2rope2_lstr1ofs1rest1(rope2_r::rest2)letreccmp_initrope1rope2rest1=matchrope1with|Leaf(str1,_)->cmp_search2rope2str10rest1[]|Node(_,_,rope1_l,_,rope1_r)->cmp_initrope1_lrope2(rope1_r::rest1)letcomparer1r2=cmp_initr1r2[]letequalr1r2=lengthr1=lengthr2&&comparer1r2=0(* +-----------------------------------------------------------------+
| Zippers |
+-----------------------------------------------------------------+ *)moduleZip=structtyperope_zipper={str:Zed_string.t;(* The string of the current leaf. *)ofs:int;(* The offset of the current leaf in the whole rope. *)leaf:t;(* The current leaf. *)rest_b:tlist;rest_f:tlist;}typet={idx:int;(* The index in byte of the zipper in the current leaf. *)pos:int;(* The index in character of the zipper in the current leaf. *)zip:rope_zipper;}letrecmake_recofsropeposrest_brest_f=matchropewith|Leaf(str,_)->{idx=Zed_string.movestr0pos;pos=pos;zip={str;ofs=ofs-pos;leaf=rope;rest_b;rest_f}}|Node(_,_,r1,_,r2)->letlen1=lengthr1inifpos<len1thenmake_recofsr1posrest_b(r2::rest_f)elsemake_recofsr2(pos-len1)(r1::rest_b)rest_fletmake_fropepos=ifpos<0||pos>lengthropethenraiseOut_of_bounds;make_recposropepos[][]letmake_bropepos=letlen=lengthropeinifpos<0||pos>lenthenraiseOut_of_bounds;letpos=len-posinmake_recposropepos[][]letoffsetzip=zip.zip.ofs+zip.posletrecnext_leafofsroperest_brest_f=matchropewith|Leaf(str,_)->letchr,idx=Zed_string.extract_nextstr0in(chr,{idx;pos=1;zip={str;ofs;leaf=rope;rest_b;rest_f}})|Node(_,_,r1,_,r2)->next_leafofsr1rest_b(r2::rest_f)letnextzip=ifzip.idx=Zed_string.byteszip.zip.strthenmatchzip.zip.rest_fwith|[]->raiseOut_of_bounds|rope::rest->next_leaf(zip.zip.ofs+lengthzip.zip.leaf)rope(zip.zip.leaf::zip.zip.rest_b)restelseletchr,idx=Zed_string.extract_nextzip.zip.strzip.idxin(chr,{zipwithidx;pos=zip.pos+1})letrecprev_leafofsroperest_brest_f=matchropewith|Leaf(str,(len,_size))->letchr,idx=Zed_string.extract_prevstr(Zed_string.bytesstr)in(chr,{idx;pos=len-1;zip={str;ofs=ofs-len;leaf=rope;rest_b;rest_f}})|Node(_,_,r1,_,r2)->prev_leafofsr2(r1::rest_b)rest_fletprevzip=ifzip.pos=0thenmatchzip.zip.rest_bwith|[]->raiseOut_of_bounds|rope::rest->prev_leafzip.zip.ofsroperest(zip.zip.leaf::zip.zip.rest_f)elseletchr,idx=Zed_string.extract_prevzip.zip.strzip.idxin(chr,{zipwithidx;pos=zip.pos-1})letrecmove_fnofsroperest_brest_f=matchropewith|Leaf(str,(len,_size))->ifn<=lenthen{idx=Zed_string.movestr0n;pos=n;zip={str;ofs;leaf=rope;rest_b;rest_f}}elsebeginmatchrest_fwith|[]->raiseOut_of_bounds|rope'::rest_f->move_f(n-len)(ofs+len)rope'(rope::rest_b)rest_fend|Node(_,_,r1,_,r2)->move_fnofsr1rest_b(r2::rest_f)letrecmove_bnofsroperest_brest_f=matchropewith|Leaf(str,(len,_size))->ifn<=lenthen{idx=Zed_string.movestr(Zed_string.bytesstr)(-n);pos=len-n;zip={str;ofs;leaf=rope;rest_b;rest_f}}elsebeginmatchrest_bwith|[]->raiseOut_of_bounds|rope'::rest_b->move_b(n-len)(ofs-len)rope'rest_b(rope::rest_f)end|Node(_,_,r1,_,r2)->move_bnofsr2(r1::rest_b)rest_fletmovenzip=ifn>0thenletlen=lengthzip.zip.leafinifzip.pos+n<=lenthen{zipwithidx=Zed_string.movezip.zip.strzip.idxn;pos=zip.pos+n}elsematchzip.zip.rest_fwith|[]->raiseOut_of_bounds|rope::rest_f->move_f(n-(len-zip.pos))(zip.zip.ofs+len)rope(zip.zip.leaf::zip.zip.rest_b)rest_felseifzip.pos+n>=0then{zipwithidx=Zed_string.movezip.zip.strzip.idxn;pos=zip.pos+n}elsematchzip.zip.rest_bwith|[]->raiseOut_of_bounds|rope::rest_b->move_b(n-zip.pos)zip.zip.ofsroperest_b(zip.zip.leaf::zip.zip.rest_f)letat_boszip=zip.zip.rest_b=[]&&zip.idx=0letat_eoszip=zip.zip.rest_f=[]&&zip.idx=Zed_string.byteszip.zip.strletrecsub_recaccropeslen=matchropeswith|[]->iflen>0thenraiseOut_of_boundselseacc|rope::rest->letlen'=lengthropeiniflen<=len'thenappendacc(subrope0len)elsesub_rec(appendaccrope)rest(len-len')letsubziplen=iflen<0thenraiseOut_of_boundselseletlen'=lengthzip.zip.leaf-zip.posiniflen<=len'thenletstr=Zed_string.sub~pos:zip.pos~lenzip.zip.strinletsize=Zed_string.sizestrinLeaf(str,(len,size))elseletstr=Zed_string.sub~pos:zip.pos~len:(Zed_string.lengthzip.zip.str-zip.pos)zip.zip.strinletsize=Zed_string.sizestrinsub_rec(Leaf(str,(len',size)))zip.zip.rest_f(len-len')letslicezip1zip2=letofs1=offsetzip1andofs2=offsetzip2inifofs1<=ofs2thensubzip1(ofs2-ofs1)elsesubzip2(ofs1-ofs2)letrecfind_ffzip=ifat_eoszipthenzipelseletch,zip'=nextzipiniffchthenzipelsefind_ffzip'letrecfind_bfzip=ifat_boszipthenzipelseletch,zip'=prevzipiniffchthenzipelsefind_bfzip'endmoduleZip_raw=structtyperope_zipper={str:Zed_string.t;(* The string of the current leaf. *)ofs:int;(* The offset of the current leaf in the whole rope. *)leaf:t;(* The current leaf. *)rest_b:tlist;rest_f:tlist;}typet={idx:int;(* The index in byte of the zipper in the current leaf. *)pos:int;(* The index in character of the zipper in the current leaf. *)zip:rope_zipper;}letrecmake_f_recofsropeposrest_brest_f=matchropewith|Leaf(str,_)->{idx=Zed_string.move_rawstr0pos;pos=pos;zip={str;ofs=ofs-pos;leaf=rope;rest_b;rest_f}}|Node(_,_,r1,_,r2)->letsize1=sizer1inifpos<size1thenmake_f_recofsr1posrest_b(r2::rest_f)elsemake_f_recofsr2(pos-size1)(r1::rest_b)rest_fletmake_fropepos=ifpos<0||pos>sizeropethenraiseOut_of_bounds;make_f_recposropepos[][]letrecmake_b_recofsropeposrest_brest_f=matchropewith|Leaf(str,(len,_))->{idx=Zed_string.move_rawstr(Zed_string.bytesstr)(-(len-pos));pos=pos;zip={str;ofs=ofs-pos;leaf=rope;rest_b;rest_f}}|Node(_,_,r1,_,r2)->letlen1=lengthr1inifpos<len1thenmake_b_recofsr1posrest_b(r2::rest_f)elsemake_b_recofsr2(pos-len1)(r1::rest_b)rest_fletmake_bropepos=letsize=sizeropeinifpos<0||pos>sizethenraiseOut_of_bounds;letpos=size-posinmake_b_recposropepos[][]letoffsetzip=zip.zip.ofs+zip.posletrecnext_leafofsroperest_brest_f=matchropewith|Leaf(str,_)->letchr,idx=Zed_utf8.unsafe_extract_next(Zed_string.to_utf8str)0in(chr,{idx;pos=1;zip={str;ofs;leaf=rope;rest_b;rest_f}})|Node(_,_,r1,_,r2)->next_leafofsr1rest_b(r2::rest_f)letnextzip=ifzip.pos=Zed_string.sizezip.zip.strthenmatchzip.zip.rest_fwith|[]->raiseOut_of_bounds|rope::rest->next_leaf(zip.zip.ofs+sizezip.zip.leaf)rope(zip.zip.leaf::zip.zip.rest_b)restelseletchr,idx=Zed_utf8.unsafe_extract_next(Zed_string.to_utf8zip.zip.str)zip.idxin(chr,{zipwithidx;pos=zip.pos+1})letrecprev_leafofsroperest_brest_f=matchropewith|Leaf(str,(_len,size))->letchr,idx=letstr=Zed_string.to_utf8strinZed_utf8.unsafe_extract_prevstr(String.lengthstr)in(chr,{idx;pos=size-1;zip={str;ofs=ofs-size;leaf=rope;rest_b;rest_f}})|Node(_,_,r1,_,r2)->prev_leafofsr2(r1::rest_b)rest_fletprevzip=ifzip.pos=0thenmatchzip.zip.rest_bwith|[]->raiseOut_of_bounds|rope::rest->prev_leafzip.zip.ofsroperest(zip.zip.leaf::zip.zip.rest_f)elseletchr,idx=Zed_utf8.unsafe_extract_prev(Zed_string.to_utf8zip.zip.str)zip.idxin(chr,{zipwithidx;pos=zip.pos-1})letrecmove_fnofsroperest_brest_f=matchropewith|Leaf(str,(_,size))->ifn<=sizethen{idx=Zed_string.move_rawstr0n;pos=n;zip={str;ofs;leaf=rope;rest_b;rest_f}}elsebeginmatchrest_fwith|[]->raiseOut_of_bounds|rope'::rest_f->move_f(n-size)(ofs+size)rope'(rope::rest_b)rest_fend|Node(_,_,r1,_,r2)->move_fnofsr1rest_b(r2::rest_f)letrecmove_bnofsroperest_brest_f=matchropewith|Leaf(str,(_,size))->ifn<=sizethen{idx=Zed_string.move_rawstr(Zed_string.bytesstr)(-n);pos=size-n;zip={str;ofs;leaf=rope;rest_b;rest_f}}elsebeginmatchrest_bwith|[]->raiseOut_of_bounds|rope'::rest_b->move_b(n-size)(ofs-size)rope'rest_b(rope::rest_f)end|Node(_,_,r1,_,r2)->move_bnofsr2(r1::rest_b)rest_fletmovenzip=ifn>0thenletsize=sizezip.zip.leafinifzip.pos+n<=sizethen{zipwithidx=Zed_string.move_rawzip.zip.strzip.idxn;pos=zip.pos+n}elsematchzip.zip.rest_fwith|[]->raiseOut_of_bounds|rope::rest_f->move_f(n-(size-zip.pos))(zip.zip.ofs+size)rope(zip.zip.leaf::zip.zip.rest_b)rest_felseifzip.pos+n>=0then{zipwithidx=Zed_string.move_rawzip.zip.strzip.idx(-n);pos=zip.pos+n}elsematchzip.zip.rest_bwith|[]->raiseOut_of_bounds|rope::rest_b->move_b(n-zip.pos)zip.zip.ofsroperest_b(zip.zip.leaf::zip.zip.rest_f)letat_boszip=zip.zip.rest_b=[]&&zip.idx=0letat_eoszip=zip.zip.rest_f=[]&&zip.idx=Zed_string.byteszip.zip.strletrecfind_ffzip=ifat_eoszipthenzipelseletch,zip'=nextzipiniffchthenzipelsefind_ffzip'letrecfind_bfzip=ifat_boszipthenzipelseletch,zip'=prevzipiniffchthenzipelsefind_bfzip'end(* +-----------------------------------------------------------------+
| Buffers |
+-----------------------------------------------------------------+ *)moduleString_buffer=BuffermoduleBuffer=structtypet={mutableacc:rope;mutablebuf:Zed_string.Buf.buf;mutableidx:int;}letcreate()={acc=empty();buf=Zed_string.Buf.create1024;idx=0;}letaddbufferx=ifbuffer.idx=max_leaf_sizethenbeginletstr=Zed_string.Buf.contentsbuffer.bufinletsize=Zed_string.sizestrinbuffer.acc<-appendbuffer.acc(Leaf(str,(max_leaf_size,size)));Zed_string.Buf.resetbuffer.buf;Zed_string.Buf.add_zCharbuffer.bufx;buffer.idx<-Zed_string.Buf.lengthbuffer.bufendelsebeginZed_string.Buf.add_zCharbuffer.bufx;buffer.idx<-Zed_string.Buf.lengthbuffer.bufendletadd_uCharbufferx=ifbuffer.idx=max_leaf_sizethenbeginletstr=Zed_string.Buf.contentsbuffer.bufinletsize=Zed_string.sizestrinbuffer.acc<-appendbuffer.acc(Leaf(str,(max_leaf_size,size)));Zed_string.Buf.resetbuffer.buf;Zed_string.Buf.add_uCharbuffer.bufx;buffer.idx<-Zed_string.Buf.lengthbuffer.bufendelsebeginZed_string.Buf.add_uCharbuffer.bufx;buffer.idx<-Zed_string.Buf.lengthbuffer.bufendletadd_ropebufrope=iter(addbuf)ropeletadd_stringbufstr=Zed_string.iter(addbuf)strletcontentsbuffer=ifbuffer.idx=0thenbuffer.accelseletstr=Zed_string.Buf.contentsbuffer.bufinletsize=Zed_string.sizestrinappendbuffer.acc(Leaf(str,(buffer.idx,size)))letresetbuffer=Zed_string.Buf.resetbuffer.buf;buffer.acc<-empty();buffer.idx<-0end(* +-----------------------------------------------------------------+
| Init |
+-----------------------------------------------------------------+ *)letinitnf=letbuf=Buffer.create()infori=0ton-1doBuffer.addbuf(fi)done;Buffer.contentsbufletinit_from_uCharslenf=matchlenwith|0->empty()|lenwhenlen>0->letreccreaten=ifn>0thenf(len-n)::create(n-1)else[]inletuChars=createleninletzChars,_=Zed_char.zChars_of_uCharsuCharsinletbuf=Buffer.create()inList.iter(Buffer.addbuf)zChars;Buffer.contentsbuf|_->raise(Invalid_argument"Zed_rope.init_from_uChars")letof_strings=letbuf=Buffer.create()inBuffer.add_stringbufs;Buffer.contentsbufletrecto_stringt=matchtwith|Leaf(s,_)->s|Node(_,_,l,_,r)->Zed_string.append(to_stringl)(to_stringr)letcase_mapf?locale:_t=letbuf=Buffer.create()inletrecloopzip=matchZip_raw.nextzipwith|exceptionOut_of_bounds->Buffer.contentsbuf|u,zip->beginmatchfuwith|`Self->Buffer.add_uCharbufu|`Ucharsus->List.iter(Buffer.add_uCharbuf)usend;loopzipinloop(Zip_raw.make_ft0)letlowercase?localet=case_mapUucp.Case.Map.to_lower?localetletuppercase?localet=case_mapUucp.Case.Map.to_upper?localet