qemacs-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemacs-commit] qemacs extra-modes.c


From: Charlie Gordon
Subject: [Qemacs-commit] qemacs extra-modes.c
Date: Sun, 06 Mar 2016 18:45:20 +0000

CVSROOT:        /sources/qemacs
Module name:    qemacs
Changes by:     Charlie Gordon <chqrlie>        16/03/06 18:45:20

Modified files:
        .              : extra-modes.c 

Log message:
        extra-modes: syntax fixes, added new syntaxes
        - fixed fortran number syntax
        - fixed comments for init files
        - added coffee-mode for the Javascript-like Coffee language
        - use coffee-mode for Cakefile
        - added scad-mode for the OpenSCAD language
        - use single set_color for ini_colorize_line, python_colorize_line
          ruby_colorize_line, elixir_colorize_line, agena_colorize_line

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/qemacs/extra-modes.c?cvsroot=qemacs&r1=1.49&r2=1.50

Patches:
Index: extra-modes.c
===================================================================
RCS file: /sources/qemacs/qemacs/extra-modes.c,v
retrieving revision 1.49
retrieving revision 1.50
diff -u -b -r1.49 -r1.50
--- extra-modes.c       1 Sep 2015 10:14:30 -0000       1.49
+++ extra-modes.c       6 Mar 2016 18:45:20 -0000       1.50
@@ -978,13 +978,20 @@
         }
         /* parse numbers */
         if (qe_isdigit(c)) {
-            for (; i < n; i++) {
-                /* XXX: should parse actual Fortran number syntax,
-                 * with D or E for exponent
-                 */
-                if (!qe_isalnum(str[i])
-                &&  !(str[i] == '.' && !qe_isalpha(str[i + 1]) && 
!qe_isalpha(str[i + 2]))) {
-                    break;
+            /* Parse actual Fortran number syntax, with D or E for exponent */
+            for (; qe_isdigit(str[i]); i++)
+                continue;
+            if (str[i] == '.' && qe_isdigit(str[i + 1])) {
+                for (i += 2; qe_isdigit(str[i]); i++)
+                    continue;
+            }
+            if ((c = qe_tolower(str[i])) == 'e' || c == 'd') {
+                int k = i + 1;
+                if (str[k] == '+' || str[k] == '-')
+                    k++;
+                if (qe_isdigit(str[k])) {
+                    for (i = k + 1; qe_isdigit(str[i]); i++)
+                        continue;
                 }
             }
             SET_COLOR(str, start, i, FORTRAN_STYLE_NUMBER);
@@ -1051,31 +1058,38 @@
 static void ini_colorize_line(QEColorizeContext *cp,
                               unsigned int *str, int n, ModeDef *syn)
 {
-    int i = 0, start, c, bol = 1;
+    int i = 0, start, c, style = 0, indent;
+
+    while (qe_isblank(str[i]))
+        i++;
+
+    indent = i;
 
     while (i < n) {
         start = i;
         c = str[i++];
         switch (c) {
         case ';':
-            if (!bol)
-                break;
+            if (start == indent) {
             i = n;
-            SET_COLOR(str, start, i, INI_STYLE_COMMENT);
+                style = INI_STYLE_COMMENT;
+                break;
+            }
             continue;
         case '#':
-            if (!bol)
-                break;
+            if (start == indent) {
             i = n;
-            SET_COLOR(str, start, i, INI_STYLE_PREPROCESS);
+                style = INI_STYLE_PREPROCESS;
+                break;
+            }
             continue;
         case '[':
             if (start == 0) {
                 i = n;
-                SET_COLOR(str, start, i, INI_STYLE_FUNCTION);
-                continue;
-            }
+                style = INI_STYLE_FUNCTION;
             break;
+            }
+            continue;
         case '\"':
             /* parse string const */
             while (i < n) {
@@ -1083,25 +1097,20 @@
                 if (str[i++] == '\"')
                     break;
             }
-            SET_COLOR(str, start, i, INI_STYLE_STRING);
-            continue;
+            style = INI_STYLE_STRING;
+            break;
         case ' ':
         case '\t':
-            if (bol)
                 continue;
-            break;
         default:
-            break;
-        }
-        bol = 0;
         /* parse numbers */
         if (qe_isdigit(c)) {
             for (; i < n; i++) {
                 if (!qe_isalnum(str[i]))
                     break;
             }
-            SET_COLOR(str, start, i, INI_STYLE_NUMBER);
-            continue;
+                style = INI_STYLE_NUMBER;
+                break;
         }
         /* parse identifiers and keywords */
         if (start == 0 && (qe_isalpha_(c) || c == '@' || c == '$')) {
@@ -1110,9 +1119,14 @@
                     break;
             }
             if (i < n) {
-                SET_COLOR(str, start, i, INI_STYLE_IDENTIFIER);
+                    style = INI_STYLE_IDENTIFIER;
             }
-            continue;
+                break;
+            }
+        }
+        if (style) {
+            SET_COLOR(str, start, i, style);
+            style = 0;
         }
     }
 }
