lbuf: report buffer modification
[neatvi.git] / lbuf.c
blob5cab8812f8194f56f2f7c3709c8a350be294470f
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include "vi.h"
7 #define NMARKS 128
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[NMARKS]; /* mark lines */
20 int mark_off[NMARKS]; /* mark line offsets */
21 struct lopt hist[128]; /* buffer history */
22 int undo; /* current index into hist[] */
23 int useq; /* current operation sequence */
24 char **ln; /* lines */
25 int ln_n; /* number of lbuf in l[] */
26 int ln_sz; /* size of l[] */
27 int mod_new; /* clear modification marks */
28 int useq_zero; /* useq for lbuf_saved() */
29 int useq_last; /* useq before hist[] */
32 struct lbuf *lbuf_make(void)
34 struct lbuf *lb = malloc(sizeof(*lb));
35 int i;
36 memset(lb, 0, sizeof(*lb));
37 for (i = 0; i < LEN(lb->mark); i++)
38 lb->mark[i] = -1;
39 return lb;
42 void lbuf_free(struct lbuf *lb)
44 int i;
45 for (i = 0; i < lb->ln_n; i++)
46 free(lb->ln[i]);
47 for (i = 0; i < LEN(lb->hist); i++)
48 free(lb->hist[i].buf);
49 free(lb->ln);
50 free(lb);
53 /* insert a line at pos */
54 static void lbuf_insertline(struct lbuf *lb, int pos, char *s)
56 if (lb->ln_n == lb->ln_sz) {
57 int nsz = lb->ln_sz + 512;
58 char **nln = malloc(nsz * sizeof(nln[0]));
59 memcpy(nln, lb->ln, lb->ln_n * sizeof(lb->ln[0]));
60 free(lb->ln);
61 lb->ln = nln;
62 lb->ln_sz = nsz;
64 memmove(lb->ln + pos + 1, lb->ln + pos,
65 (lb->ln_n - pos) * sizeof(lb->ln[0]));
66 lb->ln_n++;
67 lb->ln[pos] = s;
70 /* low-level insertion */
71 static void lbuf_insert(struct lbuf *lb, int pos, char *s)
73 int len = strlen(s);
74 struct sbuf *sb;
75 int lb_len = lbuf_len(lb);
76 int beg = pos, end;
77 int i;
78 sb = sbuf_make();
79 for (i = 0; i < len; i++) {
80 sbuf_chr(sb, (unsigned char) s[i]);
81 if (s[i] == '\n') {
82 lbuf_insertline(lb, pos++, sbuf_done(sb));
83 sb = sbuf_make();
86 sbuf_free(sb);
87 for (i = 0; i < LEN(lb->mark); i++) /* updating marks */
88 if (lb->mark[i] >= pos)
89 lb->mark[i] += lbuf_len(lb) - lb_len;
90 end = beg + lbuf_len(lb) - lb_len;
91 if (lb->mod_new || lb->mark['['] < 0 || lb->mark['['] > beg)
92 lbuf_mark(lb, '[', beg, 0);
93 if (lb->mod_new || lb->mark[']'] < 0 || lb->mark[']'] < end - 1)
94 lbuf_mark(lb, ']', end - 1, 0);
95 lb->mod_new = 0;
98 /* low-level deletion */
99 static void lbuf_delete(struct lbuf *lb, int beg, int end)
101 int i;
102 for (i = beg; i < end; i++)
103 free(lb->ln[i]);
104 memmove(lb->ln + beg, lb->ln + end, (lb->ln_n - end) * sizeof(lb->ln[0]));
105 lb->ln_n -= end - beg;
106 for (i = 0; i < LEN(lb->mark); i++) /* updating marks */
107 if (lb->mark[i] > beg)
108 lb->mark[i] = MAX(beg, lb->mark[i] + beg - end);
109 if (lb->mod_new || lb->mark['['] < 0 || lb->mark['['] > beg)
110 lbuf_mark(lb, '[', beg, 0);
111 if (lb->mod_new || lb->mark[']'] < 0 || lb->mark[']'] < beg)
112 lbuf_mark(lb, ']', beg, 0);
113 lb->mod_new = 0;
116 /* append undo/redo history */
117 static void lbuf_opt(struct lbuf *lb, int ins, int beg, int end)
119 struct lopt *lo = &lb->hist[0];
120 int n = LEN(lb->hist);
121 int i;
122 if (lb->undo) {
123 for (i = 0; i < lb->undo; i++)
124 free(lb->hist[i].buf);
125 memmove(lb->hist + 1, lb->hist + lb->undo,
126 (n - lb->undo) * sizeof(lb->hist[0]));
127 for (i = n - lb->undo + 1; i < n; i++)
128 lb->hist[i].buf = NULL;
129 } else {
130 if (lb->hist[n - 1].buf)
131 lb->useq_last = lb->hist[n - 1].seq;
132 free(lb->hist[n - 1].buf);
133 memmove(lb->hist + 1, lb->hist, (n - 1) * sizeof(lb->hist[0]));
135 lo->ins = ins;
136 lo->beg = beg;
137 lo->end = end;
138 lo->buf = lbuf_cp(lb, beg, end);
139 lo->seq = lb->useq;
140 lb->undo = 0;
143 void lbuf_rd(struct lbuf *lbuf, int fd, int pos)
145 char buf[1 << 8];
146 struct sbuf *sb;
147 int nr;
148 sb = sbuf_make();
149 while ((nr = read(fd, buf, sizeof(buf))) > 0)
150 sbuf_mem(sb, buf, nr);
151 lbuf_put(lbuf, pos, sbuf_buf(sb));
152 sbuf_free(sb);
155 void lbuf_wr(struct lbuf *lbuf, int fd, int beg, int end)
157 int i;
158 for (i = beg; i < end; i++)
159 write(fd, lbuf->ln[i], strlen(lbuf->ln[i]));
162 void lbuf_rm(struct lbuf *lb, int beg, int end)
164 if (end > lb->ln_n)
165 end = lb->ln_n;
166 lbuf_opt(lb, 0, beg, end);
167 lbuf_delete(lb, beg, end);
170 void lbuf_put(struct lbuf *lb, int pos, char *s)
172 int lb_len = lbuf_len(lb);
173 lbuf_insert(lb, pos, s);
174 lbuf_opt(lb, 1, pos, pos + lbuf_len(lb) - lb_len);
177 char *lbuf_cp(struct lbuf *lb, int beg, int end)
179 struct sbuf *sb;
180 int i;
181 sb = sbuf_make();
182 for (i = beg; i < end; i++)
183 if (i < lb->ln_n)
184 sbuf_str(sb, lb->ln[i]);
185 return sbuf_done(sb);
188 char *lbuf_get(struct lbuf *lb, int pos)
190 return pos >= 0 && pos < lb->ln_n ? lb->ln[pos] : NULL;
193 int lbuf_len(struct lbuf *lb)
195 return lb->ln_n;
198 void lbuf_mark(struct lbuf *lbuf, int mark, int pos, int off)
200 if (mark >= NMARKS)
201 return;
202 lbuf->mark[mark] = pos;
203 lbuf->mark_off[mark] = off;
206 int lbuf_jump(struct lbuf *lbuf, int mark, int *pos, int *off)
208 if (mark >= NMARKS || lbuf->mark[mark] < 0)
209 return 1;
210 *pos = lbuf->mark[mark];
211 if (off)
212 *off = lbuf->mark_off[mark];
213 return 0;
216 static struct lopt *lbuf_lopt(struct lbuf *lb, int i)
218 struct lopt *lo = &lb->hist[i];
219 return i >= 0 && i < LEN(lb->hist) && lo->buf ? lo : NULL;
222 int lbuf_undo(struct lbuf *lb)
224 struct lopt *lo = lbuf_lopt(lb, lb->undo);
225 int useq = lo ? lo->seq : 0;
226 if (!lo)
227 return 1;
228 lb->mod_new = 1;
229 while (lo && lo->seq == useq) {
230 lb->undo++;
231 if (lo->ins)
232 lbuf_delete(lb, lo->beg, lo->end);
233 else
234 lbuf_insert(lb, lo->beg, lo->buf);
235 lo = lbuf_lopt(lb, lb->undo);
237 return 0;
240 int lbuf_redo(struct lbuf *lb)
242 struct lopt *lo = lbuf_lopt(lb, lb->undo - 1);
243 int useq = lo ? lo->seq : 0;
244 if (!lo)
245 return 1;
246 lb->mod_new = 1;
247 while (lo && lo->seq == useq) {
248 lb->undo--;
249 if (lo->ins)
250 lbuf_insert(lb, lo->beg, lo->buf);
251 else
252 lbuf_delete(lb, lo->beg, lo->end);
253 lo = lbuf_lopt(lb, lb->undo - 1);
255 return 0;
258 static int lbuf_seq(struct lbuf *lb)
260 struct lopt *lo = lbuf_lopt(lb, lb->undo);
261 return lo ? lo->seq : lb->useq_last;
264 /* mark buffer as saved and, if clear, clear the undo history */
265 void lbuf_saved(struct lbuf *lb, int clear)
267 int i;
268 if (clear) {
269 for (i = 0; i < LEN(lb->hist); i++)
270 free(lb->hist[i].buf);
271 memset(lb->hist, 0, sizeof(lb->hist));
272 lb->undo = 0;
273 lb->useq_last = lb->useq;
275 lb->useq_zero = lbuf_seq(lb);
278 /* was the file modified since the last lbuf_modreset() */
279 int lbuf_modified(struct lbuf *lb)
281 lb->mod_new = 1;
282 lb->useq++;
283 return lbuf_seq(lb) != lb->useq_zero;