#include #include #include #include #include #include "buf.h" struct undo_line { undo_type_t type; int y; struct line *line; TAILQ_ENTRY(undo_line) list; }; struct undo_atom { TAILQ_HEAD(undo_lines_head, undo_line) lines; TAILQ_ENTRY(undo_atom) list; }; struct undos { TAILQ_HEAD(undo_atoms_head, undo_atom) atoms; }; static struct undos undos, redos; static struct undo_atom *curr_atom; static int mode; void undo_init(void) { TAILQ_INIT(&undos.atoms); TAILQ_INIT(&redos.atoms); } void undo_start(struct cmd *c) { struct undo_atom *ua; if (c->key == 'u') return; ua = malloc(sizeof(struct undo_atom)); if (ua == NULL) abort(); TAILQ_INIT(&ua->lines); curr_atom = ua; } void undo_add_line(struct line *l, int y, undo_type_t type) { struct undo_line *ul; struct line *_l; TAILQ_FOREACH(ul, &curr_atom->lines, list) { if (ul->y == y) { /* * We already have the original line, so don't * overwrite it. */ if ((type == UND_MOD) && (ul->type == UND_MOD)) return; /* * XXX we might need to do something when * type == UND_DEL and ul->type == UND_MOD * (trying to delete a line that was previously * modified). */ } } ul = malloc(sizeof(struct undo_line)); if (ul == NULL) abort(); _l = line_dup(l); if (_l == NULL) abort(); ul->line = _l; ul->y = y; ul->type = type; TAILQ_INSERT_TAIL(&curr_atom->lines, ul, list); } static char * undo_type_s(undo_type_t t) { switch (t) { case UND_ADD: return ("ADD"); case UND_MOD: return ("MOD"); case UND_DEL: return ("DEL"); default: return ("UNKNOWN"); } } static void undo_print_undos(struct undos *u) { struct undo_atom *ua; struct undo_line *ul; int i, j; i = 0; TAILQ_FOREACH(ua, &u->atoms, list) { fprintf(stderr, "%d:\n", i); TAILQ_FOREACH(ul, &ua->lines, list) { fprintf(stderr, " %s %d: ", undo_type_s(ul->type), ul->y); for (j = 0; j < ul->line->len; j++) fputc(ul->line->line[j], stderr); fputc('\n', stderr); } i++; } } void undo_finish(struct cmd *c) { struct undo_atom *t, *ua; struct undo_line *tl, *ul; if (!(c->flags & CMD_SUCCESS)) return; if (c->key != 'u') { mode = ~0; TAILQ_INSERT_HEAD(&undos.atoms, curr_atom, list); TAILQ_FOREACH_SAFE(ua, &redos.atoms, list, t) { TAILQ_REMOVE(&redos.atoms, ua, list); TAILQ_FOREACH_SAFE(ul, &ua->lines, list, tl) { TAILQ_REMOVE(&ua->lines, ul, list); line_free(ul->line); free(ul); } free(ua); } } if (debug) { fprintf(stderr, "UNDOS:\n"); undo_print_undos(&undos); fprintf(stderr, "REDOS:\n"); undo_print_undos(&redos); fputc('\n', stderr); } } int cmd_undo(struct cmd *c, struct buf *b) { TAILQ_HEAD(undo_lines_head, undo_line) lines; struct undo_atom *ua; struct undo_line *ul, *tmp; struct undos *u, *p; struct line *l, *l2, *l3; int i, y; char *t; if (!(c->flags & CMD_DOT)) mode = ~mode; if (mode == 0) { u = &undos; p = &redos; } else { u = &redos; p = &undos; } ua = TAILQ_FIRST(&u->atoms); if (ua == NULL) return (1); TAILQ_REMOVE(&u->atoms, ua, list); TAILQ_INIT(&lines); i = 0; y = b->num_lines; TAILQ_FOREACH_REVERSE_SAFE(ul, &ua->lines, undo_lines_head, list, tmp) { i++; if (ul->y < y) y = ul->y; TAILQ_REMOVE(&ua->lines, ul, list); switch (ul->type) { case UND_ADD: l = buf_get_line(b, ul->y); if (l == NULL) abort(); line_remove(b, l); /* Flip type for redo */ ul->type = UND_DEL; line_free(ul->line); ul->line = l; break; case UND_MOD: l = buf_get_line(b, ul->y); if (l == NULL) abort(); t = malloc(ul->line->len); if (t == NULL) abort(); memcpy(t, ul->line->line, ul->line->len); l3 = line_dup(l); if (l3 == NULL) abort(); line_replace(l, t, ul->line->len); line_free(ul->line); ul->line = l3; break; case UND_DEL: l2 = line_dup(ul->line); if (l2 == NULL) abort(); line_add(b, l2, ul->y); /* Flip type for redo */ ul->type = UND_ADD; } TAILQ_INSERT_HEAD(&lines, ul, list); } /* Reverse the lines in the atom for redo */ TAILQ_FOREACH_SAFE(ul, &lines, list, tmp) { TAILQ_REMOVE(&lines, ul, list); TAILQ_INSERT_HEAD(&ua->lines, ul, list); } if (i > 5) printd("%d lines undone\n", i); TAILQ_INSERT_HEAD(&p->atoms, ua, list); c->new_pos.y = y; c->new_pos.x = 0; /* XXX Remember x if we do single line */ return (0); }