@@ -2217,6 +2231,367 @@
     return 0;
 }
 
+/*---------------- Coffee coloring ----------------*/
+
+static char const coffee_keywords[] = {
+    // keywords common with Javascript:
+    "true|false|null|this|new|delete|typeof|in|instanceof|"
+    "return|throw|break|continue|debugger|yield|if|else|"
+    "switch|for|while|do|try|catch|finally|class|extends|super|"
+    // CoffeeScript only keywords:
+    "undefined|then|unless|until|loop|of|by|when|"
+    // aliasses
+    "and|or|is|isnt|not|yes|no|on|off|"
+    // reserved: should be flagged as errors
+    "case|default|function|var|void|with|const|let|enum|export|import|"
+    "native|implements|interface|package|private|protected|public|static|"
+    // proscribed in strict mode
+    "arguments|eval|yield*|"
+};
+
+enum {
+    IN_COFFEE_STRING       = 0x100,
+    IN_COFFEE_STRING2      = 0x200,
+    IN_COFFEE_REGEX        = 0x400,
+    IN_COFFEE_LONG_STRING  = 0x01,
+    IN_COFFEE_LONG_STRING2 = 0x02,
+    IN_COFFEE_LONG_REGEX   = 0x04,
+    IN_COFFEE_REGEX_CCLASS = 0x08,
+    IN_COFFEE_JSTOKEN      = 0x10,
+    IN_COFFEE_LONG_COMMENT = 0x20,
+};
+
+enum {
+    COFFEE_STYLE_TEXT =     QE_STYLE_DEFAULT,
+    COFFEE_STYLE_COMMENT =  QE_STYLE_COMMENT,
+    COFFEE_STYLE_STRING =   QE_STYLE_STRING,
+    COFFEE_STYLE_REGEX =    QE_STYLE_STRING,
+    COFFEE_STYLE_JSTOKEN =  QE_STYLE_STRING,
+    COFFEE_STYLE_NUMBER =   QE_STYLE_NUMBER,
+    COFFEE_STYLE_KEYWORD =  QE_STYLE_KEYWORD,
+    COFFEE_STYLE_FUNCTION = QE_STYLE_FUNCTION,
+    COFFEE_STYLE_ERROR =    QE_STYLE_ERROR,
+};
+
+static void coffee_colorize_line(QEColorizeContext *cp,
+                                 unsigned int *str, int n, ModeDef *syn)
+{
+    int i = 0, start = i, c, style = 0, sep, klen, prev, i1;
+    int state = cp->colorize_state;
+    char kbuf[32];
+
+    if (state & IN_COFFEE_STRING) {
+        sep = '\'';
+        goto parse_string;
+    }
+    if (state & IN_COFFEE_STRING2) {
+        sep = '\"';
+        goto parse_string;
+    }
+    if (state & IN_COFFEE_REGEX) {
+        goto parse_regex;
+    }
+    if (state & IN_COFFEE_LONG_STRING) {
+        sep = '\'';
+        goto parse_long_string;
+    }
+    if (state & IN_COFFEE_LONG_STRING2) {
+        sep = '\"';
+        goto parse_long_string;
+    }
+    if (state & IN_COFFEE_LONG_REGEX) {
+        goto parse_regex;
+    }
+    if (state & IN_COFFEE_JSTOKEN) {
+        goto parse_jstoken;
+    }
+    if (state & IN_COFFEE_LONG_COMMENT) {
+        goto parse_long_comment;
+    }
+
+    while (i < n) {
+        start = i;
+        c = str[i++];
+        switch (c) {
+        case '#':
+            if (str[i] == '#' && str[i + 1] == '#') {
+                /* multi-line block comments with ### */
+                state = IN_COFFEE_LONG_COMMENT;
+            parse_long_comment:
+                while (i < n) {
+                    c = str[i++];
+                    if (c == '#' && str[i] == '#' && str[i + 1] == '#') {
+                        i += 2;
+                        state = 0;
+                        break;
+                    }
+                }
+            } else {
+                i = n;
+            }
+            style = COFFEE_STYLE_COMMENT;
+            break;
+
+        case '\'':
+        case '\"':
+            /* parse string constant */
+            i--;
+            sep = str[i++];
+            if (str[i] == (unsigned int)sep && str[i + 1] == (unsigned 
int)sep) {
+                /* long string */
+                state = (sep == '\"') ? IN_COFFEE_LONG_STRING2 :
+                        IN_COFFEE_LONG_STRING;
+                i += 2;
+            parse_long_string:
+                while (i < n) {
+                    c = str[i++];
+                    if (c == '\\') {
+                        if (i < n) {
+                            i += 1;
+                        }
+                    } else
+                    if (c == sep && str[i] == (unsigned int)sep && str[i + 1] 
== (unsigned int)sep) {
+                        i += 2;
+                        state = 0;
+                        break;
+                    }
+                }
+            } else {
+                state = (sep == '\"') ? IN_COFFEE_STRING2 : IN_COFFEE_STRING;
+            parse_string:
+                while (i < n) {
+                    c = str[i++];
+                    if (c == '\\') {
+                        if (i < n) {
+                            i += 1;
+                        }
+                    } else
+                    if (c == sep) {
+                        state = 0;
+                        break;
+                    }
+                }
+                if (state) {
+                    state = 0;
+                    // unterminated string literal, should flag unless
+                    // point is at end of line.
+                    style = COFFEE_STYLE_ERROR;
+                    break;
+                }
+            }
+            style = COFFEE_STYLE_STRING;
+            break;
+
+        case '`':
+            /* parse multi-line JS token */
+            state = IN_COFFEE_JSTOKEN;
+        parse_jstoken:
+            while (i < n) {
+                c = str[i++];
+                if (c == '\\') {
+                    if (i < n) {
+                        i += 1;
+                    }
+                } else
+                if (c == '`') {
+                    state = 0;
+                    break;
+                }
+            }
+            style = COFFEE_STYLE_JSTOKEN;
+            break;
+
+        case '.':
+            if (qe_isdigit(str[i]))
+                goto parse_decimal;
+            if (str[i] == '.') /* .. range operator */
+                i++;
+            if (str[i] == '.') /* ... range operator */
+                i++;
+            continue;
+
+        case '/':
+            /* XXX: should use more context to tell regex from divide */
+            if (str[i] == '/') {
+                i++;
+                if (str[i] == '/') {
+                    /* multiline /// regex */
+                    state = IN_COFFEE_LONG_REGEX;
+                    i++;
+                    goto parse_regex;
+                } else {
+                    /* floor divide // operator */
+                    break;
+                }
+            }
+            prev = ' ';
+            for (i1 = start; i1 > 0; ) {
+                prev = str[--i1] & CHAR_MASK;
+                if (!qe_isblank(prev))
+                    break;
+            }
+            if (qe_findchar(" [({},;=<>!~^&|*/%?:", prev)
+            ||  qe_findchar("^\\?.[{},;<>!~&|*%:", str[i])
+            ||  (str[i] == '=' && str[i + 1] == '/')
+            ||  (str[i] == '(' && str[i + 1] == '?')
+            ||  (str[i1] >> STYLE_SHIFT) == COFFEE_STYLE_KEYWORD
+            ||  (str[i] != ' ' && (str[i] != '=' || str[i + 1] != ' ')
+            &&   !(qe_isalnum(prev) || qe_findchar(")]}\"\'?:", prev)))) {
+                state = IN_COFFEE_REGEX;
+            parse_regex:
+                style = COFFEE_STYLE_REGEX;
+                while (i < n) {
+                    c = str[i++];
+                    if (c == '\\') {
+                        if (i < n) {
+                            i += 1;
+                        }
+                    } else
+                    if (state & IN_COFFEE_REGEX_CCLASS) {
+                        if (c == ']') {
+                            state &= ~IN_COFFEE_REGEX_CCLASS;
+                        }
+                        /* ignore '/' inside char classes */
+                    } else {
+                        if (c == '[') {
+                            state |= IN_COFFEE_REGEX_CCLASS;
+                            if (str[i] == '^')
+                                i++;
+                            if (str[i] == ']')
+                                i++;
+                        } else
+                        if (state & IN_COFFEE_LONG_REGEX) {
+                            if (c == '/' && str[i] == '/' && str[i + 1] == 
'/') {
+                                i += 2;
+                                state = 0;
+                                while (qe_isalpha(str[i]))
+                                    i++;
+                                break;
+                            } else
+                            if (qe_isblank(c) && str[i] == '#' && str[i+1] != 
'{') {
+                                SET_COLOR(str, start, i, style);
+                                start = i;
+                                i = n;
+                                style = COFFEE_STYLE_COMMENT;
+                                break;
+                            }
+                        } else {
+                            if (c == '/') {
+                                state = 0;
+                                while (qe_isalpha(str[i]))
+                                    i++;
+                                break;
+                            }
+                        }
+                    }
+                }
+                if (state & ~IN_COFFEE_LONG_REGEX) {
+                    state = 0;
+                    // unterminated regex literal, should flag unless
+                    // point is at end of line.
+                    style = COFFEE_STYLE_ERROR;
+                    break;
+                }
+                break;
+            }
+            continue;
+
+        default:
+            if (qe_isdigit(c)) {
+                if (c == '0' && str[i] == 'b') {
+                    /* binary numbers */
+                    for (i += 1; qe_isbindigit(str[i]); i++)
+                        continue;
+                } else
+                if (c == '0' && str[i] == 'o') {
+                    /* octal numbers */
+                    for (i += 1; qe_isoctdigit(str[i]); i++)
+                        continue;
+                } else
+                if (c == '0' && str[i] == 'x') {
+                    /* hexadecimal numbers */
+                    for (i += 1; qe_isxdigit(str[i]); i++)
+                        continue;
+                } else {
+                    /* decimal numbers */
+                    for (; qe_isdigit(str[i]); i++)
+                        continue;
+                    if (str[i] == '.' && qe_isdigit(str[i + 1])) {
+                        i++;
+                    parse_decimal:
+                        /* decimal floats require a digit after the '.' */
+                        for (; qe_isdigit(str[i]); i++)
+                            continue;
+                    }
+                    if (str[i] == 'e') {
+                        int k = i + 1;
+                        if (str[k] == '+' || str[k] == '-')
+                            k++;
+                        if (qe_isdigit(str[k])) {
+                            for (i = k + 1; qe_isdigit(str[i]); i++)
+                                continue;
+                        }
+                    }
+                }
+
+                /* XXX: should detect malformed number constants */
+                style = COFFEE_STYLE_NUMBER;
+                break;
+            }
+            if (qe_isalpha_(c)) {
+                for (klen = 0, i--; qe_isalnum_(str[i]); i++) {
+                    if (klen < countof(kbuf) - 1)
+                        kbuf[klen++] = str[i];
+                }
+                kbuf[klen] = '\0';
+
+                if (strfind(syn->keywords, kbuf)) {
+                    style = COFFEE_STYLE_KEYWORD;
+                    break;
+                }
+                if (check_fcall(str, i)) {
+                    style = COFFEE_STYLE_FUNCTION;
+                    break;
+                }
+                continue;
+            }
+            continue;
+        }
+        if (style) {
+            SET_COLOR(str, start, i, style);
+            style = 0;
+        }
+    }
+    cp->colorize_state = state;
+}
+
+static int coffee_mode_probe(ModeDef *mode, ModeProbeData *p)
+{
+    if (match_extension(p->filename, mode->extensions)
+    ||  match_shell_handler(cs8(p->buf), mode->shell_handlers)
+    ||  stristart(p->filename, "Cakefile", NULL)) {
+        return 80;
+    }
+    return 1;
+}
+
+static ModeDef coffee_mode = {
+    .name = "CoffeeScript",
+    .mode_name = "coffee",
+    .extensions = "coffee",
+    .shell_handlers = "coffee",
+    .mode_probe = coffee_mode_probe,
+    .keywords = coffee_keywords,
+    .colorize_func = coffee_colorize_line,
+};
+
+static int coffee_init(void)
+{
+    qe_register_mode(&coffee_mode, MODEF_SYNTAX);
+
+    return 0;
+}
+
 /*---------------- Python coloring ----------------*/
 
 static char const python_keywords[] = {
@@ -2248,7 +2623,7 @@
 static void python_colorize_line(QEColorizeContext *cp,
                                  unsigned int *str, int n, ModeDef *syn)
 {
-    int i = 0, start = i, c, sep = 0, klen;
+    int i = 0, start = i, c, style = 0, sep, klen;
     int state = cp->colorize_state;
     char kbuf[32];
 
@@ -2275,12 +2650,12 @@
         switch (c) {
         case '#':
             i = n;
-            SET_COLOR(str, start, i, PYTHON_STYLE_COMMENT);
-            continue;
+            style = PYTHON_STYLE_COMMENT;
+            break;
 
         case '\'':
         case '\"':
-            /* parse string const */
+            /* parse string constant */
             i--;
         has_quote:
             sep = str[i++];
@@ -2319,13 +2694,13 @@
                     }
                 }
             }
