tlbuf: preserve marks during change and delete commands - neatvi - [fork] simple vi-type editor with UTF-8 support
git clone git://src.adamsgaard.dk/neatvi
Log
Files
Refs
README
---
commit 70f7eb3e153a4b749d07c242945d4a693085c6fc
parent 86c9ce2ce6d2ca663846199b322fcb341e23d527
Author: Ali Gholami Rudi 
Date:   Thu,  3 Sep 2015 18:22:28 +0430

lbuf: preserve marks during change and delete commands

Also, the undo command restores the marks inside the changing region.

Diffstat:
  M ex.c                                |      29 +++++++++++++----------------
  M lbuf.c                              |     171 ++++++++++++++-----------------
  M vi.c                                |      41 ++++++++++++-------------------
  M vi.h                                |       5 ++---

4 files changed, 110 insertions(+), 136 deletions(-)
---
diff --git a/ex.c b/ex.c
t@@ -336,8 +336,7 @@ static int ec_edit(char *ec)
                 bufs_switch(bufs_open(path));
         fd = open(ex_path(), O_RDONLY);
         if (fd >= 0) {
-                lbuf_rm(xb, 0, lbuf_len(xb));
-                lbuf_rd(xb, fd, 0);
+                lbuf_rd(xb, fd, 0, lbuf_len(xb));
                 close(fd);
                 snprintf(msg, sizeof(msg), "\"%s\"  %d lines  [r]\n",
                                 ex_path(), lbuf_len(xb));
t@@ -362,19 +361,21 @@ static int ec_read(char *ec)
         if (ex_region(loc, &beg, &end))
                 return 1;
         if (arg[0] == '!') {
+                int pos = MIN(xrow + 1, lbuf_len(xb));
                 if (ex_expand(arg, ex_argeol(ec)))
                         return 1;
                 obuf = cmd_pipe(arg + 1, NULL, 0, 1);
                 if (obuf)
-                        lbuf_put(xb, MIN(xrow + 1, lbuf_len(xb)), obuf);
+                        lbuf_edit(xb, obuf, pos, pos);
                 free(obuf);
         } else {
                 int fd = open(path, O_RDONLY);
+                int pos = lbuf_len(xb) ? end : 0;
                 if (fd < 0) {
                         ex_show("read failed\n");
                         return 1;
                 }
-                lbuf_rd(xb, fd, lbuf_len(xb) ? end : 0);
+                lbuf_rd(xb, fd, pos, pos);
                 close(fd);
         }
         xrow = end + lbuf_len(xb) - n - 1;
t@@ -445,11 +446,6 @@ static int ec_insert(char *ec)
         ex_loc(ec, loc);
         if (ex_region(loc, &beg, &end) && (beg != 0 || end != 0))
                 return 1;
-        if (cmd[0] == 'c') {
-                if (lbuf_len(xb))
-                        lbuf_rm(xb, beg, end);
-                end = beg + 1;
-        }
         sb = sbuf_make();
         while ((s = ex_read(""))) {
                 if (!strcmp(".", s)) {
t@@ -461,10 +457,12 @@ static int ec_insert(char *ec)
                 free(s);
         }
         if (cmd[0] == 'a')
-                if (end > lbuf_len(xb))
-                        end = lbuf_len(xb);
+                if (beg + 1 <= lbuf_len(xb))
+                        beg++;
+        if (cmd[0] != 'c')
+                end = beg;
         n = lbuf_len(xb);
-        lbuf_put(xb, end, sbuf_buf(sb));
+        lbuf_edit(xb, sbuf_buf(sb), beg, end);
         xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
         sbuf_free(sb);
         return 0;
t@@ -522,7 +520,7 @@ static int ec_delete(char *ec)
         if (ex_region(loc, &beg, &end) || !lbuf_len(xb))
                 return 1;
         ex_yank(arg[0], beg, end);
-        lbuf_rm(xb, beg, end);
+        lbuf_edit(xb, NULL, beg, end);
         xrow = beg;
         return 0;
 }
t@@ -553,7 +551,7 @@ static int ec_put(char *ec)
         buf = reg_get(arg[0], &lnmode);
         if (!buf || ex_region(loc, &beg, &end))
                 return 1;
-        lbuf_put(xb, end, buf);
+        lbuf_edit(xb, buf, end, end);
         xrow = MIN(lbuf_len(xb) - 1, end + lbuf_len(xb) - n - 1);
         return 0;
 }
