tlbuf: preserve marks during change and delete commands - neatvi - [fork] simple vi-type editor with UTF-8 support
 (HTM) git clone git://src.adamsgaard.dk/neatvi
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit 70f7eb3e153a4b749d07c242945d4a693085c6fc
 (DIR) parent 86c9ce2ce6d2ca663846199b322fcb341e23d527
 (HTM) Author: Ali Gholami Rudi <ali@rudi.ir>
       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(-)
       ---
 (DIR) 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);
 (DIR) diff --git a/lbuf.c b/lbuf.c
       t@@ -1,3 +1,4 @@
       +#include <ctype.h>
        #include <stdlib.h>
        #include <stdio.h>
        #include <string.h>
       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;
        }
 (DIR) 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);
 (DIR) 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);