-            SET_COLOR(str, start, i, PYTHON_STYLE_STRING);
-            continue;
+            style = PYTHON_STYLE_STRING;
+            break;
 
         case '.':
             if (qe_isdigit(str[i]))
                 goto parse_decimal;
-            break;
+            continue;
 
         case 'b':
         case 'B':
@@ -2393,8 +2768,8 @@
                 }
 
                 /* XXX: should detect malformed number constants */
-                SET_COLOR(str, start, i, PYTHON_STYLE_NUMBER);
-                continue;
+                style = PYTHON_STYLE_NUMBER;
+                break;
             }
         has_alpha:
             if (qe_isalpha_(c)) {
@@ -2405,16 +2780,20 @@
                 kbuf[klen] = '\0';
 
                 if (strfind(syn->keywords, kbuf)) {
-                    SET_COLOR(str, start, i, PYTHON_STYLE_KEYWORD);
-                    continue;
+                    style = PYTHON_STYLE_KEYWORD;
+                    break;
                 }
                 if (check_fcall(str, i)) {
-                    SET_COLOR(str, start, i, PYTHON_STYLE_FUNCTION);
+                    style = PYTHON_STYLE_FUNCTION;
+                    break;
+                }
                     continue;
                 }
                 continue;
             }
-            break;
+        if (style) {
+            SET_COLOR(str, start, i, style);
+            style = 0;
         }
     }
     cp->colorize_state = state;
