} 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 {
} 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;
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);
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);
/* 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
{
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));
}
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));
}
--- /dev/null
+#include <stdc.h>
+#include <dbc.h>
+#include <utf.h>
+#include <edit.h>
+#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);
+ }
+}
+
+
+
void gapbuf_init(GapBuf* buf)
{
+ require(buf != NULL);
buf->bufsize = 8192;
buf->bufstart = malloc(buf->bufsize);
buf->bufend = buf->bufstart + buf->bufsize;
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;
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));
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};
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))
{
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;
void gapbuf_del(GapBuf* buf, size_t off, size_t len)
{
+ require(buf != NULL);
syncgap(buf, off);
buf->gapend += len;
}