| tled.c - neatvi - [fork] simple vi-type editor with UTF-8 support | |
| git clone git://src.adamsgaard.dk/neatvi | |
| Log | |
| Files | |
| Refs | |
| README | |
| --- | |
| tled.c (9295B) | |
| --- | |
| 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" | |
| 8 | |
| 9 static char *kmap_map(int kmap, int c) | |
| 10 { | |
| 11 static char cs[4]; | |
| 12 char **keymap = conf_kmap(kmap); | |
| 13 cs[0] = c; | |
| 14 return keymap[c] ? keymap[c] : cs; | |
| 15 } | |
| 16 | |
| 17 static int led_posctx(int dir, int pos, int beg, int end) | |
| 18 { | |
| 19 return dir >= 0 ? pos - beg : end - pos - 1; | |
| 20 } | |
| 21 | |
| 22 /* map cursor horizontal position to terminal column number */ | |
| 23 int led_pos(char *s, int pos) | |
| 24 { | |
| 25 return led_posctx(dir_context(s), pos, xleft, xleft + xcols); | |
| 26 } | |
| 27 | |
| 28 static int led_offdir(char **chrs, int *pos, int i) | |
| 29 { | |
| 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; | |
| 35 } | |
| 36 | |
| 37 /* highlight text in reverse direction */ | |
| 38 static void led_markrev(int n, char **chrs, int *pos, int *att) | |
| 39 { | |
| 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++; | |
| 52 } | |
| 53 } | |
| 54 | |
| 55 /* render and highlight a line */ | |
| 56 static char *led_render(char *s0, int cbeg, int cend, char *syn) | |
| 57 { | |
| 58 int n; | |
| 59 int *pos; /* pos[i]: the screen position of the i-th char… | |
| 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 spac… | |
| 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, … | |
| 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, ce… | |
| 81 } | |
| 82 att = syn_highlight((n <= xlim && xhl) ? syn : "", s0); | |
| 83 /* the attribute of \n character is used for blanks */ | |
| 84 for (i = 0; i < n; i++) | |
| 85 if (chrs[i][0] == '\n') | |
| 86 att_blank = att[i]; | |
| 87 led_markrev(n, chrs, pos, att); | |
| 88 /* generate term output */ | |
| 89 out = sbuf_make(); | |
| 90 sbuf_str(out, conf_lnpref()); | |
| 91 i = cbeg; | |
| 92 while (i < cend) { | |
| 93 int o = off[i - cbeg]; | |
| 94 int att_new = o >= 0 ? att[o] : att_blank; | |
| 95 sbuf_str(out, term_att(att_new, att_old)); | |
| 96 att_old = att_new; | |
| 97 if (o >= 0) { | |
| 98 if (ren_translate(chrs[o], s0)) | |
| 99 sbuf_str(out, ren_translate(chrs[o], s0)… | |
| 100 else if (uc_isprint(chrs[o])) | |
| 101 sbuf_mem(out, chrs[o], uc_len(chrs[o])); | |
| 102 else | |
| 103 for (j = i; j < cend && off[j - cbeg] ==… | |
| 104 sbuf_chr(out, ' '); | |
| 105 while (i < cend && off[i - cbeg] == o) | |
| 106 i++; | |
| 107 } else { | |
| 108 sbuf_chr(out, ' '); | |
| 109 i++; | |
| 110 } | |
| 111 } | |
| 112 sbuf_str(out, term_att(0, att_old)); | |
| 113 free(att); | |
| 114 free(pos); | |
| 115 free(off); | |
| 116 free(chrs); | |
| 117 return sbuf_done(out); | |
| 118 } | |
| 119 | |
| 120 /* print a line on the screen */ | |
| 121 void led_print(char *s, int row, char *syn) | |
| 122 { | |
| 123 char *r = led_render(s, xleft, xleft + xcols, syn); | |
| 124 term_pos(row, 0); | |
| 125 term_kill(); | |
| 126 term_str(r); | |
| 127 free(r); | |
| 128 } | |
| 129 | |
| 130 /* set xtd and return its old value */ | |
| 131 static int td_set(int td) | |
| 132 { | |
| 133 int old = xtd; | |
| 134 xtd = td; | |
| 135 return old; | |
| 136 } | |
| 137 | |
| 138 /* print a line on the screen; for ex messages */ | |
| 139 void led_printmsg(char *s, int row, char *syn) | |
| 140 { | |
| 141 int td = td_set(+2); | |
| 142 char *r = led_render(s, xleft, xleft + xcols, syn); | |
| 143 td_set(td); | |
| 144 term_pos(row, 0); | |
| 145 term_kill(); | |
| 146 term_str(r); | |
| 147 free(r); | |
| 148 } | |
| 149 | |
| 150 static int led_lastchar(char *s) | |
| 151 { | |
| 152 char *r = *s ? strchr(s, '\0') : s; | |
| 153 if (r != s) | |
| 154 r = uc_beg(s, r - 1); | |
| 155 return r - s; | |
| 156 } | |
| 157 | |
| 158 static int led_lastword(char *s) | |
| 159 { | |
| 160 char *r = *s ? uc_beg(s, strchr(s, '\0') - 1) : s; | |
| 161 int kind; | |
| 162 while (r > s && uc_isspace(r)) | |
| 163 r = uc_beg(s, r - 1); | |
| 164 kind = r > s ? uc_kind(r) : 0; | |
| 165 while (r > s && uc_kind(uc_beg(s, r - 1)) == kind) | |
| 166 r = uc_beg(s, r - 1); | |
| 167 return r - s; | |
| 168 } | |
| 169 | |
| 170 static void led_printparts(char *ai, char *pref, char *main, | |
| 171 char *post, int kmap, char *syn) | |
| 172 { | |
| 173 struct sbuf *ln; | |
| 174 int off, pos; | |
| 175 int idir = 0; | |
| 176 ln = sbuf_make(); | |
| 177 sbuf_str(ln, ai); | |
| 178 sbuf_str(ln, pref); | |
| 179 sbuf_str(ln, main); | |
| 180 off = uc_slen(sbuf_buf(ln)); | |
| 181 /* cursor position for inserting the next character */ | |
| 182 if (*pref || *main || *ai) { | |
| 183 int len = sbuf_len(ln); | |
| 184 sbuf_str(ln, kmap_map(kmap, 'a')); | |
| 185 sbuf_str(ln, post); | |
| 186 idir = ren_pos(sbuf_buf(ln), off) - | |
| 187 ren_pos(sbuf_buf(ln), off - 1) < 0 ? -1 : +1; | |
| 188 sbuf_cut(ln, len); | |
| 189 } | |
| 190 term_record(); | |
| 191 sbuf_str(ln, post); | |
| 192 pos = ren_cursor(sbuf_buf(ln), ren_pos(sbuf_buf(ln), MAX(0, off … | |
| 193 if (pos >= xleft + xcols) | |
| 194 xleft = pos - xcols / 2; | |
| 195 if (pos < xleft) | |
| 196 xleft = pos < xcols ? 0 : pos - xcols / 2; | |
| 197 led_print(sbuf_buf(ln), -1, syn); | |
| 198 term_pos(-1, led_pos(sbuf_buf(ln), pos + idir)); | |
| 199 sbuf_free(ln); | |
| 200 term_commit(); | |
| 201 } | |
| 202 | |
| 203 /* continue reading the character starting with c */ | |
| 204 static char *led_readchar(int c, int kmap) | |
| 205 { | |
| 206 static char buf[8]; | |
| 207 int c1, c2; | |
| 208 int i, n; | |
| 209 if (c == TK_CTL('v')) { /* literal character */ | |
| 210 buf[0] = term_read(); | |
| 211 buf[1] = '\0'; | |
| 212 return buf; | |
| 213 } | |
| 214 if (c == TK_CTL('k')) { /* digraph */ | |
| 215 c1 = term_read(); | |
| 216 if (TK_INT(c1)) | |
| 217 return NULL; | |
| 218 c2 = term_read(); | |
| 219 if (TK_INT(c2)) | |
| 220 return NULL; | |
| 221 return conf_digraph(c1, c2); | |
| 222 } | |
| 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; | |
| 230 } | |
| 231 return kmap_map(kmap, c); | |
| 232 } | |
| 233 | |
| 234 /* read a character from the terminal */ | |
| 235 char *led_read(int *kmap) | |
| 236 { | |
| 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); | |
| 248 } | |
| 249 c = term_read(); | |
| 250 } | |
| 251 return NULL; | |
| 252 } | |
| 253 | |
| 254 /* read a line from the terminal */ | |
| 255 static char *led_line(char *pref, char *post, char *ai, | |
| 256 int ai_max, int *key, int *kmap, char *syn) | |
| 257 { | |
| 258 struct sbuf *sb; | |
| 259 int ai_len = strlen(ai); | |
| 260 int c, lnmode; | |
| 261 char *cs; | |
| 262 sb = sbuf_make(); | |
| 263 if (!pref) | |
| 264 pref = ""; | |
| 265 if (!post) | |
| 266 post = ""; | |
| 267 while (1) { | |
| 268 led_printparts(ai, pref, sbuf_buf(sb), post, *kmap, syn); | |
| 269 c = term_read(); | |
| 270 switch (c) { | |
| 271 case TK_CTL('f'): | |
| 272 *kmap = xkmap_alt; | |
| 273 continue; | |
| 274 case TK_CTL('e'): | |
| 275 *kmap = 0; | |
| 276 continue; | |
| 277 case TK_CTL('h'): | |
| 278 case 127: | |
| 279 if (sbuf_len(sb)) | |
| 280 sbuf_cut(sb, led_lastchar(sbuf_buf(sb))); | |
| 281 break; | |
| 282 case TK_CTL('u'): | |
| 283 sbuf_cut(sb, 0); | |
| 284 break; | |
| 285 case TK_CTL('w'): | |
| 286 if (sbuf_len(sb)) | |
| 287 sbuf_cut(sb, led_lastword(sbuf_buf(sb))); | |
| 288 break; | |
| 289 case TK_CTL('t'): | |
| 290 if (ai_len < ai_max) { | |
| 291 ai[ai_len++] = '\t'; | |
| 292 ai[ai_len] = '\0'; | |
| 293 } | |
| 294 break; | |
| 295 case TK_CTL('d'): | |
| 296 /* when ai and pref are empty, remove the first … | |
| 297 if (ai_len == 0 && !pref[0]) { | |
| 298 char *buf = sbuf_buf(sb); | |
| 299 if (buf[0] == ' ' || buf[0] == '\t') { | |
| 300 char *dup = uc_dup(buf + 1); | |
| 301 sbuf_cut(sb, 0); | |
| 302 sbuf_str(sb, dup); | |
| 303 free(dup); | |
| 304 } | |
| 305 } | |
| 306 if (ai_len > 0) | |
| 307 ai[--ai_len] = '\0'; | |
| 308 break; | |
| 309 case TK_CTL('p'): | |
| 310 if (reg_get(0, &lnmode)) | |
| 311 sbuf_str(sb, reg_get(0, &lnmode)); | |
| 312 break; | |
| 313 default: | |
| 314 if (c == '\n' || TK_INT(c)) | |
| 315 break; | |
| 316 if ((cs = led_readchar(c, *kmap))) | |
| 317 sbuf_str(sb, cs); | |
| 318 } | |
| 319 if (c == '\n' || TK_INT(c)) | |
| 320 break; | |
| 321 } | |
| 322 *key = c; | |
| 323 return sbuf_done(sb); | |
| 324 } | |
| 325 | |
| 326 /* read an ex command */ | |
| 327 char *led_prompt(char *pref, char *post, int *kmap, char *syn) | |
| 328 { | |
| 329 int key; | |
| 330 int td = td_set(+2); | |
| 331 char *s = led_line(pref, post, "", 0, &key, kmap, syn); | |
| 332 td_set(td); | |
| 333 if (key == '\n') { | |
| 334 struct sbuf *sb = sbuf_make(); | |
| 335 if (pref) | |
| 336 sbuf_str(sb, pref); | |
| 337 sbuf_str(sb, s); | |
| 338 if (post) | |
| 339 sbuf_str(sb, post); | |
| 340 free(s); | |
| 341 return sbuf_done(sb); | |
| 342 } | |
| 343 free(s); | |
| 344 return NULL; | |
| 345 } | |
| 346 | |
| 347 /* read visual command input */ | |
| 348 char *led_input(char *pref, char *post, int *kmap, char *syn) | |
| 349 { | |
| 350 struct sbuf *sb = sbuf_make(); | |
| 351 char ai[128]; | |
| 352 int ai_max = sizeof(ai) - 1; | |
| 353 int n = 0; | |
| 354 int key; | |
| 355 while (n < ai_max && (*pref == ' ' || *pref == '\t')) | |
| 356 ai[n++] = *pref++; | |
| 357 ai[n] = '\0'; | |
| 358 while (1) { | |
| 359 char *ln = led_line(pref, post, ai, ai_max, &key, kmap, … | |
| 360 int ln_sp = 0; /* number of initial spaces in ln … | |
| 361 while (ln[ln_sp] && (ln[ln_sp] == ' ' || ln[ln_sp] == '\… | |
| 362 ln_sp++; | |
| 363 /* append the auto-indent only if there are other charac… | |
| 364 if (ln[ln_sp] || (pref && pref[0]) || | |
| 365 (key != '\n' && post[0] && post[0] != '\… | |
| 366 sbuf_str(sb, ai); | |
| 367 if (pref) | |
| 368 sbuf_str(sb, pref); | |
| 369 sbuf_str(sb, ln); | |
| 370 if (key == '\n') | |
| 371 sbuf_chr(sb, '\n'); | |
| 372 led_printparts(ai, pref ? pref : "", uc_lastline(ln), | |
| 373 key == '\n' ? "" : post, *kmap, syn); | |
| 374 if (key == '\n') | |
| 375 term_chr('\n'); | |
| 376 if (!pref || !pref[0]) { /* updating autoindent */ | |
| 377 int ai_len = ai_max ? strlen(ai) : 0; | |
| 378 int ai_new = ln_sp; | |
| 379 if (ai_len + ai_new > ai_max) | |
| 380 ai_new = ai_max - ai_len; | |
| 381 memcpy(ai + ai_len, ln, ai_new); | |
| 382 ai[ai_len + ai_new] = '\0'; | |
| 383 } | |
| 384 if (!xai) | |
| 385 ai[0] = '\0'; | |
| 386 free(ln); | |
| 387 if (key != '\n') | |
| 388 break; | |
| 389 term_room(1); | |
| 390 pref = NULL; | |
| 391 n = 0; | |
| 392 while (xai && (post[n] == ' ' || post[n] == '\t')) | |
| 393 n++; | |
| 394 memmove(post, post + n, strlen(post) - n + 1); | |
| 395 } | |
| 396 sbuf_str(sb, post); | |
| 397 if (TK_INT(key)) | |
| 398 return sbuf_done(sb); | |
| 399 sbuf_free(sb); | |
| 400 return NULL; | |
| 401 } |