vi: update the dimensions of a single window when the terminal is resized
[neatvi.git] / lbuf.c
blob21377cec5a180117d5d160c945e2beff52286b20
1 #include <ctype.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include "vi.h"
8 #define NMARKS_BASE ('z' - 'a' + 2)
9 #define NMARKS 32
11 /* line operations */
12 struct lopt {
13 char *ins; /* inserted text */
14 char *del; /* deleted text */
15 int pos, n_ins, n_del; /* modification location */
16 int pos_off; /* cursor line offset */
17 int seq; /* operation number */
18 int *mark, *mark_off; /* saved marks */
21 /* line buffers */
22 struct lbuf {
23 int mark[NMARKS]; /* mark lines */
24 int mark_off[NMARKS]; /* mark line offsets */
25 char **ln; /* buffer lines */
26 char *ln_glob; /* line global mark */
27 int ln_n; /* number of lines in ln[] */
28 int ln_sz; /* size of ln[] */
29 int useq; /* current operation sequence */
30 struct lopt *hist; /* buffer history */
31 int hist_sz; /* size of hist[] */
32 int hist_n; /* current history head in hist[] */
33 int hist_u; /* current undo head in hist[] */
34 int useq_zero; /* useq for lbuf_saved() */
35 int useq_last; /* useq before hist[] */
38 struct lbuf *lbuf_make(void)
40 struct lbuf *lb = malloc(sizeof(*lb));
41 int i;
42 memset(lb, 0, sizeof(*lb));
43 for (i = 0; i < LEN(lb->mark); i++)
44 lb->mark[i] = -1;
45 lb->useq = 1;
46 return lb;
49 static void lopt_done(struct lopt *lo)
51 free(lo->ins);
52 free(lo->del);
53 free(lo->mark);
54 free(lo->mark_off);
57 static void lbuf_savemark(struct lbuf *lb, struct lopt *lo, int m)
59 if (lb->mark[m] >= 0) {
60 if (!lo->mark) {
61 lo->mark = malloc(sizeof(lb->mark));
62 lo->mark_off = malloc(sizeof(lb->mark_off));
63 memset(lo->mark, 0xff, sizeof(lb->mark));
65 lo->mark[m] = lb->mark[m];
66 lo->mark_off[m] = lb->mark_off[m];
70 static void lbuf_loadmark(struct lbuf *lb, struct lopt *lo, int m)
72 if (lo->mark && lo->mark[m] >= 0) {
73 lb->mark[m] = lo->mark[m];
74 lb->mark_off[m] = lo->mark_off[m];
78 static int markidx(int mark)
80 if (islower(mark))
81 return mark - 'a';
82 if (mark == '\'' || mark == '`')
83 return 'z' - 'a' + 1;
84 if (mark == '*')
85 return 'z' - 'a' + 2;
86 if (mark == '[')
87 return 'z' - 'a' + 3;
88 if (mark == ']')
89 return 'z' - 'a' + 4;
90 return -1;
93 static void lbuf_savepos(struct lbuf *lb, struct lopt *lo)
95 if (lb->mark[markidx('*')] >= 0)
96 lo->pos_off = lb->mark_off[markidx('*')];
99 static void lbuf_loadpos(struct lbuf *lb, struct lopt *lo)
101 lb->mark[markidx('*')] = lo->pos;
102 lb->mark_off[markidx('*')] = lo->pos_off;
105 void lbuf_free(struct lbuf *lb)
107 int i;
108 for (i = 0; i < lb->ln_n; i++)
109 free(lb->ln[i]);
110 for (i = 0; i < lb->hist_n; i++)
111 lopt_done(&lb->hist[i]);
112 free(lb->hist);
113 free(lb->ln);
114 free(lb->ln_glob);
115 free(lb);
118 static int linelength(char *s)
120 char *r = strchr(s, '\n');
121 return r ? r - s + 1 : strlen(s);
124 static int linecount(char *s)
126 int n;
127 for (n = 0; s && *s; n++)
128 s += linelength(s);
129 return n;
133 /* low-level line replacement */
134 static void lbuf_replace(struct lbuf *lb, char *s, int pos, int n_del)
136 int n_ins = linecount(s);
137 int i;
138 while (lb->ln_n + n_ins - n_del >= lb->ln_sz) {
139 int nsz = lb->ln_sz + (lb->ln_sz ? lb->ln_sz : 512);
140 char **nln = malloc(nsz * sizeof(nln[0]));
141 char *nln_glob = malloc(nsz * sizeof(nln_glob[0]));
142 memcpy(nln, lb->ln, lb->ln_n * sizeof(lb->ln[0]));
143 memcpy(nln_glob, lb->ln_glob, lb->ln_n * sizeof(lb->ln_glob[0]));
144 free(lb->ln);
145 free(lb->ln_glob);
146 lb->ln = nln;
147 lb->ln_glob = nln_glob;
148 lb->ln_sz = nsz;
150 for (i = 0; i < n_del; i++)
151 free(lb->ln[pos + i]);
152 if (n_ins != n_del) {
153 memmove(lb->ln + pos + n_ins, lb->ln + pos + n_del,
154 (lb->ln_n - pos - n_del) * sizeof(lb->ln[0]));
155 memmove(lb->ln_glob + pos + n_ins, lb->ln_glob + pos + n_del,
156 (lb->ln_n - pos - n_del) * sizeof(lb->ln_glob[0]));
158 lb->ln_n += n_ins - n_del;
159 for (i = 0; i < n_ins; i++) {
160 int l = s ? linelength(s) : 0;
161 int l_nonl = l - (s[l - 1] == '\n');
162 char *n = malloc(l_nonl + 2);
163 memcpy(n, s, l_nonl);
164 n[l_nonl + 0] = '\n';
165 n[l_nonl + 1] = '\0';
166 lb->ln[pos + i] = n;
167 s += l;
169 for (i = n_del; i < n_ins; i++)
170 lb->ln_glob[pos + i] = 0;
171 for (i = 0; i < LEN(lb->mark); i++) { /* updating marks */
172 if (!s && lb->mark[i] >= pos && lb->mark[i] < pos + n_del)
173 lb->mark[i] = -1;
174 else if (lb->mark[i] >= pos + n_del)
175 lb->mark[i] += n_ins - n_del;
176 else if (lb->mark[i] >= pos + n_ins)
177 lb->mark[i] = pos + n_ins - 1;
179 lbuf_mark(lb, '[', pos, 0);
180 lbuf_mark(lb, ']', pos + (n_ins ? n_ins - 1 : 0), 0);
183 /* append undo/redo history */
184 static void lbuf_opt(struct lbuf *lb, char *buf, int pos, int n_del)
186 struct lopt *lo;
187 int i;
188 for (i = lb->hist_u; i < lb->hist_n; i++)
189 lopt_done(&lb->hist[i]);
190 lb->hist_n = lb->hist_u;
191 if (lb->hist_n == lb->hist_sz) {
192 int sz = lb->hist_sz + (lb->hist_sz ? lb->hist_sz : 128);
193 struct lopt *hist = malloc(sz * sizeof(hist[0]));
194 memcpy(hist, lb->hist, lb->hist_n * sizeof(hist[0]));
195 free(lb->hist);
196 lb->hist = hist;
197 lb->hist_sz = sz;
199 lo = &lb->hist[lb->hist_n];
200 lb->hist_n++;
201 lb->hist_u = lb->hist_n;
202 memset(lo, 0, sizeof(*lo));
203 lo->pos = pos;
204 lo->n_del = n_del;
205 lo->del = n_del ? lbuf_cp(lb, pos, pos + n_del) : NULL;
206 lo->n_ins = buf ? linecount(buf) : 0;
207 lo->ins = buf ? uc_dup(buf) : NULL;
208 lo->seq = lb->useq;
209 lbuf_savepos(lb, lo);
210 for (i = 0; i < NMARKS_BASE; i++)
211 if (lb->mark[i] >= pos && lb->mark[i] < pos + n_del)
212 lbuf_savemark(lb, lo, i);
215 int lbuf_rd(struct lbuf *lbuf, int fd, int beg, int end)
217 char buf[1 << 10];
218 struct sbuf *sb;
219 long nr;
220 sb = sbuf_make();
221 while ((nr = read(fd, buf, sizeof(buf))) > 0)
222 sbuf_mem(sb, buf, nr);
223 if (!nr)
224 lbuf_edit(lbuf, sbuf_buf(sb), beg, end);
225 sbuf_free(sb);
226 return nr != 0;
229 int lbuf_wr(struct lbuf *lbuf, int fd, int beg, int end)
231 int i;
232 for (i = beg; i < end; i++) {
233 char *ln = lbuf->ln[i];
234 long nw = 0;
235 long nl = strlen(ln);
236 while (nw < nl) {
237 long nc = write(fd, ln + nw, nl - nw);
238 if (nc < 0)
239 return 1;
240 nw += nc;
243 return 0;
246 /* replace lines beg through end with buf */
247 void lbuf_edit(struct lbuf *lb, char *buf, int beg, int end)
249 if (beg > lb->ln_n)
250 beg = lb->ln_n;
251 if (end > lb->ln_n)
252 end = lb->ln_n;
253 if (beg == end && !buf)
254 return;
255 lbuf_opt(lb, buf, beg, end - beg);
256 lbuf_replace(lb, buf, beg, end - beg);
259 char *lbuf_cp(struct lbuf *lb, int beg, int end)
261 struct sbuf *sb;
262 int i;
263 sb = sbuf_make();
264 for (i = beg; i < end; i++)
265 if (i < lb->ln_n)
266 sbuf_str(sb, lb->ln[i]);
267 return sbuf_done(sb);
270 char *lbuf_get(struct lbuf *lb, int pos)
272 return pos >= 0 && pos < lb->ln_n ? lb->ln[pos] : NULL;
275 int lbuf_len(struct lbuf *lb)
277 return lb->ln_n;
280 void lbuf_mark(struct lbuf *lbuf, int mark, int pos, int off)
282 if (markidx(mark) >= 0) {
283 lbuf->mark[markidx(mark)] = pos;
284 lbuf->mark_off[markidx(mark)] = off;
288 int lbuf_jump(struct lbuf *lbuf, int mark, int *pos, int *off)
290 int mk = markidx(mark);
291 if (mk < 0 || lbuf->mark[mk] < 0)
292 return 1;
293 *pos = lbuf->mark[mk];
294 if (off)
295 *off = lbuf->mark_off[mk];
296 return 0;
299 int lbuf_undo(struct lbuf *lb)
301 int useq, i;
302 if (!lb->hist_u)
303 return 1;
304 useq = lb->hist[lb->hist_u - 1].seq;
305 while (lb->hist_u && lb->hist[lb->hist_u - 1].seq == useq) {
306 struct lopt *lo = &lb->hist[--(lb->hist_u)];
307 lbuf_replace(lb, lo->del, lo->pos, lo->n_ins);
308 lbuf_loadpos(lb, lo);
309 for (i = 0; i < LEN(lb->mark); i++)
310 lbuf_loadmark(lb, lo, i);
312 return 0;
315 int lbuf_redo(struct lbuf *lb)
317 int useq;
318 if (lb->hist_u == lb->hist_n)
319 return 1;
320 useq = lb->hist[lb->hist_u].seq;
321 while (lb->hist_u < lb->hist_n && lb->hist[lb->hist_u].seq == useq) {
322 struct lopt *lo = &lb->hist[lb->hist_u++];
323 lbuf_replace(lb, lo->ins, lo->pos, lo->n_del);
324 lbuf_loadpos(lb, lo);
326 return 0;
329 static int lbuf_seq(struct lbuf *lb)
331 return lb->hist_u ? lb->hist[lb->hist_u - 1].seq : lb->useq_last;
334 /* mark buffer as saved and, if clear, clear the undo history */
335 void lbuf_saved(struct lbuf *lb, int clear)
337 int i;
338 if (clear) {
339 for (i = 0; i < lb->hist_n; i++)
340 lopt_done(&lb->hist[i]);
341 lb->hist_n = 0;
342 lb->hist_u = 0;
343 lb->useq_last = lb->useq;
345 lb->useq_zero = lbuf_seq(lb);
346 lbuf_modified(xb);
349 /* was the file modified since the last lbuf_modreset() */
350 int lbuf_modified(struct lbuf *lb)
352 lb->useq++;
353 return lbuf_seq(lb) != lb->useq_zero;
356 /* mark the line for ex global command */
357 void lbuf_globset(struct lbuf *lb, int pos, int dep)
359 lb->ln_glob[pos] |= 1 << dep;
362 /* return and clear ex global command mark */
363 int lbuf_globget(struct lbuf *lb, int pos, int dep)
365 int o = lb->ln_glob[pos] & (1 << dep);
366 lb->ln_glob[pos] &= ~(1 << dep);
367 return o > 0;