feat: lua syntax highlighting in text editor
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* main.cpp
|
||||
* MontaukOS Text Editor - standalone Window Server app
|
||||
* Single-buffer text editor with line numbers, cursor, scrolling, file I/O,
|
||||
* syntax highlighting for C files
|
||||
* syntax highlighting for C and Lua files
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
@@ -39,6 +39,7 @@ static constexpr int MAX_CAP = 262144; // 256KB
|
||||
static constexpr int MAX_LINES = 16384;
|
||||
static constexpr int TAB_WIDTH = 4;
|
||||
static constexpr int FONT_SIZE = 16;
|
||||
static constexpr int SYN_LINE_BUF_CAP = 1024;
|
||||
|
||||
// Colors
|
||||
static constexpr Color BG_COLOR = Color::from_rgb(0xFF, 0xFF, 0xFF);
|
||||
@@ -105,7 +106,7 @@ int g_pathbar_len = 0;
|
||||
int g_pathbar_cursor = 0;
|
||||
|
||||
// Syntax highlighting
|
||||
bool g_syntax_active = false;
|
||||
SynLanguage g_syntax_language = SYN_LANG_NONE;
|
||||
|
||||
// ============================================================================
|
||||
// Pixel drawing helpers
|
||||
@@ -466,7 +467,7 @@ static void load_file(const char* path) {
|
||||
else
|
||||
montauk::strncpy(g_filename, path, 63);
|
||||
|
||||
g_syntax_active = syn_is_c_file(g_filepath);
|
||||
g_syntax_language = syn_detect_language(g_filepath);
|
||||
|
||||
recompute_lines();
|
||||
update_cursor_pos();
|
||||
@@ -613,15 +614,14 @@ static void render(uint32_t* pixels) {
|
||||
|
||||
int text_start_x = LINE_NUM_W + 4;
|
||||
|
||||
// Syntax highlighting: compute block comment state up to first visible line
|
||||
bool syn_block_comment = false;
|
||||
SynToken syn_line_buf[1024];
|
||||
if (g_syntax_active) {
|
||||
// Syntax highlighting: compute multi-line syntax state up to the first visible line
|
||||
SynState syn_state = syn_make_state();
|
||||
SynToken syn_line_buf[SYN_LINE_BUF_CAP];
|
||||
if (g_syntax_language != SYN_LANG_NONE) {
|
||||
for (int i = 0; i < g_scroll_y && i < g_line_count; i++) {
|
||||
int ls = g_line_offsets[i];
|
||||
int ll = line_length(i);
|
||||
if (ll > 1024) ll = 1024;
|
||||
syn_highlight_line(g_buffer + ls, ll, syn_line_buf, syn_block_comment);
|
||||
syn_highlight_line(g_buffer + ls, ll, nullptr, 0, g_syntax_language, syn_state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,9 +653,10 @@ static void render(uint32_t* pixels) {
|
||||
int line_len = line_length(line);
|
||||
|
||||
// Syntax highlight this line
|
||||
int syn_len = line_len > 1024 ? 1024 : line_len;
|
||||
if (g_syntax_active)
|
||||
syn_highlight_line(g_buffer + line_start, syn_len, syn_line_buf, syn_block_comment);
|
||||
int syn_len = line_len > SYN_LINE_BUF_CAP ? SYN_LINE_BUF_CAP : line_len;
|
||||
if (g_syntax_language != SYN_LANG_NONE)
|
||||
syn_highlight_line(g_buffer + line_start, line_len, syn_line_buf,
|
||||
SYN_LINE_BUF_CAP, g_syntax_language, syn_state);
|
||||
|
||||
for (int ci = 0; ci < line_len; ci++) {
|
||||
int px_x = text_start_x + ci * cell_w - g_scroll_x;
|
||||
@@ -674,7 +675,7 @@ static void render(uint32_t* pixels) {
|
||||
}
|
||||
|
||||
Color char_color = TEXT_COLOR;
|
||||
if (g_syntax_active && ci < syn_len)
|
||||
if (g_syntax_language != SYN_LANG_NONE && ci < syn_len)
|
||||
char_color = syn_color(syn_line_buf[ci]);
|
||||
|
||||
if (ch >= 32 || ch < 0) {
|
||||
@@ -768,7 +769,7 @@ static void pathbar_confirm(int win_id) {
|
||||
for (int i = 0; g_pathbar_text[i]; i++)
|
||||
if (g_pathbar_text[i] == '/') name = g_pathbar_text + i + 1;
|
||||
montauk::strncpy(g_filename, name, 63);
|
||||
g_syntax_active = syn_is_c_file(g_filepath);
|
||||
g_syntax_language = syn_detect_language(g_filepath);
|
||||
save_file();
|
||||
} else {
|
||||
load_file(g_pathbar_text);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* syntax_highlight.hpp
|
||||
* C syntax highlighting for the MontaukOS text editor
|
||||
* Activated for .c and .h files
|
||||
* C and Lua syntax highlighting for the MontaukOS text editor
|
||||
* Activated for .c, .h, and .lua files
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
|
||||
using namespace gui;
|
||||
|
||||
enum SynLanguage : uint8_t {
|
||||
SYN_LANG_NONE,
|
||||
SYN_LANG_C,
|
||||
SYN_LANG_LUA,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Token types
|
||||
// ============================================================================
|
||||
@@ -27,6 +33,12 @@ enum SynToken : uint8_t {
|
||||
SYN_OPERATOR,
|
||||
};
|
||||
|
||||
struct SynState {
|
||||
bool in_block_comment;
|
||||
SynToken long_token;
|
||||
int long_bracket_eqs;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Colors for each token type
|
||||
// ============================================================================
|
||||
@@ -65,6 +77,19 @@ inline bool syn_is_hex(char c) {
|
||||
return syn_is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
inline void syn_set_token(SynToken* out, int out_len, int idx, SynToken tok) {
|
||||
if (out && idx >= 0 && idx < out_len)
|
||||
out[idx] = tok;
|
||||
}
|
||||
|
||||
inline void syn_fill_tokens(SynToken* out, int out_len, int start, int end, SynToken tok) {
|
||||
if (!out) return;
|
||||
if (start < 0) start = 0;
|
||||
if (end > out_len) end = out_len;
|
||||
for (int i = start; i < end; i++)
|
||||
out[i] = tok;
|
||||
}
|
||||
|
||||
inline bool syn_streq(const char* buf, int len, const char* kw) {
|
||||
int i = 0;
|
||||
while (i < len && kw[i]) {
|
||||
@@ -74,7 +99,7 @@ inline bool syn_streq(const char* buf, int len, const char* kw) {
|
||||
return i == len && kw[i] == '\0';
|
||||
}
|
||||
|
||||
inline SynToken syn_classify_word(const char* buf, int len) {
|
||||
inline SynToken syn_classify_c_word(const char* buf, int len) {
|
||||
// C keywords
|
||||
static const char* keywords[] = {
|
||||
"auto", "break", "case", "const", "continue", "default", "do",
|
||||
@@ -102,33 +127,150 @@ inline SynToken syn_classify_word(const char* buf, int len) {
|
||||
return SYN_NORMAL;
|
||||
}
|
||||
|
||||
inline SynToken syn_classify_lua_word(const char* buf, int len) {
|
||||
static const char* keywords[] = {
|
||||
"and", "break", "do", "else", "elseif", "end", "false",
|
||||
"for", "function", "goto", "if", "in", "local", "nil",
|
||||
"not", "or", "repeat", "return", "then", "true",
|
||||
"until", "while",
|
||||
};
|
||||
static const char* builtins[] = {
|
||||
"_ENV", "_G", "_VERSION",
|
||||
"assert", "collectgarbage", "coroutine", "debug", "dofile",
|
||||
"error", "getmetatable", "io", "ipairs", "load", "loadfile",
|
||||
"math", "next", "os", "package", "pairs", "pcall",
|
||||
"print", "rawequal", "rawget", "rawlen", "rawset", "require",
|
||||
"select", "setmetatable", "string", "table", "tonumber",
|
||||
"tostring", "type", "utf8", "warn", "xpcall",
|
||||
};
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(keywords) / sizeof(keywords[0])); i++) {
|
||||
if (syn_streq(buf, len, keywords[i])) return SYN_KEYWORD;
|
||||
}
|
||||
for (int i = 0; i < (int)(sizeof(builtins) / sizeof(builtins[0])); i++) {
|
||||
if (syn_streq(buf, len, builtins[i])) return SYN_TYPE;
|
||||
}
|
||||
return SYN_NORMAL;
|
||||
}
|
||||
|
||||
inline SynToken syn_classify_word(SynLanguage lang, const char* buf, int len) {
|
||||
switch (lang) {
|
||||
case SYN_LANG_C: return syn_classify_c_word(buf, len);
|
||||
case SYN_LANG_LUA: return syn_classify_lua_word(buf, len);
|
||||
default: return SYN_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
inline SynState syn_make_state() {
|
||||
SynState state = {};
|
||||
state.in_block_comment = false;
|
||||
state.long_token = SYN_NORMAL;
|
||||
state.long_bracket_eqs = -1;
|
||||
return state;
|
||||
}
|
||||
|
||||
inline bool syn_match_lua_long_bracket_open(const char* line, int len, int i,
|
||||
int& eqs, int& span) {
|
||||
if (i >= len || line[i] != '[') return false;
|
||||
int j = i + 1;
|
||||
while (j < len && line[j] == '=') j++;
|
||||
if (j < len && line[j] == '[') {
|
||||
eqs = j - i - 1;
|
||||
span = j - i + 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool syn_match_lua_long_bracket_close(const char* line, int len, int i,
|
||||
int eqs, int& span) {
|
||||
if (i >= len || line[i] != ']') return false;
|
||||
int j = i + 1;
|
||||
for (int k = 0; k < eqs; k++) {
|
||||
if (j >= len || line[j] != '=') return false;
|
||||
j++;
|
||||
}
|
||||
if (j < len && line[j] == ']') {
|
||||
span = j - i + 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void syn_consume_number(const char* line, int len, int& i,
|
||||
SynToken* out, int out_len, bool c_style_suffixes) {
|
||||
int start = i;
|
||||
|
||||
if (line[i] == '0' && i + 1 < len && (line[i + 1] == 'x' || line[i + 1] == 'X')) {
|
||||
i += 2;
|
||||
while (i < len && syn_is_hex(line[i])) i++;
|
||||
if (i < len && line[i] == '.' && !(i + 1 < len && line[i + 1] == '.')) {
|
||||
i++;
|
||||
while (i < len && syn_is_hex(line[i])) i++;
|
||||
}
|
||||
if (i < len && (line[i] == 'p' || line[i] == 'P')) {
|
||||
int exp = i + 1;
|
||||
if (exp < len && (line[exp] == '+' || line[exp] == '-')) exp++;
|
||||
if (exp < len && syn_is_digit(line[exp])) {
|
||||
i = exp + 1;
|
||||
while (i < len && syn_is_digit(line[i])) i++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (line[i] == '.') i++;
|
||||
while (i < len && syn_is_digit(line[i])) i++;
|
||||
if (i < len && line[i] == '.' && !(i + 1 < len && line[i + 1] == '.')) {
|
||||
i++;
|
||||
while (i < len && syn_is_digit(line[i])) i++;
|
||||
}
|
||||
if (i < len && (line[i] == 'e' || line[i] == 'E')) {
|
||||
int exp = i + 1;
|
||||
if (exp < len && (line[exp] == '+' || line[exp] == '-')) exp++;
|
||||
if (exp < len && syn_is_digit(line[exp])) {
|
||||
i = exp + 1;
|
||||
while (i < len && syn_is_digit(line[i])) i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c_style_suffixes) {
|
||||
while (i < len && (line[i] == 'u' || line[i] == 'U' ||
|
||||
line[i] == 'l' || line[i] == 'L' ||
|
||||
line[i] == 'f' || line[i] == 'F'))
|
||||
i++;
|
||||
}
|
||||
|
||||
syn_fill_tokens(out, out_len, start, i, SYN_NUMBER);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Per-line highlighter
|
||||
// ============================================================================
|
||||
//
|
||||
// Fills `out[]` with SynToken values for each character in the line.
|
||||
// `in_block_comment` is the multi-line comment state carried across lines:
|
||||
// `state` carries the multi-line syntax state across lines:
|
||||
// - pass in the state from the previous line
|
||||
// - on return, updated for the next line
|
||||
//
|
||||
// `line` points to the first char of the line, `len` is its length
|
||||
// (excluding the newline). `out` must have room for `len` entries.
|
||||
// (excluding the newline). `out` can be null if only state tracking is needed.
|
||||
|
||||
inline void syn_highlight_line(const char* line, int len, SynToken* out, bool& in_block_comment) {
|
||||
inline void syn_highlight_line_c(const char* line, int len, SynToken* out, int out_len,
|
||||
SynState& state) {
|
||||
int i = 0;
|
||||
|
||||
while (i < len) {
|
||||
// ---- Block comment continuation ----
|
||||
if (in_block_comment) {
|
||||
if (state.in_block_comment) {
|
||||
while (i < len) {
|
||||
if (i + 1 < len && line[i] == '*' && line[i + 1] == '/') {
|
||||
out[i] = SYN_COMMENT;
|
||||
out[i + 1] = SYN_COMMENT;
|
||||
syn_set_token(out, out_len, i, SYN_COMMENT);
|
||||
syn_set_token(out, out_len, i + 1, SYN_COMMENT);
|
||||
i += 2;
|
||||
in_block_comment = false;
|
||||
state.in_block_comment = false;
|
||||
break;
|
||||
}
|
||||
out[i] = SYN_COMMENT;
|
||||
syn_set_token(out, out_len, i, SYN_COMMENT);
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
@@ -138,15 +280,15 @@ inline void syn_highlight_line(const char* line, int len, SynToken* out, bool& i
|
||||
|
||||
// ---- Line comment ----
|
||||
if (c == '/' && i + 1 < len && line[i + 1] == '/') {
|
||||
while (i < len) out[i++] = SYN_COMMENT;
|
||||
syn_fill_tokens(out, out_len, i, len, SYN_COMMENT);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- Block comment start ----
|
||||
if (c == '/' && i + 1 < len && line[i + 1] == '*') {
|
||||
in_block_comment = true;
|
||||
out[i] = SYN_COMMENT;
|
||||
out[i + 1] = SYN_COMMENT;
|
||||
state.in_block_comment = true;
|
||||
syn_set_token(out, out_len, i, SYN_COMMENT);
|
||||
syn_set_token(out, out_len, i + 1, SYN_COMMENT);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
@@ -162,30 +304,30 @@ inline void syn_highlight_line(const char* line, int len, SynToken* out, bool& i
|
||||
while (i < len) {
|
||||
// Handle line-comment inside preprocessor
|
||||
if (i + 1 < len && line[i] == '/' && line[i + 1] == '/') {
|
||||
while (i < len) out[i++] = SYN_COMMENT;
|
||||
syn_fill_tokens(out, out_len, i, len, SYN_COMMENT);
|
||||
break;
|
||||
}
|
||||
// Handle block comment start inside preprocessor
|
||||
if (i + 1 < len && line[i] == '/' && line[i + 1] == '*') {
|
||||
in_block_comment = true;
|
||||
out[i] = SYN_COMMENT;
|
||||
out[i + 1] = SYN_COMMENT;
|
||||
state.in_block_comment = true;
|
||||
syn_set_token(out, out_len, i, SYN_COMMENT);
|
||||
syn_set_token(out, out_len, i + 1, SYN_COMMENT);
|
||||
i += 2;
|
||||
// Continue consuming as comment
|
||||
while (i < len) {
|
||||
if (i + 1 < len && line[i] == '*' && line[i + 1] == '/') {
|
||||
out[i] = SYN_COMMENT;
|
||||
out[i + 1] = SYN_COMMENT;
|
||||
syn_set_token(out, out_len, i, SYN_COMMENT);
|
||||
syn_set_token(out, out_len, i + 1, SYN_COMMENT);
|
||||
i += 2;
|
||||
in_block_comment = false;
|
||||
state.in_block_comment = false;
|
||||
break;
|
||||
}
|
||||
out[i] = SYN_COMMENT;
|
||||
syn_set_token(out, out_len, i, SYN_COMMENT);
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
out[i] = SYN_PREPROCESSOR;
|
||||
syn_set_token(out, out_len, i, SYN_PREPROCESSOR);
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
@@ -194,59 +336,51 @@ inline void syn_highlight_line(const char* line, int len, SynToken* out, bool& i
|
||||
|
||||
// ---- String literal ----
|
||||
if (c == '"') {
|
||||
out[i++] = SYN_STRING;
|
||||
syn_set_token(out, out_len, i, SYN_STRING);
|
||||
i++;
|
||||
while (i < len) {
|
||||
if (line[i] == '\\' && i + 1 < len) {
|
||||
out[i] = SYN_STRING;
|
||||
out[i + 1] = SYN_STRING;
|
||||
syn_set_token(out, out_len, i, SYN_STRING);
|
||||
syn_set_token(out, out_len, i + 1, SYN_STRING);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
if (line[i] == '"') {
|
||||
out[i++] = SYN_STRING;
|
||||
syn_set_token(out, out_len, i, SYN_STRING);
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
out[i++] = SYN_STRING;
|
||||
syn_set_token(out, out_len, i, SYN_STRING);
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// ---- Character literal ----
|
||||
if (c == '\'') {
|
||||
out[i++] = SYN_CHAR;
|
||||
syn_set_token(out, out_len, i, SYN_CHAR);
|
||||
i++;
|
||||
while (i < len) {
|
||||
if (line[i] == '\\' && i + 1 < len) {
|
||||
out[i] = SYN_CHAR;
|
||||
out[i + 1] = SYN_CHAR;
|
||||
syn_set_token(out, out_len, i, SYN_CHAR);
|
||||
syn_set_token(out, out_len, i + 1, SYN_CHAR);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
if (line[i] == '\'') {
|
||||
out[i++] = SYN_CHAR;
|
||||
syn_set_token(out, out_len, i, SYN_CHAR);
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
out[i++] = SYN_CHAR;
|
||||
syn_set_token(out, out_len, i, SYN_CHAR);
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// ---- Numbers ----
|
||||
if (syn_is_digit(c) || (c == '.' && i + 1 < len && syn_is_digit(line[i + 1]))) {
|
||||
// Hex
|
||||
if (c == '0' && i + 1 < len && (line[i + 1] == 'x' || line[i + 1] == 'X')) {
|
||||
out[i] = SYN_NUMBER; out[i + 1] = SYN_NUMBER;
|
||||
i += 2;
|
||||
while (i < len && syn_is_hex(line[i])) out[i++] = SYN_NUMBER;
|
||||
} else {
|
||||
// Decimal / float
|
||||
while (i < len && (syn_is_digit(line[i]) || line[i] == '.'))
|
||||
out[i++] = SYN_NUMBER;
|
||||
}
|
||||
// Suffixes: u, l, f, etc.
|
||||
while (i < len && (line[i] == 'u' || line[i] == 'U' ||
|
||||
line[i] == 'l' || line[i] == 'L' ||
|
||||
line[i] == 'f' || line[i] == 'F'))
|
||||
out[i++] = SYN_NUMBER;
|
||||
syn_consume_number(line, len, i, out, out_len, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -254,8 +388,8 @@ inline void syn_highlight_line(const char* line, int len, SynToken* out, bool& i
|
||||
if (syn_is_alpha(c)) {
|
||||
int start = i;
|
||||
while (i < len && syn_is_alnum(line[i])) i++;
|
||||
SynToken tok = syn_classify_word(line + start, i - start);
|
||||
for (int j = start; j < i; j++) out[j] = tok;
|
||||
SynToken tok = syn_classify_word(SYN_LANG_C, line + start, i - start);
|
||||
syn_fill_tokens(out, out_len, start, i, tok);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -263,12 +397,125 @@ inline void syn_highlight_line(const char* line, int len, SynToken* out, bool& i
|
||||
if (c == '+' || c == '-' || c == '*' || c == '/' || c == '%' ||
|
||||
c == '=' || c == '!' || c == '<' || c == '>' || c == '&' ||
|
||||
c == '|' || c == '^' || c == '~' || c == '?' || c == ':') {
|
||||
out[i++] = SYN_OPERATOR;
|
||||
syn_set_token(out, out_len, i, SYN_OPERATOR);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// ---- Everything else (whitespace, braces, parens, etc.) ----
|
||||
out[i++] = SYN_NORMAL;
|
||||
syn_set_token(out, out_len, i, SYN_NORMAL);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
inline void syn_highlight_line_lua(const char* line, int len, SynToken* out, int out_len,
|
||||
SynState& state) {
|
||||
int i = 0;
|
||||
|
||||
while (i < len) {
|
||||
if (state.long_token != SYN_NORMAL) {
|
||||
int span = 0;
|
||||
if (syn_match_lua_long_bracket_close(line, len, i, state.long_bracket_eqs, span)) {
|
||||
syn_fill_tokens(out, out_len, i, i + span, state.long_token);
|
||||
i += span;
|
||||
state.long_token = SYN_NORMAL;
|
||||
state.long_bracket_eqs = -1;
|
||||
continue;
|
||||
}
|
||||
syn_set_token(out, out_len, i, state.long_token);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
char c = line[i];
|
||||
|
||||
if (c == '-' && i + 1 < len && line[i + 1] == '-') {
|
||||
int eqs = 0;
|
||||
int span = 0;
|
||||
if (i + 2 < len && syn_match_lua_long_bracket_open(line, len, i + 2, eqs, span)) {
|
||||
syn_fill_tokens(out, out_len, i, i + 2 + span, SYN_COMMENT);
|
||||
i += 2 + span;
|
||||
state.long_token = SYN_COMMENT;
|
||||
state.long_bracket_eqs = eqs;
|
||||
continue;
|
||||
}
|
||||
syn_fill_tokens(out, out_len, i, len, SYN_COMMENT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '"' || c == '\'') {
|
||||
char quote = c;
|
||||
syn_set_token(out, out_len, i, SYN_STRING);
|
||||
i++;
|
||||
while (i < len) {
|
||||
if (line[i] == '\\' && i + 1 < len) {
|
||||
syn_set_token(out, out_len, i, SYN_STRING);
|
||||
syn_set_token(out, out_len, i + 1, SYN_STRING);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
syn_set_token(out, out_len, i, SYN_STRING);
|
||||
if (line[i] == quote) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int eqs = 0;
|
||||
int span = 0;
|
||||
if (syn_match_lua_long_bracket_open(line, len, i, eqs, span)) {
|
||||
syn_fill_tokens(out, out_len, i, i + span, SYN_STRING);
|
||||
i += span;
|
||||
state.long_token = SYN_STRING;
|
||||
state.long_bracket_eqs = eqs;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (syn_is_digit(c) || (c == '.' && i + 1 < len && syn_is_digit(line[i + 1]))) {
|
||||
syn_consume_number(line, len, i, out, out_len, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (syn_is_alpha(c)) {
|
||||
int start = i;
|
||||
while (i < len && syn_is_alnum(line[i])) i++;
|
||||
SynToken tok = syn_classify_word(SYN_LANG_LUA, line + start, i - start);
|
||||
syn_fill_tokens(out, out_len, start, i, tok);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '+' || c == '-' || c == '*' || c == '/' || c == '%' ||
|
||||
c == '^' || c == '#' || c == '=' || c == '<' || c == '>' ||
|
||||
c == '~' || c == ':' || c == '.' || c == ',' || c == ';' ||
|
||||
c == '(' || c == ')' || c == '{' || c == '}' ||
|
||||
c == '[' || c == ']') {
|
||||
syn_set_token(out, out_len, i, SYN_OPERATOR);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
syn_set_token(out, out_len, i, SYN_NORMAL);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
inline void syn_highlight_line(const char* line, int len, SynToken* out, int out_len,
|
||||
SynLanguage lang, SynState& state) {
|
||||
if (!line || len <= 0) return;
|
||||
|
||||
switch (lang) {
|
||||
case SYN_LANG_C:
|
||||
syn_highlight_line_c(line, len, out, out_len, state);
|
||||
break;
|
||||
case SYN_LANG_LUA:
|
||||
syn_highlight_line_lua(line, len, out, out_len, state);
|
||||
break;
|
||||
default:
|
||||
syn_fill_tokens(out, out_len, 0, len, SYN_NORMAL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,12 +523,28 @@ inline void syn_highlight_line(const char* line, int len, SynToken* out, bool& i
|
||||
// Extension check
|
||||
// ============================================================================
|
||||
|
||||
inline bool syn_is_c_file(const char* filepath) {
|
||||
if (!filepath || filepath[0] == '\0') return false;
|
||||
int len = 0;
|
||||
while (filepath[len]) len++;
|
||||
if (len >= 2 && filepath[len - 2] == '.' &&
|
||||
(filepath[len - 1] == 'c' || filepath[len - 1] == 'h'))
|
||||
return true;
|
||||
return false;
|
||||
inline bool syn_path_ends_with(const char* path, const char* suffix) {
|
||||
if (!path || !suffix) return false;
|
||||
|
||||
int path_len = 0;
|
||||
while (path[path_len]) path_len++;
|
||||
|
||||
int suffix_len = 0;
|
||||
while (suffix[suffix_len]) suffix_len++;
|
||||
|
||||
if (path_len < suffix_len) return false;
|
||||
for (int i = 0; i < suffix_len; i++) {
|
||||
if (path[path_len - suffix_len + i] != suffix[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline SynLanguage syn_detect_language(const char* filepath) {
|
||||
if (!filepath || filepath[0] == '\0') return SYN_LANG_NONE;
|
||||
if (syn_path_ends_with(filepath, ".c") || syn_path_ends_with(filepath, ".h"))
|
||||
return SYN_LANG_C;
|
||||
if (syn_path_ends_with(filepath, ".lua"))
|
||||
return SYN_LANG_LUA;
|
||||
return SYN_LANG_NONE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user