From: Michael D. Lowis Date: Thu, 17 Oct 2019 01:11:15 +0000 (-0400) Subject: refactored undo/redo into editlog.c X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=2386a448d0347cdd957312e657f5e1240fe40fdf;p=projs%2Ftide.git refactored undo/redo into editlog.c --- diff --git a/inc/edit.h b/inc/edit.h index d351f3c..f8eaf50 100644 --- a/inc/edit.h +++ b/inc/edit.h @@ -17,13 +17,20 @@ typedef struct { } Sel; typedef struct { - size_t bufsize; /* size of the buffer in runes */ - char* bufstart; /* start of the data buffer */ - char* bufend; /* end of the data buffer */ - char* gapstart; /* start of the gap */ - char* gapend; /* end of the gap */ + size_t bufsize; /* size of the buffer in runes */ + char* bufstart; /* start of the data buffer */ + char* bufend; /* end of the data buffer */ + char* gapstart; /* start of the gap */ + char* gapend; /* end of the gap */ } GapBuf; +typedef struct { + int transid; /* id number of the current transaction */ + Log* undo; /* undo list */ + Log* redo; /* redo list */ + Log* save; /* pointer to last save position */ +} EditLog; + /* gap buffer main data structure */ typedef struct { enum { @@ -31,10 +38,13 @@ typedef struct { } status; char* path; /* the path to the open file */ GapBuf contents; /* underlying sequence data structure */ - Log* undo; /* undo list */ - Log* redo; /* redo list */ - Log* save; /* pointer to last save position */ - int transid; /* id number of the current transaction */ + EditLog log; /* underlying log of edit operations */ + + int transid; /* id number of the current transaction */ + Log* undo; /* undo list */ + Log* redo; /* redo list */ + Log* save; /* pointer to last save position */ + Sel selection; /* the currently selected text */ } Buf; @@ -46,6 +56,14 @@ char gapbuf_getb(GapBuf* buf, size_t off); void gapbuf_putb(GapBuf* buf, char b, Sel* p_sel); void gapbuf_del(GapBuf* buf, size_t off, size_t len); +void editlog_seqstart(Buf* log); +void editlog_seqstop(Buf* log); +void editlog_clear(Buf* log); +void editlog_lastins(Buf* buf, Sel* p_sel); +void editlog_undo(Buf* log); +void editlog_redo(Buf* log); +void editlog_add(Buf* buf, size_t beg, size_t end, char* data); + void buf_init(Buf* buf); void buf_setpath(Buf* buf, char* path); void buf_load(Buf* buf, char* path); @@ -61,12 +79,12 @@ char* buf_gets(Buf* buf); char* buf_getsat(Buf* buf, size_t beg, size_t end); void buf_del(Buf* buf); -void buf_undo(Buf* buf); -void buf_redo(Buf* buf); void buf_logstart(Buf* buf); void buf_logstop(Buf* buf); void buf_logclear(Buf* buf); void buf_lastins(Buf* buf); +void buf_undo(Buf* buf); +void buf_redo(Buf* buf); bool buf_isbol(Buf* buf, size_t pos); bool buf_iseol(Buf* buf, size_t pos); diff --git a/src/lib/buf.c b/src/lib/buf.c index 5402c6b..7ee3da5 100644 --- a/src/lib/buf.c +++ b/src/lib/buf.c @@ -187,213 +187,34 @@ int buf_save(Buf* buf, char* path) /* Undo/Redo Operations ******************************************************************************/ -static Log* mklog(Buf* buf, size_t beg, size_t end, char* data, Log* next) -{ - Log* log = calloc(1, sizeof(Log)); - log->transid = (buf->transid < 0 ? 0 : buf->transid); - log->beg = beg; - log->end = end; - log->data = data; - log->next = next; - return log; -} - -static void log_clear(Log** list) -{ - while (*list) - { - Log* deadite = *list; - *list = (*list)->next; - if (deadite->data) - { - free(deadite->data); - } - free(deadite); - } -} - -static void dumplog(Buf* buf) -{ -#ifdef LOG_DUMPING - printf("\nUndo:\n"); - for (Log* log = buf->undo; log; log = log->next) - { - printf(" (%d) %lu-%lu '%s'\n", log->transid, log->beg, log->end, log->data); - } - printf("Redo:\n"); - for (Log* log = buf->redo; log; log = log->next) - { - printf(" (%d) %lu-%lu '%s'\n", log->transid, log->beg, log->end, log->data); - } -#else - (void)buf; -#endif -} - -static void log_add(Buf* buf, size_t beg, size_t end, char* data) -{ - Log* prev = buf->undo; - log_clear(&(buf->redo)); - - /* decide if this is an insert or delete */ - if (!prev || (buf->transid > 0 && buf->transid != prev->transid)) - { - buf->undo = mklog(buf, beg, end, data, prev); - } - else if (!data && !prev->data && prev->end == beg) - { - prev->end = end; - } - else if (prev->data && data && prev->beg == beg) - { - char* newdata = strmcat(prev->data, data, 0); - free(data); - free(prev->data); - prev->data = newdata; - } - else if (prev->data && data && prev->beg == beg+1) - { - char* newdata = strmcat(data, prev->data, 0); - free(data); - free(prev->data); - prev->data = newdata; - prev->end = --prev->beg; - } - else - { - buf->undo = mklog(buf, beg, end, data, prev); - } - - dumplog(buf); -} - -static void log_swap(Buf* buf, Log** src, Log** dest) -{ - Log* item = *src; - if (item) - { - *src = item->next; - buf->selection.beg = item->beg; - buf->selection.end = item->end; - if (item->data) - { - /* reinsert deleted bytes */ - for (char* s = item->data; s && *s; s++, item->end++) - { - gapbuf_putb(&buf->contents, *s, &(buf->selection)); - buf->status = MODIFIED; - } - free(item->data); - item->data = NULL; - buf->selection.beg = item->beg; - buf->selection.end = item->end; - } - else - { - /* delete the added bytes */ - Sel sel = selget(buf); - item->data = buf_gets(buf); - gapbuf_del(&buf->contents, sel.beg, (item->end - item->beg)); - sel.end = sel.beg; - buf->selection = sel; - item->beg = sel.beg; - item->end = sel.end; - } - - /* push item onto destination stack */ - item->next = *dest; - *dest = item; - /* undo recursively if this is part of a transaction */ - if (*src && item->transid && item->transid == (*src)->transid) - { - log_swap(buf, src, dest); - } - else - { - dumplog(buf); - } - - if (buf->save == buf->undo) - { - buf->status = NORMAL; - } - } -} - void buf_logstart(Buf* buf) { - require(buf != NULL); - buf->transid = abs(buf->transid); + editlog_seqstart(buf); } void buf_logstop(Buf* buf) { - require(buf != NULL); - if (buf->transid > 0) - { - buf->transid = -(buf->transid + 1); - } + editlog_seqstop(buf); } -void buf_undo(Buf* buf) +void buf_logclear(Buf* buf) { - require(buf != NULL); - log_swap(buf, &(buf->undo), &(buf->redo)); - ensure(buf_valid(buf)); + editlog_clear(buf); } -void buf_redo(Buf* buf) +void buf_lastins(Buf* buf) { - require(buf != NULL); - log_swap(buf, &(buf->redo), &(buf->undo)); - ensure(buf_valid(buf)); + editlog_lastins(buf, &(buf->selection)); } -void buf_logclear(Buf* buf) +void buf_undo(Buf* buf) { - require(buf != NULL); - log_clear(&(buf->redo)); - log_clear(&(buf->undo)); + editlog_undo(buf); } -void buf_lastins(Buf* buf) +void buf_redo(Buf* buf) { - require(buf != NULL); - Log* log = buf->undo; - if (log) - { - Sel sel = {.beg = log->beg, .end = log->end }; - size_t delsize = 0; - int transid = log->transid; - - /* try and expand the selected region to encompass related inserts */ - for (; log && (log->transid == transid); log = log->next) - { - if (!log->data) - { - size_t ibeg = log->beg, iend = log->end - delsize; - if (iend < ibeg || ibeg > sel.beg || iend < sel.beg) - { - break; - } - if (ibeg < sel.beg && iend > sel.end) - { - break; - } - sel.beg = ibeg, delsize = 0; - } - else - { - /* bail if the delete doesnt overlap */ - if (log->beg != sel.beg) - { - break; - } - delsize = strlen(log->data); - } - } - buf->selection = sel; - } + editlog_redo(buf); } /* Basic Operations and Accessors @@ -441,7 +262,7 @@ void buf_puts(Buf* buf, char* s) { putch(buf, *(s++), &(buf->selection)); } - log_add(buf, beg, buf_selend(buf), NULL); + editlog_add(buf, beg, buf_selend(buf), NULL); } ensure(buf_valid(buf)); } @@ -488,7 +309,7 @@ void buf_del(Buf* buf) gapbuf_del(&buf->contents, sel.beg, nbytes); sel.end = sel.beg = (sel.beg < sel.end ? sel.beg : sel.end); buf->selection = sel; - log_add(buf, sel.beg, sel.end, str); + editlog_add(buf, sel.beg, sel.end, str); } ensure(buf_valid(buf)); } diff --git a/src/lib/editlog.c b/src/lib/editlog.c new file mode 100644 index 0000000..ae1467d --- /dev/null +++ b/src/lib/editlog.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include "config.h" + +static Log* mklog(Buf* buf, size_t beg, size_t end, char* data, Log* next) +{ + Log* log = calloc(1, sizeof(Log)); + log->transid = (buf->transid < 0 ? 0 : buf->transid); + log->beg = beg; + log->end = end; + log->data = data; + log->next = next; + return log; +} + +static void log_swap(Buf* buf, Log** src, Log** dest) +{ + Log* item = *src; + if (item) + { + *src = item->next; + buf->selection.beg = item->beg; + buf->selection.end = item->end; + if (item->data) + { + /* reinsert deleted bytes */ + for (char* s = item->data; s && *s; s++, item->end++) + { + gapbuf_putb(&buf->contents, *s, &(buf->selection)); + buf->status = MODIFIED; + } + free(item->data); + item->data = NULL; + buf->selection.beg = item->beg; + buf->selection.end = item->end; + } + else + { + /* delete the added bytes */ + Sel sel = buf->selection; + item->data = buf_gets(buf); + gapbuf_del(&buf->contents, sel.beg, (item->end - item->beg)); + sel.end = sel.beg; + buf->selection = sel; + item->beg = sel.beg; + item->end = sel.end; + } + + /* push item onto destination stack */ + item->next = *dest; + *dest = item; + /* undo recursively if this is part of a transaction */ + if (*src && item->transid && item->transid == (*src)->transid) + { + log_swap(buf, src, dest); + } + + if (buf->save == buf->undo) + { + buf->status = NORMAL; + } + } +} + +static void log_clear(Log** list) +{ + while (*list) + { + Log* deadite = *list; + *list = (*list)->next; + if (deadite->data) + { + free(deadite->data); + } + free(deadite); + } +} + +void editlog_seqstart(Buf* buf) +{ + require(buf != NULL); + buf->transid = abs(buf->transid); +} + +void editlog_seqstop(Buf* buf) +{ + require(buf != NULL); + if (buf->transid > 0) + { + buf->transid = -(buf->transid + 1); + } +} + +void editlog_clear(Buf* buf) +{ + require(buf != NULL); + log_clear(&(buf->redo)); + log_clear(&(buf->undo)); +} + +void editlog_lastins(Buf* buf, Sel* p_sel) +{ + require(buf != NULL); + Log* log = buf->undo; + if (log) + { + Sel sel = { .beg = log->beg, .end = log->end }; + size_t delsize = 0; + int transid = log->transid; + + /* try and expand the selected region to encompass related inserts */ + for (; log && (log->transid == transid); log = log->next) + { + if (!log->data) + { + size_t ibeg = log->beg, iend = log->end - delsize; + if (iend < ibeg || ibeg > sel.beg || iend < sel.beg) + { + break; + } + if (ibeg < sel.beg && iend > sel.end) + { + break; + } + sel.beg = ibeg, delsize = 0; + } + else + { + /* bail if the delete doesnt overlap */ + if (log->beg != sel.beg) + { + break; + } + delsize = strlen(log->data); + } + } + *p_sel = sel; + } +} + +void editlog_undo(Buf* buf) +{ + require(buf != NULL); + log_swap(buf, &(buf->undo), &(buf->redo)); +} + +void editlog_redo(Buf* buf) +{ + require(buf != NULL); + log_swap(buf, &(buf->redo), &(buf->undo)); +} + +void editlog_add(Buf* buf, size_t beg, size_t end, char* data) +{ + Log* prev = buf->undo; + log_clear(&(buf->redo)); + + /* decide if this is an insert or delete */ + if (!prev || (buf->transid > 0 && buf->transid != prev->transid)) + { + buf->undo = mklog(buf, beg, end, data, prev); + } + else if (!data && !prev->data && prev->end == beg) + { + prev->end = end; + } + else if (prev->data && data && prev->beg == beg) + { + char* newdata = strmcat(prev->data, data, 0); + free(data); + free(prev->data); + prev->data = newdata; + } + else if (prev->data && data && prev->beg == beg+1) + { + char* newdata = strmcat(data, prev->data, 0); + free(data); + free(prev->data); + prev->data = newdata; + prev->end = --prev->beg; + } + else + { + buf->undo = mklog(buf, beg, end, data, prev); + } +} + + + diff --git a/src/lib/gapbuf.c b/src/lib/gapbuf.c index 869e692..c188242 100644 --- a/src/lib/gapbuf.c +++ b/src/lib/gapbuf.c @@ -81,6 +81,7 @@ static void syncgap(GapBuf* buf, size_t off) void gapbuf_init(GapBuf* buf) { + require(buf != NULL); buf->bufsize = 8192; buf->bufstart = malloc(buf->bufsize); buf->bufend = buf->bufstart + buf->bufsize; @@ -90,7 +91,8 @@ void gapbuf_init(GapBuf* buf) static long writefd(int fd, char* data, long towrite) { - long nwrite = 0;; + require(fd >= 0); + long nwrite = 0; while (towrite && ((nwrite = write(fd, data, towrite)) > 0)) { data += nwrite; @@ -101,11 +103,13 @@ static long writefd(int fd, char* data, long towrite) long gapbuf_save(GapBuf* buf, char* path) { + require(buf != NULL); + require(path != NULL); long fd; - long nwrite; + long nwrite = 0; if (path && (fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) >= 0) { - long nwrite = writefd(fd, buf->bufstart, (buf->gapstart - buf->bufstart)); + nwrite = writefd(fd, buf->bufstart, (buf->gapstart - buf->bufstart)); if (nwrite >= 0) { nwrite = writefd(fd, buf->gapend, (buf->bufend - buf->gapend)); @@ -121,6 +125,8 @@ long gapbuf_save(GapBuf* buf, char* path) void gapbuf_load(GapBuf* buf, char* path) { + require(buf != NULL); + require(path != NULL); /* load the contents from the file */ int fd, nread; struct stat sb = {0}; @@ -145,6 +151,7 @@ void gapbuf_load(GapBuf* buf, char* path) char gapbuf_getb(GapBuf* buf, size_t off) { + require(buf != NULL); int c = '\n'; // TODO: get rid of this hack if (off < gapbuf_end(buf)) { @@ -163,6 +170,8 @@ char gapbuf_getb(GapBuf* buf, size_t off) void gapbuf_putb(GapBuf* buf, char b, Sel* p_sel) { + require(buf != NULL); + require(p_sel != NULL); syncgap(buf, p_sel->end); *(buf->gapstart++) = b; p_sel->end = p_sel->end + 1u; @@ -171,6 +180,7 @@ void gapbuf_putb(GapBuf* buf, char b, Sel* p_sel) void gapbuf_del(GapBuf* buf, size_t off, size_t len) { + require(buf != NULL); syncgap(buf, off); buf->gapend += len; }