tled.c - neatvi - [fork] simple vi-type editor with UTF-8 support
 (HTM) git clone git://src.adamsgaard.dk/neatvi
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) 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 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;
           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] == o; j++)
          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 - 1)));
          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 space of sb */
          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, syn);
          360                 int ln_sp = 0;        /* number of initial spaces in ln */
          361                 while (ln[ln_sp] && (ln[ln_sp] == ' ' || ln[ln_sp] == '\t'))
          362                         ln_sp++;
          363                 /* append the auto-indent only if there are other characters */
          364                 if (ln[ln_sp] || (pref && pref[0]) ||
          365                                 (key != '\n' && post[0] && post[0] != '\n'))
          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 }