* 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
4814 lines
157 KiB
C
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);
|