/* * lang_html.hpp * HTML highlighting, with embedded CSS and JS * Shared by the MontaukOS text editor (GUI) and edit.elf (CLI) * Copyright (c) 2026 Daniel Hammer */ // Included by ; not standalone. #pragma once // ============================================================================ // JavaScript keyword / builtin classification // ============================================================================ inline SynToken syn_classify_js_word(const char* buf, int len) { static const char* keywords[] = { "async", "await", "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do", "else", "export", "extends", "finally", "for", "function", "if", "import", "in", "instanceof", "let", "new", "of", "return", "static", "super", "switch", "this", "throw", "try", "typeof", "var", "void", "while", "with", "yield", "true", "false", "null", "undefined", }; static const char* builtins[] = { "Array", "Boolean", "Date", "Error", "Infinity", "JSON", "Map", "Math", "NaN", "Number", "Object", "Promise", "Proxy", "RegExp", "Set", "String", "Symbol", "WeakMap", "WeakSet", "console", "document", "globalThis", "localStorage", "navigator", "window", "fetch", "setTimeout", "setInterval", }; 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; } // ============================================================================ // JavaScript (also used for begins. inline void syn_highlight_js_range(const char* line, int limit, int& i, SynToken* out, int out_len, SynState& state) { while (i < limit) { // ---- Block comment continuation ---- if (state.in_block_comment) { while (i < limit) { if (i + 1 < limit && line[i] == '*' && line[i + 1] == '/') { syn_fill_tokens(out, out_len, i, i + 2, SYN_COMMENT); i += 2; state.in_block_comment = false; break; } syn_set_token(out, out_len, i, SYN_COMMENT); i++; } continue; } char c = line[i]; if (c == '/' && i + 1 < limit && line[i + 1] == '/') { syn_fill_tokens(out, out_len, i, limit, SYN_COMMENT); i = limit; break; } if (c == '/' && i + 1 < limit && line[i + 1] == '*') { state.in_block_comment = true; syn_fill_tokens(out, out_len, i, i + 2, SYN_COMMENT); i += 2; continue; } // ---- String / template literal (single line only) ---- if (c == '"' || c == '\'' || c == '`') { char quote = c; syn_set_token(out, out_len, i, SYN_STRING); i++; while (i < limit) { if (line[i] == '\\' && i + 1 < limit) { syn_fill_tokens(out, out_len, i, i + 2, SYN_STRING); i += 2; continue; } syn_set_token(out, out_len, i, SYN_STRING); if (line[i] == quote) { i++; break; } i++; } continue; } if (syn_is_digit(c) || (c == '.' && i + 1 < limit && syn_is_digit(line[i + 1]))) { syn_consume_number(line, limit, i, out, out_len, false); continue; } if (syn_is_alpha(c) || c == '$') { int start = i; while (i < limit && (syn_is_alnum(line[i]) || line[i] == '$')) i++; syn_fill_tokens(out, out_len, start, i, syn_classify_js_word(line + start, i - start)); continue; } if (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++; } } // ============================================================================ // CSS (also used for