15 static char Wsequence[] = "warning: changes out of sequence\n";
16 static int warned = FALSE;
19 * Log of changes made by editing commands. Three reasons for this:
20 * 1) We want addresses in commands to apply to old file, not file-in-change.
21 * 2) It's difficult to track changes correctly as things move, e.g. ,x m$
22 * 3) This gives an opportunity to optimize by merging adjacent changes.
23 * It's a little bit like the Undo/Redo log in Files, but Point 3) argues for a
24 * separate implementation. To do this well, we use Replace as well as
28 typedef struct Buflog Buflog;
31 short type; /* Replace, Filename */
32 uint q0; /* location of change (unused in f) */
33 uint nd; /* # runes to delete */
34 uint nr; /* # runes in string or file name */
39 Buflogsize = sizeof(Buflog)/sizeof(Rune)
43 * Minstring shouldn't be very big or we will do lots of I/O for small changes.
44 * Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r.
48 Minstring = 16, /* distance beneath which we merge changes */
49 Maxstring = RBUFSIZE /* maximum length of change we will merge into one */
55 if(f->elog.type != Empty)
59 f->elogbuf = emalloc(sizeof(Buffer));
61 f->elog.r = fbufalloc();
100 b.type = f->elog.type;
104 switch(f->elog.type){
106 warning(nil, "unknown elog type 0x%ux\n", f->elog.type);
113 bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr);
116 bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize);
123 elogreplace(File *f, int q0, int q1, Rune *r, int nr)
130 if(f->elog.type!=Null && q0<f->elog.q0){
132 warning(nil, Wsequence);
135 /* try to merge with previous */
136 gap = q0 - (f->elog.q0+f->elog.nd); /* gap between previous and this */
137 if(f->elog.type==Replace && f->elog.nr+gap+nr<Maxstring){
140 bufread(&f->b, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap);
143 f->elog.nd += gap + q1-q0;
144 runemove(f->elog.r+f->elog.nr, r, nr);
150 f->elog.type = Replace;
155 editerror("internal error: replacement string too large(%d)", nr);
156 runemove(f->elog.r, r, nr);
160 eloginsert(File *f, int q0, Rune *r, int nr)
167 if(f->elog.type!=Null && q0<f->elog.q0){
169 warning(nil, Wsequence);
172 /* try to merge with previous */
173 if(f->elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nr<Maxstring){
174 runemove(f->elog.r+f->elog.nr, r, nr);
180 f->elog.type = Insert;
186 runemove(f->elog.r, r, n);
193 elogdelete(File *f, int q0, int q1)
198 if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){
200 warning(nil, Wsequence);
203 /* try to merge with previous */
204 if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){
209 f->elog.type = Delete;
233 * The edit commands have already updated the selection in t->q0, t->q1,
234 * but using coordinates relative to the unmodified buffer. As we apply the log,
235 * we have to update the coordinates to be relative to the modified buffer.
236 * Textinsert and textdelete will do this for us; our only work is to apply the
237 * convention that an insertion at t->q0==t->q1 is intended to select the
242 * We constrain the addresses in here (with textconstrain()) because
243 * overlapping changes will generate bogus addresses. We will warn
244 * about changes out of sequence but proceed anyway; here we must
245 * keep things in range.
249 up = log->nc-Buflogsize;
250 bufread(log, up, (Rune*)&b, Buflogsize);
253 fprint(2, "elogapply: 0x%ux\n", b.type);
259 warning(nil, "elog replace %d %d (%d %d)\n",
260 b.q0, b.q0+b.nd, t->q0, t->q1);
265 textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
266 textdelete(t, tq0, tq1, TRUE);
268 for(i=0; i<b.nr; i+=n){
272 bufread(log, up+i, buf, n);
273 textinsert(t, tq0+i, buf, n, TRUE);
275 if(t->q0 == b.q0 && t->q1 == b.q0)
281 warning(nil, "elog delete %d %d (%d %d)\n",
282 b.q0, b.q0+b.nd, t->q0, t->q1);
287 textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
288 textdelete(t, tq0, tq1, TRUE);
293 warning(nil, "elog insert %d %d (%d %d)\n",
294 b.q0, b.q0+b.nr, t->q0, t->q1);
299 textconstrain(t, b.q0, b.q0, &tq0, &tq1);
301 for(i=0; i<b.nr; i+=n){
305 bufread(log, up+i, buf, n);
306 textinsert(t, tq0+i, buf, n, TRUE);
308 if(t->q0 == b.q0 && t->q1 == b.q0)
314 fileunsetname(f, epsilon);
321 f->name = runemalloc(u.n);
322 bufread(delta, up, f->name, u.n);
327 bufdelete(log, up, log->nc);
333 * Bad addresses will cause bufload to crash, so double check.
334 * If changes were out of order, we expect problems so don't complain further.
336 if(t->q0 > f->b.nc || t->q1 > f->b.nc || t->q0 > t->q1){
338 warning(nil, "elogapply: can't happen %d %d %d\n", t->q0, t->q1, f->b.nc);
339 t->q1 = min(t->q1, f->b.nc);
340 t->q0 = min(t->q0, t->q1);