t@@ -647,8 +645,7 @@ static int ec_substitute(char *ec)
                                 break;
                 }
                 sbuf_str(r, ln);
-                lbuf_rm(xb, i, i + 1);
-                lbuf_put(xb, i, sbuf_buf(r));
+                lbuf_edit(xb, sbuf_buf(r), i, i + 1);
                 sbuf_free(r);
         }
         rset_free(re);
diff --git a/lbuf.c b/lbuf.c
t@@ -1,3 +1,4 @@
+#include 
 #include 
 #include 
 #include 
t@@ -8,9 +9,9 @@
 
 /* line operations */
 struct lopt {
-        char *buf;                /* text inserted or deleted */
-        int ins;                /* insertion operation if non-zero */
-        int beg, end;
+        char *ins;                /* inserted text */
+        char *del;                /* deleted text */
+        int pos, n_ins, n_del;        /* modification location */
         int seq;                /* operation number */
         int *mark, *mark_off;        /* saved marks */
 };
t@@ -27,7 +28,6 @@ struct lbuf {
         int hist_sz;                /* size of hist[] */
         int hist_n;                /* current history head in hist[] */
         int hist_u;                /* current undo head in hist[] */
-        int mod_new;                /* clear modification marks */
         int useq_zero;                /* useq for lbuf_saved() */
         int useq_last;                /* useq before hist[] */
 };
t@@ -45,11 +45,33 @@ struct lbuf *lbuf_make(void)
 
 static void lopt_done(struct lopt *lo)
 {
-        free(lo->buf);
+        free(lo->ins);
+        free(lo->del);
         free(lo->mark);
         free(lo->mark_off);
 }
 
+static void lbuf_savemark(struct lbuf *lb, struct lopt *lo, int m)
+{
+        if (lb->mark[m] >= 0) {
+                if (!lo->mark) {
+                        lo->mark = malloc(sizeof(lb->mark));
+                        lo->mark_off = malloc(sizeof(lb->mark_off));
+                        memset(lo->mark, 0xff, sizeof(lb->mark));
+                }
+                lo->mark[m] = lb->mark[m];
+                lo->mark_off[m] = lb->mark_off[m];
+        }
+}
+
+static void lbuf_loadmark(struct lbuf *lb, struct lopt *lo, int m)
+{
+        if (lo->mark && lo->mark[m] >= 0) {
+                lb->mark[m] = lo->mark[m];
+                lb->mark_off[m] = lo->mark_off[m];
+        }
+}
+
 void lbuf_free(struct lbuf *lb)
 {
         int i;
t@@ -79,51 +101,46 @@ static void lbuf_insertline(struct lbuf *lb, int pos, char *s)
         lb->ln[pos] = s;
 }
 