@@ -2505,7 +2884,7 @@
 static void ruby_colorize_line(QEColorizeContext *cp,
                                unsigned int *str, int n, ModeDef *syn)
 {
-    int i = 0, j, start = i, c, indent, sig, style;
+    int i = 0, j, start = i, c, style, indent, sig;
     static int sep, sep0, level;        /* XXX: ugly patch */
     int state = cp->colorize_state;
     char kbuf[32];
@@ -2618,16 +2997,16 @@
                         break;
                     }
                 }
-                SET_COLOR(str, start, i, RUBY_STYLE_REGEX);
-                continue;
-            }
+                style = RUBY_STYLE_REGEX;
             break;
+            }
+            continue;
 
         case '#':
             i = n;
         comment:
-            SET_COLOR(str, start, i, RUBY_STYLE_COMMENT);
-            continue;
+            style = RUBY_STYLE_COMMENT;
+            break;
 
         case '%':
             /* parse alternate string/array syntaxes */
@@ -2672,10 +3051,10 @@
                         }
                     }
                 }
-                SET_COLOR(str, start, i, RUBY_STYLE_STRING4);
-                continue;
-            }
+                style = RUBY_STYLE_STRING4;
             break;
+            }
+            continue;
 
         case '\'':
             /* parse single quoted string const */
