summaryrefslogtreecommitdiff
path: root/sys/src/cmd/acme/elog.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/acme/elog.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/acme/elog.c')
-rwxr-xr-xsys/src/cmd/acme/elog.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/sys/src/cmd/acme/elog.c b/sys/src/cmd/acme/elog.c
new file mode 100755
index 000000000..d00fc8d3b
--- /dev/null
+++ b/sys/src/cmd/acme/elog.c
@@ -0,0 +1,353 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <cursor.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <frame.h>
+#include <fcall.h>
+#include <plumb.h>
+#include "dat.h"
+#include "fns.h"
+#include "edit.h"
+
+static char Wsequence[] = "warning: changes out of sequence\n";
+static int 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
+ */
+
+typedef struct Buflog Buflog;
+struct Buflog
+{
+ short type; /* Replace, Filename */
+ uint q0; /* location of change (unused in f) */
+ uint nd; /* # runes to delete */
+ uint nr; /* # runes in string or file name */
+};
+
+enum
+{
+ Buflogsize = sizeof(Buflog)/sizeof(Rune),
+};
+
+/*
+ * Minstring shouldn't be very big or we will do lots of I/O for small changes.
+ * Maxstring is RBUFSIZE so we can fbufalloc() once and not realloc elog.r.
+ */
+enum
+{
+ Minstring = 16, /* distance beneath which we merge changes */
+ Maxstring = RBUFSIZE, /* maximum length of change we will merge into one */
+};
+
+void
+eloginit(File *f)
+{
+ if(f->elog.type != Empty)
+ return;
+ f->elog.type = Null;
+ if(f->elogbuf == nil)
+ f->elogbuf = emalloc(sizeof(Buffer));
+ if(f->elog.r == nil)
+ f->elog.r = fbufalloc();
+ bufreset(f->elogbuf);
+}
+
+void
+elogclose(File *f)
+{
+ if(f->elogbuf){
+ bufclose(f->elogbuf);
+ free(f->elogbuf);
+ f->elogbuf = nil;
+ }
+}
+
+void
+elogreset(File *f)
+{
+ f->elog.type = Null;
+ f->elog.nd = 0;
+ f->elog.nr = 0;
+}
+
+void
+elogterm(File *f)
+{
+ elogreset(f);
+ if(f->elogbuf)
+ bufreset(f->elogbuf);
+ f->elog.type = Empty;
+ fbuffree(f->elog.r);
+ f->elog.r = nil;
+ warned = FALSE;
+}
+
+void
+elogflush(File *f)
+{
+ Buflog b;
+
+ b.type = f->elog.type;
+ b.q0 = f->elog.q0;
+ b.nd = f->elog.nd;
+ b.nr = f->elog.nr;
+ switch(f->elog.type){
+ default:
+ warning(nil, "unknown elog type 0x%ux\n", f->elog.type);
+ break;
+ case Null:
+ break;
+ case Insert:
+ case Replace:
+ if(f->elog.nr > 0)
+ bufinsert(f->elogbuf, f->elogbuf->nc, f->elog.r, f->elog.nr);
+ /* fall through */
+ case Delete:
+ bufinsert(f->elogbuf, f->elogbuf->nc, (Rune*)&b, Buflogsize);
+ break;
+ }
+ elogreset(f);
+}
+
+void
+elogreplace(File *f, int q0, int q1, Rune *r, int nr)
+{
+ uint gap;
+
+ if(q0==q1 && nr==0)
+ return;
+ eloginit(f);
+ if(f->elog.type!=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.type==Replace && f->elog.nr+gap+nr<Maxstring){
+ if(gap < Minstring){
+ if(gap > 0){
+ bufread(f, f->elog.q0+f->elog.nd, f->elog.r+f->elog.nr, gap);
+ f->elog.nr += gap;
+ }
+ f->elog.nd += gap + q1-q0;
+ runemove(f->elog.r+f->elog.nr, r, nr);
+ f->elog.nr += nr;
+ return;
+ }
+ }
+ elogflush(f);
+ f->elog.type = Replace;
+ f->elog.q0 = q0;
+ f->elog.nd = q1-q0;
+ f->elog.nr = nr;
+ if(nr > RBUFSIZE)
+ editerror("internal error: replacement string too large(%d)", nr);
+ runemove(f->elog.r, r, nr);
+}
+
+void
+eloginsert(File *f, int q0, Rune *r, int nr)
+{
+ int n;
+
+ if(nr == 0)
+ return;
+ eloginit(f);
+ if(f->elog.type!=Null && q0<f->elog.q0){
+ if(warned++ == 0)
+ warning(nil, Wsequence);
+ elogflush(f);
+ }
+ /* try to merge with previous */
+ if(f->elog.type==Insert && q0==f->elog.q0 && f->elog.nr+nr<Maxstring){
+ runemove(f->elog.r+f->elog.nr, r, nr);
+ f->elog.nr += nr;
+ return;
+ }
+ while(nr > 0){
+ elogflush(f);
+ f->elog.type = Insert;
+ f->elog.q0 = q0;
+ n = nr;
+ if(n > RBUFSIZE)
+ n = RBUFSIZE;
+ f->elog.nr = n;
+ runemove(f->elog.r, r, n);
+ r += n;
+ nr -= n;
+ }
+}
+
+void
+elogdelete(File *f, int q0, int q1)
+{
+ if(q0 == q1)
+ return;
+ eloginit(f);
+ if(f->elog.type!=Null && q0<f->elog.q0+f->elog.nd){
+ if(warned++ == 0)
+ warning(nil, Wsequence);
+ elogflush(f);
+ }
+ /* try to merge with previous */
+ if(f->elog.type==Delete && f->elog.q0+f->elog.nd==q0){
+ f->elog.nd += q1-q0;
+ return;
+ }
+ elogflush(f);
+ f->elog.type = Delete;
+ f->elog.q0 = q0;
+ f->elog.nd = q1-q0;
+}
+
+#define tracelog 0
+void
+elogapply(File *f)
+{
+ Buflog b;
+ Rune *buf;
+ uint i, n, up, mod;
+ uint tq0, tq1;
+ Buffer *log;
+ Text *t;
+ int owner;
+
+ elogflush(f);
+ log = f->elogbuf;
+ t = f->curtext;
+
+ buf = fbufalloc();
+ mod = FALSE;
+
+ owner = 0;
+ if(t->w){
+ owner = t->w->owner;
+ if(owner == 0)
+ t->w->owner = 'E';
+ }
+
+ /*
+ * The edit commands have already updated the selection in t->q0, t->q1,
+ * but using coordinates relative to the unmodified buffer. As we apply the log,
+ * we have to update the coordinates to be relative to the modified buffer.
+ * Textinsert and textdelete will do this for us; our only work is to apply the
+ * convention that an insertion at t->q0==t->q1 is intended to select the
+ * inserted text.
+ */
+
+ /*
+ * We constrain the addresses in here (with textconstrain()) because
+ * overlapping changes will generate bogus addresses. We will warn
+ * about changes out of sequence but proceed anyway; here we must
+ * keep things in range.
+ */
+
+ while(log->nc > 0){
+ up = log->nc-Buflogsize;
+ bufread(log, up, (Rune*)&b, Buflogsize);
+ switch(b.type){
+ default:
+ fprint(2, "elogapply: 0x%ux\n", b.type);
+ abort();
+ break;
+
+ case Replace:
+ if(tracelog)
+ warning(nil, "elog replace %d %d (%d %d)\n",
+ b.q0, b.q0+b.nd, t->q0, t->q1);
+ if(!mod){
+ mod = TRUE;
+ filemark(f);
+ }
+ textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
+ textdelete(t, tq0, tq1, TRUE);
+ up -= b.nr;
+ for(i=0; i<b.nr; i+=n){
+ n = b.nr - i;
+ if(n > RBUFSIZE)
+ n = RBUFSIZE;
+ bufread(log, up+i, buf, n);
+ textinsert(t, tq0+i, buf, n, TRUE);
+ }
+ if(t->q0 == b.q0 && t->q1 == b.q0)
+ t->q1 += b.nr;
+ break;
+
+ case Delete:
+ if(tracelog)
+ warning(nil, "elog delete %d %d (%d %d)\n",
+ b.q0, b.q0+b.nd, t->q0, t->q1);
+ if(!mod){
+ mod = TRUE;
+ filemark(f);
+ }
+ textconstrain(t, b.q0, b.q0+b.nd, &tq0, &tq1);
+ textdelete(t, tq0, tq1, TRUE);
+ break;
+
+ case Insert:
+ if(tracelog)
+ warning(nil, "elog insert %d %d (%d %d)\n",
+ b.q0, b.q0+b.nr, t->q0, t->q1);
+ if(!mod){
+ mod = TRUE;
+ filemark(f);
+ }
+ textconstrain(t, b.q0, b.q0, &tq0, &tq1);
+ up -= b.nr;
+ for(i=0; i<b.nr; i+=n){
+ n = b.nr - i;
+ if(n > RBUFSIZE)
+ n = RBUFSIZE;
+ bufread(log, up+i, buf, n);
+ textinsert(t, tq0+i, buf, n, TRUE);
+ }
+ if(t->q0 == b.q0 && t->q1 == b.q0)
+ t->q1 += b.nr;
+ break;
+
+/* case Filename:
+ f->seq = u.seq;
+ fileunsetname(f, epsilon);
+ f->mod = u.mod;
+ up -= u.n;
+ free(f->name);
+ if(u.n == 0)
+ f->name = nil;
+ else
+ f->name = runemalloc(u.n);
+ bufread(delta, up, f->name, u.n);
+ f->nname = u.n;
+ break;
+*/
+ }
+ bufdelete(log, up, log->nc);
+ }
+ fbuffree(buf);
+ elogterm(f);
+
+ /*
+ * Bad addresses will cause bufload to crash, so double check.
+ * If changes were out of order, we expect problems so don't complain further.
+ */
+ if(t->q0 > f->nc || t->q1 > f->nc || t->q0 > t->q1){
+ if(!warned)
+ warning(nil, "elogapply: can't happen %d %d %d\n", t->q0, t->q1, f->nc);
+ t->q1 = min(t->q1, f->nc);
+ t->q0 = min(t->q0, t->q1);
+ }
+
+ if(t->w)
+ t->w->owner = owner;
+}