From: Michael D. Lowis Date: Mon, 19 Dec 2016 20:29:52 +0000 (-0500) Subject: Reworked undo/redo logic to allow for batch operations. The first implemented batch... X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=3d970bdf3dff0456597534052c172206acf37a67;p=projs%2Ftide.git Reworked undo/redo logic to allow for batch operations. The first implemented batch operation is a change or replacement. This constitutes inserting text with a non-empty selection. This will log a delete and an insert with the same transaction id allowing both operations to be undone and redone as if they were a single operation --- diff --git a/inc/edit.h b/inc/edit.h index 94aca30..678a376 100644 --- a/inc/edit.h +++ b/inc/edit.h @@ -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 *****************************************************************************/ diff --git a/inc/utf.h b/inc/utf.h index 3bacfe9..1a81753 100644 --- 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); diff --git a/libedit/buf.c b/libedit/buf.c index 268d20d..5737699 100644 --- a/libedit/buf.c +++ b/libedit/buf.c @@ -3,86 +3,9 @@ #include #include -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; -} diff --git a/libedit/charset.c b/libedit/charset.c index 87f9b06..a174cff 100644 --- a/libedit/charset.c +++ b/libedit/charset.c @@ -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) { diff --git a/libedit/utf8.c b/libedit/utf8.c index 986505e..42ed72d 100644 --- a/libedit/utf8.c +++ b/libedit/utf8.c @@ -3,6 +3,7 @@ #include #define __USE_XOPEN #include +#include 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); +} + diff --git a/libedit/utils.c b/libedit/utils.c index db3c0aa..5e98487 100644 --- a/libedit/utils.c +++ b/libedit/utils.c @@ -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"); } + diff --git a/libedit/view.c b/libedit/view.c index 3f173a8..5722e79 100644 --- a/libedit/view.c +++ b/libedit/view.c @@ -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--; } } diff --git a/tests/buf.c b/tests/buf.c index fc351e3..392537c 100644 --- a/tests/buf.c +++ b/tests/buf.c @@ -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 8b06e84..40c2797 100644 --- 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 1008c5d..dc38ec9 100644 --- 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();