123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749(*
* zed_rope.ml
* -----------
* Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
* Licence : BSD3
*
* This file is a part of Zed, an editor engine.
*)openCamomileLibraryDyn.Camomile(* Maximum length of a leaf *)letmax_leaf_size=256exceptionOut_of_bounds(* +-----------------------------------------------------------------+
| Ropes representation |
+-----------------------------------------------------------------+ *)typet=|LeafofZed_utf8.t*int(* [Leaf(str, len)] *)|Nodeofint*int*t*int*t(* [Node(depth, length_left, left, length_right, right)] *)typerope=tletempty=Leaf("",0)(* +-----------------------------------------------------------------+
| Basic operations |
+-----------------------------------------------------------------+ *)letlength=function|Leaf(_,len)->len|Node(_,len_l,_,len_r,_)->len_l+len_rletdepth=function|Leaf_->0|Node(d,_,_,_,_)->dletis_empty=function|Leaf(_,0)->true|_->false(* +-----------------------------------------------------------------+
| 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,rope1,lengthrope2,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_untilforestempty2rope|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_depthemptyinbalance_recforestrope;concat_forestforestempty2(* +-----------------------------------------------------------------+
| Leaf operations |
+-----------------------------------------------------------------+ *)letappendrope1rope2=matchrope1,rope2with|Leaf(_,0),_->rope2|_,Leaf(_,0)->rope1|Leaf(text1,len1),Leaf(text2,len2)whenlen1+len2<=max_leaf_size->Leaf(text1^text2,len1+len2)|Node(d,len_l,rope_l,_,Leaf(text1,len1)),Leaf(text2,len2)whenlen1+len2<=max_leaf_size->Node(d,len_l,rope_l,len1+len2,Leaf(text1^text2,len1+len2))|Leaf(text1,len1),Node(d,_,Leaf(text2,len2),len_r,rope_r)whenlen1+len2<=max_leaf_size->Node(d,len1+len2,Leaf(text1^text2,len1+len2),len_r,rope_r)|_->balance(Node(1+max(depthrope1)(depthrope2),lengthrope1,rope1,lengthrope2,rope2))letconcatsepl=letrecloopacc=function|[]->acc|x::l->loop(append(appendaccsep)x)linmatchlwith|[]->empty|x::l->loopxlletrecunsafe_getidxrope=matchropewith|Leaf(text,_)->Zed_utf8.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_subropeidxlen=matchropewith|Leaf(text,_)->Leaf(Zed_utf8.subtextidxlen,len)|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_utf8.makelengthchar,length)elsebeginlettext=Zed_utf8.makemax_leaf_sizecharinletchunk=Leaf(text,max_leaf_size)inletrecloopaccn=ifn=0thenaccelseifn<max_leaf_sizethenappendacc(Leaf(Zed_utf8.subtext0n,n))elseloop(appendaccchunk)(n-max_leaf_size)inloopemptylengthendletsingletonch=Leaf(Zed_utf8.singletonch,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)))letlchop=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_utf8.iterftext|Node(_,_,rope_l,_,rope_r)->iterfrope_l;iterfrope_rletrecrev_iterf=function|Leaf(text,_)->Zed_utf8.rev_iterftext|Node(_,_,rope_l,_,rope_r)->rev_iterfrope_r;rev_iterfrope_lletrecfoldfropeacc=matchropewith|Leaf(text,_)->Zed_utf8.foldftextacc|Node(_,_,rope_l,_,rope_r)->foldfrope_r(foldfrope_lacc)letrecrev_foldfropeacc=matchropewith|Leaf(text,_)->Zed_utf8.rev_foldftextacc|Node(_,_,rope_l,_,rope_r)->rev_foldfrope_l(rev_foldfrope_racc)letrecmapf=function|Leaf(txt,len)->Leaf(Zed_utf8.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_utf8.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)(* +-----------------------------------------------------------------+
| Zippers |
+-----------------------------------------------------------------+ *)moduleZip=structtyperope_zipper={str:string;(* 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;}letrecmove_utf8_fstridxlen=iflen=0thenidxelsemove_utf8_fstr(Zed_utf8.unsafe_nextstridx)(len-1)letrecmake_f_recofsropeposrest_brest_f=matchropewith|Leaf(str,_)->{idx=move_utf8_fstr0pos;pos=pos;zip={str;ofs=ofs-pos;leaf=rope;rest_b;rest_f}}|Node(_,_,r1,_,r2)->letlen1=lengthr1inifpos<len1thenmake_f_recofsr1posrest_b(r2::rest_f)elsemake_f_recofsr2(pos-len1)(r1::rest_b)rest_fletmake_fropepos=ifpos<0||pos>lengthropethenraiseOut_of_bounds;make_f_recposropepos[][]letrecmove_utf8_bstridxlen=iflen=0thenidxelsemove_utf8_bstr(Zed_utf8.unsafe_prevstridx)(len-1)letrecmake_b_recofsropeposrest_brest_f=matchropewith|Leaf(str,len)->{idx=move_utf8_bstr(String.lengthstr)(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=letlen=lengthropeinifpos<0||pos>lengthropethenraiseOut_of_bounds;letpos=len-posinmake_b_recposropepos[][]letoffsetzip=zip.zip.ofs+zip.posletrecnext_leafofsroperest_brest_f=matchropewith|Leaf(str,_)->letchr,idx=Zed_utf8.unsafe_extract_nextstr0in(chr,{idx=idx;pos=1;zip={str;ofs;leaf=rope;rest_b;rest_f}})|Node(_,_,r1,_,r2)->next_leafofsr1rest_b(r2::rest_f)letnextzip=ifzip.idx=String.lengthzip.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_utf8.unsafe_extract_nextzip.zip.strzip.idxin(chr,{zipwithidx;pos=zip.pos+1})letrecprev_leafofsroperest_brest_f=matchropewith|Leaf(str,len)->letchr,idx=Zed_utf8.unsafe_extract_prevstr(String.lengthstr)in(chr,{idx=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.idx=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_prevzip.zip.strzip.idxin(chr,{zipwithidx;pos=zip.pos-1})letrecmove_fnofsroperest_brest_f=matchropewith|Leaf(str,len)->ifn<=lenthen{idx=move_utf8_fstr0n;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)->ifn<=lenthen{idx=move_utf8_bstr(String.lengthstr)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=move_utf8_fzip.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=move_utf8_bzip.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=String.lengthzip.zip.strletrecsub_recaccropeslen=matchropeswith|[]->iflen>0thenraiseOut_of_boundselseacc|rope::rest->letlen'=lengthropeiniflen<=len'thenappendacc(subrope0len)elsesub_rec(appendaccrope)rest(len-len')letunsafe_substrofslen=letres=Bytes.createleninString.unsafe_blitstrofsres0len;Bytes.unsafe_to_stringresletsubziplen=iflen<0thenraiseOut_of_boundselseletlen'=lengthzip.zip.leaf-zip.posiniflen<=len'thenLeaf(unsafe_subzip.zip.strzip.idx(move_utf8_fzip.zip.strzip.idxlen-zip.idx),len)elsesub_rec(Leaf(unsafe_subzip.zip.strzip.idx(String.lengthzip.zip.str-zip.idx),len'))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'end(* +-----------------------------------------------------------------+
| Comparison |
+-----------------------------------------------------------------+ *)letreccmp_loopstr1ofs1str2ofs2rest1rest2=ifofs1=String.lengthstr1thenmatchrest1with|[]->ifofs2=String.lengthstr2&&rest2=[]then0else-1|rope1::rest1->cmp_search1rope1str2ofs2rest1rest2elseifofs2=String.lengthstr2thenmatchrest2with|[]->1|rope2::rest2->cmp_search2rope2str1ofs1rest1rest2elseletchr1,ofs1=Zed_utf8.unsafe_extract_nextstr1ofs1andchr2,ofs2=Zed_utf8.unsafe_extract_nextstr2ofs2inletd=UChar.codechr1-UChar.codechr2inifd=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(* +-----------------------------------------------------------------+
| Buffers |
+-----------------------------------------------------------------+ *)moduleString_buffer=BuffermoduleBuffer=structtypet={mutableacc:rope;mutablebuf:String_buffer.t;mutableidx:int;}letcreate()={acc=empty;buf=String_buffer.create1024;idx=0;}letaddbufferx=ifbuffer.idx=max_leaf_sizethenbeginbuffer.acc<-appendbuffer.acc(Leaf(String_buffer.contentsbuffer.buf,max_leaf_size));String_buffer.resetbuffer.buf;String_buffer.add_stringbuffer.buf(Zed_utf8.singletonx);buffer.idx<-1endelsebeginString_buffer.add_stringbuffer.buf(Zed_utf8.singletonx);buffer.idx<-buffer.idx+1endletcontentsbuffer=ifbuffer.idx=0thenbuffer.accelseappendbuffer.acc(Leaf(String_buffer.contentsbuffer.buf,buffer.idx))letresetbuffer=String_buffer.resetbuffer.buf;buffer.acc<-empty;buffer.idx<-0end(* +-----------------------------------------------------------------+
| Init |
+-----------------------------------------------------------------+ *)letinitnf=letbuf=Buffer.create()infori=0ton-1doBuffer.addbuf(fi)done;Buffer.contentsbufletrev_initnf=letbuf=Buffer.create()infori=n-1downto0doBuffer.addbuf(fi)done;Buffer.contentsbuf(* +-----------------------------------------------------------------+
| To/from strings |
+-----------------------------------------------------------------+ *)letof_stringstr=letlen=Zed_utf8.validatestrinLeaf(str,len)letrecbyte_lengthropeacc=matchropewith|Leaf(text,_)->acc+String.lengthtext|Node(_,_,rope_l,_,rope_r)->byte_lengthrope_r(byte_lengthrope_lacc)letrecblit_ropestrofsrope=matchropewith|Leaf(text,_)->letlen=String.lengthtextinString.unsafe_blittext0strofslen;ofs+len|Node(_,_,rope_l,_,rope_r)->blit_ropestr(blit_ropestrofsrope_l)rope_rletto_stringrope=letstr=Bytes.create(byte_lengthrope0)inignore(blit_ropestr0rope);Bytes.unsafe_to_stringstr(* +-----------------------------------------------------------------+
| Camomile compatible interface |
+-----------------------------------------------------------------+ *)moduleText=structtypet=ropeletget=getletinit=initletlength=lengthtypeindex=Zip.tletlook_zip=fst(Zip.nextzip)letnthropeidx=Zip.make_fropeidxletnext_zip=Zip.move1zipletprev_zip=Zip.move(-1)zipletout_of_range_zip=Zip.at_eoszipletiter=iterletcompare=compareletfirstrope=Zip.make_frope0letlastrope=Zip.make_brope1letmove_zipdelta=Zip.movedeltazipletcompare_index_zip1zip2=Zip.offsetzip1-Zip.offsetzip2moduleBuf=structtypebuf=Buffer.tletcreate_=Buffer.create()letcontents=Buffer.contentsletclear=Buffer.resetletreset=Buffer.resetletadd_char=Buffer.addletadd_stringbufrope=iter(Buffer.addbuf)ropeletadd_bufferbufbuf'=add_stringbuf(Buffer.contentsbuf')endend