diff options
author | Vincent Labrecque <vincent@cvs.openbsd.org> | 2002-03-18 01:45:56 +0000 |
---|---|---|
committer | Vincent Labrecque <vincent@cvs.openbsd.org> | 2002-03-18 01:45:56 +0000 |
commit | d89e0da5d027bbdbf64baa6815eed0899d63850b (patch) | |
tree | d242a6b610ed6d31b3df6ffc249010b706f75929 | |
parent | e6811047b509bb20d245621bc272005683d9b812 (diff) |
Enter the new undo code. it is still disabled since it has bugs, but it's
somewhat more useful....
ok millert@ + no objections on ICB
-rw-r--r-- | usr.bin/mg/def.h | 31 | ||||
-rw-r--r-- | usr.bin/mg/line.c | 22 | ||||
-rw-r--r-- | usr.bin/mg/main.c | 9 | ||||
-rw-r--r-- | usr.bin/mg/undo.c | 403 |
4 files changed, 269 insertions, 196 deletions
diff --git a/usr.bin/mg/def.h b/usr.bin/mg/def.h index 6f4734237bf..59c79bf3273 100644 --- a/usr.bin/mg/def.h +++ b/usr.bin/mg/def.h @@ -1,4 +1,4 @@ -/* $OpenBSD: def.h,v 1.38 2002/03/16 20:29:21 vincent Exp $ */ +/* $OpenBSD: def.h,v 1.39 2002/03/18 01:45:54 vincent Exp $ */ #include <sys/queue.h> @@ -209,6 +209,17 @@ typedef struct MGWIN { struct undo_rec; /* + * This structure holds the starting position + * (as a line/offset pair) and the number of characters in a + * region of a buffer. This makes passing the specification + * of a region around a little bit easier. + */ +typedef struct { + struct LINE *r_linep; /* Origin LINE address. */ + int r_offset; /* Origin LINE offset. */ + RSIZE r_size; /* Length in characters. */ +} REGION; +/* * Text is kept in buffers. A buffer header, described * below, exists for every buffer in the system. The buffers are * kept in a big list, so that commands that search for a buffer by @@ -233,6 +244,9 @@ typedef struct BUFFER { char b_fname[NFILEN];/* File name */ struct fileinfo b_fi; /* File attributes */ LIST_HEAD(, undo_rec) b_undo; /* Undo actions list */ + REGION b_undopos; /* Where we were during the last + undo action */ + struct undo_rec *b_undoptr; } BUFFER; #define b_bufp b_list.l_p.x_bp @@ -248,18 +262,6 @@ typedef struct BUFFER { /* - * This structure holds the starting position - * (as a line/offset pair) and the number of characters in a - * region of a buffer. This makes passing the specification - * of a region around a little bit easier. - */ -typedef struct { - struct LINE *r_linep; /* Origin LINE address. */ - int r_offset; /* Origin LINE offset. */ - RSIZE r_size; /* Length in characters. */ -} REGION; - -/* * This structure holds information about recent actions for the Undo command. */ struct undo_rec { @@ -577,10 +579,9 @@ int cntnonmatchlines(int, int); /* undo.c X */ void free_undo_record(struct undo_rec *); -int undo_init(void); int undo_dump(void); int undo_enable(int); -int undo_add_custom(int, LINE *, int, void *, int); +int undo_add_custom(int, int, LINE *, int, void *, int); int undo_add_boundary(void); int undo_add_insert(LINE *, int, int); int undo_add_delete(LINE *, int, int); diff --git a/usr.bin/mg/line.c b/usr.bin/mg/line.c index 1207b01eb90..224d0ed8ac7 100644 --- a/usr.bin/mg/line.c +++ b/usr.bin/mg/line.c @@ -1,4 +1,4 @@ -/* $OpenBSD: line.c,v 1.15 2002/03/16 04:17:36 vincent Exp $ */ +/* $OpenBSD: line.c,v 1.16 2002/03/18 01:45:54 vincent Exp $ */ /* * Text line handling. @@ -201,8 +201,7 @@ linsert(int n, int c) if (wp->w_markp == lp1) wp->w_markp = lp2; } - if (!undoaction) - undo_add_insert(lp2, 0, n); + undo_add_insert(lp2, 0, n); curwp->w_doto = n; return TRUE; } @@ -232,8 +231,7 @@ linsert(int n, int c) wp->w_marko += n; } } - if (!undoaction) - undo_add_insert(curwp->w_dotp, doto, n); + undo_add_insert(curwp->w_dotp, doto, n); return TRUE; } @@ -255,11 +253,9 @@ lnewline(void) lchange(WFHARD); - if (!undoaction) { - /* XXX */ - undo_add_custom(INSERT, curwp->w_dotp, curwp->w_doto, - strdup("\n"), 1); - } + /* XXX */ + undo_add_custom(1,INSERT, curwp->w_dotp, curwp->w_doto, + strdup("\n"), 1); /* Get the address and offset of "." */ lp1 = curwp->w_dotp; @@ -328,9 +324,7 @@ ldelete(RSIZE n, int kflag) return FALSE; } - if (!undoaction) { - undo_add_delete(curwp->w_dotp, curwp->w_doto, n); - } + undo_add_delete(curwp->w_dotp, curwp->w_doto, n); /* * HACK - doesn't matter, and fixes back-over-nl bug for empty @@ -495,6 +489,8 @@ lreplace(RSIZE plen, char *st, int f) return FALSE; } + undo_add_change(curwp->w_dotp, curwp->w_doto, plen); + /* * Find the capitalization of the word that was found. f says use * exact case of replacement string (same thing that happens with diff --git a/usr.bin/mg/main.c b/usr.bin/mg/main.c index 9c2f39b218d..939e3345692 100644 --- a/usr.bin/mg/main.c +++ b/usr.bin/mg/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.18 2002/02/21 15:27:29 deraadt Exp $ */ +/* $OpenBSD: main.c,v 1.19 2002/03/18 01:45:55 vincent Exp $ */ /* * Mainline. @@ -24,9 +24,7 @@ char pat[NPAT]; /* pattern */ static void edinit(void); int -main(argc, argv) - int argc; - char **argv; +main(int argc, char **argv) { char *cp; @@ -38,8 +36,7 @@ main(argc, argv) maps_init(); /* Keymaps and modes. */ funmap_init(); /* Functions. */ ttykeymapinit(); /* Symbols, bindings. */ - undo_init(); - + /* * This is where we initialize standalone extensions that should * be loaded dynamically sometime in the future. diff --git a/usr.bin/mg/undo.c b/usr.bin/mg/undo.c index 9e98d8dae92..0ab57fb9676 100644 --- a/usr.bin/mg/undo.c +++ b/usr.bin/mg/undo.c @@ -1,4 +1,4 @@ -/* $OpenBSD: undo.c,v 1.8 2002/03/16 20:29:21 vincent Exp $ */ +/* $OpenBSD: undo.c,v 1.9 2002/03/18 01:45:55 vincent Exp $ */ /* * Copyright (c) 2002 Vincent Labrecque <vincent@openbsd.org> * All rights reserved. @@ -41,25 +41,31 @@ static int undo_free_num; * Global variables */ /* - * undo_disable_flag - - * - * Stop doing undo (useful when we know are - * going to deal with huge deletion/insertions - * that we don't plan to undo) + * undo_disable_flag: Stop doing undo (useful when we know are + * going to deal with huge deletion/insertions + * that we don't plan to undo) */ int undo_disable_flag; -int undoaction; /* Are we called indirectly from undo()? */ /* * Local functions */ -static int find_offset(LINE *, int); -static int find_linep(int, LINE **, int *); +static int find_absolute_dot(LINE *, int); +static int find_line_offset(int, LINE **, int *); static struct undo_rec *new_undo_record(void); static int drop_oldest_undo_record(void); +/* + * find_{absolute_dot,line_offset}() + * + * Find an absolute dot in the buffer from a line/offset pair, and vice-versa. + * + * Since lines can be deleted while they are referenced by undo record, we + * need to have an absolute dot to have something reliable. + */ + static int -find_offset(LINE *lp, int off) +find_absolute_dot(LINE *lp, int off) { int count = 0; LINE *p; @@ -68,7 +74,7 @@ find_offset(LINE *lp, int off) if (count != 0) { if (p == curwp->w_linep) { ewprintf("Error: Undo stuff called with a" - "nonexistent line\n"); + "nonexistent line"); return FALSE; } } @@ -80,12 +86,12 @@ find_offset(LINE *lp, int off) } static int -find_linep(int pos, LINE **olp, int *offset) +find_line_offset(int pos, LINE **olp, int *offset) { LINE *p; p = curwp->w_linep; - while (pos > 0 && pos > llength(p)) { + while (pos > llength(p)) { pos -= llength(p) + 1; if ((p = lforw(p)) == curwp->w_linep) { *olp = NULL; @@ -105,20 +111,30 @@ new_undo_record(void) struct undo_rec *rec; rec = LIST_FIRST(&undo_free); - if (rec != NULL) + if (rec != NULL) { LIST_REMOVE(rec, next); /* Remove it from the free-list */ - else { + undo_free_num--; + } else { if ((rec = malloc(sizeof *rec)) == NULL) panic("Out of memory in undo code (record)"); } memset(rec, 0, sizeof(struct undo_rec)); - + return rec; } void free_undo_record(struct undo_rec *rec) { + static int initialised = 0; + + /* + * On the first run, do initialisation of the free list. + */ + if (initialised == 0) { + LIST_INIT(&undo_free); + initialised = 1; + } if (rec->content != NULL) { free(rec->content); rec->content = NULL; @@ -128,7 +144,7 @@ free_undo_record(struct undo_rec *rec) return; } undo_free_num++; - + LIST_INSERT_HEAD(&undo_free, rec, next); } @@ -141,74 +157,78 @@ drop_oldest_undo_record(void) { struct undo_rec *rec; - rec = LIST_END(&undo_list); + rec = LIST_END(&curbp->b_undo); if (rec != NULL) { undo_free_num--; - LIST_REMOVE(rec, next); /* Remove it from the undo_list before - * we insert it in the free list */ + LIST_REMOVE(rec, next); free_undo_record(rec); return 1; } return 0; } - -int -undo_init(void) + +static __inline__ int +last_was_boundary() { - LIST_INIT(&undo_free); - - return TRUE; + struct undo_rec *rec; + + if ((rec = LIST_FIRST(&curbp->b_undo)) != NULL && + (rec->type == BOUNDARY)) + return 1; + return 0; } int undo_enable(int on) { undo_disable_flag = on ? 0 : 1; - - /* - * XXX-Vince: - * - * Here, I wonder if we should assume that the user has made a - * long term choice. If so, we could free all internal undo - * data and save memory. - */ - + return on; } int -undo_add_custom(int type, LINE *lp, int offset, void *content, int size) +undo_add_boundary(void) { struct undo_rec *rec; - if (undo_disable_flag) - return TRUE; rec = new_undo_record(); - rec->pos = find_offset(lp, offset); - rec->type = type; - rec->content = content; - rec->region.r_linep = lp; - rec->region.r_offset = offset; - rec->region.r_size = size; + rec->type = BOUNDARY; LIST_INSERT_HEAD(&curbp->b_undo, rec, next); - + return TRUE; } +/* + * If asocial is true, we arrange for this record to be let alone. forever. + * Yes, this is a bit of a hack... + */ int -undo_add_boundary(void) +undo_add_custom(int asocial, + int type, LINE *lp, int offset, void *content, int size) { struct undo_rec *rec; if (undo_disable_flag) return TRUE; - rec = new_undo_record(); - rec->type = BOUNDARY; + if (lp != NULL) + rec->pos = find_absolute_dot(lp, offset); + else + rec->pos = 0; + rec->type = type; + rec->content = content; + rec->region.r_linep = lp; + rec->region.r_offset = offset; + rec->region.r_size = size; + if (!last_was_boundary()) + undo_add_boundary(); LIST_INSERT_HEAD(&curbp->b_undo, rec, next); - + undo_add_boundary(); + if (asocial) /* Add a second one */ + undo_add_boundary(); + return TRUE; } @@ -217,40 +237,46 @@ undo_add_insert(LINE *lp, int offset, int size) { REGION reg; struct undo_rec *rec; - + int pos; + if (undo_disable_flag) return TRUE; - reg.r_linep = lp; reg.r_offset = offset; reg.r_size = size; - + + pos = find_absolute_dot(lp, offset); + /* * We try to reuse the last undo record to `compress' things. */ rec = LIST_FIRST(&curbp->b_undo); - if ((rec != NULL) && - (rec->type == INSERT) && - (rec->region.r_linep == lp)) { - int dist; + /* this will be hit like, 80% of the time... */ + if (rec != NULL && rec->type == BOUNDARY) + rec = LIST_NEXT(rec, next); - dist = rec->region.r_offset - reg.r_offset; - - if (rec->region.r_size >= dist) { + if ((rec != NULL) && + (rec->type == INSERT)) { + if (rec->pos + rec->region.r_size == pos) { rec->region.r_size += reg.r_size; return TRUE; } } - + /* * We couldn't reuse the last undo record, so prepare a new one */ rec = new_undo_record(); - rec->pos = find_offset(lp, offset); + rec->pos = pos; rec->type = INSERT; memmove(&rec->region, ®, sizeof(REGION)); rec->content = NULL; + + if (!last_was_boundary()) + undo_add_boundary(); + LIST_INSERT_HEAD(&curbp->b_undo, rec, next); + undo_add_boundary(); return TRUE; } @@ -263,7 +289,7 @@ undo_add_delete(LINE *lp, int offset, int size) { REGION reg; struct undo_rec *rec; - int dist, pos, skip = 0; + int pos; if (undo_disable_flag) return TRUE; @@ -272,56 +298,21 @@ undo_add_delete(LINE *lp, int offset, int size) reg.r_offset = offset; reg.r_size = size; - pos = find_offset(lp, offset); - - if (size == 1 && llength(lp) == 0) { - skip = 1; - } + pos = find_absolute_dot(lp, offset); - /* - * Again, try to reuse last undo record, if we can - */ - rec = LIST_FIRST(&curbp->b_undo); - if (!skip && - (rec != NULL) && - (rec->type == DELETE) && - (rec->region.r_linep == reg.r_linep)) { - char *newbuf; - int newlen; - - dist = rec->region.r_offset - reg.r_offset; - if (rec->region.r_size >= dist) { - newlen = rec->region.r_size + reg.r_size; - - do { - newbuf = malloc(newlen * sizeof(char)); - } while (newbuf == NULL && drop_oldest_undo_record()); - - if (newbuf == NULL) - panic("out of memory in undo delete code"); - - /* - * [new data][old data] - */ - region_get_data(®, newbuf, size); - memmove(newbuf + reg.r_size, rec->content, - rec->region.r_size); - - rec->pos = pos; - rec->region.r_offset = reg.r_offset; - rec->region.r_size = newlen; - if (rec->content != NULL) - free(rec->content); - rec->content = newbuf; - - return TRUE; - } + if (offset == llength(lp)) /* if it's a newline... */ + undo_add_boundary(); + else if ((rec = LIST_FIRST(&curbp->b_undo)) != NULL) { + /* + * Separate this command from the previous one if we're not + * just before the previous record... + */ + if (rec->type == DELETE){ + if (rec->pos - rec->region.r_size != pos) + undo_add_boundary(); + } else if (rec->type != BOUNDARY) + undo_add_boundary(); } - - /* - * So we couldn't reuse the last undo record? Just allocate a new - * one. - */ rec = new_undo_record(); rec->pos = pos; @@ -330,13 +321,14 @@ undo_add_delete(LINE *lp, int offset, int size) do { rec->content = malloc(reg.r_size + 1); } while ((rec->content == NULL) && drop_oldest_undo_record()); - + if (rec->content == NULL) panic("Out of memory"); - + region_get_data(®, rec->content, reg.r_size); LIST_INSERT_HEAD(&curbp->b_undo, rec, next); + undo_add_boundary(); return TRUE; } @@ -350,30 +342,35 @@ undo_add_change(LINE *lp, int offset, int size) REGION reg; struct undo_rec *rec; - if (undo_disable_flag) return TRUE; - + reg.r_linep = lp; reg.r_offset = offset; reg.r_size = size; - + rec = new_undo_record(); - rec->pos = find_offset(lp, offset); + rec->pos = find_absolute_dot(lp, offset); rec->type = CHANGE; memmove(&rec->region, ®, sizeof reg); + /* + * Try to allocate a buffer for the changed data. + */ do { rec->content = malloc(size + 1); } while ((rec->content == NULL) && drop_oldest_undo_record()); - + if (rec->content == NULL) panic("Out of memory in undo change code"); region_get_data(®, rec->content, size); + if (!last_was_boundary()) + undo_add_boundary(); LIST_INSERT_HEAD(&curbp->b_undo, rec, next); - + undo_add_boundary(); + return TRUE; } @@ -394,7 +391,7 @@ undo_dump(void) */ if ((bp = bfind("*undo*", TRUE)) == NULL) return FALSE; - + bp->b_flag |= BFREADONLY; bclear(bp); popbuf(bp); @@ -431,61 +428,143 @@ undo_dump(void) return TRUE; } +/* + * After the user did action1, then action2, then action3 : + * + * [action3] <--- Undoptr + * [action2] + * [action1] + * ------ + * [undo] + * + * After undo: + * + * [undo of action3] + * [action2] <--- Undoptr + * [action1] + * ------ + * [undo] + * + * After another undo: + * + * + * [undo of action2] + * [undo of action3] + * [action1] <--- Undoptr + * ------ + * [undo] + * + * Note that the "undo of actionX" have no special meaning. Only when, + * say, we undo a deletion, the insertion will be recorded just as if it + * was typed on the keyboard. Hence resulting in the inverse operation to be + * saved in the list. + * + * If undoptr reaches the bottom of the list, or if we moved between + * two undo actions, we make it point back at the topmost record. This is + * how we handle redoing. + */ int undo(int f, int n) { - struct undo_rec *rec; - LINE *ln; - int off; + struct undo_rec *ptr, *nptr; + int done, rval; + LINE *lp; + int offset; - /* - * Let called functions know they are below us (for - * example, ldelete don't want to record an undo record - * when called by us) - */ - undoaction++; + ptr = curbp->b_undoptr; + /* if we moved, make ptr point back to the top of the list */ + if ((curbp->b_undopos.r_linep != curwp->w_dotp) || + (curbp->b_undopos.r_offset != curwp->w_doto) || + (ptr == NULL)) + ptr = LIST_FIRST(&curbp->b_undo); + + rval = TRUE; while (n > 0) { - rec = LIST_FIRST(&curbp->b_undo); - if (rec == NULL) { - ewprintf("Nothing to undo!"); - return FALSE; - } - LIST_REMOVE(rec, next); - if (rec->type == BOUNDARY) { - continue; + /* if we have a spurious boundary, free it and move on.... */ + while (ptr && ptr->type == BOUNDARY) { + nptr = LIST_NEXT(ptr, next); + LIST_REMOVE(ptr, next); + free_undo_record(ptr); + ptr = nptr; } - - find_linep(rec->pos, &ln, &off); - if (ln == NULL) - return FALSE; - /* - * Move to where this record has to apply + * Ptr is NULL, but on the next run, it will point to the + * top again, redoing all stuff done in the buffer since + * its creation. */ - curwp->w_dotp = ln; - curwp->w_doto = off; - - switch (rec->type) { - case INSERT: - ldelete(rec->region.r_size, KFORW); - break; - case DELETE: - region_put_data(rec->content, rec->region.r_size); - break; - case CHANGE: - forwchar(0, rec->region.r_size); - lreplace(rec->region.r_size, rec->content, 1); - break; - default: + if (ptr == NULL) { + ewprintf("Nothing to undo!"); + rval = FALSE; break; } - - free_undo_record(rec); + /* + * Loop while we don't get a boundary specifying we've + * finished the current action... + */ + done = 0; + do { + /* Unlink the current node from the list */ + nptr = LIST_NEXT(ptr, next); + LIST_REMOVE(ptr, next); + + /* + * Move to where this has to apply + * + * Boundaries are put as position 0 (to save + * lookup time in find_absolute_dot) so we must + * not move there... + */ + if (ptr->type != BOUNDARY) { + if (find_line_offset(ptr->pos,&lp,&offset) + == FALSE) { + ewprintf("Internal error in Undo!"); + rval = FALSE; + break; + } + curwp->w_dotp = lp; + curwp->w_doto = offset; + } + + /* + * Do operation^-1 + */ + switch (ptr->type) { + case INSERT: + ldelete(ptr->region.r_size, KFORW); + break; + case DELETE: + region_put_data(ptr->content, + ptr->region.r_size); + break; + case CHANGE: + forwchar(0, ptr->region.r_size); + lreplace(ptr->region.r_size, + ptr->content, 1); + break; + case BOUNDARY: + done = 1; + break; + default: + break; + } + free_undo_record(ptr); + + /* And move to next record */ + ptr = nptr; + } while (ptr != NULL && !done); + + ewprintf("Undo!"); n--; } - undoaction--; - - return TRUE; + /* + * Record where we are. (we have to save our new position at the end + * since we change the dot when undoing....) + */ + curbp->b_undoptr = ptr; + curbp->b_undopos.r_linep = curwp->w_dotp; + curbp->b_undopos.r_offset = curwp->w_doto; + + return rval; } |