@@ -2691,8 +3070,8 @@
                     break;
                 }
             }
-            SET_COLOR(str, start, i, RUBY_STYLE_STRING);
-            continue;
+            style = RUBY_STYLE_STRING;
+            break;
 
         case '`':
             /* parse single quoted string const */
@@ -2713,8 +3092,8 @@
                     break;
                 }
             }
-            SET_COLOR(str, start, i, RUBY_STYLE_STRING3);
-            continue;
+            style = RUBY_STYLE_STRING3;
+            break;
 
         case '\"':
             /* parse double quoted string const */
@@ -2743,8 +3122,8 @@
                 if (state == 0)
                     state = IN_RUBY_STRING2;
             }
-            SET_COLOR(str, start, i, RUBY_STYLE_STRING2);
-            continue;
+            style = RUBY_STYLE_STRING2;
+            break;
 
         case '<':
             if (str[i] == '<') {
@@ -2793,19 +3172,20 @@
                     }
                     state |= (sig & IN_RUBY_HD_SIG);
                     i = j;
-                    SET_COLOR(str, start, i, RUBY_STYLE_HEREDOC);
+                    style = RUBY_STYLE_HEREDOC;
+                    break;
                 }
             }
-            break;
+            continue;
 
         case '?':
             /* XXX: should parse character constants */
