tsyn: syntax highlighting - 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
       ---
 (DIR) commit 71b2ab030dbda59b9d8cf44fb78bc0ff4af93073
 (DIR) parent 26f00e91254bf63cbd7a6e0bc966c158be004aea
 (HTM) Author: Ali Gholami Rudi <ali@rudi.ir>
       Date:   Fri, 22 May 2015 09:51:43 +0430
       
       syn: syntax highlighting
       
       Diffstat:
         M Makefile                            |       3 ++-
         M conf.c                              |      29 ++++++++++++++++++++++++++++-
         M conf.h                              |      30 ++++++++++++++++++++++++++++++
         M ex.c                                |       2 ++
         M led.c                               |       8 ++++++++
         A syn.c                               |      98 +++++++++++++++++++++++++++++++
         M term.c                              |      30 ++++++++++++++++++++++++++++++
         M vi.c                                |       2 ++
         M vi.h                                |      17 +++++++++++++++++
       
       9 files changed, 217 insertions(+), 2 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       t@@ -2,7 +2,8 @@ CC = cc
        CFLAGS = -Wall -O2
        LDFLAGS =
        
       -OBJS = vi.o ex.o lbuf.o sbuf.o ren.o dir.o reg.o led.o uc.o term.o rset.o cmd.o conf.o
       +OBJS = vi.o ex.o lbuf.o sbuf.o ren.o dir.o syn.o reg.o led.o \
       +        uc.o term.o rset.o cmd.o conf.o
        
        all: vi
        
 (DIR) diff --git a/conf.c b/conf.c
       t@@ -1,5 +1,6 @@
       -#include "conf.h"
       +#include <stdio.h>
        #include "vi.h"
       +#include "conf.h"
        
        char *conf_kmapalt(void)
        {
       t@@ -44,3 +45,29 @@ int conf_placeholder(int idx, char **s, char **d, int *wid)
                        *wid = placeholders[idx].wid;
                return 0;
        }
       +
       +int conf_highlight(int idx, char **ft, int *att, int *grp, char **pat)
       +{
       +        if (idx < 0 || idx >= LEN(highlights))
       +                return 1;
       +        if (ft)
       +                *ft = highlights[idx].ft;
       +        if (att)
       +                *att = highlights[idx].att;
       +        if (grp)
       +                *grp = highlights[idx].grp;
       +        if (pat)
       +                *pat = highlights[idx].pat;
       +        return 0;
       +}
       +
       +int conf_filetype(int idx, char **ft, char **pat)
       +{
       +        if (idx < 0 || idx >= LEN(filetypes))
       +                return 1;
       +        if (ft)
       +                *ft = filetypes[idx].ft;
       +        if (pat)
       +                *pat = filetypes[idx].pat;
       +        return 0;
       +}
 (DIR) diff --git a/conf.h b/conf.h
       t@@ -49,3 +49,33 @@ static struct placeholder {
                {"َ", "ـَ", 1},
                {"ّ", "ـّ", 1},
        };
       +
       +/* syntax highlighting patterns */
       +static struct highlight {
       +        char *ft;                /* the filetype of this pattern */
       +        int att;                /* attributes of the matched region */
       +        int grp;                /* regular expression subgroup to highlight */
       +        char *pat;                /* regular expression */
       +} highlights[] = {
       +        {"c", 5, 0, "\\<(char|short|int|long|float|double|void|struct|enum|union)\\>"},
       +        {"c", 5, 0, "\\<(static|extern|register)\\>"},
       +        {"c", 5, 0, "\\<(return|for|while|if|else|do|sizeof|goto|switch|case|default|break|continue)\\>"},
       +        {"c", SYN_IT | 2, 0, "//.*$"},
       +        {"c", SYN_IT | 2, 0, "/\\*([^*]|\\*[^/])*\\*/"},
       +        {"c", 6, 0, "^#[ \t]*[a-zA-Z0-9_]+"},
       +        {"c", SYN_BD, 1, "([a-zA-Z][a-zA-Z0-9_]+)\\("},
       +        {"c", 4, 0, "\"([^\"]|\\\\\")*\""},
       +        {"c", 4, 0, "'([^\\]|\\\\.)'"},
       +
       +        {"tr", SYN_BD, 0, "^\\.SH.*$"},
       +        {"tr", 4, 0, "^\\.[a-zA-Z0-9]{2}.*$"},
       +};
       +
       +/* map file names to file types */
       +static struct filetype {
       +        char *ft;                /* file type */
       +        char *pat;                /* file name pattern */
       +} filetypes[] = {
       +        {"c", "\\.[hc]$"},
       +        {"tr", "\\.(ms|tr|roff)$"},
       +};
 (DIR) diff --git a/ex.c b/ex.c
       t@@ -11,6 +11,7 @@
        
        char xpath[PATHLEN];                /* current file */
        char xpath_alt[PATHLEN];        /* alternate file */
       +char xft[32];                        /* filetype */
        int xquit;                        /* exit if set */
        int xvis;                        /* visual mode */
        int xai = 1;                        /* autoindent option */
       t@@ -189,6 +190,7 @@ static void ec_edit(char *ec)
                        xrow_alt = xrow;
                        xrow = xvis ? 0 : 1 << 20;
                }
       +        strcpy(xft, syn_filetype(xpath));
                fd = open(xpath, O_RDONLY);
                lbuf_rm(xb, 0, lbuf_len(xb));
                if (fd >= 0) {
 (DIR) diff --git a/led.c b/led.c
       t@@ -45,7 +45,9 @@ static char *led_render(char *s0)
                int n, maxcol = 0;
                int *pos;        /* pos[i]: the screen position of the i-th character */
                int *off;        /* off[i]: the character at screen position i */
       +        int *att;        /* att[i]: the attributes of i-th character */
                char **chrs;        /* chrs[i]: the i-th character in s1 */
       +        int att_old = 0;
                struct sbuf *out;
                int i, j;
                int ctx = dir_context(s0);
       t@@ -64,10 +66,14 @@ static char *led_render(char *s0)
                                }
                        }
                }
       +        att = syn_highlight(xft, s0);
                out = sbuf_make();
                i = 0;
                while (i <= maxcol) {
                        int o = off[i];
       +                int att_new = o >= 0 ? att[o] : 0;
       +                sbuf_str(out, term_att(att_new, att_old));
       +                att_old = att_new;
                        if (o >= 0) {
                                if (ren_translate(chrs[o], s0))
                                        sbuf_str(out, ren_translate(chrs[o], s0));
       t@@ -83,6 +89,8 @@ static char *led_render(char *s0)
                                i++;
                        }
                }
       +        sbuf_str(out, term_att(0, att_old));
       +        free(att);
                free(pos);
                free(off);
                free(chrs);
 (DIR) diff --git a/syn.c b/syn.c
       t@@ -0,0 +1,98 @@
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include "vi.h"
       +
       +#define NFTS                16
       +
       +/* mapping filetypes to regular expression sets */
       +static struct ftmap {
       +        char ft[32];
       +        struct rset *rs;
       +} ftmap[NFTS];
       +
       +static struct rset *syn_ftrs;
       +
       +static struct rset *syn_find(char *ft)
       +{
       +        int i;
       +        for (i = 0; i < LEN(ftmap); i++)
       +                if (!strcmp(ft, ftmap[i].ft))
       +                        return ftmap[i].rs;
       +        return NULL;
       +}
       +
       +int *syn_highlight(char *ft, char *s)
       +{
       +        int subs[16 * 2];
       +        int n = uc_slen(s);
       +        int *att = malloc(n * sizeof(att[0]));
       +        int sidx = 0;
       +        struct rset *rs = syn_find(ft);
       +        int flg = 0;
       +        int hl, j;
       +        memset(att, 0, n * sizeof(att[0]));
       +        if (!rs)
       +                return att;
       +        while ((hl = rset_find(rs, s + sidx, LEN(subs) / 2, subs, flg)) >= 0) {
       +                int beg, end;
       +                int catt, cgrp;
       +                conf_highlight(hl, NULL, &catt, &cgrp, NULL);
       +                beg = uc_off(s, sidx + subs[cgrp * 2 + 0]);
       +                end = uc_off(s, sidx + subs[cgrp * 2 + 1]);
       +                for (j = beg; j < end; j++)
       +                        att[j] = catt;
       +                sidx += subs[cgrp * 2 + 1] ? subs[cgrp * 2 + 1] : 1;
       +                flg = RE_NOTBOL;
       +        }
       +        return att;
       +}
       +
       +static void syn_initft(char *name)
       +{
       +        char *pats[128] = {NULL};
       +        char *ft, *pat;
       +        int i, n;
       +        for (i = 0; !conf_highlight(i, &ft, NULL, NULL, &pat) && i < LEN(pats); i++)
       +                if (!strcmp(ft, name))
       +                        pats[i] = pat;
       +        n = i;
       +        for (i = 0; i < LEN(ftmap); i++) {
       +                if (!ftmap[i].ft[0]) {
       +                        strcpy(ftmap[i].ft, name);
       +                        ftmap[i].rs = rset_make(n, pats, 0);
       +                        return;
       +                }
       +        }
       +}
       +
       +char *syn_filetype(char *path)
       +{
       +        int hl = rset_find(syn_ftrs, path, 0, NULL, 0);
       +        char *ft;
       +        if (!conf_filetype(hl, &ft, NULL))
       +                return ft;
       +        return "";
       +}
       +
       +void syn_init(void)
       +{
       +        char *pats[128] = {NULL};
       +        char *pat, *ft;
       +        int i;
       +        for (i = 0; !conf_highlight(i, &ft, NULL, NULL, NULL); i++)
       +                if (!syn_find(ft))
       +                        syn_initft(ft);
       +        for (i = 0; !conf_filetype(i, NULL, &pat) && i < LEN(pats); i++)
       +                pats[i] = pat;
       +        syn_ftrs = rset_make(i, pats, 0);
       +}
       +
       +void syn_done(void)
       +{
       +        int i;
       +        for (i = 0; i < LEN(ftmap); i++)
       +                if (ftmap[i].rs)
       +                        rset_free(ftmap[i].rs);
       +        rset_free(syn_ftrs);
       +}
 (DIR) diff --git a/term.c b/term.c
       t@@ -113,3 +113,33 @@ int term_read(int ms)
                        return -1;
                return (unsigned char) b;
        }
       +
       +/* return a static string that changes text attributes from old to att */
       +char *term_att(int att, int old)
       +{
       +        static char buf[128];
       +        char *s = buf;
       +        int fg = SYN_FG(att);
       +        int bg = SYN_BG(att);
       +        if (att == old)
       +                return "";
       +        s += sprintf(s, "\33[m\33[");
       +        if (fg & SYN_BD)
       +                s += sprintf(s, "1;");
       +        if (fg & SYN_IT)
       +                s += sprintf(s, "3;");
       +        else if (fg & SYN_RV)
       +                s += sprintf(s, "7;");
       +        if ((fg & 0xff) < 8)
       +                s += sprintf(s, "%d;", 30 + (fg & 0xff));
       +        else
       +                s += sprintf(s, "38;5;%d;", (fg & 0xff));
       +        if (bg) {
       +                if ((bg & 0xff) < 8)
       +                        s += sprintf(s, "%d;", 40 + (bg & 0xff));
       +                else
       +                        s += sprintf(s, "48;5;%d;", (bg & 0xff));
       +        }
       +        s += sprintf(s, "m");
       +        return buf;
       +}
 (DIR) diff --git a/vi.c b/vi.c
       t@@ -1227,6 +1227,7 @@ int main(int argc, char *argv[])
                                xvis = 1;
                }
                dir_init();
       +        syn_init();
                if (i < argc) {
                        snprintf(ecmd, PATHLEN, "e %s", argv[i]);
                        ex_command(ecmd);
       t@@ -1237,6 +1238,7 @@ int main(int argc, char *argv[])
                        ex();
                lbuf_free(xb);
                reg_done();
       +        syn_done();
                dir_done();
                return 0;
        }
 (DIR) diff --git a/vi.h b/vi.h
       t@@ -103,6 +103,7 @@ int term_cols(void);
        int term_read(int timeout);
        void term_record(void);
        void term_commit(void);
       +char *term_att(int att, int old);
        
        #define TK_CTL(x)        ((x) & 037)
        #define TK_INT(c)        ((c) < 0 || (c) == TK_ESC || (c) == TK_CTL('c'))
       t@@ -124,11 +125,26 @@ void ex_show(char *msg);
        /* process management */
        char *cmd_pipe(char *cmd, char *s);
        
       +/* syntax highlighting */
       +#define SYN_BD                0x100
       +#define SYN_IT                0x200
       +#define SYN_RV                0x400
       +#define SYN_ATTR(f, b)        (((b) << 16) | (f))
       +#define SYN_FG(a)        ((a) & 0xffff)
       +#define SYN_BG(a)        ((a) >> 16)
       +
       +int *syn_highlight(char *ft, char *s);
       +char *syn_filetype(char *path);
       +void syn_init(void);
       +void syn_done(void);
       +
        /* configuration variables */
        char *conf_kmapalt(void);
        int conf_dirmark(int idx, char **pat, int *ctx, int *dir, int *grp);
        int conf_dircontext(int idx, char **pat, int *ctx);
        int conf_placeholder(int idx, char **s, char **d, int *wid);
       +int conf_highlight(int idx, char **ft, int *att, int *grp, char **pat);
       +int conf_filetype(int idx, char **ft, char **pat);
        
        /* global variables */
        #define PATHLEN                512
       t@@ -148,3 +164,4 @@ extern int xai;
        extern int xdir;
        extern int xshape;
        extern int xorder;
       +extern char xft[];