vi: update the dimensions of a single window when the terminal is resized
[neatvi.git] / led.c
blobe937ea99cae1b589e9b9085eb395e39899dbf71f
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_posctx(int dir, int pos, int beg, int end)
19 return dir >= 0 ? pos - beg : end - pos - 1;
22 /* map cursor horizontal position to terminal column number */
23 int led_pos(char *s, int pos)
25 return led_posctx(dir_context(s), pos, xleft, xleft + xcols);
28 static int led_offdir(char **chrs, int *pos, int i)
30 if (pos[i] + ren_cwid(chrs[i], pos[i]) == pos[i + 1])
31 return +1;
32 if (pos[i + 1] + ren_cwid(chrs[i + 1], pos[i + 1]) == pos[i])
33 return -1;
34 return 0;
37 /* highlight text in reverse direction */
38 static void led_markrev(int n, char **chrs, int *pos, int *att)
40 int i = 0, j;
41 int hl = conf_hlrev();
42 while (i + 1 < n) {
43 int dir = led_offdir(chrs, pos, i);
44 int beg = i;
45 while (i + 1 < n && led_offdir(chrs, pos, i) == dir)
46 i++;
47 if (dir < 0)
48 for (j = beg; j <= i; j++)
49 att[j] = syn_merge(hl, att[j]);
50 if (i == beg)
51 i++;
55 /* render and highlight a line */
56 static char *led_render(char *s0, int cbeg, int cend, char *syn)
58 int n;
59 int *pos; /* pos[i]: the screen position of the i-th character */
60 int *off; /* off[i]: the character at screen position i */
61 int *att; /* att[i]: the attributes of i-th character */
62 char **chrs; /* chrs[i]: the i-th character in s1 */
63 int att_old = 0;
64 struct sbuf *out;
65 int i, j;
66 int ctx = dir_context(s0);
67 int att_blank = 0; /* the attribute of blank space */
68 chrs = uc_chop(s0, &n);
69 pos = ren_position(s0);
70 off = malloc((cend - cbeg) * sizeof(off[0]));
71 memset(off, 0xff, (cend - cbeg) * sizeof(off[0]));
72 /* initialise off[] using pos[] */
73 for (i = 0; i < n; i++) {
74 int curwid = ren_cwid(chrs[i], pos[i]);
75 int curbeg = led_posctx(ctx, pos[i], cbeg, cend);
76 int curend = led_posctx(ctx, pos[i] + curwid - 1, cbeg, cend);
77 if (curbeg >= 0 && curbeg < (cend - cbeg) &&
78 curend >= 0 && curend < (cend - cbeg))
79 for (j = 0; j < curwid; j++)
80 off[led_posctx(ctx, pos[i] + j, cbeg, cend)] = i;
82 att = syn_highlight(n <= xlim ? syn : "", s0);
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) {
91 int o = off[i - cbeg];
92 int att_new = o >= 0 ? att[o] : att_blank;
93 sbuf_str(out, term_att(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, ' ');
103 while (i < cend && off[i - cbeg] == o)
104 i++;
105 } else {
106 sbuf_chr(out, ' ');
107 i++;
110 sbuf_str(out, term_att(0, att_old));
111 free(att);
112 free(pos);
113 free(off);
114 free(chrs);
115 return sbuf_done(out);
118 /* print a line on the screen */
119 void led_print(char *s, int row, char *syn)
121 char *r = led_render(s, xleft, xleft + xcols, syn);
122 term_pos(row, 0);
123 term_kill();
124 term_str(r);
125 free(r);
128 /* set xtd and return its old value */
129 static int td_set(int td)
131 int old = xtd;
132 xtd = td;
133 return old;
136 /* print a line on the screen; for ex messages */
137 void led_printmsg(char *s, int row, char *syn)
139 int td = td_set(+2);
140 char *r = led_render(s, 0, xcols, syn);
141 td_set(td);
142 term_pos(row, 0);
143 term_kill();
144 term_str(r);
145 free(r);
148 static int led_lastchar(char *s)
150 char *r = *s ? strchr(s, '\0') : s;
151 if (r != s)
152 r = uc_beg(s, r - 1);
153 return r - s;
156 static int led_lastword(char *s)
158 char *r = *s ? uc_beg(s, strchr(s, '\0') - 1) : s;
159 int kind;
160 while (r > s && uc_isspace(r))
161 r = uc_beg(s, r - 1);
162 kind = r > s ? uc_kind(r) : 0;
163 while (r > s && uc_kind(uc_beg(s, r - 1)) == kind)
164 r = uc_beg(s, r - 1);
165 return r - s;
168 static void led_printparts(char *ai, char *pref, char *main,
169 char *post, int kmap, char *syn)
171 struct sbuf *ln;
172 int off, pos;
173 int idir = 0;
174 ln = sbuf_make();
175 sbuf_str(ln, ai);
176 sbuf_str(ln, pref);
177 sbuf_str(ln, main);
178 off = uc_slen(sbuf_buf(ln));
179 /* cursor position for inserting the next character */
180 if (*pref || *main || *ai) {
181 int len = sbuf_len(ln);
182 sbuf_str(ln, kmap_map(kmap, 'a'));
183 sbuf_str(ln, post);
184 idir = ren_pos(sbuf_buf(ln), off) -
185 ren_pos(sbuf_buf(ln), off - 1) < 0 ? -1 : +1;
186 sbuf_cut(ln, len);
188 term_record();
189 sbuf_str(ln, post);
190 pos = ren_cursor(sbuf_buf(ln), ren_pos(sbuf_buf(ln), MAX(0, off - 1)));
191 if (pos >= xleft + xcols)
192 xleft = pos - xcols / 2;
193 if (pos < xleft)
194 xleft = pos < xcols ? 0 : pos - xcols / 2;
195 led_print(sbuf_buf(ln), -1, syn);
196 term_pos(-1, led_pos(sbuf_buf(ln), pos + idir));
197 sbuf_free(ln);
198 term_commit();
201 /* continue reading the character starting with c */
202 static char *led_readchar(int c, int kmap)
204 static char buf[8];
205 int c1, c2;
206 int i, n;
207 if (c == TK_CTL('v')) { /* literal character */
208 buf[0] = term_read();
209 buf[1] = '\0';
210 return buf;
212 if (c == TK_CTL('k')) { /* digraph */
213 c1 = term_read();
214 if (TK_INT(c1))
215 return NULL;
216 if (c1 == TK_CTL('k'))
217 return "";
218 c2 = term_read();
219 if (TK_INT(c2))
220 return NULL;
221 return conf_digraph(c1, c2);
223 if ((c & 0xc0) == 0xc0) { /* utf-8 character */
224 buf[0] = c;
225 n = uc_len(buf);
226 for (i = 1; i < n; i++)
227 buf[i] = term_read();
228 buf[n] = '\0';
229 return buf;
231 return kmap_map(kmap, c);
234 /* read a character from the terminal */
235 char *led_read(int *kmap)
237 int c = term_read();
238 while (!TK_INT(c)) {
239 switch (c) {
240 case TK_CTL('f'):
241 *kmap = xkmap_alt;
242 break;
243 case TK_CTL('e'):
244 *kmap = 0;
245 break;
246 default:
247 return led_readchar(c, *kmap);
249 c = term_read();
251 return NULL;
254 static int led_match(char *out, int len, char *kwd, char *opt)
256 while (opt != NULL) {
257 int i = 0;
258 while (kwd[i] && kwd[i] == opt[i])
259 i++;
260 if (kwd[i] == '\0')
261 break;
262 opt = strchr(opt, '\n') == NULL ? NULL : strchr(opt, '\n') + 1;
264 out[0] = '\0';
265 if (opt != NULL) {
266 int i = 0;
267 char *beg = opt + strlen(kwd);
268 while (beg[i] && beg[i] != '\n' && i + 8 < len)
269 i += uc_len(beg + i);
270 memcpy(out, beg, i);
271 out[i] = '\0';
272 return 0;
274 return 1;
277 /* read a line from the terminal */
278 static char *led_line(char *pref, char *post, char *ai,
279 int ai_max, int *key, int *kmap, char *syn, char *hist)
281 struct sbuf *sb;
282 int ai_len = strlen(ai);
283 int c, y, lnmode;
284 char cmp[64] = "";
285 char *cs;
286 sb = sbuf_make();
287 if (pref == NULL)
288 pref = "";
289 if (post == NULL || !post[0])
290 post = cmp;
291 while (1) {
292 if (hist != NULL)
293 led_match(cmp, sizeof(cmp), sbuf_buf(sb), hist);
294 led_printparts(ai, pref, sbuf_buf(sb), post, *kmap, syn);
295 c = term_read();
296 switch (c) {
297 case TK_CTL('f'):
298 *kmap = xkmap_alt;
299 continue;
300 case TK_CTL('e'):
301 *kmap = 0;
302 continue;
303 case TK_CTL('h'):
304 case 127:
305 if (sbuf_len(sb))
306 sbuf_cut(sb, led_lastchar(sbuf_buf(sb)));
307 break;
308 case TK_CTL('u'):
309 sbuf_cut(sb, 0);
310 break;
311 case TK_CTL('w'):
312 if (sbuf_len(sb))
313 sbuf_cut(sb, led_lastword(sbuf_buf(sb)));
314 break;
315 case TK_CTL('t'):
316 if (ai_len < ai_max) {
317 ai[ai_len++] = '\t';
318 ai[ai_len] = '\0';
320 break;
321 case TK_CTL('d'):
322 /* when ai and pref are empty, remove the first space of sb */
323 if (ai_len == 0 && !pref[0]) {
324 char *buf = sbuf_buf(sb);
325 if (buf[0] == ' ' || buf[0] == '\t') {
326 char *dup = uc_dup(buf + 1);
327 sbuf_cut(sb, 0);
328 sbuf_str(sb, dup);
329 free(dup);
332 if (ai_len > 0)
333 ai[--ai_len] = '\0';
334 break;
335 case TK_CTL('p'):
336 if (reg_get(0, &lnmode))
337 sbuf_str(sb, reg_get(0, &lnmode));
338 break;
339 case TK_CTL('r'):
340 y = term_read();
341 if (y > 0 && reg_get(y, &lnmode))
342 sbuf_str(sb, reg_get(y, &lnmode));
343 break;
344 case TK_CTL('a'):
345 sbuf_str(sb, cmp);
346 break;
347 default:
348 if (c == '\n' || TK_INT(c))
349 break;
350 if ((cs = led_readchar(c, *kmap)) != NULL)
351 sbuf_str(sb, cs);
353 if (c == '\n')
354 led_printparts(ai, pref, sbuf_buf(sb), "", *kmap, syn);
355 if (c == '\n' || TK_INT(c))
356 break;
358 *key = c;
359 return sbuf_done(sb);
362 /* read an ex command */
363 char *led_prompt(char *pref, char *post, int *kmap, char *syn, char *hist)
365 int key;
366 int td = td_set(+2);
367 int oleft = xleft;
368 char *s = led_line(pref, post, "", 0, &key, kmap, syn, hist);
369 xleft = oleft;
370 td_set(td);
371 if (key == '\n') {
372 struct sbuf *sb = sbuf_make();
373 if (pref)
374 sbuf_str(sb, pref);
375 sbuf_str(sb, s);
376 if (post)
377 sbuf_str(sb, post);
378 free(s);
379 return sbuf_done(sb);
381 free(s);
382 return NULL;
385 /* read visual command input; may update xleft */
386 char *led_input(char *pref, char *post, int *kmap, char *syn)
388 struct sbuf *sb = sbuf_make();
389 char ai[128];
390 int ai_max = sizeof(ai) - 1;
391 int n = 0;
392 int key;
393 while (n < ai_max && (*pref == ' ' || *pref == '\t'))
394 ai[n++] = *pref++;
395 ai[n] = '\0';
396 while (1) {
397 char *ln = led_line(pref, post, ai, ai_max, &key, kmap, syn, NULL);
398 int ln_sp = 0; /* number of initial spaces in ln */
399 while (ln[ln_sp] && (ln[ln_sp] == ' ' || ln[ln_sp] == '\t'))
400 ln_sp++;
401 /* append the auto-indent only if there are other characters */
402 if (ln[ln_sp] || (pref && pref[0]) ||
403 (key != '\n' && post[0] && post[0] != '\n'))
404 sbuf_str(sb, ai);
405 if (pref)
406 sbuf_str(sb, pref);
407 sbuf_str(sb, ln);
408 if (key == '\n')
409 sbuf_chr(sb, '\n');
410 if (key == '\n')
411 term_chr('\n');
412 if (!pref || !pref[0]) { /* updating autoindent */
413 int ai_len = ai_max ? strlen(ai) : 0;
414 int ai_new = ln_sp;
415 if (ai_len + ai_new > ai_max)
416 ai_new = ai_max - ai_len;
417 memcpy(ai + ai_len, ln, ai_new);
418 ai[ai_len + ai_new] = '\0';
420 if (!xai)
421 ai[0] = '\0';
422 free(ln);
423 if (key != '\n')
424 break;
425 term_room(1);
426 pref = NULL;
427 n = 0;
428 while (xai && (post[n] == ' ' || post[n] == '\t'))
429 n++;
430 memmove(post, post + n, strlen(post) - n + 1);
432 sbuf_str(sb, post);
433 if (TK_INT(key))
434 return sbuf_done(sb);
435 sbuf_free(sb);
436 return NULL;