-/* low-level insertion */
-static void lbuf_insert(struct lbuf *lb, int pos, char *s)
+/* low-level replacement */
+static void lbuf_replace(struct lbuf *lb, char *s, int pos, int n_del)
 {
-        int lb_len = lbuf_len(lb);
-        int beg = pos, end;
         char *r;
+        int n_ins = 0;
         int i;
-        while ((r = strchr(s, '\n'))) {
+        for (i = 0; i < n_del; i++)
+                free(lb->ln[pos + i]);
+        memmove(lb->ln + pos, lb->ln + pos + n_del,
+                (lb->ln_n - pos - n_del) * sizeof(lb->ln[0]));
+        lb->ln_n -= n_del;
+        while (s && (r = strchr(s, '\n'))) {
                 char *n = malloc(r - s + 2);
                 memcpy(n, s, r - s + 1);
                 n[r - s + 1] = '\0';
-                lbuf_insertline(lb, pos++, n);
+                lbuf_insertline(lb, pos + n_ins++, n);
                 s = r + 1;
         }
-        for (i = 0; i < LEN(lb->mark); i++)        /* updating marks */
-                if (lb->mark[i] >= pos)
-                        lb->mark[i] += lbuf_len(lb) - lb_len;
-        end = beg + lbuf_len(lb) - lb_len;
-        if (lb->mod_new || lb->mark['['] < 0 || lb->mark['['] > beg)
-                lbuf_mark(lb, '[', beg, 0);
-        if (lb->mod_new || lb->mark[']'] < 0 || lb->mark[']'] < end - 1)
-                lbuf_mark(lb, ']', end - 1, 0);
-        lb->mod_new = 0;
+        for (i = 0; i < LEN(lb->mark); i++) {        /* updating marks */
+                if (!s && lb->mark[i] >= pos && lb->mark[i] < pos + n_del)
+                        lb->mark[i] = -1;
+                else if (lb->mark[i] >= pos + n_del)
+                        lb->mark[i] += n_ins - n_del;
+                else if (lb->mark[i] >= pos + n_ins)
+                        lb->mark[i] = pos + n_ins - 1;
+        }
+        lbuf_mark(lb, '[', pos, 0);
+        lbuf_mark(lb, ']', pos + n_ins - n_del, 0);
 }
 
-/* low-level deletion */
-static void lbuf_delete(struct lbuf *lb, int beg, int end)
+static int uc_newlines(char *s)
 {
-        int i;
-        for (i = beg; i < end; i++)
-                free(lb->ln[i]);
-        memmove(lb->ln + beg, lb->ln + end, (lb->ln_n - end) * sizeof(lb->ln[0]));
-        lb->ln_n -= end - beg;
-        for (i = 0; i < LEN(lb->mark); i++)        /* updating marks */
-                if (lb->mark[i] > beg)
-                        lb->mark[i] = MAX(beg, lb->mark[i] + beg - end);
-        if (lb->mod_new || lb->mark['['] < 0 || lb->mark['['] > beg)
-                lbuf_mark(lb, '[', beg, 0);
-        if (lb->mod_new || lb->mark[']'] < 0 || lb->mark[']'] < beg)
-                lbuf_mark(lb, ']', beg, 0);
-        lb->mod_new = 0;
+        int n;
+        for (n = 0; (s = strchr(s, '\n')); n++)
+                s++;
+        return n;
 }
 
 /* append undo/redo history */
-static void lbuf_opt(struct lbuf *lb, int ins, int beg, int end)
+static void lbuf_opt(struct lbuf *lb, char *buf, int pos, int n_del)
 {
         struct lopt *lo;
         int i;
t@@ -142,14 +159,19 @@ static void lbuf_opt(struct lbuf *lb, int ins, int beg, int end)
         lb->hist_n++;
         lb->hist_u = lb->hist_n;
         memset(lo, 0, sizeof(*lo));
-        lo->ins = ins;
-        lo->beg = beg;
-        lo->end = end;
-        lo->buf = lbuf_cp(lb, beg, end);
+        lo->pos = pos;
+        lo->n_del = n_del;
+        lo->del = n_del ? lbuf_cp(lb, pos, pos + n_del) : NULL;
+        lo->n_ins = buf ? uc_newlines(buf) : 0;
+        lo->ins = buf ? uc_dup(buf) : NULL;
         lo->seq = lb->useq;
+        for (i = 0; i < LEN(lb->mark); i++)
+                if (lb->mark[i] >= pos && lb->mark[i] < pos + n_del)
+                        if (isalpha(i))
+                                lbuf_savemark(lb, lo, i);
 }
 
-void lbuf_rd(struct lbuf *lbuf, int fd, int pos)
+void lbuf_rd(struct lbuf *lbuf, int fd, int beg, int end)
 {
         char buf[1 << 10];
         struct sbuf *sb;
t@@ -157,7 +179,7 @@ void lbuf_rd(struct lbuf *lbuf, int fd, int pos)
         sb = sbuf_make();
         while ((nr = read(fd, buf, sizeof(buf))) > 0)
                 sbuf_mem(sb, buf, nr);
-        lbuf_put(lbuf, pos, sbuf_buf(sb));
+        lbuf_edit(lbuf, sbuf_buf(sb), beg, end);
         sbuf_free(sb);
 }
 
t@@ -168,23 +190,17 @@ void lbuf_wr(struct lbuf *lbuf, int fd, int beg, int end)
                 write(fd, lbuf->ln[i], strlen(lbuf->ln[i]));
 }
 
