#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;
}
}
-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;
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++;
}
}
-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) {
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);
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);
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);
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;
}
}
-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]) &&
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;
*end = log->data.ins.end;
}
}
-
-void buf_loglock(Buf* buf) {
- if (buf->undo)
- buf->undo->locked = true;
-}