ref: 951fd81dd334fa33718bf7699cf1c6faed289c48
parent: faa5d55642c8bbd26151561eef13b6525f786154
author: qwx <qwx@sciops.net>
date: Sun Jan 1 14:07:15 EST 2023
tentative undo implementation and fix cut/paste not snarfing
--- a/chunk.c
+++ b/chunk.c
@@ -12,22 +12,61 @@
};
static Chunk *norris;
+int
+Δfmt(Fmt *fmt)
+{
+ Dot *d;
+
+ d = va_arg(fmt->args, Dot*);
+ if(d == nil)
+ return fmtstrcpy(fmt, "[??:??:??]");
+ return fmtprint(fmt, "[from=%08zux cur=%08zux to=%08zux]",
+ d->from, d->pos, d->to);
+}
+
+int
+χfmt(Fmt *fmt)
+{
+ Chunk *c;
+
+ c = va_arg(fmt->args, Chunk*);
+ if(c == nil)
+ return fmtstrcpy(fmt, "[]");
+ return fmtprint(fmt, "0x%08p:%08zux::0x%08p:0x%08p", c, c->len, c->left, c->right);
+}
+
static void
printchunks(Chunk *r)
{
+ usize len;
Chunk *c;
- fprint(2, "chunklist dot %zux %zux %zux: ",
- dot.from.pos, dot.pos, dot.to.pos);
c = r;
+ len = 0;
do{
- fprint(2, "[%#p:%zd:←%#p→%#p] ", c, c->len, c->left, c->right);
+ fprint(2, "\t%χ\toff=%08zux\n", c, len);
assert(c->right->left == c);
+ len += c->len;
c = c->right;
}while(c != r);
fprint(2, "\n");
}
+void
+dprint(Chunk *c, char *fmt, ...)
+{
+ char s[256];
+ va_list arg;
+
+ if(!debug)
+ return;
+ va_start(arg, fmt);
+ vseprint(s, s+sizeof s, fmt, arg);
+ va_end(arg);
+ fprint(2, "%s", s);
+ printchunks(c == nil ? norris : c);
+}
+
static Chunk *
newchunk(Buf *b)
{
@@ -172,8 +211,8 @@
{
Chunk *left, *right;
- left = p2c(dot.from.pos, off);
- right = p2c(dot.to.pos, nil);
+ left = p2c(dot.from, off);
+ right = p2c(dot.to, nil);
if(left == right)
return left;
while(left->right != right)
@@ -182,15 +221,25 @@
}
#endif
+usize
+chunklen(Chunk *c)
+{
+ usize n;
+ Chunk *cp;
+
+ for(cp=c, n=cp->len, cp=cp->right; cp!=c; cp=cp->right)
+ n += cp->len;
+ return n;
+}
+
Chunk *
p2c(usize p, usize *off)
{
- int x;
Chunk *c;
- for(c=norris, x=0; p>=c->len; c=c->right){
- if(c == norris && ++x > 1){
- c = norris->left;
+ for(c=norris; p>=c->len; c=c->right){
+ if(c == norris->left){
+ assert(p == c->len);
break;
}
p -= c->len;
@@ -216,10 +265,11 @@
int n;
n = c2p(norris->left) + norris->left->len;
- if(dot.to.pos == totalsz || dot.to.pos > n)
- dot.to.pos = n;
- if(dot.pos < dot.from.pos || dot.pos > dot.to.pos)
- dot.pos = dot.from.pos;
+ if(dot.to == totalsz || dot.to > n)
+ dot.to = n;
+ if(dot.pos < dot.from || dot.pos > dot.to)
+ dot.pos = dot.from;
+ dprint(nil, "final %Δ\n", &dot);
totalsz = n;
}
@@ -231,7 +281,7 @@
Chunk *c, *pc;
Buf *b;
- ASSERT(dot.pos >= dot.from.pos && dot.pos < dot.to.pos);
+ ASSERT(dot.pos >= dot.from && dot.pos < dot.to);
for(pc=norris, n=pc->len, c=pc->right; c!=norris; pc=c, c=c->right){
b = c->b;
ASSERT(b != nil);
@@ -244,7 +294,7 @@
}
if(exact){
ASSERT(n <= totalsz);
- ASSERT(dot.to.pos <= totalsz);
+ ASSERT(dot.to <= totalsz);
}
}
#undef ASSERT
@@ -252,19 +302,34 @@
void
setdot(Dot *dot, Chunk *right)
{
- dot->from.pos = 0;
+ dot->from = 0;
if(right == nil)
- dot->to.pos = c2p(norris->left) + norris->left->len;
+ dot->to = c2p(norris->left) + norris->left->len;
else
- dot->to.pos = c2p(right);
+ dot->to = c2p(right);
}
+void
+fixroot(Chunk *rc, usize off)
+{
+ Chunk *c;
+
+ dprint(rc, "fixroot [%χ] %08zux\n", rc, off);
+ for(c=rc->left; off>0; off-=c->len, c=c->left){
+ if(off - c->len == 0)
+ break;
+ assert(off - c->len < off);
+ }
+ norris = c;
+}
+
Chunk *
splitchunk(Chunk *c, usize off)
{
Chunk *nc;
- if(off == 0)
+ dprint(nil, "splitchunk %Δ [%χ] off=%08zux\n", &dot, c, off);
+ if(off == 0 || c == norris->left && off == c->len)
return c;
assert(off <= c->len);
nc = clonechunk(c);
@@ -282,6 +347,7 @@
usize off;
Chunk *c;
+ dprint(nil, "splitrange from=%08zux to=%08zux\n", from, to);
c = p2c(from, &off);
if(off > 0){
splitchunk(c, off);
@@ -302,6 +368,7 @@
{
Chunk *c, *left, *right;
+ dprint(nil, "cutrange from=%08zux to=%08zux\n", from, to);
if(splitrange(from, to, &left, &right) < 0)
return nil;
c = left->left;
@@ -316,12 +383,15 @@
Chunk *
croprange(usize from, usize to, Chunk **latch)
{
- Chunk *left, *right;
+ Chunk *cut, *left, *right;
+ dprint(nil, "croprange from=%08zux to=%08zux\n", from, to);
if(splitrange(from, to, &left, &right) < 0)
return nil;
norris = left;
- *latch = right->right;
+ cut = right->right;
+ if(latch != nil)
+ *latch = cut;
unlink(right->right, left->left);
return left;
}
@@ -332,8 +402,10 @@
{
Chunk *left;
+ dprint(c, "inserton from=%08zux to=%08zux\n", from, to);
left = cutrange(from, to, latch);
linkchunk(left, c);
+ dprint(nil, "done\n");
return left;
}
@@ -343,6 +415,7 @@
usize off;
Chunk *left;
+ dprint(c, "insertat cur=%08zux\n", pos);
if(pos == 0){
left = norris->left;
norris = c;
@@ -350,6 +423,8 @@
left = p2c(pos, &off);
splitchunk(left, off);
}
+ if(off == 0)
+ left = left->left;
linkchunk(left, c);
return left;
}
@@ -366,7 +441,7 @@
return nil;
}
c = p2c(d->pos, &off);
- Δloop = d->to.pos - d->pos;
+ Δloop = d->to - d->pos;
Δbuf = c->len - off;
if(n < Δloop && n < Δbuf){
*sz = n;
@@ -373,7 +448,7 @@
d->pos += n;
}else if(Δloop <= Δbuf){
*sz = Δloop;
- d->pos = d->from.pos;
+ d->pos = d->from;
}else{
*sz = Δbuf;
d->pos += Δbuf;
--- a/cmd.c
+++ b/cmd.c
@@ -12,43 +12,94 @@
// FIXME: crazy idea, multisnarf with addressable elements; $n registers; fork pplay to display them → ?
enum{
- Nhold = 64,
+ OPins,
+ OPdel,
+ OPcrop,
+
+ Nops = 128,
};
-static Chunk *hold[Nhold], *snarf;
static int epfd[2];
+typedef struct Op Op;
+struct Op{
+ int type;
+ usize from;
+ usize to;
+ Chunk *c;
+};
+static int ohead, otail;
+static Chunk *hold;
+static Op ops[Nops];
+
void
setrange(usize from, usize to)
{
assert((from & 3) == 0);
assert((to & 3) == 0);
- dot.from.pos = from;
- dot.to.pos = to;
+ dot.from = from;
+ dot.to = to;
if(dot.pos < from || dot.pos >= to)
dot.pos = from;
}
int
-setpos(usize off)
+jump(usize off)
{
- assert((off & 3) == 0);
- setrange(0, totalsz);
- assert(off >= dot.from.pos && off < dot.to.pos);
+ if(off < dot.from || off > dot.to){
+ werrstr("cannot jump outside of loop bounds\n");
+ return -1;
+ }
dot.pos = off;
return 0;
}
+// FIXME: needs a different way of managing ops
int
-jump(usize off)
+unpop(char *)
{
- if(off < dot.from.pos || off > dot.to.pos){
- werrstr("cannot jump outside of loop bounds\n");
- return -1;
- }
- dot.pos = off;
return 0;
}
+int
+popop(char *) // FIXME: u[n]
+{
+ Op *op;
+
+ if(otail == ohead)
+ return 0;
+ ohead = ohead - 1 & nelem(ops) - 1;
+ op = ops + ohead;
+ dprint(op->c, "cmd/pop dot=%Δ type=%d from=%08zux to=%08zux c=%#p\n",
+ &dot, op->type, op->from, op->to, op->c);
+ switch(op->type){
+ case OPdel:
+ if(insertat(op->from, op->c) == nil)
+ return -1;
+ break;
+ case OPins:
+ if(cutrange(op->from, op->to, nil) == nil)
+ return -1;
+ break;
+ case OPcrop:
+ if(insertat(op->to - op->from, op->c) == nil)
+ return -1;
+ dprint(nil, "uncropped with loose root\n");
+ fixroot(op->c, op->from + (op->to - op->from));
+ break;
+ default: werrstr("phase error: unknown op %d\n", op->type); return -1;
+ }
+ memset(ops+ohead, 0, sizeof *ops);
+ return 1;
+}
+
+void
+pushop(int type, usize from, usize to, Chunk *c)
+{
+ freechain(ops[ohead].c);
+ ops[ohead] = (Op){type, from, to, c};
+ ohead = ohead + 1 & nelem(ops) - 1;
+}
+
static int
replace(char *, Chunk *c)
{
@@ -58,10 +109,12 @@
fprint(2, "replace: nothing to paste\n");
return -1;
}
- if((left = inserton(dot.from.pos, dot.to.pos, c, &latch)) == nil){
+ if((left = inserton(dot.from, dot.to, c, &latch)) == nil){
fprint(2, "insert: %r\n");
return -1;
}
+ pushop(OPdel, dot.from, dot.to, latch);
+ pushop(OPins, dot.from, dot.to, nil);
setdot(&dot, nil);
dot.pos = c2p(left->right);
return 1;
@@ -76,6 +129,9 @@
fprint(2, "insert: nothing to paste\n");
return -1;
}
+ dprint(nil, "cmd/insert %Δ\n", &dot);
+ dprint(c, "buffered\n");
+ pushop(OPins, dot.pos, dot.pos+chunklen(c)-1, nil);
if((left = insertat(dot.pos, c)) == nil){
fprint(2, "insert: %r\n");
return -1;
@@ -82,6 +138,7 @@
}
setdot(&dot, nil);
dot.pos = c2p(left->right);
+ dprint(nil, "end\n");
return 1;
}
@@ -88,26 +145,34 @@
static int
paste(char *s, Chunk *c)
{
- if(c == nil && (c = snarf) == nil){
+ if(c == nil && (c = hold) == nil){
werrstr("paste: no buffer");
return -1;
}
c = replicate(c, c->left);
- if(dot.from.pos == 0 && dot.to.pos == totalsz)
+ if(dot.from == 0 && dot.to == totalsz)
return insert(s, c);
else
return replace(s, c);
}
+static void
+snarf(Chunk *c)
+{
+ dprint(hold, "snarf was:\n");
+ freechain(hold);
+ hold = c;
+ dprint(hold, "snarf now:\n");
+}
+
static int
copy(char *)
{
- Chunk *c, *left, *right;
+ Chunk *left, *right;
- splitrange(dot.from.pos, dot.to.pos, &left, &right);
- c = replicate(left, right);
- freechain(snarf);
- snarf = c;
+ dprint(hold, "cmd/copy %Δ\n", &dot);
+ splitrange(dot.from, dot.to, &left, &right);
+ snarf(replicate(left, right));
return 0;
}
@@ -116,11 +181,15 @@
{
Chunk *latch;
- if(dot.from.pos == 0 && dot.to.pos == totalsz){
+ if(dot.from == 0 && dot.to == totalsz){
werrstr("cut: no range selected");
return -1;
}
- cutrange(dot.from.pos, dot.to.pos, &latch);
+ dprint(nil, "cmd/cut %Δ\n", &dot);
+ cutrange(dot.from, dot.to, &latch);
+ dprint(latch, "latched\n");
+ snarf(replicate(latch, latch->left));
+ pushop(OPdel, dot.from, dot.from+chunklen(latch)-1, latch);
setdot(&dot, nil);
return 1;
}
@@ -130,8 +199,11 @@
{
Chunk *latch;
- if(croprange(dot.from.pos, dot.to.pos, &latch) == nil)
+ dprint(nil, "cmd/crop %Δ\n", &dot);
+ if(croprange(dot.from, dot.to, &latch) == nil)
return -1;
+ dprint(latch, "latched\n");
+ pushop(OPcrop, dot.from, dot.to, latch);
setdot(&dot, nil);
dot.pos = 0;
return 1;
@@ -165,8 +237,8 @@
usize n, m, c, k;
Dot d;
- d.pos = d.from.pos = dot.from.pos;
- d.to.pos = dot.to.pos;
+ d.pos = d.from = dot.from;
+ d.to = dot.to;
if((nio = iounit(fd)) == 0)
nio = 8192;
if(bufsz < nio){
@@ -173,7 +245,7 @@
buf = erealloc(buf, nio, bufsz);
bufsz = nio;
}
- for(m=d.to.pos-d.from.pos, c=0; m>0;){
+ for(m=d.to-d.from, c=0; m>0;){
k = nio < m ? nio : m;
if(getbuf(d, k, buf, bufsz) < 0){
fprint(2, "writebuf: couldn\'t snarf: %r\n");
@@ -243,8 +315,9 @@
threadexits("failed reading from pipe: %r");
close(fd);
dot = d;
+ pushop(OPins, dot.from, dot.from+chunklen(c)-1, nil);
paste(nil, c);
- dot.pos = dot.from.pos;
+ dot.pos = dot.from;
setdot(&dot, nil);
recalcsize();
redraw(0);
@@ -312,7 +385,7 @@
{
int r, fd;
- if(dot.to.pos - dot.from.pos == 0){
+ if(dot.to - dot.from == 0){
werrstr("writeto: dot isn't a range");
return -1;
}
@@ -356,6 +429,8 @@
case 'p': x = paste(s, nil); break;
case 'q': threadexitsall(nil);
case 'r': x = readfrom(s); break;
+// case 'U': x = unpop(s); break;
+ case 'u': x = popop(s); break;
case 'w': x = writeto(s); break;
case 'x': x = crop(s); break;
default: werrstr("unknown command %C", r); x = -1; break;
--- a/dat.h
+++ b/dat.h
@@ -1,5 +1,4 @@
typedef struct Chunk Chunk;
-typedef struct Pos Pos;
typedef struct Dot Dot;
typedef struct Buf Buf;
@@ -19,13 +18,10 @@
Chunk *left;
Chunk *right;
};
-struct Pos{
- usize pos; /* bytes */
-};
extern struct Dot{
- Pos;
- Pos from;
- Pos to;
+ usize pos;
+ usize from;
+ usize to;
};
extern Dot dot;
extern usize totalsz;
--- a/draw.c
+++ b/draw.c
@@ -79,9 +79,9 @@
lockdisplay(display);
draw(viewbg, viewbg->r, col[Cbg], nil, ZP);
unlockdisplay(display);
- d.from.pos = 0;
+ d.from = 0;
d.pos = views;
- d.to.pos = totalsz;
+ d.to = totalsz;
m = viewe - views;
x = 0;
qlock(&lsync);
@@ -157,16 +157,16 @@
b2t(dot.pos, &th, &tm, &ts, &tμ);
p = seprint(s, s+sizeof s, "T %zd @ %02d:%02d:%02d.%03d (%zd) ⋅ ",
T/4, th, tm, ts, tμ, dot.pos/4);
- if(dot.from.pos > 0){
- b2t(dot.from.pos, &th, &tm, &ts, &tμ);
+ if(dot.from > 0){
+ b2t(dot.from, &th, &tm, &ts, &tμ);
p = seprint(p, s+sizeof s, "%02d:%02d:%02d.%03d (%zd) ↺ ",
- th, tm, ts, tμ, dot.from.pos/4);
+ th, tm, ts, tμ, dot.from/4);
}else
p = seprint(p, s+sizeof s, "0 ↺ ");
- if(dot.to.pos != totalsz){
- b2t(dot.to.pos, &th, &tm, &ts, &tμ);
+ if(dot.to != totalsz){
+ b2t(dot.to, &th, &tm, &ts, &tμ);
seprint(p, s+sizeof s, "%02d:%02d:%02d.%03d (%zd)",
- th, tm, ts, tμ, dot.to.pos/4);
+ th, tm, ts, tμ, dot.to/4);
}else
seprint(p, s+sizeof s, "∞");
string(screen, statp, col[Cline], ZP, font, s);
@@ -179,7 +179,7 @@
usize left, right;
Rectangle r;
- left = dot.from.pos;
+ left = dot.from;
draw(view, view->r, viewbg, nil, ZP);
if(left != 0 && left >= views){
x = (left - views) / T;
@@ -188,7 +188,7 @@
r.max.x = r.min.x + 1;
draw(view, r, col[Cloop], nil, ZP);
}
- right = dot.to.pos;
+ right = dot.to;
if(right != totalsz){
x = (right - views) / T;
r = view->r;
@@ -285,9 +285,9 @@
if(off < 0 || off > totalsz)
return;
if(off < dot.pos)
- setrange(off, dot.to.pos);
+ setrange(off, dot.to);
else
- setrange(dot.from.pos, off);
+ setrange(dot.from, off);
update();
}
@@ -294,7 +294,7 @@
static void
setcur(usize off)
{
- if(off < dot.from.pos || off > dot.to.pos - Outsz)
+ if(off < dot.from || off > dot.to - Outsz)
return;
jump(off);
update();
--- a/fns.h
+++ b/fns.h
@@ -1,4 +1,7 @@
+void fixroot(Chunk*, usize);
+void dprint(Chunk*, char*, ...);
void freechain(Chunk*);
+usize chunklen(Chunk*);
void recalcsize(void);
void paranoia(int);
void setdot(Dot*, Chunk*);
@@ -30,7 +33,13 @@
uchar* getslice(Dot*, usize, usize*);
vlong getbuf(Dot, usize, uchar*, usize);
int loadin(int);
+int χfmt(Fmt*);
+int Δfmt(Fmt*);
void* emalloc(usize);
void* erealloc(void*, usize, usize);
char* estrdup(char*);
int setpri(int);
+
+#pragma varargck argpos dprint 2
+#pragma varargck type "χ" Chunk*
+#pragma varargck type "Δ" Dot*
--- a/pplay.c
+++ b/pplay.c
@@ -98,6 +98,8 @@
}ARGEND
if((fd = *argv != nil ? open(*argv, OREAD) : 0) < 0)
sysfatal("open: %r");
+ fmtinstall(L'Δ', Δfmt);
+ fmtinstall(L'χ', χfmt);
if(loadin(fd) < 0)
sysfatal("inittrack: %r");
close(fd);
@@ -141,11 +143,11 @@
switch(r){
case Kdel:
case 'q': threadexitsall(nil);
- case 'D': debugdraw ^= 1; break;
+ case 'D': debug ^= 1; debugdraw ^= 1; break;
case ' ': toggleplay(); break;
- case 'b': setjump(dot.from.pos); break;
+ case 'b': setjump(dot.from); break;
case Kesc: setrange(0, totalsz); update(); break;
- case '\n': zoominto(dot.from.pos, dot.to.pos); break;
+ case '\n': zoominto(dot.from, dot.to); break;
case 'z': zoominto(0, totalsz); break;
case '-': setzoom(-1, 0); break;
case '=': setzoom(1, 0); break;