-void lbuf_rm(struct lbuf *lb, int beg, int end)
+/* replace lines beg through end with buf */
+void lbuf_edit(struct lbuf *lb, char *buf, int beg, int end)
 {
+        if (beg > lb->ln_n)
+                beg = lb->ln_n;
         if (end > lb->ln_n)
                 end = lb->ln_n;
-        if (beg == end)
-                return;
-        lbuf_opt(lb, 0, beg, end);
-        lbuf_delete(lb, beg, end);
-}
-
-void lbuf_put(struct lbuf *lb, int pos, char *s)
-{
-        int lb_len = lbuf_len(lb);
-        if (!*s)
+        if (beg == end && !buf)
                 return;
-        lbuf_insert(lb, pos, s);
-        lbuf_opt(lb, 1, pos, pos + lbuf_len(lb) - lb_len);
+        lbuf_opt(lb, buf, beg, end - beg);
+        lbuf_replace(lb, buf, beg, end - beg);
 }
 
 char *lbuf_cp(struct lbuf *lb, int beg, int end)
t@@ -226,42 +242,18 @@ int lbuf_jump(struct lbuf *lbuf, int mark, int *pos, int *off)
         return 0;
 }
 
-static void lbuf_savemarks(struct lbuf *lb, struct lopt *lo)
-{
-        int i;
-        lo->mark = malloc(sizeof(lb->mark));
-        lo->mark_off = malloc(sizeof(lb->mark_off));
-        for (i = 0; i < LEN(lb->mark); i++)
-                lo->mark[i] = -1;
-        lo->mark['*'] = lb->mark['*'];
-        lo->mark_off['*'] = lb->mark_off['*'];
-}
-
-static void lbuf_loadmarks(struct lbuf *lb, struct lopt *lo)
-{
-        int i;
-        for (i = 0; lo->mark && i < LEN(lb->mark); i++) {
-                if (lo->mark[i] >= 0) {
-                        lb->mark[i] = lo->mark[i];
-                        lb->mark_off[i] = lo->mark_off[i];
-                }
-        }
-}
-
 int lbuf_undo(struct lbuf *lb)
 {
-        int useq;
+        int useq, i;
         if (!lb->hist_u)
                 return 1;
         useq = lb->hist[lb->hist_u - 1].seq;
-        lb->mod_new = 1;
         while (lb->hist_u && lb->hist[lb->hist_u - 1].seq == useq) {
                 struct lopt *lo = &lb->hist[--(lb->hist_u)];
-                if (lo->ins)
-                        lbuf_delete(lb, lo->beg, lo->end);
-                else
-                        lbuf_insert(lb, lo->beg, lo->buf);
-                lbuf_loadmarks(lb, lo);
+                lbuf_replace(lb, lo->del, lo->pos, lo->n_ins);
+                lbuf_loadmark(lb, lo, '*');
+                for (i = 0; i < LEN(lb->mark); i++)
+                        lbuf_loadmark(lb, lo, i);
         }
         return 0;
 }
t@@ -272,14 +264,10 @@ int lbuf_redo(struct lbuf *lb)
         if (lb->hist_u == lb->hist_n)
                 return 1;
         useq = lb->hist[lb->hist_u].seq;
