ref: cd3d99b944f1be8b5eae83d9347557f768fed40a
parent: 4c06284d5e66f17c06ec132607c0237ee26ce235
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Sun Feb 23 09:33:35 EST 2020
plan9: undo/redo
--- a/orca.man
+++ b/orca.man
@@ -16,6 +16,8 @@
] [
.I -c cursor
] [
+.I -l undo_limit
+] [
.I file
]
.SH DESCRIPTION
@@ -61,6 +63,9 @@
.TP
.B -c
Sets the cursor character.
+.TP
+.B -l
+Sets the maximum number of undo steps. Default is 100.
.PP
.SH KEYS
.TP
@@ -98,6 +103,12 @@
.TP
.B Ctrl+v
Paste from the snarf buffer.
+.TP
+.B Ctrl+z
+Undo.
+.TP
+.B Ctrl+y
+Redo.
.TP
.B Ctrl+u Ctrl+l
Convert selected text to upper/lower case.
--- a/plan9.c
+++ b/plan9.c
@@ -55,6 +55,16 @@
Mark_flag_selected = 1<<7,
};
+typedef struct Snap Snap;
+
+struct Snap {
+ int bpm, apm;
+ vlong tick;
+ Point cur, scroll;
+ Rectangle sel;
+ Field field;
+};
+
static int bpm = 120, apm = 120;
static Point rulers = {8, 8};
static int rulerstyle = Sfancy, dotstyle = Sfancy;
@@ -80,6 +90,10 @@
static long framedev; /* frame deviation in ms */
static char *shellcmd;
+static Snap *snaps;
+static int numsnaps, nextsnap;
+static int maxsnaps = 100;
+
static struct {
struct {
char *menu;
@@ -138,6 +152,73 @@
.item = menu3i,
};
+static void
+snapshot(void)
+{
+ Snap *s;
+ int i;
+
+ if (maxsnaps < 1)
+ return;
+
+ if (nextsnap >= maxsnaps) {
+ nextsnap--;
+ field_deinit(&snaps->field);
+ memmove(snaps, snaps+1, sizeof(Snap)*nextsnap);
+ }
+ s = &snaps[nextsnap++];
+ s->bpm = bpm;
+ s->apm = apm;
+ s->tick = tick;
+ s->cur = cur;
+ s->scroll = scroll;
+ s->sel = sel;
+ for (i = nextsnap; i < numsnaps; i++)
+ field_deinit(&snaps[i].field);
+ field_init(&s->field);
+ field_copy(&field, &s->field);
+ numsnaps = nextsnap;
+}
+
+static void
+gotosnapshot(int i)
+{
+ Snap *s;
+
+ s = &snaps[i];
+ bpm = s->bpm;
+ apm = s->apm;
+ tick = s->tick;
+ cur = s->cur;
+ scroll = s->scroll;
+ sel = s->sel;
+ field_copy(&s->field, &field);
+}
+
+static void
+undo(void)
+{
+ if (nextsnap < 1)
+ return;
+
+ if (nextsnap == numsnaps) {
+ snapshot();
+ nextsnap--;
+ }
+ gotosnapshot(--nextsnap);
+}
+
+static void
+redo(void)
+{
+ if (nextsnap >= numsnaps-1)
+ return;
+
+ gotosnapshot(++nextsnap);
+ if (nextsnap == numsnaps)
+ nextsnap--;
+}
+
Usz
orca_round_up_power2(Usz x)
{
@@ -888,6 +969,7 @@
if ((n = read(0, buf, sizeof(buf)-1)) <= 0)
break;
+ snapshot();
for (i = 0; i < n; i++) {
if (buf[i] == '\r' || buf[i] == '\n' || buf[i] == 0) {
buf[i] = 0;
@@ -1012,7 +1094,7 @@
static void
usage(void)
{
- print("usage: %s [-p] [-b bpm] [-c cursor] [-s WxH] [-r random_seed] [-i ip_address] [-u udp_port] [-m midi_path] [file]\n", argv0);
+ print("usage: %s [-p] [-b bpm] [-c cursor] [-l undo_limit] [-s WxH] [-r random_seed] [-i ip_address] [-u udp_port] [-m midi_path] [file]\n", argv0);
threadexitsall("usage");
}
@@ -1085,6 +1167,12 @@
free(midipath);
midipath = EARGF(usage());
break;
+ case 'l':
+ if ((maxsnaps = atoi(EARGF(usage()))) < 0) {
+ fprint(2, "invalid undo limit %s\n", EARGF(usage()));
+ threadexitsall("args");
+ }
+ break;
default:
usage();
}ARGEND
@@ -1157,6 +1245,8 @@
netdial(nil, nil);
midiopen(nil);
+ snaps = calloc(maxsnaps, sizeof(Snap));
+
for (;;) {
redraw(complete);
complete = false;
@@ -1241,8 +1331,10 @@
if (shiftdown || mode == Mselect)
selext(0, -move.y);
else {
- if (altdown || mode == Mslide)
+ if (altdown || mode == Mslide) {
+ snapshot();
selmove(0, -move.y);
+ }
curmove(0, -move.y);
}
break;
@@ -1250,8 +1342,10 @@
if (shiftdown || mode == Mselect)
selext(0, move.y);
else {
- if (altdown || mode == Mslide)
+ if (altdown || mode == Mslide) {
+ snapshot();
selmove(0, move.y);
+ }
curmove(0, move.y);
}
break;
@@ -1259,20 +1353,21 @@
if (shiftdown || mode == Mselect) {
selext(-move.x, 0);
} else {
- if (altdown || mode == Mslide)
+ if (altdown || mode == Mslide) {
+ snapshot();
selmove(-move.x, 0);
+ }
curmove(-move.x, 0);
}
break;
- case 0x0c: /* C-l */
- selmap(tolower);
- break;
case Kright:
if (shiftdown || mode == Mselect)
selext(move.x, 0);
else {
- if (altdown || mode == Mslide)
+ if (altdown || mode == Mslide) {
+ snapshot();
selmove(move.x, 0);
+ }
curmove(move.x, 0);
}
break;
@@ -1312,6 +1407,7 @@
snprint(filename, sizeof(filename), "%s", tmp);
break;
case 0x18: /* C-x */
+ snapshot();
selcopy();
selset('.');
break;
@@ -1319,6 +1415,7 @@
selcopy();
break;
case 0x16: /* C-v */
+ snapshot();
selpaste();
break;
case '[':
@@ -1338,15 +1435,19 @@
complete = true;
break;
case '(':
+ snapshot();
w = snaplow(w, rulers.x);
break;
case ')':
+ snapshot();
w = snaphigh(w, rulers.x);
break;
case '_':
+ snapshot();
h = snaplow(h, rulers.y);
break;
case '+':
+ snapshot();
h = snaphigh(h, rulers.y);
break;
case '>':
@@ -1361,6 +1462,7 @@
break;
case 0x0b: /* C-k */
tmp[0] = 0;
+ snapshot();
if (enter("command:", tmp, sizeof(tmp), nil, &kctl, nil) > 0)
command(tmp);
break;
@@ -1375,6 +1477,16 @@
case Kack: /* C-f */
forward = true;
break;
+ case 0x1a: /* C-z */
+ undo();
+ w = field.width;
+ h = field.height;
+ break;
+ case 0x19:
+ redo();
+ w = field.width;
+ h = field.height;
+ break;
case '`':
case '~':
case L'´':
@@ -1384,9 +1496,15 @@
mode = mode != Mselect ? Mselect : Minsert;
break;
case Knack: /* C-u */
+ snapshot();
selmap(toupper);
break;
+ case 0x0c: /* C-l */
+ snapshot();
+ selmap(tolower);
+ break;
case Kbs: /* C-h */
+ snapshot();
if (mode != Mappend) {
selset('.');
} else {
@@ -1403,6 +1521,7 @@
if (r == Kdel || r == ' ')
r = '.';
if (orca_is_valid_glyph(r)) {
+ snapshot();
if (mode != Mappend) {
selset(r);
} else {