vi: move xoff before EOL after motions
[neatvi.git] / lbuf.c
blob8daed98c81ee685988b1a2e5c1b99ed96721bfb3
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include "vi.h"
7 #define MARK(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' : 30)
9 /* line operations */
10 struct lopt {
11 char *buf; /* text inserted or deleted */
12 int ins; /* insertion operation if non-zero */
13 int beg, end;
14 int seq; /* operation number */
17 /* line buffers */
18 struct lbuf {
19 int mark[32]; /* buffer marks */
20 struct lopt hist[128]; /* buffer history */
21 int undo; /* current index into hist[] */
22 int useq; /* current operation sequence */
23 char **ln; /* lines */
24 int ln_n; /* number of lbuf in l[] */
25 int ln_sz; /* size of l[] */
28 struct lbuf *lbuf_make(void)
30 struct lbuf *lb = malloc(sizeof(*lb));
31 int i;
32 memset(lb, 0, sizeof(*lb));
33 for (i = 0; i < LEN(lb->mark); i++)
34 lb->mark[i] = -1;
35 return lb;
38 void lbuf_free(struct lbuf *lb)
40 int i;
41 for (i = 0; i < lb->ln_n; i++)
42 free(lb->ln[i]);
43 for (i = 0; i < LEN(lb->hist); i++)
44 free(lb->hist[i].buf);
45 free(lb->ln);
46 free(lb);
49 /* insert a line at pos */
50 static void lbuf_insertline(struct lbuf *lb, int pos, char *s)
52 if (lb->ln_n == lb->ln_sz) {
53 int nsz = lb->ln_sz + 512;
54 char **nln = malloc(nsz * sizeof(nln[0]));
55 memcpy(nln, lb->ln, lb->ln_n * sizeof(lb->ln[0]));
56 free(lb->ln);
57 lb->ln = nln;
58 lb->ln_sz = nsz;
60 memmove(lb->ln + pos + 1, lb->ln + pos,
61 (lb->ln_n - pos) * sizeof(lb->ln[0]));
62 lb->ln_n++;
63 lb->ln[pos] = s;
66 /* low-level insertion */
67 static void lbuf_insert(struct lbuf *lb, int pos, char *s)
69 int len = strlen(s);
70 struct sbuf *sb;
71 int lb_len = lbuf_len(lb);
72 int i;
73 sb = sbuf_make();
74 for (i = 0; i < len; i++) {
75 sbuf_chr(sb, (unsigned char) s[i]);
76 if (s[i] == '\n') {
77 lbuf_insertline(lb, pos++, sbuf_done(sb));
78 sb = sbuf_make();
81 sbuf_free(sb);
82 for (i = 0; i < LEN(lb->mark); i++) /* updating marks */
83 if (lb->mark[i] >= pos)
84 lb->mark[i] += lbuf_len(lb) - lb_len;
87 /* low-level deletion */
88 static void lbuf_delete(struct lbuf *lb, int beg, int end)
90 int i;
91 for (i = beg; i < end; i++)
92 free(lb->ln[i]);
93 memmove(lb->ln + beg, lb->ln + end, (lb->ln_n - end) * sizeof(lb->ln[0]));
94 lb->ln_n -= end - beg;
95 for (i = 0; i < LEN(lb->mark); i++) /* updating marks */
96 if (lb->mark[i] > beg)
97 lb->mark[i] = MAX(beg, lb->mark[i] + beg - end);
100 /* append undo/redo history */
101 static void lbuf_opt(struct lbuf *lb, int ins, int beg, int end)
103 struct lopt *lo = &lb->hist[0];
104 int n = LEN(lb->hist);
105 int i;
106 if (lb->undo) {
107 for (i = 0; i < lb->undo; i++)
108 free(lb->hist[i].buf);
109 memmove(lb->hist + 1, lb->hist + lb->undo,
110 (n - lb->undo) * sizeof(lb->hist[0]));
111 for (i = n - lb->undo + 1; i < n; i++)
112 lb->hist[i].buf = NULL;
113 } else {
114 free(lb->hist[n - 1].buf);
115 memmove(lb->hist + 1, lb->hist, (n - 1) * sizeof(lb->hist[0]));
117 lo->ins = ins;
118 lo->beg = beg;
119 lo->end = end;
120 lo->buf = lbuf_cp(lb, beg, end);
121 lo->seq = lb->useq;
122 lb->undo = 0;
125 void lbuf_rd(struct lbuf *lbuf, int fd, int pos)
127 char buf[1 << 8];
128 struct sbuf *sb;
129 int nr;
130 sb = sbuf_make();
131 while ((nr = read(fd, buf, sizeof(buf))) > 0)
132 sbuf_mem(sb, buf, nr);
133 lbuf_put(lbuf, pos, sbuf_buf(sb));
134 sbuf_free(sb);
137 void lbuf_wr(struct lbuf *lbuf, int fd, int beg, int end)
139 int i;
140 for (i = beg; i < end; i++)
141 write(fd, lbuf->ln[i], strlen(lbuf->ln[i]));
144 void lbuf_rm(struct lbuf *lb, int beg, int end)
146 if (end > lb->ln_n)
147 end = lb->ln_n;
148 lbuf_opt(lb, 0, beg, end);
149 lbuf_delete(lb, beg, end);
152 void lbuf_put(struct lbuf *lb, int pos, char *s)
154 int lb_len = lbuf_len(lb);
155 lbuf_insert(lb, pos, s);
156 lbuf_opt(lb, 1, pos, pos + lbuf_len(lb) - lb_len);
159 char *lbuf_cp(struct lbuf *lb, int beg, int end)
161 struct sbuf *sb;
162 int i;
163 sb = sbuf_make();
164 for (i = beg; i < end; i++)
165 if (i < lb->ln_n)
166 sbuf_str(sb, lb->ln[i]);
167 return sbuf_done(sb);
170 char *lbuf_get(struct lbuf *lb, int pos)
172 return pos >= 0 && pos < lb->ln_n ? lb->ln[pos] : NULL;
175 int lbuf_len(struct lbuf *lb)
177 return lb->ln_n;
180 void lbuf_mark(struct lbuf *lbuf, int mark, int pos)
182 lbuf->mark[MARK(mark)] = pos;
185 int lbuf_markpos(struct lbuf *lbuf, int mark)
187 return lbuf->mark[MARK(mark)];
190 static struct lopt *lbuf_lopt(struct lbuf *lb, int i)
192 struct lopt *lo = &lb->hist[i];
193 return i >= 0 && i < LEN(lb->hist) && lo->buf ? lo : NULL;
196 void lbuf_undo(struct lbuf *lb)
198 struct lopt *lo = lbuf_lopt(lb, lb->undo);
199 int useq = lo ? lo->seq : 0;
200 while (lo && lo->seq == useq) {
201 lb->undo++;
202 if (lo->ins)
203 lbuf_delete(lb, lo->beg, lo->end);
204 else
205 lbuf_insert(lb, lo->beg, lo->buf);
206 lo = lbuf_lopt(lb, lb->undo);
210 void lbuf_redo(struct lbuf *lb)
212 struct lopt *lo = lbuf_lopt(lb, lb->undo - 1);
213 int useq = lo ? lo->seq : 0;
214 while (lo && lo->seq == useq) {
215 lb->undo--;
216 if (lo->ins)
217 lbuf_insert(lb, lo->beg, lo->buf);
218 else
219 lbuf_delete(lb, lo->beg, lo->end);
220 lo = lbuf_lopt(lb, lb->undo - 1);
224 void lbuf_undofree(struct lbuf *lb)
226 int i;
227 for (i = 0; i < LEN(lb->hist); i++)
228 free(lb->hist[i].buf);
229 memset(lb->hist, 0, sizeof(lb->hist));
230 lb->undo = 0;
233 void lbuf_undomark(struct lbuf *lbuf)
235 lbuf->useq++;