123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092(*
* zed_edit.ml
* -----------
* Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
* Licence : BSD3
*
* This file is a part of Zed, an editor engine.
*)openReact(* +-----------------------------------------------------------------+
| Types |
+-----------------------------------------------------------------+ *)typeclipboard={clipboard_get:unit->Zed_rope.t;clipboard_set:Zed_rope.t->unit;}type'at={mutabledata:'aoption;(* Custom data attached to the engine. *)mutabletext:Zed_rope.t;(* The contents of the engine. *)mutablelines:Zed_lines.t;(* The set of line position of [text]. *)changes:Zed_cursor.changesevent;send_changes:Zed_cursor.changes->unit;(* Changes of the contents. *)erase_mode:boolsignal;set_erase_mode:bool->unit;(* The current erase mode. *)editable:int->int->bool;(* The editable function of the engine. *)clipboard:clipboard;(* The clipboard for this engine. *)mutablemark:Zed_cursor.t;(* The cursor that points to the mark. *)selection:boolsignal;set_selection:bool->unit;(* The current selection state. *)match_word:Zed_rope.t->int->intoption;(* The function for matching words. *)locale:stringoptionsignal;(* The buffer's locale. *)undo:(Zed_rope.t*Zed_lines.t*int*int*int*int*int*int)array;(* The undo buffer. It is an array of element of the form [(text,
lines, pos, new_pos, added, removed, added_width, removed_width)]. *)undo_size:int;(* Size of the undo buffer. *)mutableundo_start:int;(* Position of the first used cell in the undo buffer. *)mutableundo_index:int;(* Position of the next available cell in the undo buffer. *)mutableundo_count:int;(* Number of used cell in the undo buffer. *)}(* +-----------------------------------------------------------------+
| Creation |
+-----------------------------------------------------------------+ *)letdummy_cursor=Zed_cursor.create0E.never(fun()->Zed_lines.empty)00letnew_clipboard()=letr=ref(Zed_rope.empty())in{clipboard_get=(fun()->!r);clipboard_set=(funx->r:=x)}letis_spaceuchar=matchUucp.Gc.general_categoryucharwith|`Cc|`Zs|`Zl|`Zp|`Mn->true|_->falseletis_not_spaceuchar=not(is_spaceuchar)letdefault_match_word=letrecloop_startsegmenterzip=matchZed_rope.Zip_raw.nextzipwith|exceptionZed_rope.Out_of_bounds->None|ch,zip->matchUuseg.addsegmenter(`Ucharch)with|`Await->loop_startsegmenterzip|`Uchar_|`End->None|`Boundary->loop_wordsegmenterzip~pos:0`Awaitandloop_wordsegmenterzipv~pos=matchUuseg.addsegmentervwith|`Boundary|`End->Somepos|`Ucharchar->letpos=(* combining characters do not step forward postion *)ifZed_char.is_printable_corecharthenpos+1elseposinloop_wordsegmenterzip`Await~pos|`Await->matchZed_rope.Zip_raw.nextzipwith|exceptionZed_rope.Out_of_bounds->Some(pos+1)|ch,zip->loop_wordsegmenterzip(`Ucharch)~posinfunropeidx->ifZed_rope.getropeidx|>Zed_char.core|>is_spacethenNoneelseletzip=Zed_rope.Zip_raw.make_fropeidxinmatchloop_start(Uuseg.create`Word)zipwith|Somepos->Some(idx+pos)|None->Noneletcreate?(editable=fun_pos_len->true)?(move=(+))?clipboard?(match_word=default_match_word)?(locale=S.constNone)?(undo_size=1000)()=(* I'm not sure how to disable the unused warning with ocaml.warning and the
argument can't be removed as it's part of the interface *)let_=moveinletchanges,send_changes=E.create()inleterase_mode,set_erase_mode=S.createfalseinletselection,set_selection=S.createfalseinletclipboard=matchclipboardwith|Someclipboard->clipboard|None->new_clipboard()inletedit={data=None;text=Zed_rope.empty();lines=Zed_lines.empty;changes;send_changes;erase_mode;set_erase_mode;editable;clipboard;mark=dummy_cursor;selection;set_selection;match_word;locale;undo=Array.makeundo_size(Zed_rope.empty(),Zed_lines.empty,0,0,0,0,0,0);undo_size;undo_start=0;undo_index=0;undo_count=0;}inedit.mark<-Zed_cursor.create0changes(fun()->edit.lines)00;edit(* +-----------------------------------------------------------------+
| State |
+-----------------------------------------------------------------+ *)letget_dataengine=matchengine.datawith|Somedata->data|None->raiseNot_foundletset_dataenginedata=engine.data<-Somedataletclear_dataengine=engine.data<-Nonelettextengine=engine.textletlinesengine=engine.linesletchangesengine=engine.changesleterase_modeengine=engine.erase_modeletget_erase_modeengine=S.valueengine.erase_modeletset_erase_modeenginestate=engine.set_erase_modestateletmarkengine=engine.markletselectionengine=engine.selectionletget_selectionengine=S.valueengine.selectionletset_selectionenginestate=engine.set_selectionstateletget_lineei=lettxt=texteinletlines=lineseinletstart=Zed_lines.line_startlinesiinletstop=Zed_lines.line_stoplinesiinZed_rope.subtxtstart(stop-start)letupdateenginecursors=E.select(E.stampengine.changes()::E.stamp(S.changesengine.selection)()::E.stamp(S.changes(Zed_cursor.positionengine.mark))()::List.map(funcursor->E.stamp(S.changes(Zed_cursor.positioncursor))())cursors)(* +-----------------------------------------------------------------+
| Cursors |
+-----------------------------------------------------------------+ *)letnew_cursorengine=Zed_cursor.create(Zed_rope.lengthengine.text)engine.changes(fun()->engine.lines)00(* +-----------------------------------------------------------------+
| Actions |
+-----------------------------------------------------------------+ *)exceptionCannot_edittype'acontext={edit:'at;cursor:Zed_cursor.t;check:bool;}letcontext?(check=true)editcursor={edit;cursor;check}leteditctx=ctx.editletcursorctx=ctx.cursorletcheckctx=ctx.checkletwith_checkcheckctx={ctxwithcheck}letgotoctx?set_wanted_columnnew_position=Zed_cursor.gotoctx.cursor?set_wanted_columnnew_positionletset_positionctxnew_position=Zed_cursor.gotoctx.cursor~set_wanted_column:falsenew_positionletmovectx?set_wanted_columndelta=Zed_cursor.movectx.cursor?set_wanted_columndeltaletnext_line_nctxn=letindex=Zed_cursor.get_linectx.cursorinifindex+n>Zed_lines.countctx.edit.linesthengotoctx~set_wanted_column:false(Zed_rope.lengthctx.edit.text)elsebeginletstop=ifindex+n=Zed_lines.countctx.edit.linesthenZed_rope.lengthctx.edit.textelseZed_lines.line_startctx.edit.lines(index+n+1)-1inletwanted_idx=Zed_lines.get_idx_by_widthctx.edit.lines(index+n)(Zed_cursor.get_wanted_columnctx.cursor)ingotoctx~set_wanted_column:false(minwanted_idxstop)endletprev_line_nctxn=letindex=Zed_cursor.get_linectx.cursorinifindex-n<0thenbegingotoctx~set_wanted_column:false0endelsebeginletstop=Zed_lines.line_startctx.edit.lines(index-(n-1))-1inletwanted_idx=Zed_lines.get_idx_by_widthctx.edit.lines(index-n)(Zed_cursor.get_wanted_columnctx.cursor)ingotoctx~set_wanted_column:false(minwanted_idxstop)endletmove_linectxdelta=matchdeltawith|_whendelta<0->prev_line_nctx(-delta)|_whendelta>0->next_line_nctxdelta|_->()letpositionctx=Zed_cursor.get_positionctx.cursorletlinectx=Zed_cursor.get_linectx.cursorletcolumnctx=Zed_cursor.get_columnctx.cursorletcolumn_displayctx=Zed_cursor.get_column_displayctx.cursorletat_bolctx=Zed_cursor.get_columnctx.cursor=0letat_eolctx=letposition=Zed_cursor.get_positionctx.cursorinletindex=Zed_cursor.get_linectx.cursorinifindex=Zed_lines.countctx.edit.linesthenposition=Zed_rope.lengthctx.edit.textelseposition=Zed_lines.line_startctx.edit.lines(index+1)-1letat_botctx=Zed_cursor.get_positionctx.cursor=0letat_eotctx=Zed_cursor.get_positionctx.cursor=Zed_rope.lengthctx.edit.textletmodify{edit;_}textlinespositionnew_positionaddedremovedadded_widthremoved_width=ifedit.undo_size>0thenbeginedit.undo.(edit.undo_index)<-(text,lines,position,new_position,added,removed,added_width,removed_width);edit.undo_index<-(edit.undo_index+1)modedit.undo_size;ifedit.undo_count=edit.undo_sizethenedit.undo_start<-(edit.undo_start+1)modedit.undo_sizeelseedit.undo_count<-edit.undo_count+1end;edit.send_changes{position;added;removed;added_width;removed_width}letinsertctxrope=letposition=Zed_cursor.get_positionctx.cursorinifnotctx.check||ctx.edit.editableposition0thenbeginletlen=Zed_rope.lengthropeinlettext=ctx.edit.textandlines=ctx.edit.linesinifS.valuectx.edit.erase_modethenbeginlettext_len=Zed_rope.lengthctx.edit.textinifposition+len>text_lenthenbeginletorig_width=Zed_string.(aval_width(widthZed_rope.(to_string(subtextposition(text_len-position)))))inletcurr_width=Zed_string.(aval_width(widthZed_rope.(to_stringrope)))inctx.edit.text<-Zed_rope.replacetextposition(text_len-position)rope;ctx.edit.lines<-Zed_lines.replacectx.edit.linesposition(text_len-position)(Zed_lines.of_roperope);modifyctxtextlinespositionpositionlen(text_len-position)curr_widthorig_widthendelsebeginletorig_width=Zed_string.(aval_width(widthZed_rope.(to_string(subtextpositionlen))))inletcurr_width=Zed_string.(aval_width(widthZed_rope.(to_stringrope)))inctx.edit.text<-Zed_rope.replacetextpositionlenrope;ctx.edit.lines<-Zed_lines.replacectx.edit.linespositionlen(Zed_lines.of_roperope);modifyctxtextlinespositionpositionlenlencurr_widthorig_width;end;movectxlenendelsebeginletwidth_add=Zed_string.aval_width(Zed_string.width(Zed_rope.to_stringrope))inctx.edit.text<-Zed_rope.insertctx.edit.textpositionrope;ctx.edit.lines<-Zed_lines.insertctx.edit.linesposition(Zed_lines.of_roperope);modifyctxtextlinespositionpositionlen0width_add0;movectxlenendendelseraiseCannot_editletinsert_charctxch=ifZed_char.is_combining_markchthenletposition=Zed_cursor.get_positionctx.cursorinifnotctx.check||ctx.edit.editableposition0thenbeginlettext=ctx.edit.textandlines=ctx.edit.linesintryctx.edit.text<-Zed_rope.insert_uCharctx.edit.textpositionch;modifyctxtextlinespositionposition1100;movectx0;next_line_nctx0;with_->()endelseraiseCannot_editelseinsertctx(Zed_rope.of_string(fst(Zed_string.of_uChars[ch])))letinsert_no_erasectxrope=letposition=Zed_cursor.get_positionctx.cursorinifnotctx.check||ctx.edit.editableposition0thenbeginletlen=Zed_rope.lengthropeandtext=ctx.edit.textandlines=ctx.edit.linesinletwidth_add=Zed_string.aval_width(Zed_string.width(Zed_rope.to_stringrope))inctx.edit.text<-Zed_rope.inserttextpositionrope;ctx.edit.lines<-Zed_lines.insertctx.edit.linesposition(Zed_lines.of_roperope);modifyctxtextlinespositionpositionlen0width_add0;movectxlenendelseraiseCannot_editletremove_nextctxlen=letposition=Zed_cursor.get_positionctx.cursorinlettext_len=Zed_rope.lengthctx.edit.textinletlen=ifposition+len>text_lenthentext_len-positionelseleninifnotctx.check||ctx.edit.editablepositionlenthenbeginlettext=ctx.edit.textandlines=ctx.edit.linesinletwidth_remove=Zed_string.(aval_width(widthZed_rope.(to_string(subtextpositionlen))))inctx.edit.text<-Zed_rope.removetextpositionlen;ctx.edit.lines<-Zed_lines.removectx.edit.linespositionlen;modifyctxtextlinespositionposition0len0width_remove;endelseraiseCannot_editletremove_prevctxlen=letposition=Zed_cursor.get_positionctx.cursorinletlen=minpositionleninifnotctx.check||ctx.edit.editable(position-len)lenthenbeginlettext=ctx.edit.textandlines=ctx.edit.linesinletwidth_remove=Zed_string.(aval_width(widthZed_rope.(to_string(subtext(position-len)len))))inctx.edit.text<-Zed_rope.removetext(position-len)len;ctx.edit.lines<-Zed_lines.removectx.edit.lines(position-len)len;modifyctxtextlines(position-len)position0len0width_remove;endelseraiseCannot_editletremove=remove_nextletreplacectxlenrope=letposition=Zed_cursor.get_positionctx.cursorinlettext_len=Zed_rope.lengthctx.edit.textinletlen=ifposition+len>text_lenthentext_len-positionelseleninifnotctx.check||ctx.edit.editablepositionlenthenbeginletrope_len=Zed_rope.lengthropeandtext=ctx.edit.textandlines=ctx.edit.linesinletorig_width=Zed_string.(aval_width(widthZed_rope.(to_string(subtextpositionlen))))inletcurr_width=Zed_string.(aval_width(widthZed_rope.(to_stringrope)))inctx.edit.text<-Zed_rope.replacetextpositionlenrope;ctx.edit.lines<-Zed_lines.replacectx.edit.linespositionlen(Zed_lines.of_roperope);modifyctxtextlinespositionpositionrope_lenlencurr_widthorig_width;movectxrope_lenendelseraiseCannot_editletnewline_rope=Zed_rope.singleton(Zed_char.unsafe_of_char'\n')letnewlinectx=insertctxnewline_ropeletnext_charctx=ifnot(at_eotctx)thenmovectx1letprev_charctx=ifnot(at_botctx)thenmovectx(-1)letnext_linectx=letindex=Zed_cursor.get_linectx.cursorinifindex=Zed_lines.countctx.edit.linesthengotoctx~set_wanted_column:false(Zed_rope.lengthctx.edit.text)elsebeginletstop=ifindex+1=Zed_lines.countctx.edit.linesthenZed_rope.lengthctx.edit.textelseZed_lines.line_startctx.edit.lines(index+2)-1inletwanted_idx=Zed_lines.get_idx_by_widthctx.edit.lines(index+1)(Zed_cursor.get_wanted_columnctx.cursor)ingotoctx~set_wanted_column:false(minwanted_idxstop)endletprev_linectx=letindex=Zed_cursor.get_linectx.cursorinifindex=0thenbegingotoctx~set_wanted_column:false0endelsebeginletstop=Zed_lines.line_startctx.edit.linesindex-1inletwanted_idx=Zed_lines.get_idx_by_widthctx.edit.lines(index-1)(Zed_cursor.get_wanted_columnctx.cursor)ingotoctx~set_wanted_column:false(minwanted_idxstop)endletjoin_linectx=lettext=ctx.edit.textinletlines=linesctx.editinletlines_num=Zed_lines.countlinesinletindex=linectxinletposition=Zed_lines.line_stopctx.edit.linesindexinletlen=1inifindex<lines_numthenifnotctx.check||ctx.edit.editablepositionlenthenbeginletwidth_remove=1inifis_not_space(Zed_char.core(Zed_rope.gettext(position-1)))&&is_not_space(Zed_char.core(Zed_rope.gettext(position+1)))thenletspace=Zed_rope.of_string@@Zed_string.of_utf8" "inletlines_space=Zed_lines.of_ropespaceinctx.edit.text<-Zed_rope.replacetextpositionlenspace;ctx.edit.lines<-Zed_lines.replacectx.edit.linespositionlenlines_space;modifyctxtextlinespositionposition0000else(ctx.edit.text<-Zed_rope.removetextpositionlen;ctx.edit.lines<-Zed_lines.removectx.edit.linespositionlen;modifyctxtextlinespositionposition0len0width_remove)endelseraiseCannot_editletgoto_bolctx=gotoctx(Zed_lines.line_startctx.edit.lines(Zed_cursor.get_linectx.cursor))letgoto_eolctx=letindex=Zed_cursor.get_linectx.cursorinifindex=Zed_lines.countctx.edit.linesthengotoctx(Zed_rope.lengthctx.edit.text)elsegotoctx(Zed_lines.line_startctx.edit.lines(index+1)-1)letgoto_botctx=gotoctx0letgoto_eotctx=gotoctx(Zed_rope.lengthctx.edit.text)letdelete_next_charsctxn=ifnot(at_eotctx)thenbeginctx.edit.set_selectionfalse;remove_nextctxn;endletdelete_prev_charsctxn=ifnot(at_botctx)thenbeginctx.edit.set_selectionfalse;remove_prevctxn;endletkill_next_charsctxn=letposition=Zed_cursor.get_positionctx.cursorinletend_pos=min(position+n)(Zed_rope.lengthctx.edit.text)inletn=end_pos-positioninctx.edit.clipboard.clipboard_set(Zed_rope.subctx.edit.textpositionn);ctx.edit.set_selectionfalse;removectxnletkill_prev_charsctxn=letposition=Zed_cursor.get_positionctx.cursorinletstart=max0(position-n)inletn=position-startinctx.edit.clipboard.clipboard_set(Zed_rope.subctx.edit.textstartn);ctx.edit.set_selectionfalse;remove_prevctxnletdelete_next_charctx=ifnot(at_eotctx)thenbeginctx.edit.set_selectionfalse;remove_nextctx1endletdelete_prev_charctx=ifnot(at_botctx)thenbeginctx.edit.set_selectionfalse;remove_prevctx1endletdelete_next_linectx=ctx.edit.set_selectionfalse;letposition=Zed_cursor.get_positionctx.cursorinletindex=Zed_cursor.get_linectx.cursorinifindex=Zed_lines.countctx.edit.linesthenremove_nextctx(Zed_rope.lengthctx.edit.text-position)elseremove_nextctx(Zed_lines.line_startctx.edit.lines(index+1)-position)letdelete_prev_linectx=ctx.edit.set_selectionfalse;letposition=Zed_cursor.get_positionctx.cursorinletstart=Zed_lines.line_startctx.edit.lines(Zed_cursor.get_linectx.cursor)inremove_prevctx(position-start)letkill_next_linectx=letposition=Zed_cursor.get_positionctx.cursorinletindex=Zed_cursor.get_linectx.cursorinifindex=Zed_lines.countctx.edit.linesthenbeginctx.edit.clipboard.clipboard_set(Zed_rope.afterctx.edit.textposition);ctx.edit.set_selectionfalse;removectx(Zed_rope.lengthctx.edit.text-position)endelsebeginletlen=Zed_lines.line_startctx.edit.lines(index+1)-positioninctx.edit.clipboard.clipboard_set(Zed_rope.subctx.edit.textpositionlen);ctx.edit.set_selectionfalse;removectxlenendletkill_prev_linectx=letposition=Zed_cursor.get_positionctx.cursorinletstart=Zed_lines.line_startctx.edit.lines(Zed_cursor.get_linectx.cursor)inctx.edit.clipboard.clipboard_set(Zed_rope.subctx.edit.textstart(position-start));ctx.edit.set_selectionfalse;remove_prevctx(position-start)letswitch_erase_modectx=ctx.edit.set_erase_mode(not(S.valuectx.edit.erase_mode))letset_markctx=Zed_cursor.gotoctx.edit.mark(Zed_cursor.get_positionctx.cursor);ctx.edit.set_selectiontrueletgoto_markctx=gotoctx(Zed_cursor.get_positionctx.edit.mark)letcopyctx=ifS.valuectx.edit.selectionthenbeginleta=Zed_cursor.get_positionctx.cursorandb=Zed_cursor.get_positionctx.edit.markinleta=minabandb=maxabinctx.edit.clipboard.clipboard_set(Zed_rope.subctx.edit.texta(b-a));ctx.edit.set_selectionfalseendletcopy_sequencectxstartlen=ctx.edit.clipboard.clipboard_set(Zed_rope.subctx.edit.textstartlen)letkillctx=ifS.valuectx.edit.selectionthenbeginleta=Zed_cursor.get_positionctx.cursorandb=Zed_cursor.get_positionctx.edit.markinleta=minabandb=maxabinctx.edit.clipboard.clipboard_set(Zed_rope.subctx.edit.texta(b-a));ctx.edit.set_selectionfalse;gotoctxa;leta=Zed_cursor.get_positionctx.cursorinifa<=bthenremovectx(b-a)endletyankctx=ctx.edit.set_selectionfalse;insertctx(ctx.edit.clipboard.clipboard_get())letsearch_word_forwardctx=letlen=Zed_rope.lengthctx.edit.textinletrecloopidx=ifidx=lenthenNoneelsematchctx.edit.match_wordctx.edit.textidxwith|Someidx'->Some(idx,idx')|None->loop(idx+1)inloop(Zed_cursor.get_positionctx.cursor)letsearch_word_backwardctx=letrecloopidx=ifidx=-1thenNoneelsematchctx.edit.match_wordctx.edit.textidxwith|Someidx'->loop2(idx-1)(idx,idx')|None->loop(idx-1)andloop2idxresult=ifidx=-1thenSomeresultelsematchctx.edit.match_wordctx.edit.textidxwith|Someidx'->(* the match_word method now uses unicode segmentation algorithm,
so the tail postion should be traced *)let(_,r_end)=resultinifidx'=r_endthenloop2(idx-1)(idx,idx')elseSomeresult|None->Someresultinloop(Zed_cursor.get_positionctx.cursor-1)letcapitalize_wordctx=matchsearch_word_forwardctxwith|Some(idx1,idx2)->gotoctxidx1;ifZed_cursor.get_positionctx.cursor=idx1&&idx1<idx2thenbeginletstr=Zed_rope.subctx.edit.textidx1(idx2-idx1)inletch,str'=Zed_rope.breakstr1inreplacectx(Zed_rope.lengthstr)(Zed_rope.append(Zed_rope.uppercase?locale:(S.valuectx.edit.locale)ch)(Zed_rope.lowercase?locale:(S.valuectx.edit.locale)str'))end|None->()letlowercase_wordctx=matchsearch_word_forwardctxwith|Some(idx1,idx2)->gotoctxidx1;ifZed_cursor.get_positionctx.cursor=idx1thenbeginletstr=Zed_rope.subctx.edit.textidx1(idx2-idx1)inreplacectx(Zed_rope.lengthstr)(Zed_rope.lowercase?locale:(S.valuectx.edit.locale)str)end|None->()letuppercase_wordctx=matchsearch_word_forwardctxwith|Some(idx1,idx2)->gotoctxidx1;ifZed_cursor.get_positionctx.cursor=idx1thenbeginletstr=Zed_rope.subctx.edit.textidx1(idx2-idx1)inreplacectx(Zed_rope.lengthstr)(Zed_rope.uppercase?locale:(S.valuectx.edit.locale)str)end|None->()letnext_wordctx=matchsearch_word_forwardctxwith|Some(_idx1,idx2)->gotoctxidx2|None->gotoctx(Zed_rope.lengthctx.edit.text)letprev_wordctx=matchsearch_word_backwardctxwith|Some(idx1,_idx2)->gotoctxidx1|None->gotoctx0letdelete_next_wordctx=letposition=Zed_cursor.get_positionctx.cursorinletword_end=matchsearch_word_forwardctxwith|Some(_idx1,idx2)->idx2|None->Zed_rope.lengthctx.edit.textinremovectx(word_end-position)letdelete_prev_wordctx=letposition=Zed_cursor.get_positionctx.cursorinletstart=matchsearch_word_backwardctxwith|Some(idx1,_idx2)->idx1|None->0inremove_prevctx(position-start)letkill_next_wordctx=letposition=Zed_cursor.get_positionctx.cursorinletword_end=matchsearch_word_forwardctxwith|Some(_idx1,idx2)->idx2|None->Zed_rope.lengthctx.edit.textinctx.edit.clipboard.clipboard_set(Zed_rope.subctx.edit.textposition(word_end-position));ctx.edit.set_selectionfalse;removectx(word_end-position)letkill_prev_wordctx=letposition=Zed_cursor.get_positionctx.cursorinletstart=matchsearch_word_backwardctxwith|Some(idx1,_idx2)->idx1|None->0inctx.edit.clipboard.clipboard_set(Zed_rope.subctx.edit.textstart(position-start));ctx.edit.set_selectionfalse;remove_prevctx(position-start)letundo{check;edit;cursor}=ifedit.undo_count>0thenbeginletindex=ifedit.undo_index=0thenedit.undo_size-1elseedit.undo_index-1inlettext,lines,pos,new_pos,added,removed,added_width,removed_width=edit.undo.(index)inifnotcheck||edit.editableposaddedthenbeginedit.undo_count<-edit.undo_count-1;edit.undo_index<-index;edit.text<-text;edit.lines<-lines;edit.send_changes{position=pos;removed=added;added=removed;added_width=removed_width;removed_width=added_width};Zed_cursor.gotocursornew_posendelseraiseCannot_editend(* +-----------------------------------------------------------------+
| Action by names |
+-----------------------------------------------------------------+ *)typeaction=|InsertofZed_char.t|Insert_strofZed_string.t|Newline|Next_char|Prev_char|Next_line|Prev_line|Join_line|Set_posofint|Gotoofint|Goto_bol|Goto_eol|Goto_bot|Goto_eot|Delete_next_charsofint|Delete_prev_charsofint|Kill_next_charsofint|Kill_prev_charsofint|Delete_next_char|Delete_prev_char|Delete_next_line|Delete_prev_line|Kill_next_line|Kill_prev_line|Switch_erase_mode|Set_mark|Goto_mark|Copy|Kill|Yank|Capitalize_word|Lowercase_word|Uppercase_word|Next_word|Prev_word|Delete_next_word|Delete_prev_word|Kill_next_word|Kill_prev_word|Undoletget_action=function|Insertch->(functx->ifZed_char.lengthch=1theninsert_charctx(Zed_char.corech)elseinsertctx(Zed_rope.singletonch))|Insert_strstr->(functx->insertctx(Zed_rope.of_stringstr))|Newline->newline|Next_char->next_char|Prev_char->prev_char|Next_line->next_line|Prev_line->prev_line|Join_line->join_line|Set_posn->functx->set_positionctxn|Goton->functx->gotoctxn|Goto_bol->goto_bol|Goto_eol->goto_eol|Goto_bot->goto_bot|Goto_eot->goto_eot|Delete_next_charsn->(functx->delete_next_charsctxn)|Delete_prev_charsn->(functx->delete_prev_charsctxn)|Kill_next_charsn->(functx->kill_next_charsctxn)|Kill_prev_charsn->(functx->kill_prev_charsctxn)|Delete_next_char->delete_next_char|Delete_prev_char->delete_prev_char|Delete_next_line->delete_next_line|Delete_prev_line->delete_prev_line|Kill_next_line->kill_next_line|Kill_prev_line->kill_prev_line|Switch_erase_mode->switch_erase_mode|Set_mark->set_mark|Goto_mark->goto_mark|Copy->copy|Kill->kill|Yank->yank|Capitalize_word->capitalize_word|Lowercase_word->lowercase_word|Uppercase_word->uppercase_word|Next_word->next_word|Prev_word->prev_word|Delete_next_word->delete_next_word|Delete_prev_word->delete_prev_word|Kill_next_word->kill_next_word|Kill_prev_word->kill_prev_word|Undo->undoletdoc_of_action=function|Insert_->"insert the given character."|Insert_str_->"insert the given string."|Newline->"insert a newline character."|Next_char->"move the cursor to the next character."|Prev_char->"move the cursor to the previous character."|Next_line->"move the cursor to the next line."|Prev_line->"move the cursor to the previous line."|Join_line->"join two lines into one."|Set_pos_->"move the cursor to the position without poking wanted_column"|Goto_->"move the cursor to the position"|Goto_bol->"move the cursor to the beginning of the current line."|Goto_eol->"move the cursor to the end of the current line."|Goto_bot->"move the cursor to the beginning of the text."|Goto_eot->"move the cursor to the end of the text."|Delete_next_chars_->"delete several characters after the cursor."|Delete_prev_chars_->"delete several characters before the cursor."|Kill_next_chars_->"cut several characters after the cursor."|Kill_prev_chars_->"cut several characters before the cursor."|Delete_next_char->"delete the character after the cursor."|Delete_prev_char->"delete the character before the cursor."|Delete_next_line->"delete everything until the end of the current line."|Delete_prev_line->"delete everything until the beginning of the current line."|Kill_next_line->"cut everything until the end of the current line."|Kill_prev_line->"cut everything until the beginning of the current line."|Switch_erase_mode->"switch the current erasing mode."|Set_mark->"set the mark to the current position."|Goto_mark->"move the cursor to the mark."|Copy->"copy the current region to the clipboard."|Kill->"cut the current region to the clipboard."|Yank->"paste the contents of the clipboard at current position."|Capitalize_word->"capitalize the first word after the cursor."|Lowercase_word->"convert the first word after the cursor to lowercase."|Uppercase_word->"convert the first word after the cursor to uppercase."|Next_word->"move the cursor to the end of the next word."|Prev_word->"move the cursor to the beginning of the previous word."|Delete_next_word->"delete up until the next non-word character."|Delete_prev_word->"delete the word behind the cursor."|Kill_next_word->"cut up until the next non-word character."|Kill_prev_word->"cut the word behind the cursor."|Undo->"revert the last action."letactions=[Newline,"newline";Next_char,"next-char";Prev_char,"prev-char";Next_line,"next-line";Prev_line,"prev-line";Join_line,"join-line";Goto_bol,"goto-bol";Goto_eol,"goto-eol";Goto_bot,"goto-bot";Goto_eot,"goto-eot";Delete_next_char,"delete-next-char";Delete_prev_char,"delete-prev-char";Delete_next_line,"delete-next-line";Delete_prev_line,"delete-prev-line";Kill_next_line,"kill-next-line";Kill_prev_line,"kill-prev-line";Switch_erase_mode,"switch-erase-mode";Set_mark,"set-mark";Goto_mark,"goto-mark";Copy,"copy";Kill,"kill";Yank,"yank";Capitalize_word,"capitalize-word";Lowercase_word,"lowercase-word";Uppercase_word,"uppercase-word";Next_word,"next-word";Prev_word,"prev-word";Delete_next_word,"delete-next-word";Delete_prev_word,"delete-prev-word";Kill_next_word,"kill-next-word";Kill_prev_word,"kill-prev-word";Undo,"undo";]letactions_to_names=Array.of_list(List.sort(fun(a1,_)(a2,_)->comparea1a2)actions)letnames_to_actions=Array.of_list(List.sort(fun(_,n1)(_,n2)->comparen1n2)actions)letparse_insertx=ifZed_utf8.starts_withx"insert("&&Zed_utf8.ends_withx")"thenbeginletstr=String.subx7(String.lengthx-8)inifString.lengthstr=1&&Char.codestr.[0]<128thenInsert(Zed_char.unsafe_of_uChar(Uchar.of_charstr.[0]))elseifString.lengthstr>2&&str.[0]='U'&&str.[1]='+'thenletacc=ref0infori=2toString.lengthstr-1doletch=str.[i]inacc:=!acc*16+(matchchwith|'0'..'9'->Char.codech-Char.code'0'|'a'..'f'->Char.codech-Char.code'a'+10|'A'..'F'->Char.codech-Char.code'A'+10|_->raiseNot_found)done;tryInsert(Zed_char.unsafe_of_uChar(Uchar.of_int!acc))with_->raiseNot_foundelseraiseNot_foundendelseraiseNot_foundletparse_insert_strstr=ifZed_utf8.starts_withstr"insert_str("&&Zed_utf8.ends_withstr")"thenletstr=String.substr11(String.lengthstr-12)intryInsert_str(Zed_string.of_utf8str)with_->raiseNot_foundelseraiseNot_foundletparse_action_countactionact_namestr=letact_len=String.lengthact_nameinifZed_utf8.starts_withstr(act_name^"(")&&Zed_utf8.ends_withstr")"thenletstr=String.substr(act_len+1)(String.lengthstr-(act_len+2))intryaction(int_of_stringstr)with_->raiseNot_foundelseraiseNot_foundletparse_set_pos=parse_action_count(func->Set_posc)"set-pos"letparse_goto=parse_action_count(func->Gotoc)"goto"letparse_delete_next_chars=parse_action_count(func->Delete_next_charsc)"delete-next-chars"letparse_delete_prev_chars=parse_action_count(func->Delete_prev_charsc)"delete-prev-chars"letparse_kill_next_chars=parse_action_count(func->Kill_next_charsc)"kill-next-chars"letparse_kill_prev_chars=parse_action_count(func->Kill_prev_charsc)"kill-prev-chars"letparse_action_paramx=tryparse_insertxwithNot_found->tryparse_insert_strxwithNot_found->tryparse_set_posxwithNot_found->tryparse_gotoxwithNot_found->tryparse_delete_next_charsxwithNot_found->tryparse_delete_prev_charsxwithNot_found->tryparse_kill_next_charsxwithNot_found->parse_kill_prev_charsxletaction_of_namex=letrecloopab=ifa=bthenparse_action_paramxelseletc=(a+b)/2inletaction,name=Array.unsafe_getnames_to_actionscinmatchcomparexnamewith|dwhend<0->loopac|dwhend>0->loop(c+1)b|_->actioninloop0(Array.lengthnames_to_actions)letname_of_actionx=letrecloopab=ifa=bthenraiseNot_foundelseletc=(a+b)/2inletaction,name=Array.unsafe_getactions_to_namescinmatchcomparexactionwith|dwhend<0->loopac|dwhend>0->loop(c+1)b|_->nameinletopenPrintfinletparam_action_to_strnamec=sprintf"%s(%d)"namecinmatchxwith|Insertch->letcode=Uchar.to_int(Zed_char.corech)inifcode<=255thenletch=Char.chr(Uchar.to_int(Zed_char.corech))inmatchchwith|'a'..'z'|'A'..'Z'|'0'..'9'->sprintf"insert(%c)"ch|_->sprintf"insert(U+%02x)"codeelseifcode<=0xffffthensprintf"insert(U+%04x)"codeelsesprintf"insert(U+%06x)"code|Insert_strs->sprintf"insert-str(%s)"(Zed_string.to_utf8s)|Set_posc->param_action_to_str"set-pos"c|Gotoc->param_action_to_str"goto"c|Delete_next_charsc->param_action_to_str"delete-next-chars"c|Delete_prev_charsc->param_action_to_str"delete-prev-chars"c|Kill_next_charsc->param_action_to_str"kill-next-chars"c|Kill_prev_charsc->param_action_to_str"kill-prev-chars"c|_->loop0(Array.lengthactions_to_names)