-            break;
+            continue;
 
         case '.':
             if (qe_isdigit_(str[i]))
                 goto parse_decimal;
-            break;
+            continue;
 
         case '$':
             /* XXX: should parse precise $ syntax,
@@ -2813,16 +3193,16 @@
              */
             if (i < n)
                 i++;
-            break;
+            continue;
 
         case ':':
             /* XXX: should parse Ruby symbol */
-            break;
+            continue;
 
         case '@':
             i += ruby_get_name(kbuf, countof(kbuf), str + i);
-            SET_COLOR(str, start, i, RUBY_STYLE_MEMBER);
-            continue;
+            style = RUBY_STYLE_MEMBER;
+            break;
 
         default:
             if (qe_isdigit(c)) {
@@ -2866,26 +3246,30 @@
                     }
                 }
                 /* XXX: should detect malformed number constants */
-                SET_COLOR(str, start, i, RUBY_STYLE_NUMBER);
-                continue;
+                style = RUBY_STYLE_NUMBER;
+                break;
             }
             if (qe_isalpha_(c)) {
                 i--;
                 i += ruby_get_name(kbuf, countof(kbuf), str + i);
 
                 if (strfind(syn->keywords, kbuf)) {
-                    SET_COLOR(str, start, i, RUBY_STYLE_KEYWORD);
-                    continue;
+                    style = RUBY_STYLE_KEYWORD;
+                    break;
                 }
                 if (qe_isblank(str[i]))
                     i++;
                 if (str[i] == '(' || str[i] == '{') {
-                    SET_COLOR(str, start, i, RUBY_STYLE_FUNCTION);
+                    style = RUBY_STYLE_FUNCTION;
+                    break;
+                }
                     continue;
                 }
                 continue;
             }
-            break;
+        if (style) {
+            SET_COLOR(str, start, i, style);
+            style = 0;
         }
     }
     cp->colorize_state = state;
