#ifndef SYNTAX_DECL_H #define SYNTAX_DECL_H #include "io/knot.h" #include "io/sysio.h" struct Trie { struct TrieNode { bool is_word = false; std::array children{}; TrieNode() { children.fill(nullptr); } }; Trie() : root(new TrieNode()) {} ~Trie() { clear_trie(root); } void build(const std::vector &words) { for (const auto &word : words) { TrieNode *node = root; for (char c : word) { unsigned char uc = static_cast(c); if (!node->children[uc]) node->children[uc] = new TrieNode(); node = node->children[uc]; } node->is_word = true; } } uint32_t match(const char *text, uint32_t pos, uint32_t len, bool (*is_word_char)(char c)) const { const TrieNode *node = root; uint32_t max_len = 0; for (uint32_t i = pos; i < len; ++i) { unsigned char uc = static_cast(text[i]); if (uc >= 128) return 0; if (!node->children[uc]) { if (node->is_word && !is_word_char(text[i])) return i - pos; break; } node = node->children[uc]; if (node->is_word) max_len = i - pos + 1; } if (max_len > 0) if (pos + max_len < len && is_word_char(text[pos + max_len])) return 0; return max_len; } void clear() { clear_trie(root); root = new TrieNode(); } private: TrieNode *root; void clear_trie(TrieNode *node) { if (!node) return; for (auto *child : node->children) clear_trie(child); delete node; } }; struct Highlight { uint32_t fg; uint32_t bg; uint8_t flags; }; enum struct TokenKind : uint8_t { #define ADD(name) name, #include "syntax/tokens.def" #undef ADD Count }; constexpr size_t TOKEN_KIND_COUNT = static_cast(TokenKind::Count); const std::unordered_map kind_map = { #define ADD(name) {#name, TokenKind::name}, #include "syntax/tokens.def" #undef ADD }; extern std::array highlights; inline void load_theme(std::string filename) { uint32_t len = 0; char *raw = load_file(filename.c_str(), &len); if (!raw) return; std::string data(raw, len); free(raw); json j = json::parse(data); Highlight default_hl = {0xFFFFFF, 0, 0}; if (j.contains("Default")) { auto def = j["Default"]; if (def.contains("fg") && def["fg"].is_string()) default_hl.fg = HEX(def["fg"]); if (def.contains("bg") && def["bg"].is_string()) default_hl.bg = HEX(def["bg"]); if (def.contains("italic") && def["italic"].get()) default_hl.flags |= CF_ITALIC; if (def.contains("bold") && def["bold"].get()) default_hl.flags |= CF_BOLD; if (def.contains("underline") && def["underline"].get()) default_hl.flags |= CF_UNDERLINE; if (def.contains("strikethrough") && def["strikethrough"].get()) default_hl.flags |= CF_STRIKETHROUGH; } for (auto &hl : highlights) hl = default_hl; for (auto &[key, value] : j.items()) { if (key == "Default") continue; auto it = kind_map.find(key); if (it == kind_map.end()) continue; Highlight hl = {0xFFFFFF, 0, 0}; if (value.contains("fg") && value["fg"].is_string()) hl.fg = HEX(value["fg"]); if (value.contains("bg") && value["bg"].is_string()) hl.bg = HEX(value["bg"]); if (value.contains("italic") && value["italic"].get()) hl.flags |= CF_ITALIC; if (value.contains("bold") && value["bold"].get()) hl.flags |= CF_BOLD; if (value.contains("underline") && value["underline"].get()) hl.flags |= CF_UNDERLINE; if (value.contains("strikethrough") && value["strikethrough"].get()) hl.flags |= CF_STRIKETHROUGH; highlights[static_cast(it->second)] = hl; } } struct Token { uint32_t start; uint32_t end; TokenKind type; }; struct LineData { std::shared_ptr in_state{nullptr}; std::vector tokens; std::shared_ptr out_state{nullptr}; }; #endif