ex: increase the maximum number of buffers
[neatvi.git] / led.c
blobc710cfc836b93a3649213083b14587d72bf2d7bf
1 /* line editing and drawing */
2 #include <ctype.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include "vi.h"
9 static char *kmap_map(int kmap, int c)
11 static char cs[4];
12 char **keymap = conf_kmap(kmap);
13 cs[0] = c;
14 return keymap[c] ? keymap[c] : cs;
17 static int led_pos(int dir, int pos, int beg, int end)
19 return dir >= 0 ? pos - beg : end - pos - 1;
22 static int led_offdir(char **chrs, int *pos, int i)
24 if (pos[i] + ren_cwid(chrs[i], pos[i]) == pos[i + 1])
25 return +1;
26 if (pos[i + 1] + ren_cwid(chrs[i + 1], pos[i + 1]) == pos[i])
27 return -1;
28 return 0;
31 /* highlight text in reverse direction */
32 static void led_markrev(int n, char **chrs, int *pos, int *att)
34 int i = 0, j;
35 int hl = conf_hlrev();
36 while (i + 1 < n) {
37 int dir = led_offdir(chrs, pos, i);
38 int beg = i;
39 while (i + 1 < n && led_offdir(chrs, pos, i) == dir)
40 i++;
41 if (dir < 0)
42 for (j = beg; j <= i; j++)
43 att[j] = syn_merge(hl, att[j]);
44 if (i == beg)
45 i++;
49 /* render and highlight a line */
50 static char *led_render(char *s0, int cbeg, int cend, char *syn)
52 int n;
53 int *pos; /* pos[i]: the screen position of the i-th character */
54 int *off; /* off[i]: the character at screen position i */
55 int *att; /* att[i]: the attributes of i-th character */
56 char **chrs; /* chrs[i]: the i-th character in s1 */
57 int clast = 0;
58 int att_old = 0;
59 struct sbuf *out;
60 int i, j;
61 int ctx = dir_context(s0);
62 int att_blank = 0; /* the attribute of blank space */
63 chrs = uc_chop(s0, &n);
64 pos = ren_position(s0);
65 off = malloc((cend - cbeg) * sizeof(off[0]));
66 memset(off, 0xff, (cend - cbeg) * sizeof(off[0]));
67 /* initialise off[] using pos[] */
68 for (i = 0; i < n; i++) {
69 int curwid = ren_cwid(chrs[i], pos[i]);
70 int curbeg = led_pos(ctx, pos[i], cbeg, cend);
71 int curend = led_pos(ctx, pos[i] + curwid - 1, cbeg, cend);
72 if (curbeg >= 0 && curbeg < (cend - cbeg) &&
73 curend >= 0 && curend < (cend - cbeg)) {
74 for (j = 0; j < curwid; j++)
75 off[led_pos(ctx, pos[i] + j, cbeg, cend)] = i;
78 att = syn_highlight(n <= xlim ? syn : "", s0);
79 /* find the last non-empty column */
80 for (i = cbeg; i < cend; i++)
81 if (off[i - cbeg] >= 0)
82 clast = i;
83 /* the attribute of the last character is used for blanks */
84 att_blank = n > 0 ? att[n - 1] : 0;
85 led_markrev(n, chrs, pos, att);
86 /* generate term output */
87 out = sbuf_make();
88 sbuf_str(out, conf_lnpref());
89 i = cbeg;
90 while (i < cend && i <= clast) {
91 int o = off[i - cbeg];
92 int att_new = o >= 0 ? att[o] : att_blank;
93 sbuf_str(out, term_seqattr(att_new, att_old));
94 att_old = att_new;
95 if (o >= 0) {
96 if (ren_translate(chrs[o], s0)) {
97 sbuf_str(out, ren_translate(chrs[o], s0));
98 } else if (uc_isprint(chrs[o])) {
99 sbuf_mem(out, chrs[o], uc_len(chrs[o]));
100 } else {
101 for (j = i; j < cend && off[j - cbeg] == o; j++)
102 sbuf_chr(out, ' ');
104 while (i < cend && off[i - cbeg] == o)
105 i++;
106 } else {
107 sbuf_chr(out, ' ');
108 i++;
111 if (clast < cend - 1)
112 sbuf_str(out, term_seqkill());
113 sbuf_str(out, term_seqattr(0, att_old));
114 free(att);
115 free(pos);
116 free(off);
117 free(chrs);
118 return sbuf_done(out);
121 /* print a line on the screen */
122 void led_print(char *s, int row, int left, char *syn)
124 char *r = led_render(s, left, left + xcols, syn);
125 term_pos(row, 0);
126 term_kill();
127 term_str(r);
128 free(r);
131 /* set xtd and return its old value */
132 static int td_set(int td)
134 int old = xtd;
135 xtd = td;
136 return old;
139 /* print a line on the screen; for ex messages */
140 void led_printmsg(char *s, int row, char *syn)
142 int td = td_set(+2);
143 char *r = led_render(s, 0, xcols, syn);
144 td_set(td);
145 term_pos(row, 0);
146 term_kill();
147 term_str(r);
148 free(r);
151 static int led_lastchar(char *s)
153 char *r = *s ? strchr(s, '\0') : s;
154 if (r != s)
155 r = uc_beg(s, r - 1);
156 return r - s;
159 static int led_lastword(char *s)
161 char *r = *s ? uc_beg(s, strchr(s, '\0') - 1) : s;
162 int kind;
163 while (r > s && uc_isspace(r))
164 r = uc_beg(s, r - 1);
165 kind = r > s ? uc_kind(r) : 0;
166 while (r > s && uc_kind(uc_beg(s, r - 1)) == kind)
167 r = uc_beg(s, r - 1);
168 return r - s;
171 static void led_printparts(char *ai, char *pref, char *main,
172 char *post, int *left, int kmap, char *syn)
174 struct sbuf *ln;
175 int off, pos;
176 int idir = 0;
177 ln = sbuf_make();
178 sbuf_str(ln, ai);
179 sbuf_str(ln, pref);
180 sbuf_str(ln, main);
181 off = uc_slen(sbuf_buf(ln));
182 /* cursor position for inserting the next character */
183 if (*pref || *main || *ai) {
184 int len = sbuf_len(ln);
185 sbuf_str(ln, kmap_map(kmap, 'a'));
186 sbuf_str(ln, post);
187 idir = ren_pos(sbuf_buf(ln), off) -
188 ren_pos(sbuf_buf(ln), off - 1) < 0 ? -1 : +1;
189 sbuf_cut(ln, len);
191 term_record();
192 sbuf_str(ln, post);
193 pos = ren_cursor(sbuf_buf(ln), ren_pos(sbuf_buf(ln), MAX(0, off - 1)));
194 if (pos >= *left + xcols)
195 *left = pos - xcols / 2;
196 if (pos < *left)
197 *left = pos < xcols ? 0 : pos - xcols / 2;
198 led_print(sbuf_buf(ln), -1, *left, syn);
199 term_pos(-1, led_pos(dir_context(sbuf_buf(ln)), pos + idir, *left, *left + xcols));
200 sbuf_free(ln);
201 term_commit();
204 /* continue reading the character starting with c */
205 static char *led_readchar(int c, int kmap)
207 static char buf[8];
208 int c1, c2;
209 int i, n;
210 if (c == TK_CTL('v')) { /* literal character */
211 buf[0] = term_read();
212 buf[1] = '\0';
213 return buf;
215 if (c == TK_CTL('k')) { /* digraph */
216 c1 = term_read();
217 if (TK_INT(c1))
218 return NULL;
219 if (c1 == TK_CTL('k'))
220 return "";
221 c2 = term_read();
222 if (TK_INT(c2))
223 return NULL;
224 return conf_digraph(c1, c2);
226 if ((c & 0xc0) == 0xc0) { /* utf-8 character */
227 buf[0] = c;
228 n = uc_len(buf);
229 for (i = 1; i < n; i++)
230 buf[i] = term_read();
231 buf[n] = '\0';
232 return buf;
234 return kmap_map(kmap, c);
237 /* read a character from the terminal */
238 char *led_read(int *kmap)
240 int c = term_read();
241 while (!TK_INT(c)) {
242 switch (c) {
243 case TK_CTL('f'):
244 *kmap = xkmap_alt;
245 break;
246 case TK_CTL('e'):
247 *kmap = 0;
248 break;
249 default:
250 return led_readchar(c, *kmap);
252 c = term_read();
254 return NULL;
257 static int led_match(char *out, int len, char *kwd, char *opt)
259 while (opt != NULL) {
260 int i = 0;
261 while (kwd[i] && kwd[i] == opt[i])
262 i++;
263 if (kwd[i] == '\0')
264 break;
265 opt = strchr(opt, '\n') == NULL ? NULL : strchr(opt, '\n') + 1;
267 out[0] = '\0';
268 if (opt != NULL) {
269 int i = 0;
270 char *beg = opt + strlen(kwd);
271 while (beg[i] && beg[i] != '\n' && i + 8 < len)
272 i += uc_len(beg + i);
273 memcpy(out, beg, i);
274 out[i] = '\0';
275 return 0;
277 return 1;
280 /* read a line from the terminal */
281 static char *led_line(char *pref, char *post, char *ai, int ai_max, int *left,
282 int *key, int *kmap, char *syn, char *hist, void (*showinfo)(char *ln))
284 struct sbuf *sb;
285 int ai_len = strlen(ai);
286 int c, y, lnmode;
287 char cmp[64] = "";
288 char *cs;
289 sb = sbuf_make();
290 if (pref == NULL)
291 pref = "";
292 if (post == NULL || !post[0])
293 post = cmp;
294 while (1) {
295 if (hist != NULL)
296 led_match(cmp, sizeof(cmp), sbuf_buf(sb), hist);
297 led_printparts(ai, pref, sbuf_buf(sb), post, left, *kmap, syn);
298 c = term_read();
299 switch (c) {
300 case TK_CTL('f'):
301 *kmap = xkmap_alt;
302 continue;
303 case TK_CTL('e'):
304 *kmap = 0;
305 continue;
306 case TK_CTL('h'):
307 case 127:
308 if (sbuf_len(sb))
309 sbuf_cut(sb, led_lastchar(sbuf_buf(sb)));
310 break;
311 case TK_CTL('u'):
312 sbuf_cut(sb, 0);
313 break;
314 case TK_CTL('w'):
315 if (sbuf_len(sb))
316 sbuf_cut(sb, led_lastword(sbuf_buf(sb)));
317 break;
318 case TK_CTL('t'):
319 if (ai_len < ai_max) {
320 ai[ai_len++] = '\t';
321 ai[ai_len] = '\0';
323 break;
324 case TK_CTL('d'):
325 /* when ai and pref are empty, remove the first space of sb */
326 if (ai_len == 0 && !pref[0]) {
327 char *buf = sbuf_buf(sb);
328 if (buf[0] == ' ' || buf[0] == '\t') {
329 char *dup = uc_dup(buf + 1);
330 sbuf_cut(sb, 0);
331 sbuf_str(sb, dup);
332 free(dup);
335 if (ai_len > 0)
336 ai[--ai_len] = '\0';
337 break;
338 case TK_CTL('p'):
339 if (reg_get(0, &lnmode))
340 sbuf_str(sb, reg_get(0, &lnmode));
341 break;
342 case TK_CTL('r'):
343 y = term_read();
344 if (y > 0 && reg_get(y, &lnmode))
345 sbuf_str(sb, reg_get(y, &lnmode));
346 break;
347 case TK_CTL('a'):
348 if (showinfo != NULL) {
349 char *ln = uc_cat(pref, sbuf_buf(sb));
350 showinfo(ln);
351 free(ln);
352 } else {
353 sbuf_str(sb, cmp);
355 break;
356 default:
357 if (c == '\n' || TK_INT(c))
358 break;
359 if ((cs = led_readchar(c, *kmap)) != NULL)
360 sbuf_str(sb, cs);
362 if (c == '\n')
363 led_printparts(ai, pref, sbuf_buf(sb), "", left, *kmap, syn);
364 if (c == '\n' || TK_INT(c))
365 break;
367 *key = c;
368 return sbuf_done(sb);
371 /* read an ex command */
372 char *led_prompt(char *pref, char *post, int *kmap, char *syn, char *hist)
374 int key;
375 int td = td_set(+2);
376 int left = 0;
377 char *s = led_line(pref, post, "", 0, &left, &key, kmap, syn, hist, NULL);
378 td_set(td);
379 if (key == '\n') {
380 struct sbuf *sb = sbuf_make();
381 if (pref)
382 sbuf_str(sb, pref);
383 sbuf_str(sb, s);
384 if (post)
385 sbuf_str(sb, post);
386 free(s);
387 return sbuf_done(sb);
389 free(s);
390 return NULL;
393 static int linecount(char *s)
395 int n;
396 for (n = 0; s; n++)
397 if ((s = strchr(s, '\n')))
398 s++;
399 return n;
402 /* read visual command input */
403 char *led_input(char *pref, char *post, int *left, int *kmap, char *syn, void (*nextline)(void), void (*showinfo)(char *ln))
405 struct sbuf *sb = sbuf_make();
406 char ai[128];
407 int ai_max = sizeof(ai) - 1;
408 int n = 0;
409 int key;
410 while (n < ai_max && (*pref == ' ' || *pref == '\t'))
411 ai[n++] = *pref++;
412 ai[n] = '\0';
413 while (1) {
414 char *ln = led_line(pref, post, ai, ai_max, left, &key, kmap, syn, NULL, showinfo);
415 int ln_sp = 0; /* number of initial spaces in ln */
416 int lncnt = linecount(ln) - 1 + (key == '\n');
417 while (ln[ln_sp] && (ln[ln_sp] == ' ' || ln[ln_sp] == '\t'))
418 ln_sp++;
419 /* append the auto-indent only if there are other characters */
420 if (ln[ln_sp] || (pref && pref[0]) ||
421 (key != '\n' && post[0] && post[0] != '\n'))
422 sbuf_str(sb, ai);
423 if (pref)
424 sbuf_str(sb, pref);
425 sbuf_str(sb, ln);
426 if (key == '\n')
427 sbuf_chr(sb, '\n');
428 while (lncnt-- > 0)
429 nextline();
430 if (!pref || !pref[0]) { /* updating autoindent */
431 int ai_len = ai_max ? strlen(ai) : 0;
432 int ai_new = ln_sp;
433 if (ai_len + ai_new > ai_max)
434 ai_new = ai_max - ai_len;
435 memcpy(ai + ai_len, ln, ai_new);
436 ai[ai_len + ai_new] = '\0';
438 if (!xai)
439 ai[0] = '\0';
440 free(ln);
441 if (key != '\n')
442 break;
443 pref = NULL;
444 n = 0;
445 while (xai && (post[n] == ' ' || post[n] == '\t'))
446 n++;
447 memmove(post, post + n, strlen(post) - n + 1);
449 sbuf_str(sb, post);
450 if (TK_INT(key))
451 return sbuf_done(sb);
452 sbuf_free(sb);
453 return NULL;