]> git.mdlowis.com Git - projs/tide.git/commitdiff
Reworked undo/redo logic to allow for batch operations. The first implemented batch...
authorMichael D. Lowis <mike.lowis@gentex.com>
Mon, 19 Dec 2016 20:29:52 +0000 (15:29 -0500)
committerMichael D. Lowis <mike.lowis@gentex.com>
Mon, 19 Dec 2016 20:29:52 +0000 (15:29 -0500)
inc/edit.h
inc/utf.h
libedit/buf.c
libedit/charset.c
libedit/utf8.c
libedit/utils.c
libedit/view.c
tests/buf.c
xedit.c
xpick.c

index 94aca307425854b4a1b23a485ba859b690d31e6c..678a376a2ba5515b26405183c6e030a89243ef4c 100644 (file)
@@ -8,8 +8,6 @@ typedef struct {
 FMap fmap(char* path);
 void funmap(FMap file);
 uint64_t getmillis(void);
-bool risword(Rune r);
-bool risblank(Rune r);
 char* stringdup(const char* str);
 char* fdgets(int fd);
 char* chomp(char* in);
@@ -18,8 +16,8 @@ char* chomp(char* in);
  *****************************************************************************/
 typedef struct Log {
     struct Log* next;   /* pointer to next operation in the stack */
-    bool locked;        /* whether new operations can be coalesced or not */
     bool insert;        /* whether this operation was an insert or delete */
+    uint transid;       /* transaction id used to group related edits together */
     union {
         struct {
             size_t beg; /* offset in the file where insertion started */
@@ -47,6 +45,7 @@ typedef struct buf {
     bool modified;    /* tracks whether the buffer has been modified */
     bool expand_tabs; /* tracks current mode */
     bool copy_indent; /* copy the indent level from the previous line on new lines */
+    uint transid;     /* tracks the last used transaction id for log entries */
 } Buf;
 
 typedef struct {
@@ -55,15 +54,21 @@ typedef struct {
     size_t col;
 } Sel;
 
+void buf_init(Buf* buf);
 unsigned buf_load(Buf* buf, char* path);
 void buf_save(Buf* buf);
-void buf_init(Buf* buf);
-unsigned buf_ins(Buf* buf, bool indent, unsigned off, Rune rune);
-void buf_del(Buf* buf, unsigned pos);
+
+Rune buf_get(Buf* buf, unsigned pos);
+unsigned buf_end(Buf* buf);
+
+unsigned buf_insert(Buf* buf, bool indent, unsigned off, Rune rune);
+unsigned buf_delete(Buf* buf, unsigned beg, unsigned end);
+unsigned buf_change(Buf* buf, unsigned beg, unsigned end);
+
 unsigned buf_undo(Buf* buf, unsigned pos);
 unsigned buf_redo(Buf* buf, unsigned pos);
-unsigned buf_setln(Buf* buf, unsigned line);
-Rune buf_get(Buf* buf, unsigned pos);
+void buf_loglock(Buf* buf);
+
 bool buf_iseol(Buf* buf, unsigned pos);
 unsigned buf_bol(Buf* buf, unsigned pos);
 unsigned buf_eol(Buf* buf, unsigned pos);
@@ -71,16 +76,18 @@ unsigned buf_bow(Buf* buf, unsigned pos);
 unsigned buf_eow(Buf* buf, unsigned pos);
 unsigned buf_lscan(Buf* buf, unsigned pos, Rune r);
 unsigned buf_rscan(Buf* buf, unsigned pos, Rune r);
-void buf_find(Buf* buf, size_t* beg, size_t* end);
-void buf_findstr(Buf* buf, char* str, size_t* beg, size_t* end);
-unsigned buf_end(Buf* buf);
+
 unsigned buf_byrune(Buf* buf, unsigned pos, int count);
 unsigned buf_byword(Buf* buf, unsigned pos, int count);
 unsigned buf_byline(Buf* buf, unsigned pos, int count);
+
+void buf_find(Buf* buf, size_t* beg, size_t* end);
+void buf_findstr(Buf* buf, char* str, size_t* beg, size_t* end);
+
+unsigned buf_setln(Buf* buf, unsigned line);
 unsigned buf_getcol(Buf* buf, unsigned pos);
 unsigned buf_setcol(Buf* buf, unsigned pos, unsigned col);
 void buf_lastins(Buf* buf, size_t* beg, size_t* end);
-void buf_loglock(Buf* buf);
 
 /* Charset Handling
  *****************************************************************************/
index 3bacfe9d04ca4068292531753e80e8233cce03a5..1a817533da3a2535d5097e4c95eba12abb41104e 100644 (file)
--- a/inc/utf.h
+++ b/inc/utf.h
@@ -15,3 +15,7 @@ bool utf8decode(Rune* rune, size_t* length, int byte);
 Rune fgetrune(FILE* f);
 void fputrune(Rune rune, FILE* f);
 int runewidth(unsigned col, Rune r);
+size_t rstrlen(Rune* runes);
+Rune* charstorunes(char* str);
+bool risword(Rune r);
+bool risblank(Rune r);
index 268d20dced307fb4c26f6cbdba8e31ca3ba9e77f..573769997dbc62982f6da5d99daf3f9404abb213 100644 (file)
@@ -3,86 +3,9 @@
 #include <edit.h>
 #include <ctype.h>
 
-unsigned buf_load(Buf* buf, char* path) {
-    if (path && path[0] == '.' && path[1] == '/')
-        path += 2;
-    unsigned off = 0;
-    buf->path = stringdup(path);
-    char* addr = strrchr(buf->path, ':');
-    if (addr) *addr = '\0', addr++;
-    if (!strcmp(buf->path,"-")) {
-        buf->charset = UTF_8;
-        Rune r;
-        while (RUNE_EOF != (r = fgetrune(stdin)))
-            buf_ins(buf, false, buf_end(buf), r);
-    } else {
-        FMap file = fmap(buf->path);
-        buf->charset = (file.buf ? charset(file.buf, file.len, &buf->crlf) : UTF_8);
-        /* load the file contents if it has any */
-        if (buf->charset > UTF_8) {
-            die("Unsupported character set");
-        } else if (buf->charset == BINARY) {
-            binload(buf, file);
-        } else {
-            utf8load(buf, file);
-        }
-        funmap(file);
-        if (addr)
-            off = buf_setln(buf, strtoul(addr, NULL, 0));
-    }
-    buf->modified = false;
-    free(buf->undo);
-    buf->undo = NULL;
-    return off;
-}
-
-void buf_save(Buf* buf) {
-    if (!buf->path) return;
-    FILE* file = fopen(buf->path, "wb");
-    if (!file) return;
-    if (buf->charset == BINARY)
-        binsave(buf, file);
-    else
-        utf8save(buf, file);
-    fclose(file);
-    buf->modified = false;
-}
+static void buf_resize(Buf* buf, size_t sz);
 
-void buf_resize(Buf* buf, size_t sz) {
-    /* allocate the new buffer and gap */
-    Buf copy = *buf;
-    copy.bufsize  = sz;
-    copy.bufstart = (Rune*)malloc(copy.bufsize * sizeof(Rune));
-    copy.bufend   = copy.bufstart + copy.bufsize;
-    copy.gapstart = copy.bufstart;
-    copy.gapend   = copy.bufend;
-    /* copy the data from the old buffer to the new one */
-    for (Rune* curr = buf->bufstart; curr < buf->gapstart; curr++)
-        *(copy.gapstart++) = *(curr);
-    for (Rune* curr = buf->gapend; curr < buf->bufend; curr++)
-        *(copy.gapstart++) = *(curr);
-    /* free the buffer and commit the changes */
-    free(buf->bufstart);
-    *buf = copy;
-}
-
-static void syncgap(Buf* buf, unsigned off) {
-    assert(off <= buf_end(buf));
-    /* If the buffer is full, resize it before syncing */
-    if (0 == (buf->gapend - buf->gapstart))
-        buf_resize(buf, buf->bufsize << 1);
-    /* Move the gap to the desired offset */
-    Rune* newpos = (buf->bufstart + off);
-    if (newpos < buf->gapstart) {
-        while (newpos < buf->gapstart)
-            *(--buf->gapend) = *(--buf->gapstart);
-    } else {
-        while (newpos > buf->gapstart)
-            *(buf->gapstart++) = *(buf->gapend++);
-    }
-}
-
-void log_clear(Log** list) {
+static void log_clear(Log** list) {
     while (*list) {
         Log* deadite = *list;
         *list = (*list)->next;
@@ -92,49 +15,32 @@ void log_clear(Log** list) {
     }
 }
 
-void buf_init(Buf* buf) {
-    /* cleanup old data if there is any */
-    if (buf->bufstart) free(buf->bufstart);
-    if (buf->undo) log_clear(&(buf->undo));
-    if (buf->redo) log_clear(&(buf->redo));
-    
-    /* reset the state to defaults */
-    buf->modified    = false;
-    buf->expand_tabs = true;
-    buf->copy_indent = true;
-    buf->charset     = DEFAULT_CHARSET;
-    buf->crlf        = DEFAULT_CRLF;
-    buf->bufsize     = BufSize;
-    buf->bufstart    = (Rune*)malloc(buf->bufsize * sizeof(Rune));
-    buf->bufend      = buf->bufstart + buf->bufsize;
-    buf->gapstart    = buf->bufstart;
-    buf->gapend      = buf->bufend;
-    buf->undo        = NULL;
-    buf->redo        = NULL;
-}
-
-static void log_insert(Log** list, unsigned beg, unsigned end) {
+static void log_insert(Buf* buf, Log** list, unsigned beg, unsigned end) {
     Log* log = *list;
-    if (!log || log->locked || !log->insert || (end != log->data.ins.end+1)) {
+    bool locked = (!log || log->transid != buf->transid);
+    if (locked || !log->insert || (end != log->data.ins.end+1)) {
+        buf_loglock(buf);
         Log* newlog  = (Log*)calloc(sizeof(Log), 1);
+        newlog->transid = buf->transid;
         newlog->insert = true;
         newlog->data.ins.beg = beg;
         newlog->data.ins.end = end;
         newlog->next = *list;
-        if (log) log->locked = true;
         *list = newlog;
-    } else if (beg <= log->data.ins.beg) {
+    } else if (beg < log->data.ins.beg) {
         log->data.ins.beg--;
     } else {
         log->data.ins.end++;
     }
 }
 
-static void log_delete(Log** list, unsigned off, Rune* r, size_t len) {
+static void log_delete(Buf* buf, Log** list, unsigned off, Rune* r, size_t len) {
     Log* log = *list;
-    if (!log || log->locked || log->insert ||
-        ((off != log->data.del.off) && (off+1 != log->data.del.off))) {
+    bool locked = (!log || log->transid != buf->transid);
+    if (locked || log->insert || ((off != log->data.del.off) && (off+1 != log->data.del.off))) {
+        buf_loglock(buf);
         Log* newlog = (Log*)calloc(sizeof(Log), 1);
+        newlog->transid = buf->transid;
         newlog->insert = false;
         newlog->data.del.off = off;
         newlog->data.del.len = len;
@@ -142,7 +48,6 @@ static void log_delete(Log** list, unsigned off, Rune* r, size_t len) {
         for (size_t i = 0; i < len; i++)
             newlog->data.del.runes[i] = r[i];
         newlog->next = *list;
-        if (log) log->locked = true;
         *list = newlog;
     } else if (off == log->data.del.off) {
         log->data.del.len++;
@@ -158,18 +63,38 @@ static void log_delete(Log** list, unsigned off, Rune* r, size_t len) {
     }
 }
 
-static unsigned insert(Buf* buf, unsigned off, Rune rune) {
-    unsigned rcount = 1;
-    syncgap(buf, off);
-    if (buf->crlf && rune == '\n' && buf_get(buf, off-1) == '\r') {
-        rcount = 0;
-        *(buf->gapstart-1) = RUNE_CRLF;
-    } else if (buf->crlf && rune == '\n') {
-        *(buf->gapstart++) = RUNE_CRLF;
+static void syncgap(Buf* buf, unsigned off) {
+    assert(off <= buf_end(buf));
+    /* If the buffer is full, resize it before syncing */
+    if (0 == (buf->gapend - buf->gapstart))
+        buf_resize(buf, buf->bufsize << 1);
+    /* Move the gap to the desired offset */
+    Rune* newpos = (buf->bufstart + off);
+    if (newpos < buf->gapstart) {
+        while (newpos < buf->gapstart)
+            *(--buf->gapend) = *(--buf->gapstart);
     } else {
-        *(buf->gapstart++) = rune;
+        while (newpos > buf->gapstart)
+            *(buf->gapstart++) = *(buf->gapend++);
     }
-    return rcount;
+}
+
+static void buf_resize(Buf* buf, size_t sz) {
+    /* allocate the new buffer and gap */
+    Buf copy = *buf;
+    copy.bufsize  = sz;
+    copy.bufstart = (Rune*)malloc(copy.bufsize * sizeof(Rune));
+    copy.bufend   = copy.bufstart + copy.bufsize;
+    copy.gapstart = copy.bufstart;
+    copy.gapend   = copy.bufend;
+    /* copy the data from the old buffer to the new one */
+    for (Rune* curr = buf->bufstart; curr < buf->gapstart; curr++)
+        *(copy.gapstart++) = *(curr);
+    for (Rune* curr = buf->gapend; curr < buf->bufend; curr++)
+        *(copy.gapstart++) = *(curr);
+    /* free the buffer and commit the changes */
+    free(buf->bufstart);
+    *buf = copy;
 }
 
 static void clear_redo(Buf* buf) {
@@ -190,49 +115,51 @@ static unsigned getindent(Buf* buf, unsigned off) {
     return buf_getcol(buf, off) / TabWidth;
 }
 
-unsigned buf_ins(Buf* buf, bool fmt, unsigned off, Rune rune) {
-    buf->modified = true;
-    if (fmt && buf->expand_tabs && rune == '\t') {
-        size_t n = (TabWidth - ((off - buf_bol(buf, off)) % TabWidth));
-        log_insert(&(buf->undo), off, off+n);
-        for(; n > 0; n--) off += insert(buf, off, ' ');
+static void delete(Buf* buf, unsigned off) {
+    syncgap(buf, off);
+    buf->gapend++;
+}
+
+static unsigned insert(Buf* buf, unsigned off, Rune rune) {
+    unsigned rcount = 1;
+    syncgap(buf, off);
+    if (buf->crlf && rune == '\n' && buf_get(buf, off-1) == '\r') {
+        rcount = 0;
+        *(buf->gapstart-1) = RUNE_CRLF;
+    } else if (buf->crlf && rune == '\n') {
+        *(buf->gapstart++) = RUNE_CRLF;
     } else {
-        size_t n = insert(buf, off, rune);
-        if (n > 0) {
-            log_insert(&(buf->undo), off, off+n);
-            off += n;
-        }
-    }
-    if (fmt && buf->copy_indent && (rune == '\n' || rune == RUNE_CRLF)) {
-        unsigned indent = getindent(buf, off-1);
-        for (; indent > 0; indent--)
-            off = buf_ins(buf, indent, off, '\t');
+        *(buf->gapstart++) = rune;
     }
-    clear_redo(buf);
-    return off;
+    return rcount;
 }
 
-static void delete(Buf* buf, unsigned off) {
-    syncgap(buf, off);
-    buf->gapend++;
+static int range_match(Buf* buf, unsigned dbeg, unsigned dend, unsigned mbeg, unsigned mend) {
+    unsigned n1 = dend-dbeg, n2 = mend-mbeg;
+    if (n1 != n2) return n1-n2;
+    for (; n1 > 0; n1--, dbeg++, mbeg++) {
+        int cmp = buf_get(buf, dbeg) - buf_get(buf, mbeg);
+        if (cmp != 0) return cmp;
+    }
+    return 0;
 }
 
-void buf_del(Buf* buf, unsigned off) {
-    buf->modified = true;
-    Rune r = buf_get(buf, off);
-    log_delete(&(buf->undo), off, &r, 1);
-    clear_redo(buf);
-    delete(buf, off);
+static int rune_match(Buf* buf, unsigned mbeg, unsigned mend, Rune* runes) {
+    for (; *runes; runes++, mbeg++) {
+        int cmp = *runes - buf_get(buf, mbeg);
+        if (cmp != 0) return cmp;
+    }
+    return 0;
 }
 
-unsigned swaplog(Buf* buf, Log** from, Log** to, unsigned pos) {
+static unsigned swaplog(Buf* buf, Log** from, Log** to, unsigned pos) {
     /* pop the last action */
     Log* log = *from;
     if (!log) return pos;
     *from = log->next;
     /* invert the log type and move it to the destination */
     Log* newlog = (Log*)calloc(sizeof(Log), 1);
-    newlog->locked = true;
+    newlog->transid = log->transid;
     if (log->insert) {
         newlog->insert = false;
         size_t n = (log->data.ins.end - log->data.ins.beg);
@@ -259,21 +186,76 @@ unsigned swaplog(Buf* buf, Log** from, Log** to, unsigned pos) {
     return pos;
 }
 
-unsigned buf_undo(Buf* buf, unsigned pos) {
-    return swaplog(buf, &(buf->undo), &(buf->redo), pos);
-}
+/*****************************************************************************/
 
-unsigned buf_redo(Buf* buf, unsigned pos) {
-    return swaplog(buf, &(buf->redo), &(buf->undo), pos);
+void buf_init(Buf* buf) {
+    /* cleanup old data if there is any */
+    if (buf->bufstart) free(buf->bufstart);
+    if (buf->undo) log_clear(&(buf->undo));
+    if (buf->redo) log_clear(&(buf->redo));
+    
+    /* reset the state to defaults */
+    buf->modified    = false;
+    buf->expand_tabs = true;
+    buf->copy_indent = true;
+    buf->charset     = DEFAULT_CHARSET;
+    buf->crlf        = DEFAULT_CRLF;
+    buf->bufsize     = BufSize;
+    buf->bufstart    = (Rune*)malloc(buf->bufsize * sizeof(Rune));
+    buf->bufend      = buf->bufstart + buf->bufsize;
+    buf->gapstart    = buf->bufstart;
+    buf->gapend      = buf->bufend;
+    buf->undo        = NULL;
+    buf->redo        = NULL;
 }
 
-unsigned buf_setln(Buf* buf, unsigned line) {
+unsigned buf_load(Buf* buf, char* path) {
+    if (path && path[0] == '.' && path[1] == '/')
+        path += 2;
     unsigned off = 0;
-    while (line > 1 && off < buf_end(buf))
-        line--, off = buf_byline(buf, off, DOWN);
+    buf->path = stringdup(path);
+    char* addr = strrchr(buf->path, ':');
+    if (addr) *addr = '\0', addr++;
+    if (!strcmp(buf->path,"-")) {
+        buf->charset = UTF_8;
+        Rune r;
+        while (RUNE_EOF != (r = fgetrune(stdin)))
+            buf_insert(buf, false, buf_end(buf), r);
+    } else {
+        FMap file = fmap(buf->path);
+        buf->charset = (file.buf ? charset(file.buf, file.len, &buf->crlf) : UTF_8);
+        /* load the file contents if it has any */
+        if (buf->charset > UTF_8) {
+            die("Unsupported character set");
+        } else if (buf->charset == BINARY) {
+            binload(buf, file);
+        } else {
+            utf8load(buf, file);
+        }
+        funmap(file);
+        if (addr)
+            off = buf_setln(buf, strtoul(addr, NULL, 0));
+    }
+    buf->modified = false;
+    free(buf->undo);
+    buf->undo = NULL;
     return off;
 }
 
+void buf_save(Buf* buf) {
+    if (!buf->path) return;
+    FILE* file = fopen(buf->path, "wb");
+    if (!file) return;
+    if (buf->charset == BINARY)
+        binsave(buf, file);
+    else
+        utf8save(buf, file);
+    fclose(file);
+    buf->modified = false;
+}
+
+/*****************************************************************************/
+
 Rune buf_get(Buf* buf, unsigned off) {
     if (off >= buf_end(buf)) return (Rune)'\n';
     size_t bsz = (buf->gapstart - buf->bufstart);
@@ -283,6 +265,90 @@ Rune buf_get(Buf* buf, unsigned off) {
         return *(buf->gapend + (off - bsz));
 }
 
+unsigned buf_end(Buf* buf) {
+    size_t bufsz = buf->bufend - buf->bufstart;
+    size_t gapsz = buf->gapend - buf->gapstart;
+    return (bufsz - gapsz);
+}
+
+/*****************************************************************************/
+
+unsigned buf_insert(Buf* buf, bool fmt, unsigned off, Rune rune) {
+    buf->modified = true;
+    if (fmt && buf->expand_tabs && rune == '\t') {
+        size_t n = (TabWidth - ((off - buf_bol(buf, off)) % TabWidth));
+        log_insert(buf, &(buf->undo), off, off+n);
+        for(; n > 0; n--) off += insert(buf, off, ' ');
+    } else {
+        size_t n = insert(buf, off, rune);
+        if (n > 0) {
+            log_insert(buf, &(buf->undo), off, off+n);
+            off += n;
+        }
+    }
+    if (fmt && buf->copy_indent && (rune == '\n' || rune == RUNE_CRLF)) {
+        unsigned indent = getindent(buf, off-1);
+        for (; indent > 0; indent--)
+            off = buf_insert(buf, indent, off, '\t');
+    }
+    clear_redo(buf);
+    return off;
+}
+
+unsigned buf_delete(Buf* buf, unsigned beg, unsigned end) {
+    buf->modified = true;
+    clear_redo(buf);
+    for (unsigned i = end-beg; i > 0; i--) {
+        Rune r = buf_get(buf, beg);
+        log_delete(buf, &(buf->undo), beg, &r, 1);
+        delete(buf, beg);
+    }
+    return beg;
+}
+
+unsigned buf_change(Buf* buf, unsigned beg, unsigned end) {
+    /* delete the range first */
+    unsigned off = buf_delete(buf, beg, end);
+    /* now create a new insert item of length 0 witht he same transaction id as 
+       the delete. This will cause subsequent inserts to be coalesced into the 
+       same transaction */
+    Log* dellog = buf->undo; 
+    Log* inslog = (Log*)calloc(sizeof(Log), 1);
+    inslog->transid = dellog->transid;
+    inslog->insert = true;
+    inslog->data.ins.beg = beg;
+    inslog->data.ins.end = beg;
+    inslog->next = dellog;
+    buf->undo = inslog;
+    return off;
+}
+
+/*****************************************************************************/
+
+unsigned buf_undo(Buf* buf, unsigned pos) {
+    if (!buf->undo) return pos;
+    uint transid = buf->undo->transid;
+    while (buf->undo && (buf->undo->transid == transid))
+        pos = swaplog(buf, &(buf->undo), &(buf->redo), pos);
+    return pos;
+}
+
+unsigned buf_redo(Buf* buf, unsigned pos) {
+    if (!buf->redo) return pos;
+    uint transid = buf->redo->transid;
+    while (buf->redo && (buf->redo->transid == transid))
+        pos = swaplog(buf, &(buf->redo), &(buf->undo), pos);
+    return pos;
+}
+
+void buf_loglock(Buf* buf) {
+    Log* log = buf->undo;
+    if (log && log->transid == buf->transid)
+        buf->transid++;
+}
+
+/*****************************************************************************/
+
 bool buf_iseol(Buf* buf, unsigned off) {
     Rune r = buf_get(buf, off);
     return (r == '\n' || r == RUNE_CRLF);
@@ -321,16 +387,52 @@ unsigned buf_rscan(Buf* buf, unsigned pos, Rune r) {
     return (buf_get(buf, off) == r ? off : pos);
 }
 
-static int range_match(Buf* buf, unsigned dbeg, unsigned dend, unsigned mbeg, unsigned mend) {
-    unsigned n1 = dend-dbeg, n2 = mend-mbeg;
-    if (n1 != n2) return n1-n2;
-    for (; n1 > 0; n1--, dbeg++, mbeg++) {
-        int cmp = buf_get(buf, dbeg) - buf_get(buf, mbeg);
-        if (cmp != 0) return cmp;
+/*****************************************************************************/
+
+unsigned buf_byrune(Buf* buf, unsigned pos, int count) {
+    int move = (count < 0 ? -1 : 1);
+    count *= move; // remove the sign if there is one
+    for (; count > 0; count--)
+        if (move < 0) {
+            if (pos > 0) pos--;
+        } else {
+            if (pos < buf_end(buf)) pos++;
+        }
+    return pos;
+}
+
+unsigned buf_byword(Buf* buf, unsigned off, int count) {
+    int move = (count < 0 ? -1 : 1);
+    unsigned end = buf_end(buf);
+    if (move < 0) {
+        for (; off > 0 && !risword(buf_get(buf, off-1)); off--);
+        for (; off > 0 && risword(buf_get(buf, off-1)); off--);
+    } else {
+        for (; off < end && risword(buf_get(buf, off+1)); off++);
+        for (; off < end && !risword(buf_get(buf, off+1)); off++);
+        if (off < buf_end(buf)) off++;
     }
-    return 0;
+    return off;
 }
 
+unsigned buf_byline(Buf* buf, unsigned pos, int count) {
+    int move = (count < 0 ? -1 : 1);
+    count *= move; // remove the sign if there is one
+    for (; count > 0; count--) {
+        if (move < 0) {
+            if (pos > buf_eol(buf, 0))
+                pos = buf_bol(buf, buf_bol(buf, pos)-1);
+        } else {
+            unsigned next = buf_eol(buf, pos)+1;
+            if (next < buf_end(buf))
+                pos = next;
+        }
+    }
+    return pos;
+}
+
+/*****************************************************************************/
+
 void buf_find(Buf* buf, size_t* beg, size_t* end) {
     unsigned dbeg = *beg, dend = *end;
     unsigned mbeg = dbeg+1, mend = dend+1;
@@ -351,41 +453,10 @@ void buf_find(Buf* buf, size_t* beg, size_t* end) {
     }
 }
 
-static Rune* charstorunes(char* str) {
-    size_t len = 0;
-    Rune* runes = NULL;
-    while (str && *str) {
-        Rune rune = 0;
-        size_t length = 0;
-        while (!utf8decode(&rune, &length, *str++));
-        runes = realloc(runes, (len + 1) * sizeof(Rune));
-        runes[len++] = rune;
-    }
-    if (runes) {
-        runes = realloc(runes, (len + 1) * sizeof(Rune));
-        runes[len++] = '\0';
-    }
-    return runes;
-}
-
-static size_t runelen(Rune* runes) {
-    size_t len = 0;
-    for (; runes[len]; len++);
-    return len;
-}
-
-static int rune_match(Buf* buf, unsigned mbeg, unsigned mend, Rune* runes) {
-    for (; *runes; runes++, mbeg++) {
-        int cmp = *runes - buf_get(buf, mbeg);
-        if (cmp != 0) return cmp;
-    }
-    return 0;
-}
-
 void buf_findstr(Buf* buf, char* str, size_t* beg, size_t* end) {
     if (!str) return;
     Rune* runes = charstorunes(str);
-    size_t rlen = runelen(runes);
+    size_t rlen = rstrlen(runes);
     unsigned start = *beg, mbeg = start+1, mend = mbeg + rlen;
     while (mbeg != start) {
         if ((buf_get(buf, mbeg) == runes[0]) &&
@@ -403,54 +474,15 @@ void buf_findstr(Buf* buf, char* str, size_t* beg, size_t* end) {
     free(runes);
 }
 
-unsigned buf_end(Buf* buf) {
-    size_t bufsz = buf->bufend - buf->bufstart;
-    size_t gapsz = buf->gapend - buf->gapstart;
-    return (bufsz - gapsz);
-}
-
-unsigned buf_byrune(Buf* buf, unsigned pos, int count) {
-    int move = (count < 0 ? -1 : 1);
-    count *= move; // remove the sign if there is one
-    for (; count > 0; count--)
-        if (move < 0) {
-            if (pos > 0) pos--;
-        } else {
-            if (pos < buf_end(buf)) pos++;
-        }
-    return pos;
-}
+/*****************************************************************************/
 
-unsigned buf_byword(Buf* buf, unsigned off, int count) {
-    int move = (count < 0 ? -1 : 1);
-    unsigned end = buf_end(buf);
-    if (move < 0) {
-        for (; off > 0 && !risword(buf_get(buf, off-1)); off--);
-        for (; off > 0 && risword(buf_get(buf, off-1)); off--);
-    } else {
-        for (; off < end && risword(buf_get(buf, off+1)); off++);
-        for (; off < end && !risword(buf_get(buf, off+1)); off++);
-        if (off < buf_end(buf)) off++;
-    }
+unsigned buf_setln(Buf* buf, unsigned line) {
+    unsigned off = 0;
+    while (line > 1 && off < buf_end(buf))
+        line--, off = buf_byline(buf, off, DOWN);
     return off;
 }
 
-unsigned buf_byline(Buf* buf, unsigned pos, int count) {
-    int move = (count < 0 ? -1 : 1);
-    count *= move; // remove the sign if there is one
-    for (; count > 0; count--) {
-        if (move < 0) {
-            if (pos > buf_eol(buf, 0))
-                pos = buf_bol(buf, buf_bol(buf, pos)-1);
-        } else {
-            unsigned next = buf_eol(buf, pos)+1;
-            if (next < buf_end(buf))
-                pos = next;
-        }
-    }
-    return pos;
-}
-
 unsigned buf_getcol(Buf* buf, unsigned pos) {
     unsigned curr = buf_bol(buf, pos);
     unsigned col = 0;
@@ -486,8 +518,3 @@ void buf_lastins(Buf* buf, size_t* beg, size_t* end) {
         *end = log->data.ins.end;
     }
 }
-
-void buf_loglock(Buf* buf) {
-    if (buf->undo)
-        buf->undo->locked = true;
-}
index 87f9b0618c2d3be93b2ac582981a86083ae80c69..a174cffe713a8980054d07c8f9230f3566a3be1d 100644 (file)
@@ -49,7 +49,7 @@ int charset(const uint8_t* buf, size_t len, int* crlf) {
 
 void binload(Buf* buf, FMap file) {
     for (size_t i = 0; i < file.len; i++)
-        buf_ins(buf, false, buf_end(buf), file.buf[i]);
+        buf_insert(buf, false, buf_end(buf), file.buf[i]);
 }
 
 void binsave(Buf* buf, FILE* file) {
index 986505ea07d833116063830a962be329b9bef70d..42ed72db008fe480dbc5653356c5eac738a348a1 100644 (file)
@@ -3,6 +3,7 @@
 #include <edit.h>
 #define __USE_XOPEN
 #include <wchar.h>
+#include <ctype.h>
 
 const uint8_t UTF8_SeqBits[] = { 0x00u, 0x80u, 0xC0u, 0xE0u, 0xF0u, 0xF8u, 0xFCu, 0xFEu };
 const uint8_t UTF8_SeqMask[] = { 0x00u, 0xFFu, 0x1Fu, 0x0Fu, 0x07u, 0x03u, 0x01u, 0x00u };
@@ -91,7 +92,7 @@ void utf8load(Buf* buf, FMap file) {
         Rune r = 0;
         size_t len = 0;
         while (!utf8decode(&r, &len, file.buf[i++]));
-        buf_ins(buf, false, buf_end(buf), r);
+        buf_insert(buf, false, buf_end(buf), r);
     }
 }
 
@@ -115,3 +116,36 @@ int runewidth(unsigned col, Rune r) {
     return width;
 }
 
+size_t rstrlen(Rune* runes) {
+    size_t len = 0;
+    for (; runes[len]; len++);
+    return len;
+}
+
+Rune* charstorunes(char* str) {
+    size_t len = 0;
+    Rune* runes = NULL;
+    while (str && *str) {
+        Rune rune = 0;
+        size_t length = 0;
+        while (!utf8decode(&rune, &length, *str++));
+        runes = realloc(runes, (len + 1) * sizeof(Rune));
+        runes[len++] = rune;
+    }
+    if (runes) {
+        runes = realloc(runes, (len + 1) * sizeof(Rune));
+        runes[len++] = '\0';
+    }
+    return runes;
+}
+
+bool risword(Rune r) {
+    return (r < 127 && (isalnum(r) || r == '_' || r == ':' || r == '!' || 
+                          r == '|' || r == '>' || r == '<' || r == '/' ||
+                          r == '.'));
+}
+
+bool risblank(Rune r) {
+    return (r == ' ' || r == '\t' || r == '\n' || r == '\r' || r == RUNE_CRLF);
+}
+
index db3c0aafed40774a453d4f26453142dc73de9401..5e984873e51db2cd4d9d53266e253ff486451521 100644 (file)
@@ -48,16 +48,6 @@ void funmap(FMap file) {
         munmap(file.buf, file.len);
 }
 
-bool risword(Rune r) {
-    return (r < 127 && (isalnum(r) || r == '_' || r == ':' || r == '!' || 
-                          r == '|' || r == '>' || r == '<' || r == '/' ||
-                          r == '.'));
-}
-
-bool risblank(Rune r) {
-    return (r == ' ' || r == '\t' || r == '\n' || r == '\r' || r == RUNE_CRLF);
-}
-
 char* stringdup(const char* s) {
     char* ns = (char*)malloc(strlen(s) + 1);
     strcpy(ns,s);
@@ -80,3 +70,4 @@ char* fdgets(int fd) {
 char* chomp(char* in) {
     return strtok(in, "\r\n");
 }
+
index 3f173a869ecd778b39452d11bf02ca2c9caef29f..5722e79ba96ccfd9177b33b4584bc73b9d8fccf1 100644 (file)
@@ -411,9 +411,13 @@ void view_insert(View* view, bool indent, Rune rune) {
     /* ignore non-printable control characters */
     if (!isspace(rune) && rune < 0x20)
         return;
-    if (num_selected(view->selection))
-        view_delete(view, RIGHT, false);
-    view->selection.end = buf_ins(&(view->buffer), indent, view->selection.end, rune);
+    if (num_selected(view->selection)) {
+        Sel sel = view->selection;
+        selswap(&sel);
+        sel.beg = sel.end = buf_change(&(view->buffer), sel.beg, sel.end);
+        view->selection = sel;
+    }
+    view->selection.end = buf_insert(&(view->buffer), indent, view->selection.end, rune);
     view->selection.beg = view->selection.end;
     view->selection.col = buf_getcol(&(view->buffer), view->selection.end);
     view->sync_needed   = true;
@@ -423,17 +427,15 @@ void view_delete(View* view, int dir, bool byword) {
     Sel sel = view->selection;
     selswap(&sel);
     size_t num = num_selected(sel);
-    if (num != 0) {
-        for (size_t i = 0; i < num; i++)
-            buf_del(&(view->buffer), sel.beg);
-        sel.end = sel.beg;
-    } else {
-        if ((dir == LEFT) && (sel.end > 0))
-            buf_del(&(view->buffer), --sel.end);
-        else if ((dir == RIGHT) && (sel.end < buf_end(&(view->buffer))))
-            buf_del(&(view->buffer), sel.end);
-    }
-    view->selection.beg = view->selection.end = sel.end;
+    if (num != 0)
+        sel.end = buf_delete(&(view->buffer), sel.beg, sel.end);
+    else if ((dir == LEFT) && (sel.end > 0))
+        sel.end = buf_delete(&(view->buffer), --sel.end, sel.end);
+    else if ((dir == RIGHT) && (sel.end < buf_end(&(view->buffer))))
+        sel.end = buf_delete(&(view->buffer), sel.end, sel.end+1);
+    sel.beg = sel.end;
+    /* update the selection */
+    view->selection = sel;
     view->selection.col = buf_getcol(&(view->buffer), view->selection.end);
     view->sync_needed = true;
 }
@@ -507,7 +509,7 @@ void view_append(View* view, char* str) {
     if (view->selection.end != end)
         view->selection = (Sel){ .beg = end, .end = end };
     if (!num_selected(view->selection) && !buf_iseol(&(view->buffer), view->selection.end-1)) {
-        buf_ins(&(view->buffer), false, view->selection.end++, '\n');
+        buf_insert(&(view->buffer), false, view->selection.end++, '\n');
         view->selection.beg++;
     }
     view_putstr(view, str);
@@ -583,20 +585,20 @@ void view_indent(View* view, int dir) {
     if (num_selected(view->selection) == 0) return;
     while (off >= view->selection.beg) {
         if (dir == RIGHT) {
-            buf_ins(buf, true, off, '\t');
+            buf_insert(buf, true, off, '\t');
             view->selection.end += indoff;
         } else if (dir == LEFT) {
             unsigned i = 4;
             for (; i > 0; i--) {
                 if (' ' == buf_get(buf, off)) {
-                    buf_del(buf, off);
+                    buf_delete(buf, off, off+1);
                     view->selection.end--;
                 } else {
                     break;
                 }
             }
             if (i && '\t' == buf_get(buf, off)) {
-                buf_del(buf, off);
+                buf_delete(buf, off, off+1);
                 view->selection.end--;
             }
         }
index fc351e3cf5b34298ac38764c4da351d92e6fe516..392537c9ee9f69907c4b2fae3edd73ade3517b4a 100644 (file)
@@ -12,7 +12,7 @@ static void set_buffer_text(char* str) {
     for (Rune* curr = TestBuf.bufstart; curr < TestBuf.bufend; curr++)
         *curr = '-';
     while (*str)
-        buf_ins(&TestBuf, false, i++, (Rune)*str++);
+        buf_insert(&TestBuf, false, i++, (Rune)*str++);
 }
 
 static bool buf_text_eq(char* str) {
@@ -34,41 +34,41 @@ TEST_SUITE(BufferTests) {
      *************************************************************************/
     /* Insertions
      *************************************************************************/
-    TEST(buf_ins should insert at 0 in empty buf) {
+    TEST(buf_insert should insert at 0 in empty buf) {
         buf_init(&TestBuf);
-        buf_ins(&TestBuf, false, 0, 'a');
+        buf_insert(&TestBuf, false, 0, 'a');
         CHECK(buf_text_eq("a"));
     }
 
-    TEST(buf_ins should insert at 0) {
+    TEST(buf_insert should insert at 0) {
         buf_init(&TestBuf);
-        buf_ins(&TestBuf, false, 0, 'b');
-        buf_ins(&TestBuf, false, 0, 'a');
+        buf_insert(&TestBuf, false, 0, 'b');
+        buf_insert(&TestBuf, false, 0, 'a');
         CHECK(buf_text_eq("ab"));
     }
 
-    TEST(buf_ins should insert at 1) {
+    TEST(buf_insert should insert at 1) {
         buf_init(&TestBuf);
-        buf_ins(&TestBuf, false, 0, 'a');
-        buf_ins(&TestBuf, false, 1, 'b');
+        buf_insert(&TestBuf, false, 0, 'a');
+        buf_insert(&TestBuf, false, 1, 'b');
         CHECK(buf_text_eq("ab"));
     }
 
-    TEST(buf_ins should insert at 1) {
+    TEST(buf_insert should insert at 1) {
         buf_init(&TestBuf);
-        buf_ins(&TestBuf, false, 0, 'a');
-        buf_ins(&TestBuf, false, 1, 'c');
-        buf_ins(&TestBuf, false, 1, 'b');
+        buf_insert(&TestBuf, false, 0, 'a');
+        buf_insert(&TestBuf, false, 1, 'c');
+        buf_insert(&TestBuf, false, 1, 'b');
         CHECK(buf_text_eq("abc"));
     }
 
-    TEST(buf_ins should sentence in larger text) {
+    TEST(buf_insert should sentence in larger text) {
         set_buffer_text(
             "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
         );
 
-        buf_ins(&TestBuf, false, 5, ' ');
-        buf_ins(&TestBuf, false, 6, 'a');
+        buf_insert(&TestBuf, false, 5, ' ');
+        buf_insert(&TestBuf, false, 6, 'a');
 
         CHECK(buf_text_eq(
             "Lorem a ipsum dolor sit amet, consectetur adipiscing elit."
@@ -89,13 +89,13 @@ TEST_SUITE(BufferTests) {
 
     TEST(buf_get should indexed character before the gap) {
         set_buffer_text("ac");
-        buf_ins(&TestBuf, false, 1, 'b');
+        buf_insert(&TestBuf, false, 1, 'b');
         CHECK('a' == buf_get(&TestBuf, 0));
     }
 
     TEST(buf_get should indexed character after the gap) {
         set_buffer_text("ac");
-        buf_ins(&TestBuf, false, 1, 'b');
+        buf_insert(&TestBuf, false, 1, 'b');
         CHECK('c' == buf_get(&TestBuf, 2));
     }
 
diff --git a/xedit.c b/xedit.c
index 8b06e84bc1a25aa2d0bf5d74b3f4afa9ff2d41ac..40c279745729ec3db2835eaee006605a8922f487 100644 (file)
--- a/xedit.c
+++ b/xedit.c
@@ -160,7 +160,7 @@ static KeyBinding Bindings[] = {
 
     /* Implementation Specific */
     { ModNone, KEY_ESCAPE, select_prev  },
-    //{ ModCtrl, KEY_ESCAPE, debug_dump   },
+    { ModCtrl, KEY_ESCAPE, debug_dump   },
     { ModCtrl, 't',        change_focus },
     { ModCtrl, 'q',        quit         },
     { ModCtrl, 'f',        search       },
@@ -622,15 +622,14 @@ static void eol_mode(void) {
     exec(crlf ? "|dos2unix" : "|unix2dos");
 }
 
-#if 0
 static void dump_log(Log* log) {
     for (; log != NULL; log = log->next) {
         if (log->insert) {
             printf("    INS %d %lu %lu\n", 
-                log->locked, log->data.ins.beg, log->data.ins.end);
+                log->transid, log->data.ins.beg, log->data.ins.end);
         } else {
-            printf("    DEL %d %lu %lu ''\n",
-                log->locked, log->data.del.off, log->data.del.len);
+            printf("    DEL %d %lu %lu\n",
+                log->transid, log->data.del.off, log->data.del.len);
         }
     }
 }
@@ -654,7 +653,6 @@ static void debug_dump(void) {
     printf("redo:\n");
     dump_log(buf->redo);
 }
-#endif
 
 /* Tag/Cmd Execution
  *****************************************************************************/
diff --git a/xpick.c b/xpick.c
index 1008c5ddb6d1f4487dafc230438bc01f08495c8d..dc38ec9ddea4ff1370683ac3ff552155e5af3233 100644 (file)
--- a/xpick.c
+++ b/xpick.c
@@ -181,13 +181,13 @@ static void keyboard_input(int mods, uint32_t key) {
             break;
         case '\b':
             if (Pos > 0)
-                buf_del(&Query, --Pos);
+                buf_delete(&Query, --Pos, Pos);
             break;
         case RUNE_ERR:
             break;
         default:
             ChoiceIdx = 0;
-            buf_ins(&Query, false, Pos++, key);
+            buf_insert(&Query, false, Pos++, key);
             break;
     }
     score();