ref: 15fe0eb1c1c82b9a90fdb924ae04a3c3e2cc23ad
parent: 1d8e44d00b264710d842a35a06073d7963f0dd5f
author: phil9 <telephil9@gmail.com>
date: Wed Dec 29 06:10:26 EST 2021
implement undo and redo
--- a/README.md
+++ b/README.md
@@ -10,6 +10,8 @@
`i` inserts a byte before the current selection.
`p` inserts a byte after the current selection.
`x` deletes the currently selected byte.
+`u` undo last edit.
+`r` redo last undo'ed edit.
Del or q exit the program.
**THIS IS WORK IN PROGRESS. USE AT YOUR OWN RISK**
--- a/a.h
+++ b/a.h
@@ -1,3 +1,4 @@
+/* BUFFER */
typedef struct Buffer Buffer;
struct Buffer
@@ -12,6 +13,32 @@
int delete(Buffer*, int);
int insert(Buffer*, int);
int append(Buffer*, int);
+
+/* UNDO */
+typedef struct Undo Undo;
+
+enum
+{
+ Udelete,
+ Uinsert,
+ Uappend,
+ Uset,
+};
+
+struct Undo
+{
+ int action;
+ int index;
+ uchar value;
+ uchar newvalue;
+};
+
+int canundo(void);
+void undo(Undo*);
+int canredo(void);
+void redo(Undo*);
+void pushundo(int, int, uchar, uchar);
+void patchundo(uchar);
/* COLORS */
enum
--- a/mkfile
+++ b/mkfile
@@ -2,7 +2,7 @@
BIN=/$objtype/bin
TARG=vexed
-OFILES=vexed.$O buf.$O cols.$O
+OFILES=vexed.$O buf.$O undo.$O cols.$O
HFILES=a.h
</sys/src/cmd/mkone
--- /dev/null
+++ b/undo.c
@@ -1,0 +1,61 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "a.h"
+
+enum { Stacksize = 1024 };
+Undo ustack[Stacksize];
+int ucount = 0;
+int ucur = -1;
+
+int
+canundo(void)
+{
+ return ucur >= 0;
+}
+
+void
+undo(Undo *undo)
+{
+ undo->action = ustack[ucur].action;
+ undo->index = ustack[ucur].index;
+ undo->value = ustack[ucur].value;
+ undo->newvalue = ustack[ucur].newvalue;
+ ucur -= 1;
+}
+
+int
+canredo(void)
+{
+ return ucur < ucount - 1;
+}
+
+void
+redo(Undo *undo)
+{
+ ucur += 1;
+ undo->action = ustack[ucur].action;
+ undo->index = ustack[ucur].index;
+ undo->value = ustack[ucur].value;
+ undo->newvalue = ustack[ucur].newvalue;
+}
+
+void
+pushundo(int action, int index, uchar value, uchar newvalue)
+{
+ if(ucur == Stacksize - 1)
+ return;
+ ucur += 1;
+ ucount = ucur + 1;
+ ustack[ucur].action = action;
+ ustack[ucur].index = index;
+ ustack[ucur].value = value;
+ ustack[ucur].newvalue = newvalue;
+}
+
+void
+patchundo(uchar value)
+{
+ ustack[ucur].newvalue = value;
+}
+
--- a/vexed.c
+++ b/vexed.c
@@ -8,6 +8,7 @@
#include "a.h"
void redraw(void);
+void drawselchange(int);
enum
{
@@ -23,8 +24,8 @@
Scrollwidth = 12,
};
-enum { Mgoto, Mdelete, Minsert, Mappend };
-char *menu2str[] = { "go...", "delete", "insert", "append", 0 };
+enum { Mundo, Mredo, Mgoto, Mdelete, Minsert, Mappend };
+char *menu2str[] = { "undo", "redo", "go...", "delete", "insert", "append", 0 };
Menu menu2 = { menu2str };
enum { Msave, Mquit, };
@@ -47,6 +48,73 @@
int sel = 0;
void
+xundo(void)
+{
+ Undo u;
+
+ if(!canundo())
+ return;
+ undo(&u);
+ switch(u.action){
+ case Udelete:
+ if(insert(&buf, u.index) < 0)
+ sysfatal("insert: %r");
+ buf.data[u.index] = u.value;
+ break;
+ case Uinsert:
+ if(delete(&buf, u.index) < 0)
+ sysfatal("delete: %r");
+ break;
+ case Uappend:
+ if(delete(&buf, u.index + 1) < 0)
+ sysfatal("delete: %r");
+ break;
+ case Uset:
+ buf.data[u.index] = u.value;
+ break;
+ }
+ sel = u.index;
+ modified = 1;
+ blines = buf.count / 16;
+ redraw();
+}
+
+void
+xredo(void)
+{
+ Undo u;
+
+ if(!canredo())
+ return;
+ redo(&u);
+ switch(u.action){
+ case Udelete:
+ if(delete(&buf, u.index) < 0)
+ sysfatal("insert: %r");
+ sel = u.index;
+ break;
+ case Uinsert:
+ if(insert(&buf, u.index) < 0)
+ sysfatal("insert: %r");
+ sel = u.index;
+ break;
+ case Uappend:
+ if(insert(&buf, u.index + 1) < 0)
+ sysfatal("insert: %r");
+ sel = u.index + 1;
+ break;
+ case Uset:
+ buf.data[u.index] = u.newvalue;
+ break;
+ }
+ if(sel == buf.count)
+ --sel;
+ modified = 1;
+ blines = buf.count / 16;
+ redraw();
+}
+
+void
xgoto(void)
{
char b[16] = {0}, *endp;
@@ -66,6 +134,7 @@
void
xdelete(void)
{
+ pushundo(Udelete, sel, buf.data[sel], 0);
if(delete(&buf, sel) < 0)
sysfatal("delete: %r");
if(sel == buf.count)
@@ -78,6 +147,7 @@
void
xinsert(void)
{
+ pushundo(Uinsert, sel, 0, 0);
if(insert(&buf, sel) < 0)
sysfatal("insert: %r");
modified = 1;
@@ -88,6 +158,7 @@
void
xappend(void)
{
+ pushundo(Uappend, sel, 0, 0);
if(append(&buf, sel) < 0)
sysfatal("append: %r");
sel += 1;
@@ -256,6 +327,12 @@
n = menuhit(2, mctl, &menu2, nil);
switch(n){
+ case Mundo:
+ xundo();
+ break;
+ case Mredo:
+ xredo();
+ break;
case Mgoto:
xgoto();
break;
@@ -420,6 +497,14 @@
redraw();
}
break;
+ case 'u':
+ case 'U':
+ xundo();
+ break;
+ case 'r':
+ case 'R':
+ xredo();
+ break;
case 'g':
case 'G':
xgoto();
@@ -440,9 +525,11 @@
if(isxdigit(k)){
if(e - lastk < 2 && lastv > 0) {
buf.data[sel] = lastv*16 + hexval(k);
+ patchundo(buf.data[sel]);
lastv = -1;
}else{
lastv = hexval(k);
+ pushundo(Uset, sel, buf.data[sel], lastv);
buf.data[sel] = lastv;
}
modified = 1;