X-Git-Url: https://pd.if.org/git/?p=pd_readline;a=blobdiff_plain;f=mg%2Fundo.c;fp=mg%2Fundo.c;h=0000000000000000000000000000000000000000;hp=1efd2a1851c45e6ad06247650c3703d880fbb328;hb=4bb27266f935c9aafad6870ffc8847fc65c8120f;hpb=3f771e17236364ded86e96ee64f99344337991f8 diff --git a/mg/undo.c b/mg/undo.c deleted file mode 100644 index 1efd2a1..0000000 --- a/mg/undo.c +++ /dev/null @@ -1,585 +0,0 @@ -/* $OpenBSD: undo.c,v 1.50 2010/06/30 19:12:54 oga Exp $ */ -/* - * This file is in the public domain - */ - -#include "def.h" -#include "kbd.h" - -#define MAX_FREE_RECORDS 32 - -/* - * Local variables - */ -static struct undoq undo_free; -static int undo_free_num; -static int boundary_flag = TRUE; -static int undo_enable_flag = TRUE; - -/* - * Local functions - */ -static int find_dot(struct line *, int); -static int find_lo(int, struct line **, int *, int *); -static struct undo_rec *new_undo_record(void); -static int drop_oldest_undo_record(void); - -/* - * find_dot, find_lo() - * - * 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_dot(struct line *lp, int off) -{ - int count = 0; - struct line *p; - - for (p = curbp->b_headp; p != lp; p = lforw(p)) { - if (count != 0) { - if (p == curbp->b_headp) { - ewprintf("Error: Undo stuff called with a" - "nonexistent line"); - return (FALSE); - } - } - count += llength(p) + 1; - } - count += off; - - return (count); -} - -static int -find_lo(int pos, struct line **olp, int *offset, int *lnum) -{ - struct line *p; - int lineno; - - p = curbp->b_headp; - lineno = 0; - while (pos > llength(p)) { - pos -= llength(p) + 1; - if ((p = lforw(p)) == curbp->b_headp) { - *olp = NULL; - *offset = 0; - return (FALSE); - } - lineno++; - } - *olp = p; - *offset = pos; - *lnum = lineno; - - return (TRUE); -} - -static struct undo_rec * -new_undo_record(void) -{ - struct undo_rec *rec; - - rec = TAILQ_FIRST(&undo_free); - if (rec != NULL) { - /* Remove it from the free-list */ - TAILQ_REMOVE(&undo_free, rec, next); - 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) { - TAILQ_INIT(&undo_free); - initialised = 1; - } - if (rec->content != NULL) { - free(rec->content); - rec->content = NULL; - } - if (undo_free_num >= MAX_FREE_RECORDS) { - free(rec); - return; - } - undo_free_num++; - - TAILQ_INSERT_HEAD(&undo_free, rec, next); -} - -/* - * Drop the oldest undo record in our list. Return 1 if we could remove it, - * 0 if the undo list was empty. - */ -static int -drop_oldest_undo_record(void) -{ - struct undo_rec *rec; - - rec = TAILQ_LAST(&curbp->b_undo, undoq); - if (rec != NULL) { - undo_free_num--; - TAILQ_REMOVE(&curbp->b_undo, rec, next); - free_undo_record(rec); - return (1); - } - return (0); -} - -static int -lastrectype(void) -{ - struct undo_rec *rec; - - if ((rec = TAILQ_FIRST(&curbp->b_undo)) != NULL) - return (rec->type); - return (0); -} - -/* - * Returns TRUE if undo is enabled, FALSE otherwise. - */ -int -undo_enabled(void) -{ - return (undo_enable_flag); -} - -/* - * undo_enable: toggle undo_enable. - * Returns the previous value of the flag. - */ -int -undo_enable(int f, int n) -{ - int pon = undo_enable_flag; - - if (f & (FFARG | FFRAND)) - undo_enable_flag = n > 0; - else - undo_enable_flag = !undo_enable_flag; - - if (!(f & FFRAND)) - ewprintf("Undo %sabled", undo_enable_flag ? "en" : "dis"); - - return (pon); -} - -/* - * If undo is enabled, then: - * Toggle undo boundary recording. - * If called with an argument, (n > 0) => enable. Otherwise disable. - * In either case, add an undo boundary - * If undo is disabled, this function has no effect. - */ -int -undo_boundary_enable(int f, int n) -{ - int bon = boundary_flag; - - if (!undo_enable_flag) - return (FALSE); - - undo_add_boundary(FFRAND, 1); - - if (f & (FFARG | FFRAND)) - boundary_flag = n > 0; - else - boundary_flag = !boundary_flag; - - if (!(f & FFRAND)) - ewprintf("Undo boundaries %sabled", - boundary_flag ? "en" : "dis"); - - return (bon); -} - -/* - * Record an undo boundary, unless boundary_flag == FALSE. - * Does nothing if previous undo entry is already a boundary or 'modified' flag. - */ -int -undo_add_boundary(int f, int n) -{ - struct undo_rec *rec; - int last; - - if (boundary_flag == FALSE) - return (FALSE); - - last = lastrectype(); - if (last == BOUNDARY || last == MODIFIED) - return (TRUE); - - rec = new_undo_record(); - rec->type = BOUNDARY; - - TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next); - - return (TRUE); -} - -/* - * Record an undo "modified" boundary - */ -void -undo_add_modified(void) -{ - struct undo_rec *rec; - - rec = new_undo_record(); - rec->type = MODIFIED; - - TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next); - - return; -} - -int -undo_add_insert(struct line *lp, int offset, int size) -{ - struct region reg; - struct undo_rec *rec; - int pos; - - if (!undo_enable_flag) - return (TRUE); - reg.r_linep = lp; - reg.r_offset = offset; - reg.r_size = size; - - pos = find_dot(lp, offset); - - /* - * We try to reuse the last undo record to `compress' things. - */ - rec = TAILQ_FIRST(&curbp->b_undo); - 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 = pos; - rec->type = INSERT; - memmove(&rec->region, ®, sizeof(struct region)); - rec->content = NULL; - - undo_add_boundary(FFRAND, 1); - - TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next); - - return (TRUE); -} - -/* - * This of course must be done _before_ the actual deletion is done. - */ -int -undo_add_delete(struct line *lp, int offset, int size, int isreg) -{ - struct region reg; - struct undo_rec *rec; - int pos; - - if (!undo_enable_flag) - return (TRUE); - - reg.r_linep = lp; - reg.r_offset = offset; - reg.r_size = size; - - pos = find_dot(lp, offset); - - if (offset == llength(lp)) /* if it's a newline... */ - undo_add_boundary(FFRAND, 1); - else if ((rec = TAILQ_FIRST(&curbp->b_undo)) != NULL) { - /* - * Separate this command from the previous one if we're not - * just before the previous record... - */ - if (!isreg && rec->type == DELETE) { - if (rec->pos - rec->region.r_size != pos) - undo_add_boundary(FFRAND, 1); - } - } - rec = new_undo_record(); - rec->pos = pos; - if (isreg) - rec->type = DELREG; - else - rec->type = DELETE; - memmove(&rec->region, ®, sizeof(struct region)); - 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); - - if (isreg || lastrectype() != DELETE) - undo_add_boundary(FFRAND, 1); - - TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next); - - return (TRUE); -} - -/* - * This of course must be called before the change takes place. - */ -int -undo_add_change(struct line *lp, int offset, int size) -{ - if (!undo_enable_flag) - return (TRUE); - undo_add_boundary(FFRAND, 1); - boundary_flag = FALSE; - undo_add_delete(lp, offset, size, 0); - undo_add_insert(lp, offset, size); - boundary_flag = TRUE; - undo_add_boundary(FFRAND, 1); - - return (TRUE); -} - -/* - * Show the undo records for the current buffer in a new buffer. - */ -/* ARGSUSED */ -int -undo_dump(int f, int n) -{ - struct undo_rec *rec; - struct buffer *bp; - struct mgwin *wp; - char buf[4096], tmp[1024]; - int num; - - /* - * Prepare the buffer for insertion. - */ - if ((bp = bfind("*undo*", TRUE)) == NULL) - return (FALSE); - bp->b_flag |= BFREADONLY; - bclear(bp); - popbuf(bp, WNONE); - - for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { - if (wp->w_bufp == bp) { - wp->w_dotp = bp->b_headp; - wp->w_doto = 0; - } - } - - num = 0; - TAILQ_FOREACH(rec, &curbp->b_undo, next) { - num++; - snprintf(buf, sizeof(buf), - "%d:\t %s at %d ", num, - (rec->type == DELETE) ? "DELETE": - (rec->type == DELREG) ? "DELREGION": - (rec->type == INSERT) ? "INSERT": - (rec->type == BOUNDARY) ? "----" : - (rec->type == MODIFIED) ? "MODIFIED": "UNKNOWN", - rec->pos); - - if (rec->content) { - (void)strlcat(buf, "\"", sizeof(buf)); - snprintf(tmp, sizeof(tmp), "%.*s", rec->region.r_size, - rec->content); - (void)strlcat(buf, tmp, sizeof(buf)); - (void)strlcat(buf, "\"", sizeof(buf)); - } - snprintf(tmp, sizeof(tmp), " [%d]", rec->region.r_size); - if (strlcat(buf, tmp, sizeof(buf)) >= sizeof(buf)) { - ewprintf("Undo record too large. Aborted."); - return (FALSE); - } - addlinef(bp, "%s", buf); - } - 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 - * we undo a deletion, the insertion will be recorded just as if it - * was typed on the keyboard. Resulting in the inverse operation being - * 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. - */ -/* ARGSUSED */ -int -undo(int f, int n) -{ - struct undo_rec *ptr, *nptr; - int done, rval; - struct line *lp; - int offset, save, dot; - static int nulled = FALSE; - int lineno; - - if (n < 0) - return (FALSE); - - dot = find_dot(curwp->w_dotp, curwp->w_doto); - - ptr = curbp->b_undoptr; - - /* first invocation, make ptr point back to the top of the list */ - if ((ptr == NULL && nulled == TRUE) || rptcount == 0) { - ptr = TAILQ_FIRST(&curbp->b_undo); - nulled = TRUE; - } - - rval = TRUE; - while (n--) { - /* if we have a spurious boundary, free it and move on.... */ - while (ptr && ptr->type == BOUNDARY) { - nptr = TAILQ_NEXT(ptr, next); - TAILQ_REMOVE(&curbp->b_undo, ptr, next); - free_undo_record(ptr); - ptr = nptr; - } - /* - * 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. - */ - if (ptr == NULL) { - ewprintf("No further undo information"); - rval = FALSE; - nulled = TRUE; - break; - } - nulled = FALSE; - - /* - * Loop while we don't get a boundary specifying we've - * finished the current action... - */ - - undo_add_boundary(FFRAND, 1); - - save = boundary_flag; - boundary_flag = FALSE; - - done = 0; - do { - /* - * Move to where this has to apply - * - * Boundaries (and the modified flag) are put as - * position 0 (to save lookup time in find_dot) - * so we must not move there... - */ - if (ptr->type != BOUNDARY && ptr->type != MODIFIED) { - if (find_lo(ptr->pos, &lp, - &offset, &lineno) == FALSE) { - ewprintf("Internal error in Undo!"); - rval = FALSE; - break; - } - curwp->w_dotp = lp; - curwp->w_doto = offset; - curwp->w_markline = curwp->w_dotline; - curwp->w_dotline = lineno; - } - - /* - * Do operation^-1 - */ - switch (ptr->type) { - case INSERT: - ldelete(ptr->region.r_size, KNONE); - break; - case DELETE: - lp = curwp->w_dotp; - offset = curwp->w_doto; - region_put_data(ptr->content, - ptr->region.r_size); - curwp->w_dotp = lp; - curwp->w_doto = offset; - break; - case DELREG: - region_put_data(ptr->content, - ptr->region.r_size); - break; - case BOUNDARY: - done = 1; - break; - case MODIFIED: - curbp->b_flag &= ~BFCHG; - break; - default: - break; - } - - /* And move to next record */ - ptr = TAILQ_NEXT(ptr, next); - } while (ptr != NULL && !done); - - boundary_flag = save; - undo_add_boundary(FFRAND, 1); - - ewprintf("Undo!"); - } - /* - * 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 = find_dot(curwp->w_dotp, curwp->w_doto); - - return (rval); -}