-        lb->mod_new = 1;
         while (lb->hist_u < lb->hist_n && lb->hist[lb->hist_u].seq == useq) {
                 struct lopt *lo = &lb->hist[lb->hist_u++];
-                if (lo->ins)
-                        lbuf_insert(lb, lo->beg, lo->buf);
-                else
-                        lbuf_delete(lb, lo->beg, lo->end);
-                lbuf_loadmarks(lb, lo);
+                lbuf_replace(lb, lo->ins, lo->pos, lo->n_del);
+                lbuf_loadmark(lb, lo, '*');
         }
         return 0;
 }
t@@ -309,8 +297,7 @@ int lbuf_modified(struct lbuf *lb)
 {
         struct lopt *lo = lb->hist_n ? &lb->hist[lb->hist_n - 1] : NULL;
         if (lb->hist_u == lb->hist_n && lo && !lo->mark)
-                lbuf_savemarks(lb, lo);
-        lb->mod_new = 1;
+                lbuf_savemark(lb, lo, '*');
         lb->useq++;
         return lbuf_seq(lb) != lb->useq_zero;
 }
diff --git a/vi.c b/vi.c
t@@ -572,13 +572,14 @@ static void vi_delete(int r1, int o1, int r2, int o2, int lnmode)
         free(region);
         pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
         post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
-        lbuf_rm(xb, r1, r2 + 1);
         if (!lnmode) {
                 struct sbuf *sb = sbuf_make();
                 sbuf_str(sb, pref);
                 sbuf_str(sb, post);
-                lbuf_put(xb, r1, sbuf_buf(sb));
+                lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
                 sbuf_free(sb);
+        } else {
+                lbuf_edit(xb, NULL, r1, r2 + 1);
         }
         xrow = r1;
         xoff = lnmode ? lbuf_indents(xb, xrow) : o1;
t@@ -653,8 +654,7 @@ static void vi_change(int r1, int o1, int r2, int o2, int lnmode)
         vi_drawrm(r1, r2, 0);
         rep = vi_input(pref, post, &row, &off);
         if (rep) {
-                lbuf_rm(xb, r1, r2 + 1);
-                lbuf_put(xb, r1, rep);
+                lbuf_edit(xb, rep, r1, r2 + 1);
                 xrow = r1 + row - 1;
                 xoff = off;
                 free(rep);
t@@ -683,16 +683,15 @@ static void vi_case(int r1, int o1, int r2, int o2, int lnmode, int cmd)
         }
         pref = lnmode ? uc_dup("") : uc_sub(lbuf_get(xb, r1), 0, o1);
         post = lnmode ? uc_dup("\n") : uc_sub(lbuf_get(xb, r2), o2, -1);
-        lbuf_rm(xb, r1, r2 + 1);
         if (!lnmode) {
                 struct sbuf *sb = sbuf_make();
                 sbuf_str(sb, pref);
                 sbuf_str(sb, region);
                 sbuf_str(sb, post);
-                lbuf_put(xb, r1, sbuf_buf(sb));
+                lbuf_edit(xb, sbuf_buf(sb), r1, r2 + 1);
                 sbuf_free(sb);
         } else {
-                lbuf_put(xb, r1, region);
+                lbuf_edit(xb, region, r1, r2 + 1);
         }
         xrow = r2;
         xoff = lnmode ? lbuf_indents(xb, r2) : o2;
t@@ -711,10 +710,8 @@ static void vi_pipe(int r1, int r2)
                 return;
         text = lbuf_cp(xb, r1, r2 + 1);
         rep = cmd_pipe(cmd, text, 1, 1);
-        if (rep) {
-                lbuf_rm(xb, r1, r2 + 1);
-                lbuf_put(xb, r1, rep);
-        }
+        if (rep)
+                lbuf_edit(xb, rep, r1, r2 + 1);
         free(cmd);
         free(text);
         free(rep);
t@@ -734,8 +731,7 @@ static void vi_shift(int r1, int r2, int dir)
                 else
                         ln = ln[0] == ' ' || ln[0] == '\t' ? ln + 1 : ln;
                 sbuf_str(sb, ln);
