shithub: vexed

Download patch

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;