@@ -2904,9 +3288,9 @@
 static ModeDef ruby_mode = {
     .name = "Ruby",
     .extensions = "rb|gemspec",
-    .keywords = ruby_keywords,
     .shell_handlers = "ruby",
     .mode_probe = ruby_mode_probe,
+    .keywords = ruby_keywords,
     .colorize_func = ruby_colorize_line,
 };
 
@@ -3213,7 +3597,7 @@
 static void elixir_colorize_line(QEColorizeContext *cp,
                                  unsigned int *str, int n, ModeDef *syn)
 {
-    int i = 0, start = i, c, sep, nc, klen, has_under, style = 0;
+    int i = 0, start = i, c, style = 0, sep, klen, nc, has_under;
     int state = cp->colorize_state;
     char kbuf[32];
 
@@ -3296,7 +3680,8 @@
             style = (state & IN_ELIXIR_TRIPLE) ?
                 ELIXIR_STYLE_HEREDOC : ELIXIR_STYLE_STRING;
             while (i < n) {
-                if ((c = str[i++]) == '\\') {
+                c = str[i++];
+                if (c == '\\') {
                     if (i < n)
                         i += 1;
                     continue;
@@ -3843,7 +4228,7 @@
                                 unsigned int *str, int n, ModeDef *syn)
 {
     char keyword[MAX_KEYWORD_SIZE];
-    int i = 0, start = i, c, sep = 0, style = 0, len;
+    int i = 0, start = i, c, style = 0, sep = 0, len;
     int state = cp->colorize_state;
 
     if (state & IN_AGENA_COMMENT)
@@ -3941,9 +4326,11 @@
             }
             continue;
         }
+        if (style) {
         SET_COLOR(str, start, i, style);
         style = 0;
     }
+    }
     cp->colorize_state = state;
 }
 
@@ -4137,6 +4524,185 @@
     return 0;
 }
 
