123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- implement Editlog;
- include "common.m";
- sys: Sys;
- utils: Utils;
- buffm: Bufferm;
- filem: Filem;
- textm: Textm;
- edit: Edit;
- sprint, fprint: import sys;
- FALSE, TRUE, BUFSIZE, Empty, Null, Delete, Insert, Replace, Filename, Astring: import Dat;
- File: import filem;
- Buffer: import buffm;
- Text: import textm;
- error, warning, stralloc, strfree: import utils;
- editerror: import edit;
- init(mods : ref Dat->Mods)
- {
- sys = mods.sys;
- utils = mods.utils;
- buffm = mods.bufferm;
- filem = mods.filem;
- textm = mods.textm;
- edit = mods.edit;
- }
- Wsequence := "warning: changes out of sequence\n";
- warned := FALSE;
- #
- # Log of changes made by editing commands. Three reasons for this:
- # 1) We want addresses in commands to apply to old file, not file-in-change.
- # 2) It's difficult to track changes correctly as things move, e.g. ,x m$
- # 3) This gives an opportunity to optimize by merging adjacent changes.
- # It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a
- # separate implementation. To do this well, we use Replace as well as
- # Insert and Delete
- #
- Buflog: adt{
- typex: int; # Replace, Filename
- q0: int; # location of change (unused in f)
- nd: int; # runes to delete
- nr: int; # runes in string or file name
- };
- Buflogsize: con 7;
- SHM : con 16rffff;
- pack(b: Buflog) : string
- {
- a := "0123456";
- a[0] = b.typex;
- a[1] = b.q0&SHM;
- a[2] = (b.q0>>16)&SHM;
- a[3] = b.nd&SHM;
- a[4] = (b.nd>>16)&SHM;
- a[5] = b.nr&SHM;
- a[6] = (b.nr>>16)&SHM;
- return a;
- }
- scopy(s1: ref Astring, m: int, s2: string, n: int, o: int)
- {
- p := o-n;
- for(i := 0; i < p; i++)
- s1.s[m++] = s2[n++];
- }
- #
- # Minstring shouldn't be very big or we will do lots of I/O for small changes.
- # Maxstring is BUFSIZE so we can fbufalloc() once and not realloc elog.r.
- #
- Minstring: con 16; # distance beneath which we merge changes
- Maxstring: con BUFSIZE; # maximum length of change we will merge into one
- eloginit(f: ref File)
- {
- if(f.elog.typex != Empty)
- return;
- f.elog.typex = Null;
- if(f.elogbuf == nil)
- f.elogbuf = buffm->newbuffer();
- # f.elogbuf = ref Buffer;
- if(f.elog.r == nil)
- f.elog.r = stralloc(BUFSIZE);
- f.elogbuf.reset();
- }
- elogclose(f: ref File)
- {
- if(f.elogbuf != nil){
- f.elogbuf.close();
- f.elogbuf = nil;
- }
- }
- elogreset(f: ref File)
- {
- f.elog.typex = Null;
- f.elog.nd = 0;
- f.elog.nr = 0;
- }
- elogterm(f: ref File)
- {
- elogreset(f);
- if(f.elogbuf != nil)
- f.elogbuf.reset();
- f.elog.typex = Empty;
- if(f.elog.r != nil){
- strfree(f.elog.r);
- f.elog.r = nil;
- }
- warned = FALSE;
- }
- elogflush(f: ref File)
- {
- b: Buflog;
- b.typex = f.elog.typex;
- b.q0 = f.elog.q0;
- b.nd = f.elog.nd;
- b.nr = f.elog.nr;
- case(f.elog.typex){
- * =>
- warning(nil, sprint("unknown elog type 0x%ux\n", f.elog.typex));
- break;
- Null =>
- break;
- Insert or
- Replace =>
- if(f.elog.nr > 0)
- f.elogbuf.insert(f.elogbuf.nc, f.elog.r.s, f.elog.nr);
- f.elogbuf.insert(f.elogbuf.nc, pack(b), Buflogsize);
- break;
- Delete =>
- f.elogbuf.insert(f.elogbuf.nc, pack(b), Buflogsize);
- break;
- }
- elogreset(f);
- }
- elogreplace(f: ref File, q0: int, q1: int, r: string, nr: int)
- {
- gap: int;
- if(q0==q1 && nr==0)
- return;
- eloginit(f);
- if(f.elog.typex!=Null && q0<f.elog.q0){
- if(warned++ == 0)
- warning(nil, Wsequence);
- elogflush(f);
- }
- # try to merge with previous
- gap = q0 - (f.elog.q0+f.elog.nd); # gap between previous and this
- if(f.elog.typex==Replace && f.elog.nr+gap+nr<Maxstring){
- if(gap < Minstring){
- if(gap > 0){
- f.buf.read(f.elog.q0+f.elog.nd, f.elog.r, f.elog.nr, gap);
- f.elog.nr += gap;
- }
- f.elog.nd += gap + q1-q0;
- scopy(f.elog.r, f.elog.nr, r, 0, nr);
- f.elog.nr += nr;
- return;
- }
- }
- elogflush(f);
- f.elog.typex = Replace;
- f.elog.q0 = q0;
- f.elog.nd = q1-q0;
- f.elog.nr = nr;
- if(nr > BUFSIZE)
- editerror(sprint("internal error: replacement string too large(%d)", nr));
- scopy(f.elog.r, 0, r, 0, nr);
- }
- eloginsert(f: ref File, q0: int, r: string, nr: int)
- {
- n: int;
- if(nr == 0)
- return;
- eloginit(f);
- if(f.elog.typex!=Null && q0<f.elog.q0){
- if(warned++ == 0)
- warning(nil, Wsequence);
- elogflush(f);
- }
- # try to merge with previous
- if(f.elog.typex==Insert && q0==f.elog.q0 && f.elog.nr+nr<Maxstring){
- ofer := f.elog.r;
- f.elog.r = stralloc(f.elog.nr+nr);
- scopy(f.elog.r, 0, ofer.s, 0, f.elog.nr);
- scopy(f.elog.r, f.elog.nr, r, 0, nr);
- f.elog.nr += nr;
- strfree(ofer);
- return;
- }
- while(nr > 0){
- elogflush(f);
- f.elog.typex = Insert;
- f.elog.q0 = q0;
- n = nr;
- if(n > BUFSIZE)
- n = BUFSIZE;
- f.elog.nr = n;
- scopy(f.elog.r, 0, r, 0, n);
- r = r[n:];
- nr -= n;
- }
- }
- elogdelete(f: ref File, q0: int, q1: int)
- {
- if(q0 == q1)
- return;
- eloginit(f);
- if(f.elog.typex!=Null && q0<f.elog.q0+f.elog.nd){
- if(warned++ == 0)
- warning(nil, Wsequence);
- elogflush(f);
- }
- # try to merge with previous
- if(f.elog.typex==Delete && f.elog.q0+f.elog.nd==q0){
- f.elog.nd += q1-q0;
- return;
- }
- elogflush(f);
- f.elog.typex = Delete;
- f.elog.q0 = q0;
- f.elog.nd = q1-q0;
- }
- elogapply(f: ref File)
- {
- b: Buflog;
- buf: ref Astring;
- i, n, up, mod : int;
- log: ref Buffer;
- elogflush(f);
- log = f.elogbuf;
- t := f.curtext;
- a := stralloc(Buflogsize);
- buf = stralloc(BUFSIZE);
- mod = FALSE;
- #
- # The edit commands have already updated the selection in t.q0, t.q1.
- # The text.insert and text.delete calls below will update it again, so save the
- # current setting and restore it at the end.
- #
- q0 := t.q0;
- q1 := t.q1;
- while(log.nc > 0){
- up = log.nc-Buflogsize;
- log.read(up, a, 0, Buflogsize);
- b.typex = a.s[0];
- b.q0 = a.s[1]|(a.s[2]<<16);
- b.nd = a.s[3]|(a.s[4]<<16);
- b.nr = a.s[5]|(a.s[6]<<16);
- case(b.typex){
- * =>
- error(sprint("elogapply: 0x%ux\n", b.typex));
- break;
- Replace =>
- if(!mod){
- mod = TRUE;
- f.mark();
- }
- # if(b.nd == b.nr && b.nr <= BUFSIZE){
- # up -= b.nr;
- # log.read(up, buf, 0, b.nr);
- # t.replace(b.q0, b.q0+b.nd, buf.s, b.nr, TRUE, 0);
- # break;
- # }
- t.delete(b.q0, b.q0+b.nd, TRUE);
- up -= b.nr;
- for(i=0; i<b.nr; i+=n){
- n = b.nr - i;
- if(n > BUFSIZE)
- n = BUFSIZE;
- log.read(up+i, buf, 0, n);
- t.insert(b.q0+i, buf.s, n, TRUE, 0);
- }
- # t.q0 = b.q0;
- # t.q1 = b.q0+b.nr;
- break;
- Delete =>
- if(!mod){
- mod = TRUE;
- f.mark();
- }
- t.delete(b.q0, b.q0+b.nd, TRUE);
- # t.q0 = b.q0;
- # t.q1 = b.q0;
- break;
- Insert =>
- if(!mod){
- mod = TRUE;
- f.mark();
- }
- up -= b.nr;
- for(i=0; i<b.nr; i+=n){
- n = b.nr - i;
- if(n > BUFSIZE)
- n = BUFSIZE;
- log.read(up+i, buf, 0, n);
- t.insert(b.q0+i, buf.s, n, TRUE, 0);
- }
- # t.q0 = b.q0;
- # t.q1 = b.q0+b.nr;
- break;
- # Filename =>
- # f.seq = u.seq;
- # f.unsetname(epsilon);
- # f.mod = u.mod;
- # up -= u.n;
- # if(u.n == 0)
- # f.name = nil;
- # else{
- # fn0 := stralloc(u.n);
- # delta.read(up, fn0, 0, u.n);
- # f.name = fn0.s;
- # strfree(fn0);
- # }
- # break;
- #
- }
- log.delete(up, log.nc);
- }
- strfree(buf);
- strfree(a);
- elogterm(f);
- t.q0 = q0;
- t.q1 = q1;
- if(t.q1 > f.buf.nc) # can't happen
- t.q1 = f.buf.nc;
- }
|