conf.h: syntax highlighting for mails
[neatvi.git] / lbuf.c
blobbe9de0fe14936920e36e4c649fe881d7f39cd02e
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 128
10 /* line operations */
11 struct lopt {
12 char *ins; /* inserted text */
13 char *del; /* deleted text */
14 int pos, n_ins, n_del; /* modification location */
15 int seq; /* operation number */
16 int *mark, *mark_off; /* saved marks */
19 /* line buffers */
20 struct lbuf {
21 int mark[NMARKS]; /* mark lines */
22 int mark_off[NMARKS]; /* mark line offsets */
23 char **ln; /* buffer lines */
24 int ln_n; /* number of lines in ln[] */
25 int ln_sz; /* size of ln[] */
26 int useq; /* current operation sequence */
27 struct lopt *hist; /* buffer history */
28 int hist_sz; /* size of hist[] */
29 int hist_n; /* current history head in hist[] */
30 int hist_u; /* current undo head in hist[] */
31 int useq_zero; /* useq for lbuf_saved() */
32 int useq_last; /* useq before hist[] */
35 struct lbuf *lbuf_make(void)
37 struct lbuf *lb = malloc(sizeof(*lb));
38 int i;
39 memset(lb, 0, sizeof(*lb));
40 for (i = 0; i < LEN(lb->mark); i++)
41 lb->mark[i] = -1;
42 lb->useq = 1;
43 return lb;
46 static void lopt_done(struct lopt *lo)
48 free(lo->ins);
49 free(lo->del);
50 free(lo->mark);
51 free(lo->mark_off);
54 static void lbuf_savemark(struct lbuf *lb, struct lopt *lo, int m)
56 if (lb->mark[m] >= 0) {
57 if (!lo->mark) {
58 lo->mark = malloc(sizeof(lb->mark));
59 lo->mark_off = malloc(sizeof(lb->mark_off));
60 memset(lo->mark, 0xff, sizeof(lb->mark));
62 lo->mark[m] = lb->mark[m];
63 lo->mark_off[m] = lb->mark_off[m];
67 static void lbuf_loadmark(struct lbuf *lb, struct lopt *lo, int m)
69 if (lo->mark && lo->mark[m] >= 0) {
70 lb->mark[m] = lo->mark[m];
71 lb->mark_off[m] = lo->mark_off[m];
75 void lbuf_free(struct lbuf *lb)
77 int i;
78 for (i = 0; i < lb->ln_n; i++)
79 free(lb->ln[i]);
80 for (i = 0; i < lb->hist_n; i++)
81 lopt_done(&lb->hist[i]);
82 free(lb->hist);
83 free(lb->ln);
84 free(lb);
87 /* insert a line at pos */
88 static void lbuf_insertline(struct lbuf *lb, int pos, char *s)
90 if (lb->ln_n == lb->ln_sz) {
91 int nsz = lb->ln_sz + 512;
92 char **nln = malloc(nsz * sizeof(nln[0]));
93 memcpy(nln, lb->ln, lb->ln_n * sizeof(lb->ln[0]));
94 free(lb->ln);
95 lb->ln = nln;
96 lb->ln_sz = nsz;
98 memmove(lb->ln + pos + 1, lb->ln + pos,
99 (lb->ln_n - pos) * sizeof(lb->ln[0]));
100 lb->ln_n++;
101 lb->ln[pos] = s;
104 /* low-level replacement */
105 static void lbuf_replace(struct lbuf *lb, char *s, int pos, int n_del)
107 char *r;
108 int n_ins = 0;
109 int i;
110 for (i = 0; i < n_del; i++)
111 free(lb->ln[pos + i]);
112 memmove(lb->ln + pos, lb->ln + pos + n_del,
113 (lb->ln_n - pos - n_del) * sizeof(lb->ln[0]));
114 lb->ln_n -= n_del;
115 while (s && (r = strchr(s, '\n'))) {
116 char *n = malloc(r - s + 2);
117 memcpy(n, s, r - s + 1);
118 n[r - s + 1] = '\0';
119 lbuf_insertline(lb, pos + n_ins++, n);
120 s = r + 1;
122 for (i = 0; i < LEN(lb->mark); i++) { /* updating marks */
123 if (!s && lb->mark[i] >= pos && lb->mark[i] < pos + n_del)
124 lb->mark[i] = -1;
125 else if (lb->mark[i] >= pos + n_del)
126 lb->mark[i] += n_ins - n_del;
127 else if (lb->mark[i] >= pos + n_ins)
128 lb->mark[i] = pos + n_ins - 1;
130 lbuf_mark(lb, '[', pos, 0);
131 lbuf_mark(lb, ']', pos + n_ins - n_del, 0);
134 static int uc_newlines(char *s)
136 int n;
137 for (n = 0; (s = strchr(s, '\n')); n++)
138 s++;
139 return n;
142 /* append undo/redo history */
143 static void lbuf_opt(struct lbuf *lb, char *buf, int pos, int n_del)
145 struct lopt *lo;
146 int i;
147 for (i = lb->hist_u; i < lb->hist_n; i++)
148 lopt_done(&lb->hist[i]);
149 lb->hist_n = lb->hist_u;
150 if (lb->hist_n == lb->hist_sz) {
151 int sz = lb->hist_sz + 128;
152 struct lopt *hist = malloc(sz * sizeof(hist[0]));
153 memcpy(hist, lb->hist, lb->hist_n * sizeof(hist[0]));
154 free(lb->hist);
155 lb->hist = hist;
156 lb->hist_sz = sz;
158 lo = &lb->hist[lb->hist_n];
159 lb->hist_n++;
160 lb->hist_u = lb->hist_n;
161 memset(lo, 0, sizeof(*lo));
162 lo->pos = pos;
163 lo->n_del = n_del;
164 lo->del = n_del ? lbuf_cp(lb, pos, pos + n_del) : NULL;
165 lo->n_ins = buf ? uc_newlines(buf) : 0;
166 lo->ins = buf ? uc_dup(buf) : NULL;
167 lo->seq = lb->useq;
168 for (i = 0; i < LEN(lb->mark); i++)
169 if (lb->mark[i] >= pos && lb->mark[i] < pos + n_del)
170 if (isalpha(i))
171 lbuf_savemark(lb, lo, i);
174 void lbuf_rd(struct lbuf *lbuf, int fd, int beg, int end)
176 char buf[1 << 10];
177 struct sbuf *sb;
178 int nr;
179 sb = sbuf_make();
180 while ((nr = read(fd, buf, sizeof(buf))) > 0)
181 sbuf_mem(sb, buf, nr);
182 lbuf_edit(lbuf, sbuf_buf(sb), beg, end);
183 sbuf_free(sb);
186 void lbuf_wr(struct lbuf *lbuf, int fd, int beg, int end)
188 int i;
189 for (i = beg; i < end; i++)
190 write(fd, lbuf->ln[i], strlen(lbuf->ln[i]));
193 /* replace lines beg through end with buf */
194 void lbuf_edit(struct lbuf *lb, char *buf, int beg, int end)
196 if (beg > lb->ln_n)
197 beg = lb->ln_n;
198 if (end > lb->ln_n)
199 end = lb->ln_n;
200 if (beg == end && !buf)
201 return;
202 lbuf_opt(lb, buf, beg, end - beg);
203 lbuf_replace(lb, buf, beg, end - beg);
206 char *lbuf_cp(struct lbuf *lb, int beg, int end)
208 struct sbuf *sb;
209 int i;
210 sb = sbuf_make();
211 for (i = beg; i < end; i++)
212 if (i < lb->ln_n)
213 sbuf_str(sb, lb->ln[i]);
214 return sbuf_done(sb);
217 char *lbuf_get(struct lbuf *lb, int pos)
219 return pos >= 0 && pos < lb->ln_n ? lb->ln[pos] : NULL;
222 int lbuf_len(struct lbuf *lb)
224 return lb->ln_n;
227 void lbuf_mark(struct lbuf *lbuf, int mark, int pos, int off)
229 if (mark >= NMARKS)
230 return;
231 lbuf->mark[mark] = pos;
232 lbuf->mark_off[mark] = off;
235 int lbuf_jump(struct lbuf *lbuf, int mark, int *pos, int *off)
237 if (mark >= NMARKS || lbuf->mark[mark] < 0)
238 return 1;
239 *pos = lbuf->mark[mark];
240 if (off)
241 *off = lbuf->mark_off[mark];
242 return 0;
245 int lbuf_undo(struct lbuf *lb)
247 int useq, i;
248 if (!lb->hist_u)
249 return 1;
250 useq = lb->hist[lb->hist_u - 1].seq;
251 while (lb->hist_u && lb->hist[lb->hist_u - 1].seq == useq) {
252 struct lopt *lo = &lb->hist[--(lb->hist_u)];
253 lbuf_replace(lb, lo->del, lo->pos, lo->n_ins);
254 lbuf_loadmark(lb, lo, '*');
255 for (i = 0; i < LEN(lb->mark); i++)
256 lbuf_loadmark(lb, lo, i);
258 return 0;
261 int lbuf_redo(struct lbuf *lb)
263 int useq;
264 if (lb->hist_u == lb->hist_n)
265 return 1;
266 useq = lb->hist[lb->hist_u].seq;
267 while (lb->hist_u < lb->hist_n && lb->hist[lb->hist_u].seq == useq) {
268 struct lopt *lo = &lb->hist[lb->hist_u++];
269 lbuf_replace(lb, lo->ins, lo->pos, lo->n_del);
270 lbuf_loadmark(lb, lo, '*');
272 return 0;
275 static int lbuf_seq(struct lbuf *lb)
277 return lb->hist_u ? lb->hist[lb->hist_u - 1].seq : lb->useq_last;
280 /* mark buffer as saved and, if clear, clear the undo history */
281 void lbuf_saved(struct lbuf *lb, int clear)
283 int i;
284 if (clear) {
285 for (i = 0; i < lb->hist_n; i++)
286 lopt_done(&lb->hist[i]);
287 lb->hist_n = 0;
288 lb->hist_u = 0;
289 lb->useq_last = lb->useq;
291 lb->useq_zero = lbuf_seq(lb);
292 lbuf_modified(xb);
295 /* was the file modified since the last lbuf_modreset() */
296 int lbuf_modified(struct lbuf *lb)
298 struct lopt *lo = lb->hist_n ? &lb->hist[lb->hist_n - 1] : NULL;
299 if (lb->hist_u == lb->hist_n && lo && !lo->mark)
300 lbuf_savemark(lb, lo, '*');
301 lb->useq++;
302 return lbuf_seq(lb) != lb->useq_zero;