+/*---------------- OpenSCAD language ----------------*/
+
+static const char scad_keywords[] = {
+    "true|false|undef|"
+    "module|function|for|if|else|len|"
+//    "assert|break|case|const|continue|default|defined|do|exit|"
+//    "forward|goto|native|new|operator|public|return|sizeof|sleep|"
+//    "state|static|stock|switch|tagof|while|",
+};
+
+static const char scad_preprocessor_keywords[] = {
+    "use|include|"
+};
+
+static const char scad_types[] = {
+    //"void|"
+    //"bool|string|int|uint|uchar|nt8|short|ushort|long|ulong|size_t|ssize_t|"
+    //"double|va_list|unichar|"
+};
+
+enum {
+    IN_SCAD_COMMENT  = 0x01,
+};
+
+enum {
+    SCAD_STYLE_TEXT =       QE_STYLE_DEFAULT,
+    SCAD_STYLE_KEYWORD =    QE_STYLE_KEYWORD,
+    SCAD_STYLE_TYPE =       QE_STYLE_TYPE,
+    SCAD_STYLE_PREPROCESS = QE_STYLE_PREPROCESS,
+    SCAD_STYLE_COMMENT =    QE_STYLE_COMMENT,
+    SCAD_STYLE_STRING =     QE_STYLE_STRING,
+    SCAD_STYLE_NUMBER =     QE_STYLE_NUMBER,
+    SCAD_STYLE_FUNCTION =   QE_STYLE_FUNCTION,
+    SCAD_STYLE_ARGNAME =    QE_STYLE_FUNCTION,
+};
+
+static void scad_colorize_line(QEColorizeContext *cp,
+                               unsigned int *str, int n, ModeDef *syn)
+{
+    char keyword[MAX_KEYWORD_SIZE];
+    int i = 0, start = i, c, style = 0, k, len, laststyle = 0, isnum;
+    int colstate = cp->colorize_state;
+    int level = colstate >> 1;
+
+    if (colstate & IN_SCAD_COMMENT)
+        goto in_comment;
+
+    while (i < n) {
+        start = i;
+        c = str[i++];
+        switch (c) {
+        case '/':
+            if (str[i] == '/') {  /* single line comment */
+                i = n;
+                style = SCAD_STYLE_COMMENT;
+                break;
+            }
+            if (str[i] == '*') {  /* multi-line comment */
+                colstate |= IN_SCAD_COMMENT;
+                i++;
+            in_comment:
+                for (; i < n; i++) {
+                    if (str[i] == '*' && str[i + 1] == '/') {
+                        i += 2;
+                        colstate &= ~IN_SCAD_COMMENT;
+                        break;
+                    }
+                }
+                style = SCAD_STYLE_COMMENT;
+                break;
+            }
+            continue;
+        case '<':
+            if (laststyle == SCAD_STYLE_PREPROCESS) {
+                /* filename for include and use directives */
+                while (i < n) {
+                    if (str[i++] == '>')
+                        break;
+                }
+                style = SCAD_STYLE_STRING;
+                break;
+            }
+            continue;
+        case '(':
+        case '[':
+        case '{':
+            level <<= 1;
+            continue;
+        case '}':
+        case ']':
+        case ')':
+            level >>= 1;
+            continue;
+        case '\'':
+        case '\"':
+            /* parse string or char const */
+            while (i < n) {
+                /* XXX: escape sequences? */
+                if (str[i] == '\\' && i + 1 < n) {
+                    i++;
+                    continue;
+                }
+                if (str[i++] == (unsigned int)c)
+                    break;
+            }
+            style = SCAD_STYLE_STRING;
+            break;
+        default:
+            /* parse identifiers, keywords and numbers */
+            if (qe_isalnum_(c) || c == '$') {
+                isnum = qe_isdigit(c);
+                len = 0;
+                keyword[len++] = c;
+                for (; qe_isalnum_(str[i]) || str[i] == '.'; i++) {
+                    if (str[i] == '.') {
+                        if (!isnum)
+                            break;
+                    } else {
+                    if (!qe_isdigit(str[i]))
+                        isnum = 0;
+                    }
+                    if (len < countof(keyword) - 1)
+                        keyword[len++] = str[i];
+                }
+                keyword[len] = '\0';
+                if (isnum) {
+                    style = SCAD_STYLE_NUMBER;
+                }
+                if (strfind(syn->keywords, keyword)) {
+                    style = SCAD_STYLE_KEYWORD;
+                } else
+                if (strfind(scad_preprocessor_keywords, keyword)) {
+                    style = SCAD_STYLE_PREPROCESS;
+                } else
+                if (strfind(syn->types, keyword)) {
+                    style = SCAD_STYLE_TYPE;
+                } else {
+                    k = i;
+                    if (qe_isblank(str[k]))
+                        k++;
+                    if ((level & 2) && str[k] == '=') {
+                        style = SCAD_STYLE_ARGNAME;
+                    } else
+                    if (str[k] == '(') {
+                        style = SCAD_STYLE_FUNCTION;
+                        level |= 1;
+                    }
+                }
+                break;
+            }
+            continue;
+        }
+        if (style) {
+            laststyle = style;
+            SET_COLOR(str, start, i, style);
+            style = 0;
+        }
+    }
+    cp->colorize_state = (colstate & IN_SCAD_COMMENT) | (level << 1);
+}
+
+static ModeDef scad_mode = {
+    .name = "OpenSCAD",
+    .mode_name = "openscad",
+    .extensions = "scad",
+    .colorize_func = scad_colorize_line,
+    .keywords = scad_keywords,
+    .types = scad_types,
+    //.indent_func = c_indent_line,
+    //.auto_indent = 1,
+};
+
+static int scad_init(void)
+{
+    qe_register_mode(&scad_mode, MODEF_SYNTAX);
+
+    return 0;
+}
+
 /*----------------*/
 
 static int extra_modes_init(void)
@@ -4154,6 +4720,7 @@
     lua_init();
     julia_init();
     haskell_init();
+    coffee_init();
     python_init();
     ruby_init();
     erlang_init();
@@ -4162,6 +4729,7 @@
     emf_init();
     agena_init();
     smalltalk_init();
+    scad_init();
     return 0;
 }
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]