add undo support for box, visual, cut/paste - gramscii - A simple editor for AS… | |
Log | |
Files | |
Refs | |
Tags | |
README | |
LICENSE | |
--- | |
commit f3100ddd486d6f39b0c97c9c492bb6020bf3caf1 | |
parent 526ce3a130732d4a2374a6e36a689d9e0cf5cc34 | |
Author: KatolaZ <[email protected]> | |
Date: Wed, 31 Jul 2019 11:19:55 +0100 | |
add undo support for box, visual, cut/paste | |
Diffstat: | |
M TODO | 4 ++++ | |
M draw.c | 32 +++++++++++++++++------------… | |
M gramscii.1 | 24 ++++++++++++++++++++++-- | |
M gramscii.h | 4 ++-- | |
M lineset.c | 38 +++++++++++++++++------------… | |
M screen.c | 4 ++-- | |
6 files changed, 69 insertions(+), 37 deletions(-) | |
--- | |
diff --git a/TODO b/TODO | |
@@ -1,5 +1,9 @@ | |
+ optimize redraws (redraw only the modified rectangle) | |
+ undo (by storing lines changed across insert/remove operations) | |
+ * re-organise undo-ring management | |
+ - add undo for arrow mode | |
+ - add undo for text mode | |
+ - add undo for erase mode | |
- fix bug with 'g' commands in arrow mode | |
- add screen geometry option (-g 25x80?) | |
- read file at point | |
diff --git a/draw.c b/draw.c | |
@@ -112,7 +112,7 @@ void draw_box(int x1, int y1, int fix){ | |
if (fix == FIX){ | |
f = set_xy; | |
- copy_lines_to_ring(ymin, ymax, CUR); | |
+ copy_lines_to_ring(ymin, ymax, PRV_STATE); | |
} | |
else | |
f = draw_xy; | |
@@ -130,7 +130,7 @@ void draw_box(int x1, int y1, int fix){ | |
f(xmax, ymin, corner); | |
f(xmax, ymax, corner); | |
if (fix == FIX) | |
- copy_lines_to_ring(ymin, ymax, LST); | |
+ copy_lines_to_ring(ymin, ymax, NEW_STATE); | |
show_cursor(); | |
} | |
@@ -316,10 +316,10 @@ void visual_box(FILE *fc){ | |
case 'x':/* erase */ | |
if (c == 'x') | |
yank_region(MIN(orig_x,x), MIN(orig_y,… | |
- copy_lines_to_ring(MIN(orig_y, y), MAX(orig_y,… | |
+ copy_lines_to_ring(MIN(orig_y, y), MAX(orig_y,… | |
erase_box(orig_x, orig_y, f); | |
erase_blank_lines(MIN(y,orig_y), MAX(y, orig_y… | |
- copy_lines_to_ring(MIN(orig_y, y), MAX(orig_y,… | |
+ copy_lines_to_ring(MIN(orig_y, y), MAX(orig_y,… | |
modified = 1; | |
goto vis_exit; | |
@@ -345,9 +345,9 @@ void paste(){ | |
int y2; | |
y2 = y + cutbuf.num - 1; | |
- copy_lines_to_ring(y, y2, CUR); | |
+ copy_lines_to_ring(y, y2, PRV_STATE); | |
paste_region(x, y); | |
- copy_lines_to_ring(y, y2, LST); | |
+ copy_lines_to_ring(y, y2, NEW_STATE); | |
redraw(); | |
} | |
@@ -356,28 +356,32 @@ void put_lines(lineset_t *u){ | |
for (i=0; i< u->num; i++){ | |
n = u->l[i].n; | |
- ensure_line_length(&(screen.l[i]), u->l[i].lst); | |
+ ensure_line_length(&(screen.l[i]), strlen(u->l[i].s)); | |
strcpy(screen.l[n].s, u->l[i].s); | |
- screen.l[n].lst = u->l[i].lst; | |
+ screen.l[n].lst = strlen(u->l[i].s)-1; | |
} | |
} | |
void undo_change(){ | |
if (undo_cur >= 0){ | |
+ if (undo_cur > undo_lst) | |
+ undo_cur = undo_lst; | |
put_lines(& (undo[undo_cur])); | |
- undo_cur --; | |
+ undo_cur -= 2; | |
+ modified = 1; | |
} | |
redraw(); | |
- modified = 1; | |
} | |
void redo_change(){ | |
- if (undo_cur < undo_lst){ | |
- undo_cur ++; | |
- put_lines(& (undo[undo_cur])); | |
+ if (undo_cur <= undo_lst-2){ | |
+ if (undo_cur > 0) | |
+ put_lines(& (undo[undo_cur+1])); | |
+ undo_cur +=2; | |
+ put_lines(& (undo[undo_cur+1])); | |
+ modified = 1; | |
} | |
redraw(); | |
- modified = 1; | |
} | |
diff --git a/gramscii.1 b/gramscii.1 | |
@@ -63,6 +63,26 @@ buffer contains the rectangle yanked/cut in | |
.B visual | |
mode. | |
.TP 5m | |
+.BI u | |
+Undo the last change. gramscii supports an undo history of indefinite | |
+length. The command | |
+.BI u | |
+gets the last change from the history, and moves the history pointer | |
+back by one change. See the related command | |
+.BI U | |
+below. | |
+.TP 5m | |
+.BI U | |
+Redo, i.e., cancel a previous | |
+.BI u | |
+command. gramscii supports an undo history of indefinite length. The | |
+command | |
+.BI U | |
+moves to the following change, if possible. For instance, the sequence | |
+.BI uuU | |
+will go back two changes, and then forward one, effectively resetting | |
+the state of the screen to what it was before the last change occurred. | |
+.TP 5m | |
.BI q | |
Quit gramscii, and prompt for a filename if the current screen contains | |
unsaved changes. | |
@@ -516,8 +536,8 @@ support scrolling and "virtual" screens of any (reasonable)… | |
.PP | |
gramscii currently does | |
.B not | |
-provide an "undo" command. This requires a restructuring of buffer | |
-management, and will most probably be implemented in a future release. | |
+support "undo" commands for arrow, text, and erase mode. This will be | |
+added soon. | |
.SH AUTHORS | |
gramscii is written and maintained by Vincenzo "KatolaZ" Nicosia | |
<[email protected]>. You can use, copy, modify, and redistribute | |
diff --git a/gramscii.h b/gramscii.h | |
@@ -44,8 +44,8 @@ | |
#define VIDEO_NRM 0 | |
#define VIDEO_REV 7 | |
-#define CUR 0x01 | |
-#define LST 0x02 | |
+#define PRV_STATE 0x01 | |
+#define NEW_STATE 0x02 | |
/** types **/ | |
diff --git a/lineset.c b/lineset.c | |
@@ -127,19 +127,24 @@ void paste_region(int x1, int y1){ | |
} | |
void copy_lines_to_ring(int y1, int y2, int which){ | |
- lineset_t *tmp; | |
- int i, len, *idx; | |
+ int i, len, idx; | |
+ int offset; | |
+ lineset_t *tmp; | |
if (y1 > y2){ | |
y1 ^= y2; | |
y2 ^= y1; | |
y1 ^= y2; | |
} | |
- if (which == CUR) | |
- idx = &undo_cur; | |
+ if (undo_cur > undo_lst) | |
+ undo_cur = undo_lst; | |
+ if (which == PRV_STATE){ /* adding a new previous state */ | |
+ undo_cur += 2; | |
+ idx = undo_cur; | |
+ } | |
else | |
- idx = &undo_lst; | |
- if (*idx == undo_sz - 1){ | |
+ idx = undo_cur + 1; | |
+ if (idx >= undo_sz - 1){ | |
undo_sz += 10; | |
tmp = realloc(undo, undo_sz * sizeof(lineset_t)); | |
if (tmp == NULL){ | |
@@ -148,22 +153,21 @@ void copy_lines_to_ring(int y1, int y2, int which){ | |
} | |
undo = tmp; | |
} | |
- (*idx) ++; | |
- ensure_num_lines(&(undo[*idx]), y2 - y1 + 1); | |
+ ensure_num_lines(&(undo[idx]), y2 - y1 + 1); | |
for(i=y1; i<=y2; i++){ | |
len = strlen(screen.l[i].s); | |
- ensure_line_length(&(undo[*idx].l[i-y1]), len); | |
- strcpy(undo[*idx].l[i-y1].s, screen.l[i].s); | |
- undo[*idx].l[i-y1].n = i; | |
- undo[*idx].l[i-y1].lst = screen.l[i].lst; | |
+ ensure_line_length(&(undo[idx].l[i-y1]), len); | |
+ strcpy(undo[idx].l[i-y1].s, screen.l[i].s); | |
+ undo[idx].l[i-y1].n = i; | |
+ undo[idx].l[i-y1].lst = screen.l[i].lst; | |
} | |
- undo[*idx].num = y2 - y1 + 1; | |
- if (which == CUR) | |
+ undo[idx].num = y2 - y1 + 1; | |
+ if (which == PRV_STATE) | |
undo_lst = undo_cur; | |
#ifdef DEBUG | |
- fprintf(stderr, "undo_ring: y1: %d y2: %d idx: %d\n", y1, y2, *idx); | |
- for(i=0; i<undo[undo_cur].num; i++){ | |
- fprintf(stderr, "UU: %d| %s\n", undo[*idx].l[i].n, undo[*idx].… | |
+ fprintf(stderr, "undo_ring: y1: %d y2: %d idx: %d\n", y1, y2, idx); | |
+ for(i=0; i<undo[idx].num; i++){ | |
+ fprintf(stderr, "UU: %d| %s\n", undo[idx].l[i].n, undo[idx].l[… | |
} | |
#endif | |
} | |
diff --git a/screen.c b/screen.c | |
@@ -410,8 +410,8 @@ void init_screen(){ | |
cutbuf.num = 0; | |
undo_sz = 0; | |
- undo_cur = -1; | |
- undo_lst = -1; | |
+ undo_cur = -2; | |
+ undo_lst = -2; | |
} | |
void find_nonblank_rect(int *x1, int *y1, int *x2, int *y2){ |