evil-qemacs/lang/clang.c
Charlie Gordon 699bdff915 Simplify mode probing
* add `filenames` specification in `ModeDef` structures
* generic mode probing can match a list of filenames
* remove unneeded mode_probe functions
* this enables diff coloring on Makefile
2026-06-05 18:42:56 -04:00

4814 lines
157 KiB
C

/*
* C mode for QEmacs.
*
* Copyright (c) 2001-2002 Fabrice Bellard.
* Copyright (c) 2002-2026 Charlie Gordon.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qe.h"
#include "clang.h"
/* C mode options */
#define CLANG_C_TYPES 0x00080 /* add C types to syn->types list */
#define CLANG_C_KEYWORDS 0x00100 /* add C keywords to syn->keywords list */
#define CLANG_LEX 0x00200
#define CLANG_YACC 0x00400
#define CLANG_REGEX 0x00800 /* recognize / delimited regular expressions */
#define CLANG_WLITERALS 0x01000 /* recognize L, u, U, u8 string prefix */
#define CLANG_PREPROC 0x02000 /* colorize preprocessing directives */
#define CLANG_CAP_TYPE 0x04000 /* Mixed case initial cap is type */
#define CLANG_STR3 0x08000 /* Support """strings""" */
#define CLANG_LINECONT 0x10000 /* support \<newline> as line continuation */
#define CLANG_NEST_COMMENTS 0x20000 /* block comments are nested */
#define CLANG_T_TYPES 0x40000 /* _t suffix indicates type identifier */
/* FIXME: need flags for
'@' in identifiers(start / next),
'$' in identifiers(start / next),
'-' in identifiers next
'#' as comment char
UnicodeIDStart and UnicodeIDContinue in identifiers
*/
/* all C language features */
#define CLANG_CC (CLANG_LINECONT | CLANG_WLITERALS | CLANG_PREPROC | \
CLANG_C_KEYWORDS | CLANG_C_TYPES | CLANG_T_TYPES)
static const char c_keywords[] = {
"auto|break|case|const|continue|default|do|else|enum|extern|for|goto|"
"if|inline|register|restrict|return|sizeof|static|struct|switch|"
"typedef|union|volatile|while|"
/* C99 and C11 keywords */
"_Alignas|_Alignof|_Atomic|_Generic|_Noreturn|_Pragma|"
"_Static_assert|_Thread_local|"
/* C2x keywords */
"alignas|alignof|static_assert|thread_local|"
"constexpr|false|nullptr|true|typeof|typeof_unqual"
};
static const char c_types[] = {
"char|double|float|int|long|unsigned|short|signed|void|"
/* common types */
"FILE|va_list|jmp_buf|"
/* C99 and C11 types */
"_Bool|_Complex|_Imaginary|bool|complex|imaginary|"
/* C2x types */
"_BitInt|_Decimal128|_Decimal32|_Decimal64|"
};
static const char c_extensions[] = {
"c|h|i|C|H|I|" /* C language */
/* Other C flavors */
"e|" /* EEL */
"ecp|" /* Informix embedded C */
"pgc|" /* Postgres embedded C */
"pcc|" /* Oracle C++ */
"h.in|c.in|" /* preprocessed C input (should use more generic approach) */
};
static int is_c_identifier_start(char32_t c, int flavor) {
// should accept unicode escape sequence, UnicodeIDStart
return (qe_isalpha_(c)
|| c == '$'
|| (c == '@' && flavor != CLANG_PIKE)
|| (flavor == CLANG_RUST && c >= 128));
}
static int is_c_identifier_part(char32_t c, int flavor) {
// should accept unicode escape sequence, UnicodeIDContinue, ZWJ or ZWNJ
return (qe_isalnum_(c)
|| (c == '-' && flavor == CLANG_CSS)
|| (flavor == CLANG_RUST && c >= 128));
}
// FIXME: should merge into ustr_get_identifier()
int get_c_identifier(char *dest, int size, char32_t c,
const char32_t *str, int i0, int n, int flavor)
{
/*@API utils
Grab an identifier from a `char32_t` buffer for a given C flavor,
accept non-ASCII identifiers and encode in UTF-8.
@argument `dest` a pointer to the destination array
@argument `size` the length of the destination array in bytes
@argument `c` the initial code point or `0` if none
@argument `str` a valid pointer to an array of codepoints
@argument `i` the index to the next codepoint
@argument `n` the length of the codepoint array
@argument `flavor` the language variant for identifier syntax
@return the number of codepoints used in the source array.
@note `dest` can be a null pointer if `size` is `0`.
*/
int pos = 0, i = i0;
if (c == 0) {
if (!(i < n && is_c_identifier_start(c = str[i++], flavor))) {
if (size > 0)
*dest = '\0';
return 0;
}
}
for (;; i++) {
if (c < 128) {
if (pos + 1 < size) {
dest[pos++] = c;
}
} else {
char buf[6];
int len = utf8_encode(buf, c);
if (pos + len < size) {
memcpy(dest + pos, buf, len);
pos += len;
} else {
size = pos + 1;
}
}
if (i >= n)
break;
c = str[i];
if (!is_c_identifier_part(c, flavor)) {
/* include ugly c++ :: separator in identifier */
if (c == ':' && str[i + 1] == ':'
&& (flavor == CLANG_CPP || flavor == CLANG_C3)
&& is_c_identifier_start(str[i + 2], flavor)) {
if (pos + 1 < size)
dest[pos++] = ':';
if (pos + 1 < size)
dest[pos++] = ':';
i += 2;
c = str[i];
} else {
break;
}
}
}
if (pos < size)
dest[pos] = '\0';
return i - i0;
}
enum {
C_STYLE_DEFAULT = 0,
C_STYLE_PREPROCESS = QE_STYLE_PREPROCESS,
C_STYLE_COMMENT = QE_STYLE_COMMENT,
C_STYLE_REGEX = QE_STYLE_STRING_Q,
C_STYLE_STRING = QE_STYLE_STRING,
C_STYLE_STRING_Q = QE_STYLE_STRING_Q,
C_STYLE_STRING_BQ = QE_STYLE_STRING,
C_STYLE_NUMBER = QE_STYLE_NUMBER,
C_STYLE_KEYWORD = QE_STYLE_KEYWORD,
C_STYLE_TYPE = QE_STYLE_TYPE,
C_STYLE_FUNCTION = QE_STYLE_FUNCTION,
C_STYLE_VARIABLE = QE_STYLE_VARIABLE,
};
/* c-mode colorization states */
enum {
IN_C_COMMENT = 0x03, /* one of the comment styles */
IN_C_COMMENT1 = 0x01, /* single line comment with \ at EOL */
IN_C_COMMENT2 = 0x02, /* multiline C comment */
IN_C_COMMENT3 = 0x03, /* multiline D comment */
IN_C_STRING = 0x1C, /* 3 bits for string styles */
IN_C_STRING_D = 0x04, /* double-quoted string */
IN_C_STRING_Q = 0x08, /* single-quoted string */
IN_C_STRING_BQ = 0x0C, /* back-quoted string (go's multi-line string) */
IN_C_STRING_D3 = 0x14, /* """ multiline quoted string */
IN_C_STRING_Q3 = 0x18, /* ''' multiline quoted string */
IN_C_STRING_BQ3 = 0x1C, /* ``` multiline quoted string */
IN_C_PREPROCESS = 0x20, /* preprocessor directive with \ at EOL */
IN_C_REGEX = 0x40, /* regex */
IN_C_CHARCLASS = 0x80, /* regex char class */
IN_C_COMMENT_SHIFT = 8, /* shift for block comment nesting level */
IN_C_COMMENT_LEVEL = 0x700, /* mask for block comment nesting level */
};
static int rep_count(const char32_t *str, char32_t c) {
int count = 0;
while (str[count] == c)
count++;
return count;
}
static void c_colorize_line(QEColorizeContext *cp,
const char32_t *str, int n,
QETermStyle *sbuf, ModeDef *syn)
{
int i = 0, start, i1, i2, indent, level;
int style, style0, style1, type_decl, tag;
char32_t c, delim;
char kbuf[64];
int mode_flags = syn->colorize_flags;
int flavor = (mode_flags & CLANG_FLAVOR);
int state = cp->colorize_state;
indent = cp_skip_blanks(str, 0, n);
tag = !indent && cp->s->mode == syn;
start = i;
type_decl = 0;
c = 0;
style0 = style = C_STYLE_DEFAULT;
if (i >= n)
goto the_end;
if (state) {
/* if already in a state, go directly in the code parsing it */
if (state & IN_C_PREPROCESS)
style0 = style = C_STYLE_PREPROCESS;
if (state & IN_C_COMMENT) {
if ((state & IN_C_COMMENT) == IN_C_COMMENT1)
goto parse_comment1;
else
if ((state & IN_C_COMMENT) == IN_C_COMMENT2)
goto parse_comment2;
else
goto parse_comment3;
}
switch (state & IN_C_STRING) {
case IN_C_STRING_D: goto parse_string;
case IN_C_STRING_Q: goto parse_string_q;
case IN_C_STRING_BQ: goto parse_string_bq;
case IN_C_STRING_D3: goto parse_string3;
}
if (state & IN_C_REGEX) {
delim = '/';
goto parse_regex;
}
}
while (i < n) {
start = i;
reswitch:
c = str[i++];
switch (c) {
case '*':
/* lone star at the beginning of a line in a shell buffer
* is treated as a comment start. This improves colorization
* of diff and git output.
*/
if (start == indent && cp->partial_file
&& (i == n || str[i] == ' ' || str[i] == '/')) {
i--;
goto parse_comment2;
}
break;
case '/':
if (str[i] == '*') {
/* C style multi-line comment */
i++;
parse_comment2:
state |= IN_C_COMMENT2;
style = C_STYLE_COMMENT;
level = (state & IN_C_COMMENT_LEVEL) >> IN_C_COMMENT_SHIFT;
while (i < n) {
if (str[i] == '/' && str[i + 1] == '*' && (mode_flags & CLANG_NEST_COMMENTS)) {
i += 2;
level++;
} else
if (str[i] == '*' && str[i + 1] == '/') {
i += 2;
if (level == 0) {
state &= ~IN_C_COMMENT2;
style = style0;
break;
}
level--;
} else {
i++;
}
}
state = (state & ~IN_C_COMMENT_LEVEL) |
(min_int(level, 7) << IN_C_COMMENT_SHIFT);
SET_STYLE(sbuf, start, i, C_STYLE_COMMENT);
continue;
} else
if (str[i] == '/') {
/* line comment */
parse_comment1:
state |= IN_C_COMMENT1;
style = C_STYLE_COMMENT;
if (str[n - 1] != '\\')
state &= ~IN_C_COMMENT1;
i = n;
SET_STYLE(sbuf, start, i, C_STYLE_COMMENT);
continue;
}
if (flavor == CLANG_D && (str[i] == '+')) {
/* D language nesting long comment */
i++;
state |= IN_C_COMMENT3;
parse_comment3:
style = C_STYLE_COMMENT;
level = (state & IN_C_COMMENT_LEVEL) >> IN_C_COMMENT_SHIFT;
while (i < n) {
if (str[i] == '/' && str[i + 1] == '+') {
i += 2;
level++;
} else
if (str[i] == '+' && str[i + 1] == '/') {
i += 2;
if (level == 0) {
state &= ~IN_C_COMMENT3;
style = style0;
break;
}
level--;
} else {
i++;
}
}
state = (state & ~IN_C_COMMENT_LEVEL) |
(min_int(level, 7) << IN_C_COMMENT_SHIFT);
SET_STYLE(sbuf, start, i, C_STYLE_COMMENT);
continue;
}
if (mode_flags & CLANG_REGEX) {
/* XXX: should use more context to tell regex from divide */
char32_t prev = ' ';
for (i1 = start; i1 > indent; ) {
prev = str[--i1];
if (!qe_isblank(prev))
break;
}
/* ignore end of comment in grep output */
if (start > indent && str[start - 1] == '*' && cp->partial_file)
break;
if (!qe_findchar("])", prev)
&& (qe_findchar(" [({},;=<>!~^&|*/%?:", prev)
|| sbuf[i1] == C_STYLE_KEYWORD
|| (str[i] != ' ' && (str[i] != '=' || str[i + 1] != ' ')
&& !(qe_isalnum(prev) || prev == ')')))) {
/* parse regex */
state |= IN_C_REGEX;
delim = '/';
parse_regex:
style = C_STYLE_REGEX;
while (i < n) {
c = str[i++];
if (c == '\\') {
if (i < n) {
i += 1;
}
} else
if (state & IN_C_CHARCLASS) {
if (c == ']') {
state &= ~IN_C_CHARCLASS;
}
/* ECMA 5: ignore '/' inside char classes */
} else {
if (c == '[') {
state |= IN_C_CHARCLASS;
} else
if (c == delim) {
while (qe_isalnum_(str[i])) {
i++;
}
state &= ~IN_C_REGEX;
style = style0;
break;
}
}
}
SET_STYLE(sbuf, start, i, C_STYLE_REGEX);
continue;
}
}
break;
case '%':
if (flavor == CLANG_JED) {
goto parse_comment1;
}
break;
case '#': /* preprocessor */
if (start == 0 && str[i] == '!') {
/* recognize a shebang comment line */
style = style0 = C_STYLE_PREPROCESS;
i = n;
SET_STYLE(sbuf, start, i, C_STYLE_PREPROCESS);
break;
}
if (flavor == CLANG_AWK || flavor == CLANG_PHP
|| flavor == CLANG_LIMBO || flavor == CLANG_SQUIRREL) {
goto parse_comment1;
}
if (flavor == CLANG_ICI) {
delim = '#';
goto parse_regex;
}
if (flavor == CLANG_HAXE || flavor == CLANG_CBANG) {
i += get_c_identifier(kbuf, countof(kbuf), 0, str, i, n, flavor);
// XXX: check for proper preprocessor directive?
SET_STYLE(sbuf, start, i, C_STYLE_PREPROCESS);
continue;
}
if (flavor == CLANG_PIKE) {
int klen;
if (str[i] == '\"') {
i++;
goto parse_string; // FIXME: accept embedded newlines
}
if (str[i] == '(') {
// FIXME: parse literal strings until `#)`
}
if (str[i] == '[') {
// FIXME: parse literal strings until `#]`
}
if (str[i] == '{') {
// FIXME: parse literal strings until `#}`
}
if (ustr_match_keyword(str + i, "string", &klen)) {
/* Pike's version of #embed */
style = C_STYLE_PREPROCESS;
i += klen;
break;
}
}
if (mode_flags & CLANG_PREPROC) {
state |= IN_C_PREPROCESS;
style = style0 = C_STYLE_PREPROCESS;
}
break;
// case 'r':
/* XXX: D language r" wysiwyg chars " */
// case 'X':
/* XXX: D language X" hex string chars " */
// case 'q':
/* XXX: D language q" delim wysiwyg chars delim " */
/* XXX: D language q{ tokens } */
case '\'': /* character constant */
if (flavor == CLANG_SCILAB)
goto normal;
parse_string_q:
state |= IN_C_STRING_Q;
style1 = C_STYLE_STRING_Q;
delim = '\'';
goto string;
case '`':
// FIXME: Support `operator in Pike
if (flavor == CLANG_SCALA || flavor == CLANG_GMSCRIPT) {
/* scala quoted identifier */
while (i < n) {
c = str[i++];
if (c == '`')
break;
}
SET_STYLE(sbuf, start, i, C_STYLE_VARIABLE);
continue;
}
if (flavor == CLANG_C2) {
// c2 raw strings
int count = 1 + rep_count(str + i, '`');
int count2 = 0;
// check for markdown-like tag line
for (i += count - 1; i < n; i++) {
if ((count2 = rep_count(str + i, '`')) >= count)
break;
}
if (i < n) {
i += count2;
SET_STYLE(sbuf, start, i, C_STYLE_STRING_BQ);
continue;
}
i = n;
if (count >= 3) {
SET_STYLE(sbuf, start, start + count, C_STYLE_STRING_BQ);
SET_STYLE(sbuf, start + count, i, QE_STYLE_COMMENT);
continue;
}
SET_STYLE(sbuf, start, i, C_STYLE_STRING_BQ);
state |= IN_C_STRING_BQ;
continue;
}
if (flavor == CLANG_GO || flavor == CLANG_D) {
/* go language multi-line string, no escape sequences */
parse_string_bq:
state |= IN_C_STRING_BQ;
style1 = C_STYLE_STRING_BQ;
delim = '`';
while (i < n) {
c = str[i++];
if (c == delim) {
if (flavor == CLANG_C2) {
// TODO: actually match the opening count
while (str[i] == delim)
i++;
}
state &= ~IN_C_STRING;
break;
}
}
if (state & IN_C_PREPROCESS)
style1 = C_STYLE_PREPROCESS;
SET_STYLE(sbuf, start, i, style1);
continue;
}
break;
case '@':
if (flavor == CLANG_C2) {
// XXX: should colorize attributes as C_STYLE_PREPROC
// @(...)
}
if (flavor == CLANG_CSHARP || flavor == CLANG_SQUIRREL) {
if (str[i] == '\"') {
/* Csharp and Squirrel Verbatim strings */
/* ignore escape sequences and newlines */
state |= IN_C_STRING_D; // XXX: IN_RAW_STRING
style1 = C_STYLE_STRING;
delim = str[i++];
style = style1;
while (i < n) {
c = str[i++];
if (c == delim) {
if (str[i] == c) {
i++;
continue;
}
state &= ~IN_C_STRING;
style = style0;
break;
}
}
SET_STYLE(sbuf, start, i, style1);
continue;
}
}
if ((flavor == CLANG_JAVA || flavor == CLANG_SCALA) && qe_isalpha(str[i])) {
/* Java annotations */
while (qe_isalnum_(str[i]) || str[i] == '.')
i++;
if (start == 0 || str[start - 1] != '.')
SET_STYLE(sbuf, start, i, C_STYLE_PREPROCESS);
continue;
}
goto normal;
case '\"': /* string literal */
if ((mode_flags & CLANG_STR3)
&& (str[i] == '\"' && str[i + 1] == '\"')) {
/* multiline """ quoted string */
i += 2;
parse_string3:
state |= IN_C_STRING_D3;
style1 = C_STYLE_STRING;
delim = '\"';
while (i < n) {
c = str[i++];
if (c == '\\' && flavor != CLANG_KOTLIN) {
if (i < n)
i++;
} else
if (c == delim && str[i] == delim && str[i + 1] == delim) {
i += 2;
state &= ~IN_C_STRING;
style = style0;
break;
}
}
SET_STYLE(sbuf, start, i, style1);
continue;
}
parse_string:
state |= IN_C_STRING_D;
style1 = C_STYLE_STRING;
delim = '\"';
string:
style = style1;
while (i < n) {
c = str[i++];
if (c == '\\' && flavor != CLANG_SCILAB) {
if (i >= n)
break;
i++;
} else
if (c == delim) {
if (flavor == CLANG_SCILAB && str[i] == delim) {
i++;
continue;
}
state &= ~IN_C_STRING;
style = style0;
break;
}
}
if (flavor == CLANG_D) {
/* ignore optional string postfix */
if (qe_findchar("cwd", str[i]))
i++;
}
if (state & IN_C_PREPROCESS)
style1 = C_STYLE_PREPROCESS;
SET_STYLE(sbuf, start, i, style1);
continue;
case '=':
/* exit type declaration */
/* does not handle this: int i = 1, j = 2; */
type_decl = 0;
break;
case '<': /* JavaScript extension */
if (flavor == CLANG_JS) {
if (str[i] == '!' && str[i + 1] == '-' && str[i + 2] == '-')
goto parse_comment1;
}
break;
case '(':
case '{':
tag = 0;
break;
default:
normal:
if (state & IN_C_PREPROCESS)
break;
if (qe_isdigit(c) || (c == '.' && qe_isdigit(str[i]))) {
/* XXX: parsing ppnumbers, which is OK for C and C++ */
/* XXX: should parse actual number syntax */
/* XXX: D ignores embedded '_' and accepts l,u,U,f,F,i suffixes */
/* XXX: Java accepts 0b prefix for binary literals,
* ignores '_' between digits and accepts 'l' or 'L' suffixes */
/* scala ignores '_' in integers */
/* XXX: should parse decimal and hex floating point syntaxes */
/* C23/C++ syntax:
- accept ' between digits including hex and exponent part
- new suffixes: wb, WB, df, dd, dl, DF, DD, DL */
/* 2 dots in a row are a range operator, not a decimal point */
while (qe_isalnum_(str[i])
|| (str[i] == '\'' && qe_isalnum(str[i + 1]))
|| (str[i] == '.' && str[i + 1] != '.')) {
i++;
}
SET_STYLE(sbuf, start, i, C_STYLE_NUMBER);
continue;
}
if (is_c_identifier_start(c, flavor)) {
i += get_c_identifier(kbuf, countof(kbuf), c, str, i, n, flavor);
if (str[i] == '\'' || str[i] == '\"') {
/* check for encoding prefix */
if ((mode_flags & CLANG_WLITERALS) && strfind("L|u|U|u8", kbuf))
goto reswitch;
}
if (strfind(syn->keywords, kbuf)
|| ((mode_flags & CLANG_C_KEYWORDS) && strfind(c_keywords, kbuf))
|| ((flavor == CLANG_CSS) && str[i] == ':')) {
SET_STYLE(sbuf, start, i, C_STYLE_KEYWORD);
continue;
}
i1 = cp_skip_blanks(str, i, n);
i2 = i1;
while (str[i2] == '*' || qe_isblank(str[i2]))
i2++;
if (tag && qe_findchar("({[,;=", str[i1])) {
eb_add_tag(cp->b, cp->offset + start, kbuf);
}
if ((start == 0 || str[start - 1] != '.')
&& (!qe_findchar(".(:", str[i]) || flavor == CLANG_PIKE)
&& (sreg_match(syn->types, kbuf, 1)
|| ((mode_flags & CLANG_C_TYPES) && strfind(c_types, kbuf))
|| ((mode_flags & CLANG_T_TYPES) && strend(kbuf, "_t", NULL))
|| ((mode_flags & CLANG_CAP_TYPE) && qe_isupper(c) && qe_haslower(kbuf))
|| (flavor == CLANG_HAXE && qe_isupper(c) && qe_haslower(kbuf) &&
(start == 0 || !qe_findchar("(", str[start - 1]))))) {
/* if not cast, assume type declaration */
if (str[i2] != ')') {
type_decl = 1;
}
style1 = C_STYLE_TYPE;
if (str[i1] == '(' && flavor != CLANG_PIKE) {
/* function style cast */
style1 = C_STYLE_FUNCTION; //C_STYLE_KEYWORD;
}
SET_STYLE(sbuf, start, i, style1);
continue;
}
if (str[i1] == '(') {
/* function call */
/* XXX: different styles for call and definition */
SET_STYLE(sbuf, start, i, C_STYLE_FUNCTION);
continue;
}
if ((mode_flags & CLANG_CC) || flavor == CLANG_JAVA) {
/* assume typedef if starting at first column */
if (start == 0 && qe_isalpha_(str[i]))
type_decl = 1;
if (type_decl) {
if (start == 0) {
/* assume type if first column */
SET_STYLE(sbuf, start, i, C_STYLE_TYPE);
} else {
SET_STYLE(sbuf, start, i, C_STYLE_VARIABLE);
}
}
}
continue;
}
break;
}
SET_STYLE1(sbuf, start, style);
}
the_end:
if (state & (IN_C_COMMENT | IN_C_PREPROCESS | IN_C_STRING)) {
/* set style on eol char */
SET_STYLE1(sbuf, n, style);
}
/* strip state if not overflowing from a comment */
if (!(state & IN_C_COMMENT) &&
(!(mode_flags & CLANG_LINECONT) || n <= 0 || str[n - 1] != '\\')) {
state &= ~IN_C_PREPROCESS;
}
cp->colorize_state = state;
}
#define MAX_STACK_SIZE 64
/* gives the position of the first non white space character in
buf. TABs are counted correctly */
static int find_indent1(EditState *s, const char32_t *p) {
int tw = s->b->tab_width > 0 ? s->b->tab_width : 8;
int pos = 0;
for (;;) {
char32_t c = *p++;
if (c == '\t')
pos += tw - (pos % tw);
else if (c == ' ')
pos++;
else if (c == '\f')
pos = 0;
else
break;
}
return pos;
}
static int find_pos(EditState *s, char32_t *buf, int size) {
int pos, tw, i;
char32_t c;
tw = s->b->tab_width > 0 ? s->b->tab_width : 8;
pos = 0;
for (i = 0; i < size; i++) {
c = buf[i];
if (c == '\t') {
pos += tw - (pos % tw);
} else {
/* simplistic case: assume single width characters */
pos++;
}
}
return pos;
}
enum {
INDENT_NORM,
INDENT_FIND_EQ,
};
/* Normalize indentation at <offset>, return offset past indentation */
static int normalize_indent(EditState *s, int offset, int indent)
{
int ntabs, nspaces, offset1, offset2;
if (indent < 0)
indent = 0;
ntabs = 0;
nspaces = indent;
if (s->indent_tabs_mode) {
int tw = s->b->tab_width > 0 ? s->b->tab_width : 8;
ntabs = nspaces / tw;
nspaces = nspaces % tw;
}
for (offset1 = offset;;) {
char32_t c = eb_nextc(s->b, offset2 = offset1, &offset1);
if (c == '\t') {
if (offset == offset2 && ntabs) {
ntabs--;
offset = offset1;
}
} else
if (c == ' ') {
if (offset == offset2 && !ntabs && nspaces) {
nspaces--;
offset = offset1;
}
} else {
break;
}
}
if (offset2 > offset)
eb_delete_range(s->b, offset, offset2);
if (ntabs)
offset += eb_insert_char32_n(s->b, offset, '\t', ntabs);
if (nspaces)
offset += eb_insert_spaces(s->b, offset, nspaces);
return offset;
}
/* Check if line starts with a label or a switch case */
static int c_line_has_label(EditState *s, const char32_t *buf, int len,
const QETermStyle *sbuf)
{
char kbuf[64];
int i, style;
i = cp_skip_blanks(buf, 0, len);
style = sbuf[i];
if (style == C_STYLE_COMMENT
|| style == C_STYLE_STRING
|| style == C_STYLE_STRING_Q
|| style == C_STYLE_PREPROCESS)
return 0;
i += get_c_identifier(kbuf, countof(kbuf), 0, buf, i, len, CLANG_C);
if (style == C_STYLE_KEYWORD && strfind("case|default", kbuf))
return 1;
i = cp_skip_blanks(buf, i, len);
return (buf[i] == ':');
}
/* indent a line of C code starting at <offset> */
/* Rationale:
- determine the current line indentation from the current expression or
the indentation of the previous complete statement.
- scan backwards previous lines, ignoring blank and comment lines.
- scan backwards for a block start, skipping fully bracketed code fragments
or a previous statement end and the ne before that.
- if the block start char is a `(`, try and align the line at the same offset
as the code fragment following the `(`.
- if the block start char is a `{` or a `[`, indent the line one level down from
the line where the block starts
- if the previous statement is an `if|else|do|for|while|switch`, indent the line one level
- if the line starts with a label, unindent by one level + c_label_offset
- if the previous line starts with a label, increment the previous indent by one level - c_label_offset
- by default, indent the line like the previous code line,
*/
void c_indent_line(EditState *s, int offset0)
{
QEColorizeContext cp[1];
int offset, offset1, offsetl, pos, line_num, col_num;
int i, eoi_found, len, pos1, lpos, style, line_num1, state;
int off, found_comma, has_else;
char32_t c;
//int found_semi = 0;
char32_t stack[MAX_STACK_SIZE];
char kbuf[64], *q;
int stack_ptr;
cp_initialize(cp, s);
/* find start of line */
eb_get_pos(s->b, &line_num, &col_num, offset0);
line_num1 = line_num;
offset = eb_goto_bol(s->b, offset0);
/* now find previous lines and compute indent */
pos = 0;
lpos = -1; /* position of the last instruction start */
offsetl = offset;
eoi_found = 0;
found_comma = has_else = 0;
stack_ptr = 0;
state = INDENT_NORM;
for (;;) {
if (offsetl == 0)
break;
line_num--;
offsetl = eb_prev_line(s->b, offsetl);
/* XXX: deal with truncation */
len = get_colorized_line(cp, offsetl, &offset1, line_num);
/* get current indentation of this line, adjust if it has a label */
pos1 = find_indent1(s, cp->buf);
/* ignore empty and preprocessor lines */
// XXX: what if preprocessor directive is indented?
if (pos1 == len || cp->sbuf[0] == C_STYLE_PREPROCESS)
continue;
if (c_line_has_label(s, cp->buf, len, cp->sbuf)) {
pos1 = pos1 - s->qs->c_label_indent + s->indent_width;
}
/* scan the line from end to start */
for (off = len; off-- > 0;) {
c = cp->buf[off];
style = cp->sbuf[off];
/* skip strings or comments */
if (style == C_STYLE_COMMENT
|| style == C_STYLE_STRING
|| style == C_STYLE_STRING_Q
|| style == C_STYLE_PREPROCESS) {
continue;
}
if (state == INDENT_FIND_EQ) {
/* special case to search '=' or ; before { to know if
we are in data definition */
/* XXX: why do we need this special case? */
if (c == '=') {
/* data definition case */
pos = lpos;
goto end_parse;
}
if (c == ';') {
/* normal instruction case */
goto check_instr;
}
continue;
}
if (style == C_STYLE_KEYWORD) {
int off0, off1;
/* special case for if/for/while */
off1 = off;
while (off > 0 && cp->sbuf[off - 1] == C_STYLE_KEYWORD)
off--;
off0 = off;
if (stack_ptr == 0) {
q = kbuf;
while (q < kbuf + countof(kbuf) - 1 && off0 <= off1) {
*q++ = cp->buf[off0++];
}
*q = '\0';
if (!eoi_found && strfind("if|for|while|do|switch|foreach", kbuf)) {
pos = pos1 + s->indent_width;
goto end_parse;
}
if (has_else == 0)
has_else = strequal(kbuf, "else") ? 1 : -1;
lpos = pos1;
}
} else {
if (has_else == 0)
has_else = -1;
switch (c) {
case '}':
if (stack_ptr < MAX_STACK_SIZE)
stack[stack_ptr] = c;
stack_ptr++;
goto check_instr;
case '{':
if (stack_ptr == 0) {
/* start of a block: check if object definition or code block */
if (found_comma) {
pos = pos1;
eoi_found = 1;
goto end_parse;
}
if (lpos == -1) {
pos = pos1 + s->indent_width;
eoi_found = 1;
goto end_parse;
} else {
state = INDENT_FIND_EQ;
}
} else {
--stack_ptr;
if (stack_ptr < MAX_STACK_SIZE && stack[stack_ptr] != '}') {
/* XXX: syntax check ? */
/* XXX: should set mark and complain */
goto check_instr;
}
goto check_instr;
}
break;
case ')':
case ']':
if (stack_ptr < MAX_STACK_SIZE)
stack[stack_ptr] = c;
stack_ptr++;
break;
case '(':
case '[':
if (stack_ptr == 0) {
pos = find_pos(s, cp->buf, off) + 1;
goto end_parse;
} else {
char32_t matchc = (c == '(') ? ')' : ']';
--stack_ptr;
if (stack_ptr < MAX_STACK_SIZE && stack[stack_ptr] != matchc) {
/* XXX: syntax check ? */
/* XXX: should set mark and complain */
pos = pos1;
goto end_parse;
}
}
break;
case ' ':
case '\f':
case '\t':
case '\n':
break;
case ',':
if (stack_ptr == 0) {
found_comma = 1;
}
break;
//case '=':
// if (p[1] == ' ' && !eoi_found && stack_ptr == 0) {
// pos = find_pos(s, buf, p - buf) + 2;
// goto end_parse;
//}
//break;
case ';':
/* level test needed for 'for(;;)' */
if (stack_ptr == 0) {
//found_semi = 1;
/* ; { or } are found before an instruction */
check_instr:
if (lpos >= 0) {
/* start of instruction already found */
pos = lpos;
if (!eoi_found)
pos += s->indent_width;
goto end_parse;
}
eoi_found = 1;
}
break;
case ':':
/* a label line is ignored: regular, case and default labels
* are assumed to have no preceding space
*/
/* XXX: should handle labels differently:
measure the indent and adjust by label_indent */
if (style == C_STYLE_DEFAULT
&& (off == 0 || !qe_isspace(cp->buf[off - 1]))) {
off = 0;
}
break;
default:
if (stack_ptr == 0)
lpos = pos1;
break;
}
}
}
if (pos1 == 0 && len > 0) {
style = cp->sbuf[0];
if (style != C_STYLE_COMMENT
&& style != C_STYLE_STRING
&& style != C_STYLE_STRING_Q
&& style != C_STYLE_PREPROCESS) {
pos = 0;
break;
}
}
}
end_parse:
/* compute special cases which depend on the chars on the current line */
/* XXX: deal with truncation */
len = get_colorized_line(cp, offset, &offset1, line_num1);
if (cp->sbuf[0] == C_STYLE_PREPROCESS)
goto done;
if (stack_ptr == 0) {
if (!pos && lpos >= 0) {
/* start of instruction already found */
pos = lpos;
if (!eoi_found)
pos += s->indent_width;
}
}
for (i = 0; i < len; i++) {
c = cp->buf[i];
if (qe_isblank(c))
continue;
/* no looping from here down */
style = cp->sbuf[i];
if (style == C_STYLE_STRING || style == C_STYLE_STRING_Q)
break;
/* if preprocess, no indent */
/* XXX: should indent macro definitions and align continuation marks */
if (style == C_STYLE_PREPROCESS) {
pos = 0;
break;
}
if (style == C_STYLE_COMMENT) {
if (c == '/') {
break;
}
if (c == '*') {
pos += 1;
break;
}
/* comment paragraph alignment should depend on previous line */
pos += 3;
break;
}
if (qe_isalpha_(c)) {
if (has_else == 1 && cp->buf[i] == 'i' && cp->buf[i + 1] == 'f' && !qe_isalnum_(cp->buf[i + 2])) {
/* unindent if after naked else */
pos -= s->indent_width;
break;
}
if (c_line_has_label(s, cp->buf + i, len - i, cp->sbuf + i)) {
pos -= s->indent_width + s->qs->c_label_indent;
break;
}
break;
}
/* NOTE: strings & comments are correctly ignored there */
if (c == '}') {
pos -= s->indent_width;
break;
}
if ((c == '&' || c == '|') && cp->buf[i + 1] == c) {
#if 0
int j;
// XXX: should try and indent according to boolean expression depth
for (j = i + 2; cp->buf[j] == ' '; j++)
continue;
if (j == len) {
pos -= s->indent_width;
} else {
pos -= j - i + 2;
}
#else
pos -= s->indent_width;
#endif
break;
}
if (c == '{') {
if (pos == s->indent_width && !eoi_found) {
pos = 0;
break;
}
// XXX: need fix for GNU style
pos -= s->indent_width;
}
break;
}
if (pos < 0) {
pos = 0;
}
// if (i == len && state is IN_COMMENT) {
// /* XXX: should indent comment paragraphs */
// pos += 3;
// }
/* the computed indent is in 'pos' */
/* if on a blank line, reset indent to 0 unless point is on it */
if (eb_is_blank_line(s->b, offset, NULL)
&& !(s->offset >= offset && s->offset <= eb_goto_eol(s->b, offset))) {
pos = 0;
}
/* Normalize indentation, minimize buffer modifications */
offset1 = normalize_indent(s, offset, pos);
#if 0
if (s->mode->auto_indent > 1) { /* auto format */
/* recompute colorization of the current line (after re-indentation) */
len = get_colorized_line(cp, offset, &offset1, line_num1);
/* skip indentation */
for (pos = 0; qe_isblank(cp->buf[pos]); pos++)
continue;
/* XXX: keywords "if|for|while|switch -> one space before `(` */
/* XXX: keyword "return" -> one space before expression */
/* XXX: keyword "do" -> one space before '{' */
/* XXX: other words -> no space before '(', '[', `++`, `--`, `->`, `.` */
/* XXX: other words and sequences -> space before `*`, but not after */
/* XXX: unary prefix operators: ! ~ - + & * ++ -- */
/* XXX: postfix operators: ++ -- -> . [ */
/* XXX: grouping operators: ( ) [ ] */
/* XXX: binary operators: = == === != !== < > <= >= && ||
^ & | + - * / % << >> ^= &= |= += -= *= /= %= <<= >>= ? : */
/* XXX: sequence operators: , ; */
}
#endif
/* move to the indentation if point was in indent space */
if (s->offset >= offset && s->offset < offset1) {
s->offset = offset1;
}
done:
cp_destroy(cp);
}
static void do_c_indent(EditState *s)
{
QEmacsState *qs = s->qs;
if (!s->region_style
&& !(s->b->flags & BF_PREVIEW)
&& qs->last_cmd_func != (CmdFunc)do_tabulate
&& eb_is_in_indentation(s->b, s->offset)) {
c_indent_line(s, s->offset);
} else {
do_tabulate(s, 1);
}
qs->this_cmd_func = (CmdFunc)do_tabulate;
}
static void do_c_electric_key(EditState *s, int key)
{
int offset = s->offset;
int was_preview = s->b->flags & BF_PREVIEW;
do_char(s, key, 1);
if (was_preview)
return;
/* reindent line at original point */
if (s->mode->auto_indent && s->mode->indent_func)
(s->mode->indent_func)(s, eb_goto_bol(s->b, offset));
}
static void do_c_newline(EditState *s)
{
int offset = s->offset;
int was_preview = s->b->flags & BF_PREVIEW;
/* XXX: should also remove trailing spaces on current line */
do_newline(s);
if (was_preview)
return;
/* reindent line to remove indent on blank line */
if (s->mode->auto_indent && s->mode->indent_func) {
/* delete blanks at end of line (necessary for non blank lines) */
/* XXX: should factorize with do_delete_horizontal_space() */
int from = offset, to = offset;
while (qe_isblank(eb_prevc(s->b, from, &offset)))
from = offset;
eb_delete_range(s->b, from, to);
offset = from;
(s->mode->indent_func)(s, eb_goto_bol(s->b, offset));
(s->mode->indent_func)(s, s->offset);
}
}
/* forward / backward preprocessor */
static void c_forward_conditional(EditState *s, int dir)
{
QEColorizeContext cp[1];
char32_t *p;
int line_num, col_num, sharp, level;
int offset, offset0, offset1;
cp_initialize(cp, s);
offset = offset0 = eb_goto_bol(s->b, s->offset);
eb_get_pos(s->b, &line_num, &col_num, offset);
level = 0;
for (;;) {
get_colorized_line(cp, offset, &offset1, line_num);
sharp = 0;
// XXX: should ignore comments
for (p = cp->buf; *p; p++) {
char32_t c = *p;
int style = cp->sbuf[p - cp->buf];
if (qe_isblank(c))
continue;
if (c == '#' && style == C_STYLE_PREPROCESS)
sharp++;
else
break;
}
if (sharp == 1) {
if (ustrstart(p, dir < 0 ? "endif" : "if", NULL)) {
if (level || offset == offset0)
level++;
else
break;
} else
if (ustrstart(p, "el", NULL)) {
if (offset == offset0)
level++;
else
if (level <= 1)
break;
} else
if (ustrstart(p, dir > 0 ? "endif" : "if", NULL)) {
if (level)
level--;
if (!level && offset != offset0)
break;
}
}
if (dir > 0) {
line_num++;
offset = offset1;
if (offset >= s->b->total_size)
break;
} else {
if (offset <= 0)
break;
line_num--;
offset = eb_prev_line(s->b, offset);
}
}
s->offset = offset;
cp_destroy(cp);
}
static void do_c_forward_conditional(EditState *s, int n)
{
int dir = n < 0 ? -1 : 1;
for (; n != 0; n -= dir) {
c_forward_conditional(s, dir);
}
}
static void do_c_list_conditionals(EditState *s)
{
QEColorizeContext cp[1];
char32_t *p;
int line_num, col_num, sharp, level;
int offset, offset1;
EditBuffer *b;
b = qe_new_buffer(s->qs, "Preprocessor conditionals", BC_CLEAR | BF_UTF8);
if (!b)
return;
cp_initialize(cp, s);
offset = eb_goto_bol(s->b, s->offset);
eb_get_pos(s->b, &line_num, &col_num, offset);
level = 0;
while (offset > 0) {
line_num--;
offset = eb_prev_line(s->b, offset);
get_colorized_line(cp, offset, &offset1, line_num);
sharp = 0;
// XXX: should ignore comments
for (p = cp->buf; *p; p++) {
char32_t c = *p;
int style = cp->sbuf[p - cp->buf];
if (qe_isblank(c))
continue;
if (c == '#' && style == C_STYLE_PREPROCESS)
sharp++;
else
break;
}
if (sharp == 1) {
if (ustrstart(p, "endif", NULL)) {
level++;
} else
if (ustrstart(p, "el", NULL)) {
if (level == 0) {
eb_insert_buffer_convert(b, 0, s->b, offset, offset1 - offset);
}
} else
if (ustrstart(p, "if", NULL)) {
if (level) {
level--;
} else {
eb_insert_buffer_convert(b, 0, s->b, offset, offset1 - offset);
}
}
}
}
if (b->total_size > 0) {
show_popup(s, b, "Preprocessor conditionals");
} else {
eb_free(&b);
put_error(s, "Not in a #if conditional");
}
cp_destroy(cp);
}
/* C mode specific commands */
static const CmdDef c_commands[] = {
CMD2( "c-indent-line-or-region", "TAB",
"Indent the current line or highlighted region",
do_c_indent, ES, "*")
CMD2( "c-backward-conditional", "M-[",
"Move to the beginning of the previous #if preprocessing directive",
do_c_forward_conditional, ESi, "q")
CMD2( "c-forward-conditional", "M-]",
"Move to the end of the next #if preprocessing directive",
do_c_forward_conditional, ESi, "p")
CMD0( "c-list-conditionals", "M-i",
"List the preprocessing directive controlling the current line",
do_c_list_conditionals)
CMD2( "c-electric-key", "{, }, ;, :, #, &, |, *",
"Insert a character with side effects",
do_c_electric_key, ESi, "*" "k")
CMD2( "c-newline", "RET, LF",
"Insert a newline, removing trailing whitespace and autoindent",
do_c_newline, ES, "*")
};
static int c_mode_probe(ModeDef *mode, ModeProbeData *p)
{
/* trust the file extension and/or shell handler */
if (match_extension(p->filename, mode->extensions)
|| match_shell_handler(cs8(p->buf), mode->shell_handlers)) {
return 80;
}
/* weaker match on C comment start */
if (p->buf[0] == '/' && p->buf[1] == '*')
return 60;
/* even weaker match on C++ comment start */
if (p->buf[0] == '/' && p->buf[1] == '/')
return 50;
if (p->buf[0] == '#') {
/* same for files starting with a preprocessor directive */
if (strstart(cs8(p->buf), "#include", NULL)
|| strstart(cs8(p->buf), "#ifndef", NULL)
|| strstart(cs8(p->buf), "#ifdef", NULL)
|| strstart(cs8(p->buf), "#if ", NULL)
|| strstart(cs8(p->buf), "#define", NULL)
|| strstart(cs8(p->buf), "#pragma", NULL)) {
return 50;
}
}
return 1;
}
ModeDef c_mode = {
.name = "C",
.extensions = c_extensions,
.shell_handlers = "tcc",
.mode_probe = c_mode_probe,
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_C | CLANG_CC,
.keywords = "", //c_keywords,
.types = "", //c_types,
.indent_func = c_indent_line,
.auto_indent = 1,
};
/* XXX: support Yacc / Bison syntax extensions */
static ModeDef yacc_mode = {
.name = "Yacc",
.extensions = "y|yacc",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_C | CLANG_CC | CLANG_YACC,
.keywords = c_keywords,
.types = c_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/* XXX: support Lex / Flex syntax extensions */
static ModeDef lex_mode = {
.name = "Lex",
.extensions = "l|lex",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_C | CLANG_CC | CLANG_LEX,
.keywords = c_keywords,
.types = c_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- C++ programming language ----------------*/
static const char cpp_keywords[] = {
"asm|catch|class|delete|friend|inline|namespace|new|operator|"
"private|protected|public|template|try|this|virtual|throw|"
"explicit|override|mutable|using|assert|true|false|nullptr|"
// XXX: many missing keywords
};
static const char cpp_types[] = {
"bool|exception|istream|ostream|ofstream|string|vector|map|set|stack|"
"std::istream|std::ostream|std::ofstream|std::string|"
"std::vector|std::unique_ptr|std::map|std::set|std::stack|"
"std::hash|std::unordered_set|std::unordered_map|std::exception|"
"std::string::iterator|std::stringstream|std::ostringstream|"
};
static int cpp_mode_probe(ModeDef *mode, ModeProbeData *p)
{
int score;
/* trust the file extension */
if (match_extension(p->filename, mode->extensions))
return 80;
score = c_mode_probe(&c_mode, p);
if (score > 5) {
if (strstr(cs8(p->buf), "namespace")
|| strstr(cs8(p->buf), "class")
|| strstr(cs8(p->buf), "::")) {
return score + 5;
}
return score - 5;
}
return 1;
}
ModeDef cpp_mode = {
.name = "C++",
.alt_name = "cpp",
.extensions = "cc|hh|cpp|hpp|cxx|hxx|CPP|CC|c++|lzz",
.mode_probe = cpp_mode_probe,
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_CPP | CLANG_CC,
.keywords = cpp_keywords,
.types = cpp_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- C2 language ----------------*/
static const char c2_keywords[] = {
// Module related
"module|import|as|public|"
// Types -> c2_types
// Type related
"asm|cast|const|elemsof|enum|extern|"
"false|fn|local|nil|offsetof|to_container|public|"
"sizeof|static|struct|template|tlocal|true|type|union|volatile|"
// Control flow related
"break|case|continue|default|else|fallthrough|"
"for|goto|if|return|switch|when|while|"
// other
"assert|static_assert|"
// C keywords (supported in extern "C" blocks)
"typedef|"
// C keywords (reserved)
"alignas|alignof|auto|constexpr|do|inline|nullptr|"
"register|restrict|thread_local|typeof|typeof_unqual"
};
static const char c2_types[] = {
"void|bool|char|i8|i16|i32|i64|u8|u16|u32|u64|isize|usize|f32|f64|"
"reg8|reg16|reg32|reg64|va_list|"
"int|short|long|float|double|signed|unsigned|size_t|ssize_t"
};
static ModeDef c2_mode = {
.name = "C2",
.extensions = "c2|c2h|c2i|c2t",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_C2 | CLANG_PREPROC | CLANG_CAP_TYPE,
.keywords = c2_keywords,
.types = c2_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
#ifndef CONFIG_TINY
/*---------------- Carbon programming language ----------------*/
static const char carbon_keywords[] = {
"abstract|addr|alias|and|api|as|auto|base|break|"
"case|class|constraint|continue|default|else|extends|external|"
"final|fn|for|forall|friend|if|impl|import|in|interface|is|"
"let|library|like|match|namespace|not|observe|or|override|"
"package|partial|private|protected|return|returned|then|"
"var|virtual|where|while|"
/* literals */
"false|true|_|"
/* keyword candidates? */
"choice|const|destructor|dyn|me|public|sizeof|static|template|"
/* builtins */
"Optional|Assert|Self|"
/* operator interfaces */
"Negate|Add|AddWith|Sub|SubWith|Mul|MulWith|Div|DivWith|Mod|ModWith|"
"BitComplement|BitAnd|BitAndWith|BitOr|BitOrWith|BitXor|BitXorWith|"
"LeftShift|LeftShiftWith|RightShift|RightShiftWith|"
"Eq|EqWith|Ordered|OrderedWith|As|ImplicitAs|CommonTypeWith|"
};
static const char carbon_types[] = {
"bool|i8|i16|i32|i64|i128|u8|u16|u32|u64|u128|"
"f16|f32|f64|f128|auto|"
"Type|Array|Stack|String|StringView|Bfloat16|"
};
// identifiers: unicode based
// integer literals: case sensitive, `0x`, `0b`, uppercase hex,
// `_` digit separator, no suffix
// floating point literals: case sensitive, `0x`, `e`, `p` optional exponent sign,
// digit on both sides of `.`, no suffix
// string blocks: """ with optional type indicator and line position constraints
// raw strings: #"\ is a backslash, " is a quote, \#n is a newline"#
// hex digits must be uppercase (what a strange idea)
static ModeDef carbon_mode = {
.name = "Carbon",
.extensions = "carbon",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_CARBON | CLANG_STR3,
.keywords = carbon_keywords,
.types = carbon_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Objective C programming language ----------------*/
static const char objc_keywords[] = {
"self|super|class|nil|YES|NO|"
"@class|@interface|@implementation|@public|@private|@protected|"
"@try|@catch|@throw|@finally|@end|@protocol|@selector|@synchronized|"
"@encode|@defs|@optional|@required|@property|@dynamic|@synthesize|"
"@compatibility_alias|"
// context sensitive keywords
"in|out|inout|bycopy|byref|oneway|"
"getter|setter|readwrite|readonly|assign|retain|copy|nonatomic|"
};
static const char objc_types[] = {
"id|BOOL|SEL|Class|Object|"
};
static int objc_mode_probe(ModeDef *mode, ModeProbeData *mp)
{
const char *p = cs8(mp->buf);
if (match_extension(mp->filename, mode->extensions)) {
/* favor Objective C over Limbo for .m extension
* if file is empty, starts with a comment or a #import
*/
if (*p == '/' || *p == '\0'
|| strstart(p, "#import", NULL)) {
return 81;
} else {
return 80;
}
}
if (match_extension(mp->filename, "h")) {
/* favor Objective C over C/C++ for .h extension
* if file has #import or @keyword at the beginning of a line
*/
const char *q;
for (q = p;; q++) {
if ((*q == '@' && qe_isalpha(q[1]))
|| (*q == '#' && strstart(p, "#import", NULL))) {
return 85;
}
q = strchr(q, '\n');
if (q == NULL)
break;
}
}
return 1;
}
static ModeDef objc_mode = {
.name = "Objective C",
.alt_name = "objc",
.extensions = "m|mm",
.mode_probe = objc_mode_probe,
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_OBJC | CLANG_CC,
.keywords = objc_keywords,
.types = objc_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- AWK programming language ----------------*/
static const char awk_keywords[] = {
"BEGIN|break|case|continue|default|do|else|for|if|next|switch|while|"
"print|printf|split|"
};
static const char awk_types[] = {
"char|double|float|int|long|unsigned|short|signed|void|"
};
static ModeDef awk_mode = {
.name = "awk",
.extensions = "awk",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_AWK | CLANG_REGEX,
.keywords = awk_keywords,
.types = awk_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- less css preprocessor ----------------*/
static const char less_keywords[] = {
"|"
};
static const char less_types[] = {
"|"
};
static ModeDef less_mode = {
.name = "less",
.extensions = "less",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_CSS,
.keywords = less_keywords,
.types = less_types,
.indent_func = c_indent_line,
.fallback = &c_mode,
};
#endif /* CONFIG_TINY */
/*---------------- Javascript programming language ----------------*/
static const char js_keywords[] = {
"break|case|catch|continue|debugger|default|delete|do|"
"else|finally|for|function|if|in|instanceof|new|"
"return|switch|this|throw|try|typeof|while|with|"
/* FutureReservedWord */
"class|const|enum|import|export|extends|super|"
/* The following tokens are also considered to be
* FutureReservedWords when parsing strict mode code */
"implements|interface|let|package|private|protected|"
"public|static|yield|"
/* constants */
"undefined|null|true|false|Infinity|NaN|"
/* strict mode quasi keywords */
"eval|arguments|"
/* ES6 extra keywords */
"await|"
};
static const char js_types[] = {
"void|var|"
};
static int is_js_identifier_start(char32_t c) {
// should accept unicode escape sequence, UnicodeIDStart
return (qe_isalpha_(c) || c == '$' || c >= 128);
}
static int is_js_identifier_part(char32_t c) {
// should accept unicode escape sequence, UnicodeIDContinue, ZWJ or ZWNJ
return (qe_isalnum_(c) || c == '$' || c >= 128);
}
// FIXME: should merge into ustr_get_identifier()
static int get_js_identifier(char *dest, int size, char32_t c,
const char32_t *str, int i0, int n)
{
/*@API utils
Grab an identifier from a `char32_t` buffer,
accept non-ASCII identifiers and encode in UTF-8.
@argument `dest` a pointer to the destination array
@argument `size` the length of the destination array in bytes
@argument `c` the initial code point or `0` if none
@argument `str` a valid pointer to an array of codepoints
@argument `i0` the index to the next codepoint
@argument `n` the length of the codepoint array
@return the number of codepoints used in the source array.
@note `dest` can be a null pointer if `size` is `0`.
*/
int pos = 0, i = i0;
if (c == 0) {
if (!(i < n && is_js_identifier_start(c = str[i++]))) {
if (size > 0)
*dest = '\0';
return 0;
}
}
for (;; i++) {
if (c < 128) {
if (pos + 1 < size) {
dest[pos++] = c;
}
} else {
char buf[6];
int len = utf8_encode(buf, c);
if (pos + len < size) {
memcpy(dest + pos, buf, len);
pos += len;
} else {
size = pos + 1;
}
}
if (i >= n)
break;
c = str[i];
if (!is_js_identifier_part(c))
break;
}
if (pos < size)
dest[pos] = '\0';
return i - i0;
}
static void js_colorize_line(QEColorizeContext *cp,
const char32_t *str, int n,
QETermStyle *sbuf, ModeDef *syn)
{
int i = 0, start, i1, indent;
int style, tag, level;
char32_t c, delim;
char kbuf[64];
int mode_flags = syn->colorize_flags;
int flavor = (mode_flags & CLANG_FLAVOR);
int state = cp->colorize_state;
//int type_decl; /* unused */
indent = cp_skip_blanks(str, 0, n);
tag = !qe_isblank(str[0]) && (cp->s->mode == syn || cp->s->mode == &htmlsrc_mode);
start = i;
//type_decl = 0;
c = 0;
style = 0;
if (i >= n)
goto the_end;
if (state) {
/* if already in a state, go directly in the code parsing it */
if (state & IN_C_COMMENT2)
goto parse_comment2;
switch (state & IN_C_STRING) {
case IN_C_STRING_D: goto parse_string;
case IN_C_STRING_Q: goto parse_string_q;
case IN_C_STRING_BQ: goto parse_string_bq;
case IN_C_STRING_D3: goto parse_string3;
case IN_C_STRING_Q3: goto parse_string_q3;
}
if (state & IN_C_REGEX) {
delim = '/';
goto parse_regex;
}
}
while (i < n) {
start = i;
c = str[i++];
switch (c) {
case '*':
/* lone star at the beginning of a line in a shell buffer
* is treated as a comment start. This improves colorization
* of diff and git output.
*/
if (start == indent && cp->partial_file
&& (i == n || str[i] == ' ' || str[i] == '/')) {
i--;
goto parse_comment2;
}
continue;
case '/':
if (str[i] == '*') {
/* C style multi-line comment */
i++;
parse_comment2:
state |= IN_C_COMMENT2;
style = C_STYLE_COMMENT;
level = (state & IN_C_COMMENT_LEVEL) >> IN_C_COMMENT_SHIFT;
while (i < n) {
if (str[i] == '/' && str[i + 1] == '*' && (mode_flags & CLANG_NEST_COMMENTS)) {
i += 2;
level++;
} else
if (str[i] == '*' && str[i + 1] == '/') {
i += 2;
if (level == 0) {
state &= ~IN_C_COMMENT2;
break;
}
level--;
} else {
i++;
}
}
state = (state & ~IN_C_COMMENT_LEVEL) |
(min_int(level, 7) << IN_C_COMMENT_SHIFT);
break;
} else
if (str[i] == '/') {
/* line comment */
parse_comment1:
state |= IN_C_COMMENT1;
style = C_STYLE_COMMENT;
i = n;
break;
}
if (mode_flags & CLANG_REGEX) {
/* XXX: should use more context to tell regex from divide */
char32_t prev = ' ';
for (i1 = start; i1 > indent; ) {
prev = str[--i1];
if (!qe_isblank(prev))
break;
}
/* ignore end of comment in grep output */
if (start > indent && str[start - 1] == '*' && cp->partial_file)
break;
if (!qe_findchar("])", prev)
&& (qe_findchar(" [({},;=<>!~^&|*/%?:", prev)
|| sbuf[i1] == C_STYLE_KEYWORD
|| (str[i] != ' ' && (str[i] != '=' || str[i + 1] != ' ')
&& !(qe_isalnum(prev) || prev == ')')))) {
/* parse regex */
state |= IN_C_REGEX;
delim = '/';
parse_regex:
style = C_STYLE_REGEX;
while (i < n) {
c = str[i++];
if (c == '\\') {
if (i < n) {
i += 1;
}
} else
if (state & IN_C_CHARCLASS) {
if (c == ']') {
state &= ~IN_C_CHARCLASS;
}
/* ECMA 5: ignore '/' inside char classes */
} else {
if (c == '[') {
state |= IN_C_CHARCLASS;
} else
if (c == delim) {
while (qe_isalnum_(str[i])) {
i++;
}
state &= ~IN_C_REGEX;
break;
}
}
}
break;
}
}
continue;
case '#': /* preprocessor */
if (start == 0 && str[i] == '!') {
/* recognize a shebang comment line */
style = C_STYLE_PREPROCESS;
i = n;
break;
}
if (flavor == CLANG_V8 && start == 0
&& ustrstart(str + i + 1, "include", NULL)) {
/* v8: #include */
// FIXME: handle multiline comments
style = C_STYLE_PREPROCESS;
i = n;
break;
}
continue;
case '@': /* annotations */
i += get_js_identifier(kbuf, countof(kbuf), c, str, i, n);
style = C_STYLE_PREPROCESS;
break;
case '`': /* ECMA 6 template strings */
parse_string_bq:
state |= IN_C_STRING_BQ;
style = C_STYLE_STRING_BQ;
while (i < n) {
c = str[i++];
if (c == '`') {
state &= ~IN_C_STRING;
break;
}
}
break;
case '\'': /* character constant */
if ((mode_flags & CLANG_STR3)
&& (str[i] == '\'' && str[i + 1] == '\'')) {
/* multiline ''' quoted string */
i += 2;
state |= IN_C_STRING_Q3;
parse_string_q3:
style = C_STYLE_STRING_Q;
delim = '\'';
goto string3;
}
state |= IN_C_STRING_Q;
parse_string_q:
style = C_STYLE_STRING_Q;
delim = '\'';
goto string;
case '\"': /* string literal */
if ((mode_flags & CLANG_STR3)
&& (str[i] == '\"' && str[i + 1] == '\"')) {
/* multiline """ quoted string */
i += 2;
state |= IN_C_STRING_D3;
goto parse_string3;
}
state |= IN_C_STRING_D;
parse_string:
style = C_STYLE_STRING;
delim = '\"';
string:
while (i < n) {
c = str[i++];
if (c == '\\') {
if (i >= n)
break;
i++;
} else
if (c == delim) {
state &= ~IN_C_STRING;
break;
}
}
break;
parse_string3:
style = C_STYLE_STRING;
delim = '\"';
string3:
while (i < n) {
c = str[i++];
if (c == '\\') {
if (i >= n)
break;
i++;
} else
if (c == delim && str[i] == delim && str[i + 1] == delim) {
i += 2;
state &= ~IN_C_STRING;
break;
}
}
break;
case '=':
/* exit type declaration */
/* does not handle this: int i = 1, j = 2; */
//type_decl = 0;
continue;
case '<': /* JavaScript extension */
if (flavor == CLANG_JS) {
if (str[i] == '!' && str[i + 1] == '-' && str[i + 2] == '-')
goto parse_comment1;
}
continue;
case '(':
case '{':
tag = 0;
continue;
default:
if (qe_isdigit(c)) {
/* XXX: should parse actual number syntax */
/* decimal, binary, octal and hexadecimal literals:
* 1 0b1 0o1 0x1, case insensitive. 01 is a syntax error */
/* maybe ignore '_' in integers */
/* XXX: should parse decimal and hex floating point syntaxes */
while (qe_isalnum_(str[i]) || (str[i] == '.' && str[i + 1] != '.')) {
i++;
}
style = C_STYLE_NUMBER;
break;
}
if (is_js_identifier_start(c)) {
i += get_js_identifier(kbuf, countof(kbuf), c, str, i, n);
if (cp->state_only && !tag)
continue;
/* keywords used as object property tags are regular identifiers.
* `default` is always considered a keyword as the context cannot be
determined precisely by this simplistic lexical parser */
if (strfind(syn->keywords, kbuf)
&& (str[i] != ':' || strequal(kbuf, "default"))
&& (start == 0 || str[start - 1] != '.')) {
style = C_STYLE_KEYWORD;
break;
}
i1 = cp_skip_blanks(str, i, n);
if (str[i1] == '(') {
/* function call or definition */
style = C_STYLE_FUNCTION;
if (tag) {
/* tag function definition */
eb_add_tag(cp->b, cp->offset + start, kbuf);
tag = 0;
}
break;
} else
if (tag && qe_findchar("(,;=", str[i1])) {
/* tag variable definition */
eb_add_tag(cp->b, cp->offset + start, kbuf);
}
if ((start == 0 || str[start - 1] != '.')
&& !qe_findchar(".(:", str[i])
&& strfind(syn->types, kbuf)) {
/* if not cast, assume type declaration */
//type_decl = 1;
style = C_STYLE_TYPE;
break;
}
if (qe_isupper((unsigned char)kbuf[0])
&& (start >= 2 && str[start - 1] == ' ' && str[start - 2] == ':')) {
/* if type annotation and capitalized assume type name */
style = C_STYLE_TYPE;
break;
}
continue;
}
continue;
}
if (style) {
if (!cp->state_only) {
SET_STYLE(sbuf, start, i, style);
}
style = 0;
}
}
the_end:
if (state & (IN_C_COMMENT | IN_C_STRING)) {
/* set style on eol char */
SET_STYLE1(sbuf, n, style);
if ((state & IN_C_COMMENT) == IN_C_COMMENT1)
state &= ~IN_C_COMMENT1;
}
cp->colorize_state = state;
}
ModeDef js_mode = {
.name = "Javascript",
.alt_name = "js",
.extensions = "js",
.shell_handlers = "node|qjs",
.colorize_func = js_colorize_line,
.colorize_flags = CLANG_JS | CLANG_REGEX,
.keywords = js_keywords,
.types = js_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- JSON data format ----------------*/
static const char json_keywords[] = {
"null|true|false|NaN"
};
static const char json_types[] = {
""
};
static int json_mode_probe(ModeDef *mode, ModeProbeData *pd)
{
const char *p = cs8(pd->buf);
if (match_extension(pd->filename, mode->extensions))
return 80;
if (*p == '{' && p[1] == '\n') {
while (qe_isspace((unsigned char)*++p))
continue;
if (*p == '\"')
return 50;
}
return 1;
}
static ModeDef json_mode = {
.name = "json",
.extensions = "json",
.mode_probe = json_mode_probe,
.colorize_func = js_colorize_line,
.colorize_flags = CLANG_JSON,
.keywords = json_keywords,
.types = json_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Typescript programming language ----------------*/
static const char ts_keywords[] = {
/* keywords */
"break|case|catch|class|const|continue|debugger|default|"
"delete|do|else|enum|export|extends|false|finally|"
"for|function|if|import|in|instanceof|new|null|"
"return|super|switch|this|throw|true|try|typeof|"
"var|void|while|with|"
/* keywords in strict mode */
"implements|interface|let|package|"
"private|protected|public|static|yield|"
/* special names */
"abstract|as|async|await|constructor|declare|from|"
"get|is|module|namespace|of|require|set|type|"
/* other names? */
"readonly|"
/* constants */
"undefined|Infinity|NaN|"
/* strict mode quasi keywords */
"eval|arguments|"
};
static const char ts_types[] = {
"any|boolean|number|string|symbol|"
};
static ModeDef ts_mode = {
.name = "TypeScript",
.alt_name = "ts",
.extensions = "ts|tsx",
//.shell_handlers = "ts",
.colorize_func = js_colorize_line,
.colorize_flags = CLANG_TS | CLANG_REGEX,
.keywords = ts_keywords,
.types = ts_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
#ifndef CONFIG_TINY
/*---------------- V8 Torque programming language ----------------*/
static const char v8_keywords[] = {
/* constants */
"undefined|null|true|false|Infinity|NaN|"
/* classic keywords */
"import|let|const|return|if|else|break|continue|for|while|case|"
"class|extends|struct|constexpr|extern|namespace|goto|"
/* V8 specific */
"typeswitch|tail|debug|enum|"
"dcheck|check|static_assert|transitioning|operator|"
"transient|shape|bitfield|intrinsic|javascript|"
"macro|generates|otherwise|builtin|implicit|weak|"
"never|label|labels|unreachable|runtime|deferred|"
};
static const char v8_types[] = {
"void|var|type|bool|string|bit|"
"int8|int16|int31|int32|int64|uint8|uint16|uint31|uint32|uint64|"
"intptr|uintptr|bint|float16|float32|float64|"
"ByteArray|Object|Map|JSAny|JSFunction|JSObject|Smi|String|Number|"
};
static ModeDef v8_mode = {
.name = "V8 Torque",
.alt_name = "tq",
.extensions = "tq",
.colorize_func = js_colorize_line,
.colorize_flags = CLANG_V8 | CLANG_REGEX,
.keywords = v8_keywords,
.types = v8_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Bee language syntax ----------------*/
/* work in progress */
static ModeDef bee_mode = {
.name = "Bee",
.alt_name = "bee",
.extensions = "bee",
.shell_handlers = "node",
.colorize_func = js_colorize_line,
.colorize_flags = CLANG_BEE | CLANG_REGEX,
.keywords = js_keywords,
.types = js_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- CSS syntax ----------------*/
static const char css_keywords[] = {
"|"
};
static const char css_types[] = {
"|"
};
ModeDef css_mode = {
.name = "CSS",
.extensions = "css",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_CSS,
.keywords = css_keywords,
.types = css_types,
.indent_func = c_indent_line,
.fallback = &c_mode,
};
/*---------------- Onux JS++ programming language ----------------*/
/* triple quoted strings
'''
"""
numeric: /0[Xx][A-Fa-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:[Ee][-+]?\d+|[FfDdL]|UL)?/
*/
static const char jspp_keywords[] = {
// JavaScript keywords
"if|in|do|for|new|try|this|else|case|with|while|"
"break|catch|throw|return|typeof|delete|switch|"
"default|finally|continue|debugger|instanceof|"
"true|false|null|"
// JS++ warning keywords
"let|const|yield|export|extends|implements|package|"
// JS++ basic keywords
"import|external|module|foreach|typeid|enum|interface|class|"
"super|implicit|explicit|undefined|"
// JS++ modifiers
"private|protected|public|static|final|inline|property|abstract|"
"optional|virtual|override|"
// type keywords
"var|void|function|"
// JS++ operators
//"; [ ] ( ) . , : === == = !== != ! <<= << <= < >>>= >>> >>= >> >= > "
//"+= ++ + -= -- - *= * /= / %= % &&= && &= & ||= || |= | ^= ^ ~ ?=|"
/* constants */
"Infinity|NaN|"
/* strict mode quasi keywords */
"eval|arguments|"
};
static const char jspp_types[] = {
"bool|string|byte|char|double|float|int|"
"long|short|unsigned|signed|"
//"var|void|function|"
};
static ModeDef jspp_mode = {
.name = "JS++",
.alt_name = "jspp",
.extensions = "jspp|jpp",
.shell_handlers = "js++",
.colorize_func = js_colorize_line,
.colorize_flags = CLANG_JSPP | CLANG_STR3 | CLANG_REGEX,
.keywords = jspp_keywords,
.types = jspp_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Microsoft Koka programming language ----------------*/
/* triple quoted strings
'''
"""
numeric: /0[Xx][A-Fa-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:[Ee][-+]?\d+|[FfDdL]|UL)?/
- in identifiers
' is not a string delimiter, but a name modifier?
digits.string -> unit literal
hex escapes: \x## \u#### \U######
raw string: @" ... "" ... "
string: " ... escapes ... "
char: ' ... escapes ... '
line comment: // ...
block comment: / * ... * / (nested)
line directive: # ...
identifier: [a-zA-Z][a-zA-Z0-9_]+ embedded -, trailing [?]? and [']*
*/
static const char koka_keywords[] = {
"fun|function|"
"infix|infixr|infixl|prefix|type|cotype|rectype|alias|"
"forall|exists|some|fun|function|val|var|con|"
"if|then|else|elif|match|return|import|as|"
"public|private|abstract|interface|instance|with|"
"external|inline|include|effect|handle|handler|linear|"
"yield|qualified|hiding|"
"interleaved|catch|raise|resume|amb|for|foreach|"
"module|not|open|extend|struct|linear|extern|"
"False|True|Nothing|Nil|"
};
static const char koka_types[] = {
"bool|int|double|string|"
//"byte|char|float|long|short|unsigned|signed|"
};
static ModeDef koka_mode = {
.name = "Koka",
.extensions = "kk",
//.shell_handlers = "koka",
.colorize_func = js_colorize_line,
.colorize_flags = CLANG_KOKA | CLANG_REGEX | CLANG_NEST_COMMENTS,
.keywords = koka_keywords,
.types = koka_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- ActioScript programming language ----------------*/
static const char as_keywords[] = {
"as|break|case|catch|class|continue|default|do|else|false|"
"finally|for|function|if|import|interface|internal|is|new|null|"
"package|private|protected|public|return|super|switch|this|throw|"
"true|try|while|"
// The following AS3 keywords are no longer in AS4:
"delete|include|instanceof|namespace|typeof|use|with|in|const|"
// other constants
"undefined|Infinity|NaN|"
// introduced in AS4 (spec abandoned in december 2012)
//"let|defer|get|set|override|native|extends|implements|"
};
static const char as_types[] = {
"void|var|bool|byte|int|uint|long|ulong|float|double|"
"Array|Boolean|Number|Object|String|Function|Event|RegExp|"
"Class|Interface|"
};
static ModeDef as_mode = {
.name = "Actionscript",
.alt_name = "as",
.extensions = "as",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_AS | CLANG_REGEX,
.keywords = as_keywords,
.types = as_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
#endif /* CONFIG_TINY */
/*---------------- Java programming language ----------------*/
static const char java_keywords[] = {
/* language keywords */
"abstract|assert|break|case|catch|class|const|continue|"
"default|do|else|enum|extends|final|finally|for|goto|"
"if|implements|import|instanceof|interface|native|new|"
"package|private|protected|public|return|"
"static|strictfp|super|switch|synchronized|threadsafe|"
"this|throw|throws|transient|try|volatile|while|"
/* boolean and null literals */
"false|null|true|"
};
static const char java_types[] = {
"boolean|byte|char|double|float|int|long|short|void|"
};
ModeDef java_mode = {
.name = "Java",
.extensions = "jav|java",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_JAVA | CLANG_CAP_TYPE,
.keywords = java_keywords,
.types = java_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- C# programming language ----------------*/
static const char csharp_keywords[] = {
"abstract|as|base|break|case|catch|checked|class|const|continue|"
"default|delegate|do|else|enum|event|explicit|extern|false|finally|"
"fixed|for|foreach|goto|if|implicit|in|interface|internal|is|lock|"
"namespace|new|null|operator|out|override|params|private|protected|"
"public|readonly|ref|return|sealed|sizeof|stackalloc|static|"
"struct|switch|template|this|throw|true|try|typeof|unchecked|unsafe|"
"using|virtual|volatile|while|"
/* contextual keywords */
"add|remove|yield|partial|get|set|where|"
};
static const char csharp_types[] = {
"bool|byte|char|decimal|double|float|int|long|object|sbyte|short|"
"string|uint|ulong|ushort|void|"
"Boolean|Byte|DateTime|Exception|Int32|Int64|Object|String|Thread|"
"UInt32|UInt64|"
};
ModeDef csharp_mode = {
.name = "C#", /* C Sharp */
.alt_name = "csharp",
.extensions = "cs",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_CSHARP | CLANG_PREPROC,
.keywords = csharp_keywords,
.types = csharp_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- PHP programming language ----------------*/
static const char php_keywords[] = {
"abstract|as|assert|break|case|catch|class|clone|const|continue|"
"declare|default|elseif|else|enddeclare|endif|endswitch|end|exit|"
"extends|false|final|foreach|for|function|goto|if|implements|"
"include_once|include|instanceof|interface|list|namespace|new|"
"overload|parent|private|protected|public|require_once|require|return|"
"self|sizeof|static|switch|throw|trait|true|try|use|var|while|"
"NULL|null|$this"
// built-in pseudo functions
"array|count|defined|echo|empty|"
};
static const char php_types[] = {
"array|boolean|bool|double|float|integer|int|object|real|string|"
};
ModeDef php_mode = {
.name = "PHP",
.extensions = "php|php3|php4",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_PHP | CLANG_REGEX,
.keywords = php_keywords,
.types = php_types,
.fallback = &c_mode,
};
#ifndef CONFIG_TINY
/*---------------- Go programming language ----------------*/
static const char go_keywords[] = {
/* keywords */
"break|case|chan|const|continue|default|defer|else|fallthrough|"
"for|func|go|goto|if|import|interface|map|package|range|"
"return|select|struct|switch|type|var|"
/* builtins */
"append|cap|close|complex|copy|delete|imag|len|make|new|panic|"
"print|println|real|recover|"
/* Constants */
"false|iota|nil|true|"
};
static const char go_types[] = {
"bool|byte|complex128|complex64|error|float32|float64|"
"int|int16|int32|int64|int8|rune|string|"
"uint|uint16|uint32|uint64|uint8|uintptr|"
};
/* Go identifiers start with a Unicode letter or _ */
static ModeDef go_mode = {
.name = "Go",
.extensions = "go",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_GO,
.keywords = go_keywords,
.types = go_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Scala programming language ----------------*/
static const char scala_keywords[] = {
/* language keywords */
"abstract|case|catch|class|def|do|else|extends|final|"
"finally|for|forSome|if|implicit|import|lazy|match|new|"
"object|override|package|private|protected|return|sealed|super|this|throw|"
"trait|try|type|val|var|while|with|yield|"
/* boolean and null literals */
"false|null|true|_|"
};
static const char scala_types[] = {
/* all mixed case identifiers starting with an uppercase letter are types */
""
};
static ModeDef scala_mode = {
.name = "Scala",
.extensions = "scala|sbt",
.colorize_func = c_colorize_line,
.colorize_flags = (CLANG_SCALA | CLANG_CAP_TYPE | CLANG_STR3 |
CLANG_NEST_COMMENTS),
.keywords = scala_keywords,
.types = scala_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- D programming language ----------------*/
static const char d_keywords[] = {
"abstract|alias|align|asm|assert|auto|body|break|"
"case|cast|catch|class|const|continue|debug|default|"
"delegate|deprecated|do|else|enum|export|extern|false|"
"final|finally|for|foreach|foreach_reverse|function|goto|"
"if|immutable|import|in|inout|int|interface|invariant|is|"
"lazy|mixin|module|new|nothrow|null|out|override|package|"
"pragma|private|protected|public|pure|ref|return|scope|shared|"
"static|struct|super|switch|synchronized|template|this|throw|"
"true|try|typeid|typeof|union|unittest|version|while|with|"
"delete|typedef|volatile|" /* deprecated */
"macro|" /* reserved, unused */
"__FILE__|__MODULE__|__LINE__|__FUNCTION__|__PRETTY_FUNCTION__|"
"__gshared|__traits|__vector|__parameters|"
"__DATE__|__EOF__|__TIME__|__TIMESPAMP__|__VENDOR__|__VERSION__|"
};
static const char d_types[] = {
"bool|byte|ubyte|short|ushort|int|uint|long|ulong|char|wchar|dchar|"
"float|double|real|ifloat|idouble|ireal|cfloat|cdouble|creal|void|"
"|cent|ucent|string|wstring|dstring|size_t|ptrdiff_t|"
};
static ModeDef d_mode = {
.name = "D",
.extensions = "d|di",
.colorize_func = c_colorize_line,
/* only #line is supported, but can occur anywhere */
.colorize_flags = CLANG_D | CLANG_PREPROC | CLANG_T_TYPES,
.keywords = d_keywords,
.types = d_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Limbo programming language ----------------*/
static const char limbo_keywords[] = {
"adt|alt|array|break|case|chan|con|continue|cyclic|do|else|exit|"
"fn|for|hd|if|implement|import|include|len|list|load|module|nil|"
"of|or|pick|ref|return|self|spawn|tagof|tl|to|type|while|"
};
static const char limbo_types[] = {
"big|byte|int|real|string|"
};
static ModeDef limbo_mode = {
.name = "Limbo",
.extensions = "m",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_LIMBO,
.keywords = limbo_keywords,
.types = limbo_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Cyclone programming language ----------------*/
static const char cyclone_keywords[] = {
"auto|break|case|const|continue|default|do|else|enum|extern|for|goto|"
"if|inline|register|restrict|return|sizeof|static|struct|switch|"
"typedef|union|volatile|while|"
"abstract|alias|as|catch|datatype|export|fallthru|inject|let|"
"namespace|new|numelts|offsetof|region|regions|reset_region|rnew|"
"tagcheck|throw|try|using|valueof|"
"calloc|malloc|rcalloc|rmalloc|"
"NULL|"
};
static const char cyclone_types[] = {
"char|double|float|int|long|unsigned|short|signed|void|"
"_Bool|_Complex|_Imaginary|"
"bool|dynregion_t|region_t|tag_t|valueof_t|"
"@numelts|@region|@thin|@fat|@zeroterm|@nozeroterm|@notnull|@nullable|"
"@extensible|@tagged"
};
static ModeDef cyclone_mode = {
.name = "Cyclone",
.extensions = "cyc|cyl|cys",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_CYCLONE | CLANG_CC,
.keywords = cyclone_keywords,
.types = cyclone_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Ch programming language ----------------*/
static const char ch_keywords[] = {
"local|offsetof|Inf|NaN|"
};
static const char ch_types[] = {
"complex|"
};
static ModeDef ch_mode = {
.name = "Ch",
.extensions = "chf",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_CH | CLANG_CC,
.keywords = ch_keywords,
.types = ch_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Squirrel programming language ----------------*/
static const char squirrel_keywords[] = {
"base|break|continue|const|extends|for|null|throw|try|instanceof|true|"
"case|catch|class|clone|default|delete|else|enum|foreach|function|if|in|"
"resume|return|switch|this|typeof|while|yield|constructor|false|static|"
};
static const char squirrel_types[] = {
"local|"
};
static ModeDef squirrel_mode = {
.name = "Squirrel",
.extensions = "nut",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_SQUIRREL,
.keywords = squirrel_keywords,
.types = squirrel_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- ICI programming language ----------------*/
static const char ici_keywords[] = {
"array|break|case|class|continue|default|do|else|extern|float|"
"for|forall|func|if|in|module|NULL|onerror|return|set|static|struct|"
"switch|try|while|"
};
static const char ici_types[] = {
"auto|"
};
static ModeDef ici_mode = {
.name = "ICI",
.extensions = "ici",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_ICI,
.keywords = ici_keywords,
.types = ici_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- JSX programming language ----------------*/
static const char jsx_keywords[] = {
// literals shared with ECMA 262
"null|true|false|NaN|Infinity|"
// keywords shared with ECMA 262
"break|case|const|do|else|finally|for|function|if|in|"
"instanceof|new|return|switch|this|throw|try|typeof|var|while|"
// JSX keywords
"class|extends|super|import|implements|static|"
"__FILE__|__LINE__|undefined|"
// contextual keywords
// "assert|log|catch|continue|default|delete|interface",
// ECMA 262 literals but not used by JSX
"debugger|with|"
// ECMA 262 future reserved words
"export|"
// ECMA 262 strict mode future reserved words
"let|private|public|yield|protected|"
// JSX specific reserved words
"extern|native|as|operator|abstract|"
};
static const char jsx_types[] = {
"void|variant|boolean|int|number|string|Error|"
};
static ModeDef jsx_mode = {
.name = "JSX",
.extensions = "jsx",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_JSX | CLANG_REGEX,
.keywords = jsx_keywords,
.types = jsx_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Haxe programming language ----------------*/
static const char haxe_keywords[] = {
"abstract|break|case|cast|catch|class|continue|default|do|dynamic|else|"
"enum|extends|extern|false|for|function|if|implements|import|inline|"
"interface|in|macro|new|null|override|package|private|public|return|"
"static|switch|this|throw|true|try|typedef|untyped|using|var|while|"
};
static const char haxe_types[] = {
"Void|Array|Bool|Int|Float|Class|Enum|Dynamic|String|Date|Null|"
"Iterator|"
};
static ModeDef haxe_mode = {
.name = "Haxe",
.extensions = "hx",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_HAXE | CLANG_REGEX,
.keywords = haxe_keywords,
.types = haxe_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Dart programming language ----------------*/
static const char dart_keywords[] = {
"abstract|as|assert|break|call|case|catch|class|const|continue|default|do|"
"else|equals|extends|external|factory|false|final|finally|for|"
"get|if|implements|in|interface|is|negate|new|null|on|operator|return|"
"set|show|static|super|switch|this|throw|true|try|typedef|while|"
// should match only at line start
"import|include|source|library|"
"@observable|@published|@override|@runTest|"
// XXX: should colorize is! as a keyword
};
static const char dart_types[] = {
"bool|double|dynamic|int|num|var|void|"
"String|StringBuffer|Object|RegExp|Function|"
"Date|DateTime|TimeZone|Duration|Stopwatch|DartType|"
"Collection|Comparable|Completer|Future|Match|Options|Pattern|"
"HashMap|HashSet|Iterable|Iterator|LinkedHashMap|List|Map|Queue|Set|"
"Dynamic|Exception|Error|AssertionError|TypeError|FallThroughError|"
};
static ModeDef dart_mode = {
.name = "Dart",
.extensions = "dart",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_DART | CLANG_STR3,
.keywords = dart_keywords,
.types = dart_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Pike programming language ----------------*/
static const char pike_keywords[] = {
"auto|break|case|catch|class|constant|continue|const|default|do|else|enum|extern|"
"final|for|foreach|gauge|global|if|import|inherit|inline|"
"lambda|local|optional|predef|private|protected|public|"
"return|sscanf|static|switch|throw|typedef|typeof|while|"
"_Static_assert|__async__|__attribute__|__deprecated__|"
"__experimental__|__func__|__generic__|__generator__|__weak__|"
"__unused__|__unknown__"
};
static const char pike_types[] = {
"array|float|int|string|function|mapping|mixed|multiset|object|program|"
"variant|void|"
};
static ModeDef pike_mode = {
.name = "Pike",
.extensions = "pike",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_PIKE | CLANG_PREPROC,
.keywords = pike_keywords,
.types = pike_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- IDL syntax ----------------*/
static const char idl_keywords[] = {
"abstract|attribute|case|component|const|consumes|context|custom|"
"default|emits|enum|eventtype|exception|factory|finder|"
"fixed|getraises|home|import|in|inout|interface|local|module|multiple|"
"native|oneway|out|primarykey|private|provides|public|publishes|raises|"
"readonly|setraises|struct|supports|switch|"
"typedef|typeid|typeprefix|union|uses|ValueBase|valuetype|"
"sequence|iterable|truncatable|"
"unrestricted|namespace|dictionary|or|implements|optional|partial|required|"
"getter|setter|creator|deleter|callback|legacycaller|"
"false|true|null|FALSE|TRUE|"
};
static const char idl_types[] = {
"unsigned|short|long|float|double|char|wchar|string|wstring|octet|any|void|"
"byte|boolean|object|"
};
static ModeDef idl_mode = {
.name = "IDL",
.extensions = "idl",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_IDL | CLANG_PREPROC | CLANG_WLITERALS |
CLANG_REGEX | CLANG_CAP_TYPE,
.keywords = idl_keywords,
.types = idl_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- GNU Calc syntax ----------------*/
static const char calc_keywords[] = {
"if|else|for|while|do|continue|break|goto|return|local|global|static|"
"switch|case|default|quit|exit|define|read|show|help|write|mat|obj|"
"print|cd|undefine|abort|"
};
static const char calc_types[] = {
"|"
};
static ModeDef calc_mode = {
.name = "calc", /* GNU Calc */
.extensions = "cal|calc",
.shell_handlers = "calc",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_CALC | CLANG_CC,
.keywords = calc_keywords,
.types = calc_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- GNU Enscript programming language ----------------*/
static const char enscript_keywords[] = {
"if|else|return|state|extends|BEGIN|END|forever|continue|do|"
"not|and|or|orelse|switch|case|default|true|false|"
};
static const char enscript_types[] = {
"|"
};
static int enscript_mode_probe(ModeDef *mode, ModeProbeData *pd)
{
if (match_extension(pd->filename, mode->extensions)) {
if (*cs8(pd->buf) == '/')
return 80;
else
return 50;
}
return 1;
}
static ModeDef enscript_mode = {
.name = "Enscript", /* GNU Enscript */
.extensions = "st", /* syntax files */
.mode_probe = enscript_mode_probe,
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_ENSCRIPT | CLANG_REGEX,
.keywords = enscript_keywords,
.types = enscript_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- QuickScript programming language ----------------*/
static const char qs_keywords[] = {
"break|case|catch|class|continue|default|delete|do|else|"
"finally|for|function|if|import|in|instanceof|module|new|"
"return|switch|this|throw|try|typeof|while|"
"true|false|null|void|"
"get|set|"
"struct|self|def|func|as|from|arguments|target|super|"
};
static const char qs_types[] = {
"const|let|var|bool|char|double|int|string|"
"Array|Boolean|Function|Number|Object|String|Date|"
};
static int qs_mode_probe(ModeDef *mode, ModeProbeData *p)
{
if (match_extension(p->filename, mode->extensions)
|| match_filename(p->filename, mode->filenames)
|| match_shell_handler(cs8(p->buf), mode->shell_handlers)
|| strstr(p->real_filename, "/.qe/config"))
return 80;
return 1;
}
static ModeDef qscript_mode = {
.name = "QScript",
.alt_name = "qs",
.extensions = "qe|qs",
.filenames = ".qerc",
.shell_handlers = "qscript|qs|qsn",
.mode_probe = qs_mode_probe,
.colorize_func = js_colorize_line,
.colorize_flags = CLANG_QSCRIPT | CLANG_STR3 | CLANG_REGEX,
.keywords = qs_keywords,
.types = qs_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Elastic C like language ----------------*/
static const char ec_keywords[] = {
"@false|@nil|@true|new|self|"
"break|catch|class|continue|do|else|extends|for|from|function|goto|if|"
"import|in|local|method|package|private|public|return|static|super|"
"throw|try|while|"
};
static const char ec_types[] = {
"none|short|ushort|int|uint|long|ulong|char|uchar|float|double|bool|"
"string|static_string|array|callback|symbol|"
};
static ModeDef ec_mode = {
.name = "elastiC",
.alt_name = "ec",
.extensions = "ec",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_ELASTIC,
.keywords = ec_keywords,
.types = ec_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Jed's S-Lang extension language ----------------*/
static const char sl_keywords[] = {
"define|if|else|return|static|while|break|do|"
};
static const char sl_types[] = {
"variable|"
};
static ModeDef sl_mode = {
.name = "Jed", /* S-Lang */
.extensions = "sl",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_JED | CLANG_PREPROC,
.keywords = sl_keywords,
.types = sl_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Peter Koch's CSL C Scripting Language ----------------*/
static const char csl_keywords[] = {
"const|sizeof|try|catch|throw|static|extern|resize|exists|if|else|"
"switch|case|default|while|do|break|continue|for|trace|true|false|"
};
static const char csl_types[] = {
"var|void|string|int|"
};
static ModeDef csl_mode = {
.name = "CSL",
.extensions = "csl",
.shell_handlers = "csl",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_CSL | CLANG_PREPROC,
.keywords = csl_keywords,
.types = csl_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Neko programming language ----------------*/
static const char neko_keywords[] = {
"function|if|else|return|while|do|switch|default|"
"try|catch|break|continue|"
"this|null|true|false|"
};
static const char neko_types[] = {
"var|"
};
static ModeDef neko_mode = {
.name = "Neko",
.extensions = "neko",
.shell_handlers = NULL,
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_NEKO,
.keywords = neko_keywords,
.types = neko_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- NekoML programming language ----------------*/
static const char nml_keywords[] = {
"function|rec|if|then|else|return|while|do|switch|default|"
"try|catch|break|continue|when|"
"this|null|true|false|or|and|xor|"
"match|type|exception|throw|mutable|list|"
};
static const char nml_types[] = {
"var|int|float|string|bool|char|void|"
};
static ModeDef nml_mode = {
.name = "NekoML",
.extensions = "nml",
.shell_handlers = NULL,
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_NML,
.keywords = nml_keywords,
.types = nml_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Alloy programming language ----------------*/
static const char alloy_keywords[] = {
"if|else|do|for|loop|while|break|continue|match|return|use|"
"mut|_|true|false|"
"struct|enum|fn|func|self|impl"
};
static const char alloy_types[] = {
"void|bool|char|int|float|double|usize|string|"
"u8|u16|u32|u64|i8|i16|i32|i64|f64|f32|"
};
static ModeDef alloy_mode = {
.name = "Alloy",
.extensions = "ay",
.shell_handlers = NULL,
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_ALLOY,
.keywords = alloy_keywords,
.types = alloy_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- SCILab programming language ----------------*/
static const char scilab_keywords[] = {
"if|else|for|while|end|select|case|quit|return|help|what|who|"
"pause|clear|resume|then|do|apropos|abort|break|elseif|pwd|"
"function|endfunction|clc|continue|try|catch|exit|"
"global|local|get|sorted|"
};
static const char scilab_types[] = {
""
};
static int scilab_mode_probe(ModeDef *mode, ModeProbeData *p)
{
if (match_extension(p->filename, mode->extensions)
|| match_shell_handler(cs8(p->buf), mode->shell_handlers)) {
return 80;
}
if (match_extension(p->filename, "start|quit")
&& (p->buf[0] == '/' && p->buf[1] == '/')) {
return 80;
}
return 1;
}
static ModeDef scilab_mode = {
.name = "SciLab",
.extensions = "sce|sci",
.shell_handlers = NULL,
.mode_probe = scilab_mode_probe,
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_SCILAB,
.keywords = scilab_keywords,
.types = scilab_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Kotlin programming language ----------------*/
static const char kotlin_keywords[] = {
/* language keywords */
"package|import|as|fun|val|var|if|else|is|return|for|do|while|"
"break|continue|when|it|to|by|in|out|try|catch|throw|finally|"
"class|object|interface|public|private|protected|internal|inner|"
"constructor|this|super|open|override|final|abstract|enum|companion|"
"vararg|inline|reified|annotation|data|"
"infix|operator|step|downTo|until|lazy|with|also|"
//"get|set|" // do not colorize as keyword because used as function
//"case|def|extends|forSome|implicit|lazy|" // unused java keywords
//"match|new|sealed|trait|type|with|yield|" // unused java keywords
/* boolean and null literals */
"false|true|null|"
};
/* numbers: hex, binary, L suffix for Long, f/F suffix for Float */
/* escape keywords as identifiers with backticks: a.`if` */
static const char kotlin_types[] = {
/* all mixed case identifiers starting with an uppercase letter are types */
"dynamic|"
};
static ModeDef kotlin_mode = {
.name = "Kotlin",
.extensions = "kt",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_KOTLIN | CLANG_CAP_TYPE | CLANG_STR3,
.keywords = kotlin_keywords,
.types = kotlin_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- C! (cbang) from EPITA's LSE laboratory ----------------*/
static const char cbang_keywords[] = {
/* language keywords */
"enum|struct|union|packed|sizeof|static|volatile|"
"const|local|inline|if|else|while|do|for|switch|case|break|continue|"
"default|typedef|class|macro|init|del|return|import|include|open_import|"
"goto|as|interface|support|property|"
/* Builtins */
"false|true|NULL|"
};
/* integers: hex, octal, binary */
/* floats: [0-9]+([.][0-9]*)?([eE][+-]?[0-9]+)? */
static const char cbang_types[] = {
/* all mixed case identifiers starting with an uppercase letter are types */
"int|char|float|void|"
};
static ModeDef cbang_mode = {
.name = "C!",
.alt_name = "cbang",
.extensions = "cb|cbi",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_CBANG | CLANG_CAP_TYPE,
.keywords = cbang_keywords,
.types = cbang_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Gnome's Vala language ----------------*/
static const char vala_keywords[] = {
//"asm|catch|delete|friend|inline|namespace|operator|"
//"private|protected|template|try|virtual|throw|"
//"explicit|mutable|assert|"
"do|if|for|case|else|enum|break|const|while|extern|public|"
"sizeof|static|struct|switch|default|continue|volatile|"
"using|private|public|protected|class|override|abstract|weak|base|"
"foreach|in|is|as|new|this|try|lock|async|catch|throw|yield|"
"signal|throws|typeof|dynamic|ensures|finally|abstract|delegate|"
"internal|requires|construct|interface|namespace|errordomain|"
"var|get|set|out|ref|owned|inline|params|sealed|unowned|virtual|"
"null|true|false|"
};
static const char vala_types[] = {
//"void|"
"bool|string|int|uint|uchar|nt8|short|ushort|long|ulong|size_t|ssize_t|"
"double|va_list|unichar|"
};
static ModeDef vala_mode = {
.name = "Vala",
.extensions = "vala|vapi",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_VALA | CLANG_CC | CLANG_REGEX |
CLANG_CAP_TYPE | CLANG_STR3,
.keywords = vala_keywords,
.types = vala_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Compuphase's Pawn language ----------------*/
static const char pawn_keywords[] = {
"assert|break|case|const|continue|default|defined|do|else|exit|"
"for|forward|goto|if|native|new|operator|public|return|sizeof|sleep|"
"state|static|stock|switch|tagof|while|",
};
//static const char pawn_preprocessor_keywords[] = {
// "assert|define|else|elseif|endif|endinput|error|file|if|include|"
// "line|pragma|tryinclude|undef"
//};
static const char pawn_types[] = {
""
//"void|"
//"bool|string|int|uint|uchar|nt8|short|ushort|long|ulong|size_t|ssize_t|"
//"double|va_list|unichar|"
};
static int pawn_mode_probe(ModeDef *mode, ModeProbeData *p)
{
if (match_shell_handler(cs8(p->buf), mode->shell_handlers))
return 81;
if (match_extension(p->filename, mode->extensions)) {
/* disambiguate .p extension, can be Pascal too. */
#define NUL "\0"
static const char pawn_checks[] =
"@" NUL "//" NUL "/*" NUL
"#include" NUL "#define" NUL "#if" NUL
"forward" NUL "new" NUL "main()" NUL
NUL;
const char *pref;
const char *cp = cs8(p->buf);
while (qe_isspace(*cp))
cp++;
for (pref = pawn_checks; *pref; pref += strlen(pref) + 1) {
if (strstart(cp, pref, NULL))
return 81;
}
return 79;
}
return 1;
}
static ModeDef pawn_mode = {
.name = "Pawn",
.extensions = "p",
.mode_probe = pawn_mode_probe,
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_PAWN | CLANG_CC, // needs '' delimited strings
.keywords = pawn_keywords,
.types = pawn_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Kenneth Louden's C-minus language ----------------*/
/* C minus supports a very crude subset of C:
*
* numbers: [0-9]+
* identifiers: [a-zA-Z]+
* comments: multi-line C comments
* keywords: if else return while
* types: int void
* operators: = ( ) { } [ ] \ . ; - + * / > < >= <= == !=
* no strings, charconst, pointers, preproc...
*/
static const char cminus_keywords[] = {
"if|else|return|while|",
};
static const char cminus_types[] = {
"int|void|"
};
static ModeDef cminus_mode = {
.name = "C-minus",
.alt_name = "cminus",
.extensions = "cm",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_CMINUS,
.keywords = cminus_keywords,
.types = cminus_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Game Monkey scripting language ----------------*/
/* Simple C like syntax with some extensions:
c = "My " "house"; // creates the string 'My house'
d = `c:\windows\sys`; // d is a string
c = `Chris``s bike`; // creates the string 'Chris`s bike'
c = 2.4f // c is a float
a = 'BLCK'; // characters as four bytes making integer
a = 179; // decimal
a = 0xB3; // hexadecimal
a = 0b10110011; // binary
myRect = CreateRect(0, 0, 5, 10); // Construct a table that describes
// a rectangle
area = myRect.Area(); // myRect is automatically assigned to 'this'
// within the area method.
Size = function() { return .width * .height; };
s = myRect:Size(); // Calls Size function passing 'myRect' as 'this'
*/
static const char gmscript_keywords[] = {
"if|else|for|while|foreach|in|and|or|function|"
"dowhile|break|continue|return|"
"array|table|global|local|member|this|"
"true|false|null|",
};
static const char gmscript_types[] = {
""
};
static ModeDef gmscript_mode = {
.name = "Game Monkey",
.alt_name = "gmscript",
.extensions = "gm",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_GMSCRIPT,
.keywords = gmscript_keywords,
.types = gmscript_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Wren embeddable scripting language ----------------*/
/* Simple C like syntax with some extensions */
static const char wren_keywords[] = {
"break|class|construct|else|false|for|foreign|if|import|"
"in|is|null|return|static|super|this|true|while|"
};
static const char wren_types[] = {
"var|"
};
static ModeDef wren_mode = {
.name = "Wren",
.extensions = "wren",
.shell_handlers = "wren",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_WREN | CLANG_CAP_TYPE | CLANG_NEST_COMMENTS,
.keywords = wren_keywords,
.types = wren_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Jack language from nand2tetris ----------------*/
/* Simple object oriented language with C like syntax
see https://www.nand2tetris.org/ for details
*/
static const char jack_keywords[] = {
"class|constructor|method|function|"
"var|static|field|"
"let|do|if|else|while|return|"
"true|false|null|this"
};
static const char jack_types[] = {
"int|boolean|char|void"
};
static ModeDef jack_mode = {
.name = "Jack",
.extensions = "jack",
.shell_handlers = "jack",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_JACK | CLANG_CAP_TYPE,
.keywords = jack_keywords,
.types = jack_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Smac language by Bruno Pagès ----------------*/
/* Simple C-like language used in XCoral. */
static const char smac_keywords[] = {
// recognized and used:
"break|case|continue|default|do|else|for|if|return|sizeof|switch|while|"
// reserved and discarded:
"auto|const|double|enum|extern|float|goto|long|register|short|signed|"
"static|struct|typedef|union|unsigned|volatile"
};
static const char smac_types[] = {
"void|char|int"
};
static ModeDef smac_mode = {
.name = "Smac",
.extensions = "smac",
.shell_handlers = "smac",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_SMAC,
.keywords = smac_keywords,
.types = smac_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- V programming language ----------------*/
// strings enclosed in '' or "", UTF-8 encoded
// interpolate strings with 'Hello $var.name', 'Hello ${1+2}'
// char constants use `c`, with embedded \ sequences
static const char v_keywords[] = {
/* keywords */
"fn|mut|in|map|pub|struct|const|module|import|interface|enum|asm|type|"
"as|atomic|embed|__global|sizeof|union|static|"
"if|else|for|break|continue|match|return|or|assert|defer|$if|go|goto|"
"switch|case|default|"
///* builtins */
//"append|cap|close|complex|copy|delete|imag|len|make|new|panic|"
//"print|println|real|recover|"
/* Constants */
"true|false|none|err|"
};
static const char v_types[] = {
"bool|string|i8|i16|i32|i64|i128|u8|u16|u32|u64|u128|"
"byte|" // alias for u8
"int|" // alias for i32
"rune|" // alias for i32, represents a Unicode code point
"f32|f64|byteptr|voidptr|"
};
/* V identifiers start with a Unicode letter or _ */
static ModeDef v_mode = {
.name = "V",
.extensions = "v",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_V | CLANG_PREPROC | CLANG_CAP_TYPE | CLANG_NEST_COMMENTS,
.keywords = v_keywords,
.types = v_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Google Protocol Buffers ----------------*/
// strings enclosed in '' or "", UTF-8 encoded
static const char protobuf_keywords[] = {
/* keywords */
"required|optional|repeated|package|import|default|"
"message|enum|service|extensions|reserved|extend|rpc|"
"option|returns|group|to|max|oneof|"
/* builtins */
/* constants */
"true|false|"
};
static const char protobuf_types[] = {
"double|float|int32|int64|uint32|uint64|sint32|"
"sint64|fixed32|fixed64|sfixed32|sfixed64|bool|"
"string|bytes|"
};
/* protobuf identifiers start with a Unicode letter or _ */
static ModeDef protobuf_mode = {
.name = "protobuf",
.desc = "Major mode for editing Protocol Buffers description language",
.extensions = "proto",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_PROTOBUF | CLANG_CAP_TYPE,
.keywords = protobuf_keywords,
.types = protobuf_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Odin programming language ----------------*/
// string literals enclosed in "", UTF-8 encoded
// raw string literals enclosed in ``, UTF-8 encoded
// runes enclosed in ''
static const char odin_keywords[] = {
/* keywords */
"align_of|auto_cast|bit_field|bit_set|break|case|cast|const|context|"
"continue|defer|distinct|do|dynamic|else|enum|fallthrough|for|foreign|"
"if|import|in|inline|macro|map|no_inline|notin|offset_of|opaque|"
"package|proc|return|size_of|struct|switch|transmute|type_of|typeid|"
"union|using|when|"
/* predeclared identifiers */
"len|cap|complex|real|imag|conj|swizzle|expand_to_tuple|min|max|abs|clamp|"
/* constants */
"true|false|nil|_|"
};
static const char odin_types[] = {
"bool|b8|b16|b32|b64|i8|i16|i32|i64|i128|u8|u16|u32|u64|u128|"
"i16le|i32le|i64le|i128le|u16le|u32le|u64le|u128le|"
"i16be|i32be|i64be|i128be|u16be|u32be|u64be|u128be|"
"f32|f64|complex64|complex128|byte|rune|"
"uintptr|uint|int|string|cstring|any|rawptr|"
};
/* Odin identifiers start with a Unicode letter or _ */
static ModeDef odin_mode = {
.name = "Odin",
.desc = "Major mode for editing Odin programs",
.extensions = "odin",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_ODIN | CLANG_CAP_TYPE | CLANG_NEST_COMMENTS,
.keywords = odin_keywords,
.types = odin_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- Salmon programming language ----------------*/
static const char salmon_keywords[] = {
"single|routine|function|procedure|class|variable|"
"immutable|tagalong|lepton|quark|lock|static|"
"virtual|pure|construct|type|arguments|this|break"
"continue|forall|exists|"
};
static const char salmon_types[] = {
"|"
};
static void salmon_colorize_line(QEColorizeContext *cp,
const char32_t *str, int n,
QETermStyle *sbuf, ModeDef *syn)
{
int i = 0, start, i1;
int style, tag, level;
char32_t c, delim;
char kbuf[64];
int mode_flags = syn->colorize_flags;
int state = cp->colorize_state;
//int type_decl; /* unused */
//int indent = cp_skip_blanks(str, 0, n);
tag = !qe_isblank(str[0]) && (cp->s->mode == syn || cp->s->mode == &htmlsrc_mode);
start = i;
//type_decl = 0;
c = 0;
style = 0;
if (i >= n)
goto the_end;
if (state) {
/* if already in a state, go directly in the code parsing it */
if (state & IN_C_COMMENT2)
goto parse_comment2;
switch (state & IN_C_STRING) {
case IN_C_STRING_D: goto parse_string;
case IN_C_STRING_Q: goto parse_string_q;
case IN_C_STRING_BQ: goto parse_string_bq;
}
if (state & IN_C_REGEX) {
goto parse_regex;
}
}
while (i < n) {
start = i;
c = str[i++];
switch (c) {
case '/':
if (str[i] == '*') {
/* C style multi-line comment */
i++;
state |= IN_C_COMMENT2;
parse_comment2:
style = C_STYLE_COMMENT;
level = (state & IN_C_COMMENT_LEVEL) >> IN_C_COMMENT_SHIFT;
while (i < n) {
if (str[i] == '/' && str[i + 1] == '*' && (mode_flags & CLANG_NEST_COMMENTS)) {
i += 2;
level++;
} else
if (str[i] == '*' && str[i + 1] == '/') {
i += 2;
if (level == 0) {
state &= ~IN_C_COMMENT2;
break;
}
level--;
} else {
i++;
}
}
state = (state & ~IN_C_COMMENT_LEVEL) |
(min_int(level, 7) << IN_C_COMMENT_SHIFT);
break;
} else
if (str[i] == '/') {
/* line comment */
state |= IN_C_COMMENT1;
style = C_STYLE_COMMENT;
i = n;
break;
}
continue;
case '#': /* preprocessor */
if (start == 0 && str[i] == '!') {
/* recognize a shebang comment line */
style = C_STYLE_PREPROCESS;
i = n;
break;
}
style = C_STYLE_COMMENT;
i = n;
break;
case '`': /* ECMA 6 template strings */
parse_string_bq:
state |= IN_C_STRING_BQ;
style = C_STYLE_STRING_BQ;
while (i < n) {
c = str[i++];
if (c == '`') {
state &= ~IN_C_STRING;
break;
}
}
break;
case '@': /* regular expression literal */
parse_regex:
state |= IN_C_REGEX;
style = C_STYLE_REGEX;
delim = '@';
while (i < n) {
c = str[i++];
if (c == '\\') {
if (i < n) {
i += 1;
}
} else
if (state & IN_C_CHARCLASS) {
if (c == ']') {
state &= ~IN_C_CHARCLASS;
}
} else {
if (c == '[') {
state |= IN_C_CHARCLASS;
} else
if (c == delim) {
while (qe_isalnum_(str[i])) {
i++;
}
state &= ~IN_C_REGEX;
break;
}
}
}
break;
case '\'': /* character constant */
parse_string_q:
state |= IN_C_STRING_Q;
style = C_STYLE_STRING_Q;
delim = '\'';
goto string;
case '\"': /* string literal */
parse_string:
state |= IN_C_STRING_D;
style = C_STYLE_STRING;
delim = '\"';
string:
while (i < n) {
c = str[i++];
if (c == '\\') {
if (i >= n)
break;
i++;
} else
if (c == delim) {
state &= ~IN_C_STRING;
break;
}
}
break;
case '=':
/* exit type declaration */
/* does not handle this: int i = 1, j = 2; */
//type_decl = 0;
continue;
case '(':
case '{':
tag = 0;
continue;
default:
if (qe_isdigit(c)) {
/* XXX: should parse actual number syntax */
/* decimal, binary, octal and hexadecimal literals:
* 1 0b1 0o1 0x1, case insensitive. 01 is a syntax error */
/* maybe ignore '_' in integers */
/* XXX: should parse decimal and hex floating point syntaxes */
while (qe_isalnum_(str[i]) || (str[i] == '.' && str[i + 1] != '.')) {
i++;
}
style = C_STYLE_NUMBER;
break;
}
if (qe_isalpha_(c)) {
i += ustr_get_identifier(kbuf, countof(kbuf), c, str, i, n);
if (cp->state_only && !tag)
continue;
/* keywords used as object property tags are regular identifiers */
if (strfind(syn->keywords, kbuf) &&
// XXX: this is incorrect for `default` inside a switch statement */
str[i] != ':' && (start == 0 || str[start - 1] != '.')) {
style = C_STYLE_KEYWORD;
break;
}
i1 = cp_skip_blanks(str, i, n);
if (str[i1] == '(') {
/* function call or definition */
style = C_STYLE_FUNCTION;
if (tag) {
/* tag function definition */
eb_add_tag(cp->b, cp->offset + start, kbuf);
tag = 0;
}
break;
} else
if (tag && qe_findchar("(,;=", str[i1])) {
/* tag variable definition */
eb_add_tag(cp->b, cp->offset + start, kbuf);
}
if ((start == 0 || str[start - 1] != '.')
&& !qe_findchar(".(:", str[i])
&& strfind(syn->types, kbuf)) {
/* if not cast, assume type declaration */
//type_decl = 1;
style = C_STYLE_TYPE;
break;
}
if (qe_isupper((unsigned char)kbuf[0])
&& (start >= 2 && str[start - 1] == ' ' && str[start - 2] == ':')) {
/* if type annotation and capitalized assume type name */
style = C_STYLE_TYPE;
break;
}
continue;
}
continue;
}
if (style) {
if (!cp->state_only) {
SET_STYLE(sbuf, start, i, style);
}
style = 0;
}
}
the_end:
if (state & (IN_C_COMMENT | IN_C_STRING)) {
/* set style on eol char */
SET_STYLE1(sbuf, n, style);
if ((state & IN_C_COMMENT) == IN_C_COMMENT1)
state &= ~IN_C_COMMENT1;
}
cp->colorize_state = state;
}
static ModeDef salmon_mode = {
.name = "Salmon",
//.alt_name = "js",
.extensions = "salm",
.shell_handlers = "salmoneye",
.colorize_func = salmon_colorize_line,
.colorize_flags = CLANG_SALMON | CLANG_REGEX | CLANG_NEST_COMMENTS,
.keywords = salmon_keywords,
.types = salmon_types,
.indent_func = c_indent_line,
.auto_indent = 1,
.fallback = &c_mode,
};
/*---------------- PPL: Christian Neumanns' Practical Programming Language ----------------*/
static const char ppl_keywords[] = {
/* language keywords */
"factory|service|functions|function|command|script|template|param|"
"record|enum|throw|as|type|inherit|creator|default|"
"java|java_header|end|var|variable|redefine|"
"and|or|xor|is|not|may|be|out_check|assert|this|const|"
"on_error|throw_error|att|attribute|attributes|"
"return|if|then|else|"
"when|otherwise|repeat|times|to|try|catch_any|on|"
"check|and_check|attributes_check|tests|test|verify|verify_error|"
"private|public|get|set|in|out|in_out|in_all|"
/* boolean and null literals */
"yes|no|null|void|"
};
static const char ppl_phrases[] = {
"repeat for each|repeat from|repeat while|repeat forever|"
"exit repeat|next repeat|"
"case type of|case enum of|case value of|case reference of|"
};
static const char ppl_types[] = {
"any|none|non_null|yes_no|character|string|regex|list|map|"
"signed_int_64|zero_neg_64|zero_pos_64|neg_64|pos_64|"
"signed_int_32|zero_neg_32|zero_pos_32|neg_32|pos_32|"
"signed_integer_64|zero_negative_64|zero_positive_64|negative_64|positive_64|"
"signed_integer_32|zero_negative_32|zero_positive_32|negative_32|positive_32|"
"float_64|float_32|number|"
};
enum {
PPL_STYLE_DEFAULT = 0,
PPL_STYLE_PREPROCESS = QE_STYLE_PREPROCESS,
PPL_STYLE_COMMENT = QE_STYLE_COMMENT,
PPL_STYLE_STRING = QE_STYLE_STRING,
PPL_STYLE_STRING_Q = QE_STYLE_STRING_Q,
PPL_STYLE_NUMBER = QE_STYLE_NUMBER,
PPL_STYLE_KEYWORD = QE_STYLE_KEYWORD,
PPL_STYLE_TYPE = QE_STYLE_TYPE,
PPL_STYLE_FUNCTION = QE_STYLE_FUNCTION,
};
enum {
IN_PPL_COMMENT = 0x03, /* one of the comment styles */
IN_PPL_COMMENT1 = 0x01, /* single line comment // ... EOL */
IN_PPL_COMMENT2 = 0x02, /* multiline PPL comment /// ... ./// */
IN_PPL_STRING = 0x1C, /* 3 bits for string styles */
IN_PPL_STRING_D = 0x04, /* double-quoted string */
IN_PPL_STRING_Q = 0x08, /* single-quoted string */
IN_PPL_STRING_D3 = 0x14, /* """ multiline quoted string with interpolation */
IN_PPL_STRING_Q3 = 0x18, /* ''' multiline quoted string */
IN_PPL_PREPROCESS = 0x20, /* preprocessor directive */
IN_PPL_COMMENT_SHIFT = 8, /* shift for block comment nesting level */
IN_PPL_COMMENT_LEVEL = 0x700, /* mask for block comment nesting level */
IN_PPL_JAVA = 0x800,
};
static int cp_match_keywords(const char32_t *str, int n, int start, const char *s, int *end) {
/*@API utils
Match a sequence of words from a | separated list of phrases.
A space in the string matches a non empty white space sequence in the source array.
Phrases are delimited by `|` characters.
@argument `str` a valid pointer to an array of codepoints
@argument `start` the index to the next codepoint
@argument `n` the length of the codepoint array
@argument `s` a valid pointer to a string containing phrases delimited by `|`.
@argument `end` a valid pointer to store the index of the codepoint after the end of a match.
@return a boolean success indicator.
*/
int i = start;
size_t j = 0;
for (;;) {
unsigned char cc = s[j++];
if (cc == '|' || cc == '\0') {
if (i == n || !qe_isalnum_(str[i])) {
*end = i;
return 1;
}
if (cc == '\0')
return 0;
i = start;
} else {
if (cc == ' ') {
int i1 = i;
i = cp_skip_blanks(str, i, n);
if (i > i1)
continue;
} else {
if (i < n && cc == str[i++])
continue;
}
for (;;) {
cc = s[j++];
if (cc == '\0')
return 0;
if (cc == '|')
break;
}
i = start;
}
}
}
static void ppl_colorize_line(QEColorizeContext *cp,
const char32_t *str, int n,
QETermStyle *sbuf, ModeDef *syn)
{
int i = 0, start, i1;
int indent = 0, style, level, type_decl;
char32_t c, delim, last;
char kbuf[64];
int state = cp->colorize_state;
indent = cp_skip_blanks(str, 0, n);
start = i;
type_decl = 0;
c = 0;
style = 0;
last = n > 0 ? str[n - 1] : 0;
kbuf[0] = '\0';
if (state) {
/* if already in a state, go directly in the code parsing it */
if (state & IN_PPL_JAVA) {
if (cp_match_keywords(str, n, 0, " end java", &i1)
|| cp_match_keywords(str, n, 0, " end java_header", &i1)) {
state = 0;
} else {
cp->colorize_state = state & ~IN_PPL_JAVA;
cp_colorize_line(cp, str, 0, n, sbuf, &java_mode);
state = cp->colorize_state | IN_PPL_JAVA;
i = n;
goto done;
}
}
if (state & IN_PPL_COMMENT2)
goto parse_comment2;
switch (state & IN_PPL_STRING) {
case IN_PPL_STRING_D: goto parse_string;
case IN_PPL_STRING_Q: goto parse_string_q;
case IN_PPL_STRING_D3: goto parse_string3;
case IN_PPL_STRING_Q3: goto parse_string_q3;
}
}
while (i < n) {
start = i;
c = str[i++];
switch (c) {
case ' ':
case '\t':
continue;
case '*':
if (start == indent && cp->partial_file)
goto parse_comment2;
goto normal;
case '/':
if (str[i] == '/') {
if (str[i + 1] == '/') {
/* PPL multi-line comment */
i += 2;
parse_comment2:
state |= IN_PPL_COMMENT2;
style = PPL_STYLE_COMMENT;
level = (state & IN_PPL_COMMENT_LEVEL) >> IN_PPL_COMMENT_SHIFT;
while (i < n) {
if (str[i] == '/' && str[i + 1] == '/' && str[i + 2] == '/') {
i += 3;
level++;
} else
if (str[i] == '.' && str[i + 1] == '/' && str[i + 2] == '/' && str[i + 3] == '/') {
i += 4;
if (level == 0) {
state &= ~IN_PPL_COMMENT2;
break;
}
level--;
} else {
i++;
}
}
state = (state & ~IN_PPL_COMMENT_LEVEL) |
(min_int(level, 7) << IN_PPL_COMMENT_SHIFT);
break;
} else {
/* line comment */
state |= IN_PPL_COMMENT1;
style = PPL_STYLE_COMMENT;
i = n;
break;
}
}
type_decl = 0; /* division operator */
continue;
case '%': /* template instantiation */
if (is_js_identifier_start(str[i])) {
c = str[i++];
i += get_js_identifier(kbuf, countof(kbuf), c, str, i, n);
style = PPL_STYLE_PREPROCESS;
}
type_decl = 0;
break;
case '\'': /* character constant */
if (str[i] == '\'' && str[i + 1] == '\'') {
/* multiline ''' quoted string */
i += 2;
state |= IN_PPL_STRING_Q3;
parse_string_q3:
style = PPL_STYLE_STRING_Q;
delim = '\'';
goto string3;
}
state |= IN_PPL_STRING_Q;
parse_string_q:
style = PPL_STYLE_STRING_Q;
delim = '\'';
goto string;
case '\"': /* string literal */
if (str[i] == '\"' && str[i + 1] == '\"') {
/* multiline """ quoted string */
i += 2;
state |= IN_PPL_STRING_D3;
goto parse_string3;
}
state |= IN_PPL_STRING_D;
parse_string:
style = PPL_STYLE_STRING;
delim = '\"';
string:
while (i < n) {
c = str[i++];
if (c == '\\') {
if (i >= n)
break;
i++;
} else
if (c == delim) {
state &= ~IN_PPL_STRING;
break;
}
}
type_decl = 0;
break;
parse_string3:
style = PPL_STYLE_STRING;
delim = '\"';
string3:
while (i < n) {
c = str[i++];
// XXX: should detect and colorize {{ expression }}
if (c == delim && str[i] == delim && str[i + 1] == delim) {
i += 2;
if (str[i] == delim)
i++;
state &= ~IN_PPL_STRING;
break;
}
}
type_decl = 0;
break;
case '-':
if (str[i] == '>') { /* function return type */
i++;
type_decl = 1;
style = PPL_STYLE_KEYWORD;
break;
}
type_decl = 0; /* subtraction operator */
continue;
case '<':
if (str[i] != '=' && type_decl == 2)
type_decl = 1;
else
type_decl = 0;
continue;
case '>':
if (!(str[i] != '=' && type_decl == 2))
type_decl = 0;
continue;
case '#':
if (start == 0 && str[i] == '!') {
/* recognize a shebang comment line */
style = PPL_STYLE_PREPROCESS;
i = n;
break;
}
FALLTHROUGH;
case '=':
if (str[i] == 'v' || str[i] == 'r')
i++;
type_decl = 0;
continue;
case ':':
if (strequal(kbuf, "type"))
type_decl = 1;
else
type_decl = 0;
continue;
case '.':
type_decl = 0;
if (start == indent && i == n) {
style = PPL_STYLE_KEYWORD;
break;
}
continue;
default:
normal:
if (qe_isdigit(c)) {
/* XXX: should parse actual number syntax */
while (qe_isalnum(str[i]) ||
(str[i] == '.' && qe_isdigit(str[i + 1])) ||
((str[i] == '+' || str[i] == '-') &&
qe_tolower(str[i - 1]) == 'e' &&
qe_isdigit(str[i + 1])))
{
i++;
}
style = PPL_STYLE_NUMBER;
break;
}
if (is_js_identifier_start(c)) {
if (start == indent && cp_match_keywords(str, n, i - 1, ppl_phrases, &i)) {
style = PPL_STYLE_KEYWORD;
type_decl = 0;
break;
}
i += get_js_identifier(kbuf, countof(kbuf), c, str, i, n);
if (cp->state_only)
continue;
if (strfind(syn->keywords, kbuf) || str[i] == ':') {
if (strequal(kbuf, "null") && type_decl == 1) {
// null as a type
} else {
style = PPL_STYLE_KEYWORD;
if (strfind("on|factory|type|when|inherit", kbuf)) {
type_decl = 1;
} else
if (strequal(kbuf, "or") && type_decl == 2) {
type_decl = 1;
} else {
type_decl = 0;
}
if (start == indent
&& strfind("function|creator|command|template|service|factory|type", kbuf))
{
int fstart = cp_skip_blanks(str, i, n);
if (get_js_identifier(kbuf, countof(kbuf), 0, str, fstart, n))
eb_add_tag(cp->b, cp->offset + start, kbuf);
} else
if (start == indent && strfind("java|java_header", kbuf)) {
state |= IN_PPL_JAVA;
}
break;
}
}
type_decl++;
i1 = cp_skip_blanks(str, i, n);
if (str[i1] == '(') {
/* function call or definition */
style = PPL_STYLE_FUNCTION;
type_decl = 0;
break;
}
if (type_decl == 2) {
style = PPL_STYLE_TYPE;
break;
}
if (strfind(syn->types, kbuf)) {
style = PPL_STYLE_TYPE;
break;
}
continue;
}
type_decl = 0;
continue;
}
if (style) {
if (!cp->state_only) {
SET_STYLE(sbuf, start, i, style);
}
style = 0;
}
}
if (state & (IN_PPL_COMMENT | IN_PPL_STRING)) {
/* set style on eol char */
SET_STYLE1(sbuf, n, style);
if ((state & IN_PPL_COMMENT) == IN_PPL_COMMENT1)
state &= ~IN_PPL_COMMENT1;
} else {
if (last != '\\' && last != '&') {
// should line continuation extend single line comment?
state &= ~IN_PPL_PREPROCESS;
}
}
done:
cp->colorize_state = state;
}
static ModeDef ppl_mode = {
.name = "PPL",
.extensions = "ppl",
.shell_handlers = "ppl",
.colorize_func = ppl_colorize_line,
.colorize_flags = CLANG_PPL,
.keywords = ppl_keywords,
.types = ppl_types,
.indent_func = c_indent_line, // not really appropriate
.auto_indent = 1,
.fallback = &c_mode,
};
#endif /* CONFIG_TINY */
/*---------------- SerenityOS yakt programming language ----------------*/
static const char jakt_keywords[] = {
"and|anon|boxed|break|catch|class|continue|cpp|defer|else|enum|"
"extern|false|for|fn|comptime|if|import|in|is|let|loop|match|"
"must|mut|namespace|not|or|private|public|raw|return|restricted|"
"struct|this|throw|throws|true|try|unsafe|weak|while|yield|guard|"
"as|never|null|forall|type|trait|requires|implements"
};
static const char jakt_types[] = {
"bool|i8|i16|i32|i64|u8|u16|u32|u64|f32|f64|usize|c_int|c_char|void|"
/* could use CLANG_CAP_TYPE but would not match TT */
"[A-Z][A-Za-z0-9]+"
};
// FIXME: Should support Attributes: start="#!\[" end="\]"
static ModeDef jakt_mode = {
.name = "Jakt",
.extensions = "jakt",
.colorize_func = c_colorize_line,
.colorize_flags = CLANG_JAKT,
.keywords = jakt_keywords,
.types = jakt_types,
.indent_func = c_indent_line,
.auto_indent = 1,
};
/*---------------- Christoffer Lerno's C3 programming language (c3-lang.org) ----------------*/
static const char c3_keywords[] = {
"asm|assert|bitstruct|break|case|catch|const|continue|def|"
"default|defer|distinct|do|else|enum|extern|false|fault|"
"for|foreach|foreach_r|fn|if|inline|interface|import|macro|"
"module|nextcase|null|return|static|struct|switch|tlocal|true|"
"try|union|var|while|"
/* directives */
"$alignof|$and|$append|$assert|$assignable|$case|$concat|$default|"
"$defined|$echo|$else|$embed|$endfor|$endforeach|$endif|$endswitch|"
"$eval|$error|$exec|$extnameof|$feature|$for|$foreach|$if|$include|"
"$is_const|$nameof|$offsetof|$or|$qnameof|$sizeof|$stringify|"
"$switch|$typefrom|$typeof|$stringify|$vaarg|$vaconst|$vacount|"
"$vaexpr|$varef|$vasplat|$vatype|"
};
static const char c3_types[] = {
"void|bool|ichar|char|"
// Integer types
"short|ushort|int|uint|long|ulong|int128|uint128|iptr|uptr|isz|usz|"
// Floating point types
"float128|double|float|bfloat|float16|"
// Other types
"any|anyfault|typeid|"
// C compatibility types
"CChar|CShort|CUShort|CInt|CUInt|CLong|CULong|CLongLong|CULongLong|CFloat|CDouble|CLongDouble|"
// CT types
"$typefrom|$tyypeof|$vatype|"
/* could use CLANG_CAP_TYPE but would not match TT */
"[A-Z][A-Za-z0-9]+"
};
enum {
C3_STYLE_DEFAULT = 0,
C3_STYLE_PREPROCESS = QE_STYLE_PREPROCESS,
C3_STYLE_COMMENT = QE_STYLE_COMMENT,
C3_STYLE_STRING = QE_STYLE_STRING,
C3_STYLE_STRING_Q = QE_STYLE_STRING_Q,
C3_STYLE_STRING_BQ = QE_STYLE_STRING,
C3_STYLE_NUMBER = QE_STYLE_NUMBER,
C3_STYLE_KEYWORD = QE_STYLE_KEYWORD,
C3_STYLE_TYPE = QE_STYLE_TYPE,
C3_STYLE_FUNCTION = QE_STYLE_FUNCTION,
C3_STYLE_VARIABLE = QE_STYLE_VARIABLE,
};
/* c-mode colorization states */
enum {
IN_C3_COMMENT = 0x03, /* one of the comment styles */
IN_C3_COMMENT1 = 0x01, /* single line comment with \ at EOL */
IN_C3_COMMENT2 = 0x02, /* multiline C3 comment */
IN_C3_STRING_BQ = 0x04, /* back-quoted string */
IN_C3_CONTRACT1 = 0x40, /* in the beginning of a contracts specification */
IN_C3_CONTRACT2 = 0x80, /* in the contracts part of a contracts specification */
IN_C3_CONTRACTS = 0xC0, /* in a contracts specification */
IN_C3_COMMENT_SHIFT = 8, /* shift for block comment nesting level */
IN_C3_COMMENT_LEVEL = 0x700, /* mask for block comment nesting level */
};
// FIXME: clean this, keep only C3 features
static void c3_colorize_line(QEColorizeContext *cp,
const char32_t *str, int n,
QETermStyle *sbuf, ModeDef *syn)
{
int i = 0, start, i1, i2, indent;
int style, level, tag;
char32_t c, delim;
char kbuf[64];
int state = cp->colorize_state;
//int type_decl; /* unused */
indent = cp_skip_blanks(str, 0, n);
tag = !indent && cp->s->mode == syn;
start = i;
//type_decl = 0;
c = 0;
style = 0;
if (i >= n)
goto the_end;
if (state) {
/* if already in a state, go directly in the code parsing it */
if (state & IN_C3_COMMENT2)
goto parse_comment2;
if (state & IN_C3_STRING_BQ)
goto parse_string_bq;
if (state & IN_C3_CONTRACT1)
goto parse_contracts;
}
while (i < n) {
start = i;
reswitch:
c = str[i++];
switch (c) {
case '*':
if ((state & IN_C3_CONTRACTS) && str[i] == '>') {
i += 2;
state &= ~IN_C3_CONTRACTS;
style = C3_STYLE_PREPROCESS;
break;
}
/* lone star at the beginning of a line in a shell buffer
* is treated as a comment start. This improves colorization
* of diff and git output.
*/
if (start == indent && cp->partial_file
&& (i == n || str[i] == ' ' || str[i] == '/')) {
i--;
goto parse_comment2;
}
continue;
case '/':
if (str[i] == '*') {
/* C style multi-line comment */
i++;
parse_comment2:
state |= IN_C3_COMMENT2;
style = C3_STYLE_COMMENT;
level = (state & IN_C3_COMMENT_LEVEL) >> IN_C3_COMMENT_SHIFT;
while (i < n) {
if (str[i] == '/' && str[i + 1] == '*') {
i += 2;
level++;
} else
if (str[i] == '*' && str[i + 1] == '/') {
i += 2;
if (level == 0) {
state &= ~IN_C3_COMMENT2;
break;
}
level--;
} else {
i++;
}
}
state = (state & ~IN_C3_COMMENT_LEVEL) |
(min_int(level, 7) << IN_C3_COMMENT_SHIFT);
break;
} else
if (str[i] == '/') {
/* line comment */
style = C3_STYLE_COMMENT;
i = n;
break;
}
continue;
case '#': /* preprocessor */
if (start == 0 && str[i] == '!') {
/* recognize a shebang comment line */
style = C3_STYLE_PREPROCESS;
i = n;
break;
}
continue;
case '@': /* annotations or constraints */
i += get_js_identifier(kbuf, countof(kbuf), c, str, i, n);
style = C3_STYLE_PREPROCESS;
break;
case '`':
parse_string_bq:
state |= IN_C3_STRING_BQ;
style = C3_STYLE_STRING_BQ;
while (i < n) {
c = str[i++];
if (c == '`' && str[i] != '`') {
state &= ~IN_C3_STRING_BQ;
break;
}
}
break;
case '\'': /* character constant */
style = C3_STYLE_STRING_Q;
delim = '\'';
goto string;
case '\"': /* string literal */
style = C3_STYLE_STRING;
delim = '\"';
string:
while (i < n) {
c = str[i++];
if (c == '\\') {
if (i >= n)
break;
i++;
} else
if (c == delim) {
break;
}
}
break;
case '=':
/* exit type declaration */
/* does not handle this: int i = 1, j = 2; */
//type_decl = 0;
tag = 0;
continue;
case '<':
if (!(state & IN_C3_CONTRACTS) && str[i] == '*') {
state |= IN_C3_CONTRACT1;
i++;
SET_STYLE(sbuf, start, i, C3_STYLE_PREPROCESS);
start = i;
parse_contracts:
/* C3 contracts initial part */
while (i < n && qe_isspace(str[i]))
i++;
style = C3_STYLE_COMMENT;
if (str[i] == '@' && qe_islower(str[i + 1])) {
state |= IN_C3_CONTRACT2;
break;
}
while (i < n && (str[i] != '*' || str[i + 1] != '>'))
i++;
break;
}
continue;
case '[':
if (str[i] == '<') // LVEC token
i++;
continue;
case '(':
if (str[i] == '<') // LGENPAR token
i++;
tag = 0;
continue;
case '{':
tag = 0;
continue;
default:
if (qe_isdigit(c)) {
/* XXX: should parse actual number syntax */
/* decimal, binary, octal and hexadecimal literals:
* 1 0b1 0o1 0x1, case insensitive. 01 is a syntax error */
/* ignore '_' between digits */
/* XXX: should parse decimal and hex floating point syntaxes */
while (qe_isalnum_(str[i]) || (str[i] == '.' && str[i + 1] != '.')) {
i++;
}
style = C3_STYLE_NUMBER;
break;
}
if (is_js_identifier_start(c)) {
i += get_js_identifier(kbuf, countof(kbuf), c, str, i, n);
if (cp->state_only)
continue;
if (str[i] == '\'' || str[i] == '\"') {
/* check for encoding prefix */
if (strfind("x|b64", kbuf))
goto reswitch;
}
/* keywords used as object property tags are regular identifiers.
* `default` is always considered a keyword as the context cannot be
* determined precisely by this simplistic lexical parser */
if (strfind(syn->keywords, kbuf)
&& (str[i] != ':' || strequal(kbuf, "default") || strequal(kbuf, "$default"))
&& (start == 0 || str[start - 1] != '.')) {
if (*kbuf == '$')
style = C3_STYLE_PREPROCESS;
else
style = C3_STYLE_KEYWORD;
break;
}
i1 = cp_skip_blanks(str, i, n);
if (str[i1] == '(') {
/* function call or definition */
style = C3_STYLE_FUNCTION;
if (tag) {
/* tag function definition */
eb_add_tag(cp->b, cp->offset + start, kbuf);
tag = 0;
}
break;
} else
if (tag && qe_findchar("(,;=", str[i1])) {
/* tag variable definition */
eb_add_tag(cp->b, cp->offset + start, kbuf);
}
if ((start == 0 || str[start - 1] != '.')
&& !qe_findchar(".(:", str[i])
&& strfind(syn->types, kbuf)) {
/* if not cast, assume type declaration */
//type_decl = 1;
style = C3_STYLE_TYPE;
break;
}
if (qe_isupper((unsigned char)kbuf[0])) {
for (i2 = 1; kbuf[i2]; i2++) {
if (qe_islower((unsigned char)kbuf[i2]))
break;
}
/* if capitalized but not all caps assume type name */
if (kbuf[i2]) {
style = C3_STYLE_TYPE;
break;
}
}
continue;
}
continue;
}
if (style) {
if (!cp->state_only) {
SET_STYLE(sbuf, start, i, style);
}
style = 0;
}
}
the_end:
if (style == C3_STYLE_COMMENT || (state & IN_C3_STRING_BQ)) {
/* set style on eol char */
SET_STYLE1(sbuf, n, style);
}
cp->colorize_state = state;
}
static ModeDef c3_mode = {
.name = "C3",
.extensions = "c3|c3i|c3t",
.colorize_func = c3_colorize_line,
.colorize_flags = CLANG_C3 | CLANG_NEST_COMMENTS,
.keywords = c3_keywords,
.types = c3_types,
.indent_func = c_indent_line,
.auto_indent = 1,
};
/*---------------- Common initialization code ----------------*/
static int c_init(QEmacsState *qs)
{
qe_register_mode(qs, &c_mode, MODEF_SYNTAX);
qe_register_commands(qs, &c_mode, c_commands, countof(c_commands));
qe_register_mode(qs, &cpp_mode, MODEF_SYNTAX);
qe_register_mode(qs, &js_mode, MODEF_SYNTAX);
qe_register_mode(qs, &json_mode, MODEF_SYNTAX);
qe_register_mode(qs, &ts_mode, MODEF_SYNTAX);
qe_register_mode(qs, &java_mode, MODEF_SYNTAX);
qe_register_mode(qs, &php_mode, MODEF_SYNTAX);
qe_register_mode(qs, &go_mode, MODEF_SYNTAX);
qe_register_mode(qs, &yacc_mode, MODEF_SYNTAX);
qe_register_mode(qs, &lex_mode, MODEF_SYNTAX);
qe_register_mode(qs, &csharp_mode, MODEF_SYNTAX);
qe_register_mode(qs, &c2_mode, MODEF_SYNTAX);
#ifndef CONFIG_TINY
qe_register_mode(qs, &v8_mode, MODEF_SYNTAX);
qe_register_mode(qs, &bee_mode, MODEF_SYNTAX);
qe_register_mode(qs, &idl_mode, MODEF_SYNTAX);
qe_register_mode(qs, &carbon_mode, MODEF_SYNTAX);
qe_register_mode(qs, &objc_mode, MODEF_SYNTAX);
qe_register_mode(qs, &awk_mode, MODEF_SYNTAX);
qe_register_mode(qs, &css_mode, MODEF_SYNTAX);
qe_register_mode(qs, &less_mode, MODEF_SYNTAX);
qe_register_mode(qs, &jspp_mode, MODEF_SYNTAX);
qe_register_mode(qs, &koka_mode, MODEF_SYNTAX);
qe_register_mode(qs, &as_mode, MODEF_SYNTAX);
qe_register_mode(qs, &scala_mode, MODEF_SYNTAX);
qe_register_mode(qs, &d_mode, MODEF_SYNTAX);
qe_register_mode(qs, &limbo_mode, MODEF_SYNTAX);
qe_register_mode(qs, &cyclone_mode, MODEF_SYNTAX);
qe_register_mode(qs, &ch_mode, MODEF_SYNTAX);
qe_register_mode(qs, &squirrel_mode, MODEF_SYNTAX);
qe_register_mode(qs, &ici_mode, MODEF_SYNTAX);
qe_register_mode(qs, &jsx_mode, MODEF_SYNTAX);
qe_register_mode(qs, &haxe_mode, MODEF_SYNTAX);
qe_register_mode(qs, &dart_mode, MODEF_SYNTAX);
qe_register_mode(qs, &pike_mode, MODEF_SYNTAX);
qe_register_mode(qs, &idl_mode, MODEF_SYNTAX);
qe_register_mode(qs, &calc_mode, MODEF_SYNTAX);
qe_register_mode(qs, &enscript_mode, MODEF_SYNTAX);
qe_register_mode(qs, &qscript_mode, MODEF_SYNTAX);
qe_register_mode(qs, &ec_mode, MODEF_SYNTAX);
qe_register_mode(qs, &sl_mode, MODEF_SYNTAX);
qe_register_mode(qs, &csl_mode, MODEF_SYNTAX);
qe_register_mode(qs, &neko_mode, MODEF_SYNTAX);
qe_register_mode(qs, &nml_mode, MODEF_SYNTAX);
qe_register_mode(qs, &alloy_mode, MODEF_SYNTAX);
qe_register_mode(qs, &scilab_mode, MODEF_SYNTAX);
qe_register_mode(qs, &kotlin_mode, MODEF_SYNTAX);
qe_register_mode(qs, &cbang_mode, MODEF_SYNTAX);
qe_register_mode(qs, &vala_mode, MODEF_SYNTAX);
qe_register_mode(qs, &pawn_mode, MODEF_SYNTAX);
qe_register_mode(qs, &cminus_mode, MODEF_SYNTAX);
qe_register_mode(qs, &gmscript_mode, MODEF_SYNTAX);
qe_register_mode(qs, &wren_mode, MODEF_SYNTAX);
qe_register_mode(qs, &jack_mode, MODEF_SYNTAX);
qe_register_mode(qs, &smac_mode, MODEF_SYNTAX);
qe_register_mode(qs, &v_mode, MODEF_SYNTAX);
qe_register_mode(qs, &protobuf_mode, MODEF_SYNTAX);
qe_register_mode(qs, &odin_mode, MODEF_SYNTAX);
qe_register_mode(qs, &salmon_mode, MODEF_SYNTAX);
qe_register_mode(qs, &ppl_mode, MODEF_SYNTAX);
qe_register_mode(qs, &jakt_mode, MODEF_SYNTAX);
qe_register_mode(qs, &c3_mode, MODEF_SYNTAX);
#endif
return 0;
}
qe_module_init(c_init);