tlbuf.c - neatvi - [fork] simple vi-type editor with UTF-8 support | |
git clone git://src.adamsgaard.dk/neatvi | |
Log | |
Files | |
Refs | |
README | |
--- | |
tlbuf.c (8526B) | |
--- | |
1 #include <ctype.h> | |
2 #include <stdlib.h> | |
3 #include <stdio.h> | |
4 #include <string.h> | |
5 #include <unistd.h> | |
6 #include "vi.h" | |
7 | |
8 #define NMARKS_BASE ('z' - 'a' + 2) | |
9 #define NMARKS 32 | |
10 | |
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 */ | |
19 }; | |
20 | |
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[] */ | |
36 }; | |
37 | |
38 struct lbuf *lbuf_make(void) | |
39 { | |
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; | |
47 } | |
48 | |
49 static void lopt_done(struct lopt *lo) | |
50 { | |
51 free(lo->ins); | |
52 free(lo->del); | |
53 free(lo->mark); | |
54 free(lo->mark_off); | |
55 } | |
56 | |
57 static void lbuf_savemark(struct lbuf *lb, struct lopt *lo, int m) | |
58 { | |
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)); | |
64 } | |
65 lo->mark[m] = lb->mark[m]; | |
66 lo->mark_off[m] = lb->mark_off[m]; | |
67 } | |
68 } | |
69 | |
70 static void lbuf_loadmark(struct lbuf *lb, struct lopt *lo, int m) | |
71 { | |
72 if (lo->mark && lo->mark[m] >= 0) { | |
73 lb->mark[m] = lo->mark[m]; | |
74 lb->mark_off[m] = lo->mark_off[m]; | |
75 } | |
76 } | |
77 | |
78 static int markidx(int mark) | |
79 { | |
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; | |
91 } | |
92 | |
93 static void lbuf_savepos(struct lbuf *lb, struct lopt *lo) | |
94 { | |
95 if (lb->mark[markidx('*')] >= 0) | |
96 lo->pos_off = lb->mark_off[markidx('*')]; | |
97 } | |
98 | |
99 static void lbuf_loadpos(struct lbuf *lb, struct lopt *lo) | |
100 { | |
101 lb->mark[markidx('*')] = lo->pos; | |
102 lb->mark_off[markidx('*')] = lo->pos_off; | |
103 } | |
104 | |
105 void lbuf_free(struct lbuf *lb) | |
106 { | |
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); | |
116 } | |
117 | |
118 static int linelength(char *s) | |
119 { | |
120 char *r = strchr(s, '\n'); | |
121 return r ? r - s + 1 : strlen(s); | |
122 } | |
123 | |
124 static int linecount(char *s) | |
125 { | |
126 int n; | |
127 for (n = 0; s && *s; n++) | |
128 s += linelength(s); | |
129 return n; | |
130 } | |
131 | |
132 | |
133 /* low-level line replacement */ | |
134 static void lbuf_replace(struct lbuf *lb, char *s, int pos, int n_del) | |
135 { | |
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_g… | |
144 free(lb->ln); | |
145 free(lb->ln_glob); | |
146 lb->ln = nln; | |
147 lb->ln_glob = nln_glob; | |
148 lb->ln_sz = nsz; | |
149 } | |
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… | |
156 (lb->ln_n - pos - n_del) * sizeof(lb->ln_glob[0]… | |
157 } | |
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; | |
168 } | |
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_de… | |
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; | |
178 } | |
179 lbuf_mark(lb, '[', pos, 0); | |
180 lbuf_mark(lb, ']', pos + (n_ins ? n_ins - 1 : 0), 0); | |
181 } | |
182 | |
183 /* append undo/redo history */ | |
184 static void lbuf_opt(struct lbuf *lb, char *buf, int pos, int n_del) | |
185 { | |
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; | |
198 } | |
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); | |
213 } | |
214 | |
215 int lbuf_rd(struct lbuf *lbuf, int fd, int beg, int end) | |
216 { | |
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; | |
227 } | |
228 | |
229 int lbuf_wr(struct lbuf *lbuf, int fd, int beg, int end) | |
230 { | |
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; | |
241 } | |
242 } | |
243 return 0; | |
244 } | |
245 | |
246 /* replace lines beg through end with buf */ | |
247 void lbuf_edit(struct lbuf *lb, char *buf, int beg, int end) | |
248 { | |
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); | |
257 } | |
258 | |
259 char *lbuf_cp(struct lbuf *lb, int beg, int end) | |
260 { | |
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); | |
268 } | |
269 | |
270 char *lbuf_get(struct lbuf *lb, int pos) | |
271 { | |
272 return pos >= 0 && pos < lb->ln_n ? lb->ln[pos] : NULL; | |
273 } | |
274 | |
275 int lbuf_len(struct lbuf *lb) | |
276 { | |
277 return lb->ln_n; | |
278 } | |
279 | |
280 void lbuf_mark(struct lbuf *lbuf, int mark, int pos, int off) | |
281 { | |
282 if (markidx(mark) >= 0) { | |
283 lbuf->mark[markidx(mark)] = pos; | |
284 lbuf->mark_off[markidx(mark)] = off; | |
285 } | |
286 } | |
287 | |
288 int lbuf_jump(struct lbuf *lbuf, int mark, int *pos, int *off) | |
289 { | |
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; | |
297 } | |
298 | |
299 int lbuf_undo(struct lbuf *lb) | |
300 { | |
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); | |
311 } | |
312 return 0; | |
313 } | |
314 | |
315 int lbuf_redo(struct lbuf *lb) | |
316 { | |
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 == us… | |
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); | |
325 } | |
326 return 0; | |
327 } | |
328 | |
329 static int lbuf_seq(struct lbuf *lb) | |
330 { | |
331 return lb->hist_u ? lb->hist[lb->hist_u - 1].seq : lb->useq_last; | |
332 } | |
333 | |
334 /* mark buffer as saved and, if clear, clear the undo history */ | |
335 void lbuf_saved(struct lbuf *lb, int clear) | |
336 { | |
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; | |
344 } | |
345 lb->useq_zero = lbuf_seq(lb); | |
346 lbuf_modified(xb); | |
347 } | |
348 | |
349 /* was the file modified since the last lbuf_modreset() */ | |
350 int lbuf_modified(struct lbuf *lb) | |
351 { | |
352 lb->useq++; | |
353 return lbuf_seq(lb) != lb->useq_zero; | |
354 } | |
355 | |
356 /* mark the line for ex global command */ | |
357 void lbuf_globset(struct lbuf *lb, int pos, int dep) | |
358 { | |
359 lb->ln_glob[pos] |= 1 << dep; | |
360 } | |
361 | |
362 /* return and clear ex global command mark */ | |
363 int lbuf_globget(struct lbuf *lb, int pos, int dep) | |
364 { | |
365 int o = lb->ln_glob[pos] & (1 << dep); | |
366 lb->ln_glob[pos] &= ~(1 << dep); | |
367 return o > 0; | |
368 } |