-                lbuf_rm(xb, i, i + 1);
-                lbuf_put(xb, i, sbuf_buf(sb));
+                lbuf_edit(xb, sbuf_buf(sb), i, i + 1);
                 sbuf_free(sb);
         }
         xrow = r1;
t@@ -813,11 +809,9 @@ static int vc_insert(int cmd)
         vi_drawrm(xrow, xrow, cmd == 'o' || cmd == 'O');
         rep = vi_input(pref, post, &row, &off);
         if ((cmd == 'o' || cmd == 'O') && !lbuf_len(xb))
-                lbuf_put(xb, 0, "\n");
+                lbuf_edit(xb, "\n", 0, 0);
         if (rep) {
-                if (cmd != 'o' && cmd != 'O')
-                        lbuf_rm(xb, xrow, xrow + 1);
-                lbuf_put(xb, xrow, rep);
+                lbuf_edit(xb, rep, xrow, xrow + (cmd != 'o' && cmd != 'O'));
                 xrow += row - 1;
                 xoff = off;
                 free(rep);
t@@ -842,10 +836,10 @@ static int vc_put(int cmd)
                 for (i = 0; i < cnt; i++)
                         sbuf_str(sb, buf);
                 if (!lbuf_len(xb))
-                        lbuf_put(xb, 0, "\n");
+                        lbuf_edit(xb, "\n", 0, 0);
                 if (cmd == 'p')
                         xrow++;
-                lbuf_put(xb, xrow, sbuf_buf(sb));
+                lbuf_edit(xb, sbuf_buf(sb), xrow, xrow);
                 xoff = lbuf_indents(xb, xrow);
                 sbuf_free(sb);
         } else {
t@@ -860,8 +854,7 @@ static int vc_put(int cmd)
                 s = uc_sub(ln, off, -1);
                 sbuf_str(sb, s);
                 free(s);
-                lbuf_rm(xb, xrow, xrow + 1);
-                lbuf_put(xb, xrow, sbuf_buf(sb));
+                lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
                 xoff = off + uc_slen(buf) * cnt - 1;
                 sbuf_free(sb);
         }
t@@ -903,8 +896,7 @@ static int vc_join(void)
                 sbuf_mem(sb, ln, lnend - ln);
         }
         sbuf_chr(sb, '\n');
-        lbuf_rm(xb, beg, end);
-        lbuf_put(xb, beg, sbuf_buf(sb));
+        lbuf_edit(xb, sbuf_buf(sb), beg, end);
         xoff = off;
         sbuf_free(sb);
         return 0;
t@@ -963,8 +955,7 @@ static int vc_replace(void)
         for (i = 0; i < cnt; i++)
                 sbuf_str(sb, cs);
         sbuf_str(sb, post);
-        lbuf_rm(xb, xrow, xrow + 1);
-        lbuf_put(xb, xrow, sbuf_buf(sb));
+        lbuf_edit(xb, sbuf_buf(sb), xrow, xrow + 1);
         off += cnt - 1;
         xoff = off;
         sbuf_free(sb);
diff --git a/vi.h b/vi.h
t@@ -8,11 +8,10 @@
 /* line buffer, managing a number of lines */
 struct lbuf *lbuf_make(void);
 void lbuf_free(struct lbuf *lbuf);
-void lbuf_rd(struct lbuf *lbuf, int fd, int pos);
+void lbuf_rd(struct lbuf *lbuf, int fd, int beg, int end);
 void lbuf_wr(struct lbuf *lbuf, int fd, int beg, int end);
-void lbuf_rm(struct lbuf *lbuf, int beg, int end);
+void lbuf_edit(struct lbuf *lbuf, char *s, int beg, int end);
 char *lbuf_cp(struct lbuf *lbuf, int beg, int end);
-void lbuf_put(struct lbuf *lbuf, int pos, char *s);
 char *lbuf_get(struct lbuf *lbuf, int pos);
 int lbuf_len(struct lbuf *lbuf);
 void lbuf_mark(struct lbuf *lbuf, int mark, int pos, int off);