3#include "parseRawGrammar.h"
4#include "parseRawTheme.h"
6#include "utf16_utils.h"
12#include <rapidjson/document.h>
13#include <rapidjson/error/en.h>
16using namespace rapidjson;
19static std::string readFileContents(
const char* filepath) {
20 std::ifstream file(filepath);
21 if (!file.is_open()) {
24 std::stringstream buffer;
25 buffer << file.rdbuf();
30static char* stringToCString(
const std::string& str) {
31 char* cstr =
new char[str.length() + 1];
32 std::strcpy(cstr, str.c_str());
41static uint32_t hexColorToUint32(
const std::string& hexColor) {
42 if (hexColor.empty() || hexColor[0] !=
'#') {
46 std::string hex = hexColor.substr(1);
49 if (hex.length() == 6) {
53 else if (hex.length() != 8) {
58 uint32_t value = std::stoul(hex,
nullptr, 16);
67static int32_t fontStyleStringToFlags(
const std::string& fontStyle) {
70 if (fontStyle.find(
"italic") != std::string::npos) {
73 if (fontStyle.find(
"bold") != std::string::npos) {
76 if (fontStyle.find(
"underline") != std::string::npos) {
84static void freeScopeStack(ScopeStack* stack) {
86 ScopeStack* parent = stack->parent;
93static std::vector<ScopeName> parseScopePath(
const char* scopePath) {
94 std::vector<ScopeName> scopes;
95 if (!scopePath || !scopePath[0]) {
99 std::istringstream iss(scopePath);
101 while (iss >> scope) {
102 scopes.push_back(scope);
109static StyleAttributes* matchAllScopes(Theme* theme,
const std::vector<ScopeName>& scopes) {
110 if (scopes.empty()) {
114 StyleAttributes* merged =
nullptr;
118 for (
size_t i = 0; i < scopes.size(); i++) {
120 ScopeStack* scopeStack =
nullptr;
121 for (
size_t j = 0; j <= i; j++) {
122 scopeStack =
new ScopeStack(scopeStack, scopes[j]);
126 StyleAttributes* attrs = theme->match(scopeStack);
129 freeScopeStack(scopeStack);
137 if (attrs->fontStyle !=
static_cast<int>(FontStyle::NotSet)) {
138 merged->fontStyle = attrs->fontStyle;
140 if (attrs->foregroundId != 0) {
141 merged->foregroundId = attrs->foregroundId;
143 if (attrs->backgroundId != 0) {
144 merged->backgroundId = attrs->backgroundId;
168 std::string content = readFileContents(themePath);
169 if (content.empty()) {
173 Theme* theme = parseJSONTheme(content);
178 StyleAttributes* defaults = theme->getDefaults();
197 Theme* theme = parseJSONTheme(jsonContent);
202 StyleAttributes* defaults = theme->getDefaults();
217 const char* scopePath,
218 uint32_t defaultColor
226 auto colorMap = managed->theme->getColorMap();
229 if (!scopePath || !scopePath[0]) {
230 if (managed->defaults) {
231 int fgId = managed->defaults->foregroundId;
232 if (fgId > 0 && fgId <
static_cast<int>(colorMap.size())) {
233 return hexColorToUint32(colorMap[fgId]);
240 std::vector<ScopeName> scopes = parseScopePath(scopePath);
241 if (scopes.empty()) {
242 if (managed->defaults) {
243 int fgId = managed->defaults->foregroundId;
244 if (fgId > 0 && fgId <
static_cast<int>(colorMap.size())) {
245 return hexColorToUint32(colorMap[fgId]);
252 StyleAttributes* attrs = matchAllScopes(managed->theme, scopes);
256 int fgId = attrs->foregroundId;
259 if (fgId > 0 && fgId <
static_cast<int>(colorMap.size())) {
260 return hexColorToUint32(colorMap[fgId]);
265 if (managed->defaults) {
266 int fgId = managed->defaults->foregroundId;
267 if (fgId > 0 && fgId <
static_cast<int>(colorMap.size())) {
268 return hexColorToUint32(colorMap[fgId]);
280 const char* scopePath,
281 uint32_t defaultColor
289 auto colorMap = managed->theme->getColorMap();
292 if (!scopePath || !scopePath[0]) {
293 if (managed->defaults) {
294 int bgId = managed->defaults->backgroundId;
295 if (bgId > 0 && bgId <
static_cast<int>(colorMap.size())) {
296 return hexColorToUint32(colorMap[bgId]);
303 std::vector<ScopeName> scopes = parseScopePath(scopePath);
304 if (scopes.empty()) {
305 if (managed->defaults) {
306 int bgId = managed->defaults->backgroundId;
307 if (bgId > 0 && bgId <
static_cast<int>(colorMap.size())) {
308 return hexColorToUint32(colorMap[bgId]);
315 StyleAttributes* attrs = matchAllScopes(managed->theme, scopes);
319 int bgId = attrs->backgroundId;
322 if (bgId > 0 && bgId <
static_cast<int>(colorMap.size())) {
323 return hexColorToUint32(colorMap[bgId]);
328 if (managed->defaults) {
329 int bgId = managed->defaults->backgroundId;
330 if (bgId > 0 && bgId <
static_cast<int>(colorMap.size())) {
331 return hexColorToUint32(colorMap[bgId]);
343 const char* scopePath,
354 if (!scopePath || !scopePath[0]) {
355 if (managed->defaults) {
356 return managed->defaults->fontStyle;
362 std::vector<ScopeName> scopes = parseScopePath(scopePath);
363 if (scopes.empty()) {
364 if (managed->defaults) {
365 return managed->defaults->fontStyle;
371 StyleAttributes* attrs = matchAllScopes(managed->theme, scopes);
375 int fontStyle = attrs->fontStyle;
379 if (fontStyle >= 0) {
385 if (managed->defaults) {
386 return managed->defaults->fontStyle;
402 if (managed->defaults) {
403 auto colorMap = managed->theme->getColorMap();
404 int fgId = managed->defaults->foregroundId;
406 if (fgId > 0 && fgId <
static_cast<int>(colorMap.size())) {
407 return hexColorToUint32(colorMap[fgId]);
423 if (managed->defaults) {
424 auto colorMap = managed->theme->getColorMap();
425 int bgId = managed->defaults->backgroundId;
427 if (bgId > 0 && bgId <
static_cast<int>(colorMap.size())) {
428 return hexColorToUint32(colorMap[bgId]);
447 IOnigLib* onigLib =
new DefaultOnigLib();
455class ManagedRegistry {
458 std::map<std::string, IRawGrammar*> preloadedGrammars;
459 std::map<std::string, std::vector<std::string>> injections;
461 ManagedRegistry(IOnigLib* onigLib) {
462 RegistryOptions options;
463 options.onigLib = onigLib;
466 options.loadGrammar = [
this](
const ScopeName& scopeName) -> IRawGrammar* {
467 auto it = preloadedGrammars.find(scopeName);
468 if (it != preloadedGrammars.end()) {
475 options.getInjections = [
this](
const ScopeName& scopeName) -> std::vector<ScopeName> {
476 auto it = injections.find(scopeName);
477 if (it != injections.end()) {
480 return std::vector<ScopeName>();
483 registry =
new Registry(options);
497 ManagedRegistry* managed =
new ManagedRegistry(
static_cast<IOnigLib*
>(onigLib));
507 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
515 const char* grammarPath
517 if (!registry || !grammarPath) {
522 std::string content = readFileContents(grammarPath);
523 if (content.empty()) {
527 std::string pathStr = grammarPath;
528 IRawGrammar* rawGrammar = parseRawGrammar(content, &pathStr);
533 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
534 managed->preloadedGrammars[rawGrammar->scopeName] = rawGrammar;
545 const char* jsonContent
547 if (!registry || !jsonContent) {
552 IRawGrammar* rawGrammar = parseRawGrammar(jsonContent,
nullptr);
557 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
558 managed->preloadedGrammars[rawGrammar->scopeName] = rawGrammar;
569 const char* scopeName,
570 const char** injections,
571 int32_t injectionCount
573 if (!registry || !scopeName || !injections) {
578 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
579 std::vector<std::string> injectionsList;
580 for (int32_t i = 0; i < injectionCount; i++) {
582 injectionsList.push_back(injections[i]);
585 managed->injections[scopeName] = injectionsList;
594 const char* scopeName
596 if (!registry || !scopeName) {
601 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
602 Grammar* grammar = managed->registry->loadGrammar(scopeName);
617 const char* lineText,
620 if (!grammar || !lineText) {
625 Grammar* gram =
static_cast<Grammar*
>(grammar);
628 ITokenizeLineResult result = gram->tokenizeLine(lineText, state);
638 for (
int i = 0; i < cResult->
tokenCount; i++) {
639 const IToken& token = result.tokens[i];
645 cResult->
tokens[i].
scopes =
new char*[token.scopes.size()];
646 for (
size_t j = 0; j < token.scopes.size(); j++) {
647 cResult->
tokens[i].
scopes[j] = stringToCString(token.scopes[j]);
660 const char* lineText,
663 if (!grammar || !lineText) {
668 Grammar* gram =
static_cast<Grammar*
>(grammar);
671 ITokenizeLineResult2 result = gram->tokenizeLine2(lineText, state);
681 for (
int i = 0; i < cResult->
tokenCount; i++) {
682 cResult->
tokens[i] = result.tokens[i];
695 for (
int i = 0; i < result->
tokenCount; i++) {
726 if (!grammar || !lines || lineCount <= 0) {
731 Grammar* g =
static_cast<Grammar*
>(grammar);
740 for (int32_t i = 0; i < lineCount; i++) {
741 std::string lineText(lines[i]);
742 auto result = g->tokenizeLine(lineText, state);
745 state = result.ruleStack;
749 lineResult->
tokenCount = result.tokens.size();
755 for (
size_t j = 0; j < result.tokens.size(); j++) {
756 const auto& token = result.tokens[j];
762 lineResult->
tokens[j].
scopes =
new char*[token.scopes.size()];
763 for (
size_t k = 0; k < token.scopes.size(); k++) {
764 lineResult->
tokens[j].
scopes[k] = stringToCString(token.scopes[k]);
781 for (int32_t i = 0; i < result->
lineCount; i++) {
796 const char* lineText,
799 if (!grammar || !lineText) {
804 Grammar* gram =
static_cast<Grammar*
>(grammar);
807 ITokenizeLineResult result = gram->tokenizeLine(lineText, state);
810 auto map = tml::buildByteToUtf16Map(lineText, std::strlen(lineText));
822 for (
int i = 0; i < cResult->
tokenCount; i++) {
823 const IToken& token = result.tokens[i];
825 cResult->
tokens[i].
endIndex = tml::mapByteToUtf16(map, token.endIndex);
829 cResult->
tokens[i].
scopes =
new char*[token.scopes.size()];
830 for (
size_t j = 0; j < token.scopes.size(); j++) {
831 cResult->
tokens[i].
scopes[j] = stringToCString(token.scopes[j]);
844 const char* lineText,
847 if (!grammar || !lineText) {
852 Grammar* gram =
static_cast<Grammar*
>(grammar);
855 ITokenizeLineResult2 result = gram->tokenizeLine2(lineText, state);
858 auto map = tml::buildByteToUtf16Map(lineText, std::strlen(lineText));
869 for (
int i = 0; i < cResult->
tokenCount; i++) {
872 cResult->
tokens[i] = tml::mapByteToUtf16(map, result.tokens[i]);
875 cResult->
tokens[i] = result.tokens[i];
892 if (!grammar || !lines || lineCount <= 0) {
897 Grammar* g =
static_cast<Grammar*
>(grammar);
906 for (int32_t i = 0; i < lineCount; i++) {
907 std::string lineText(lines[i]);
908 auto result = g->tokenizeLine(lineText, state);
911 state = result.ruleStack;
914 auto map = tml::buildByteToUtf16Map(lineText.c_str(), lineText.size());
918 lineResult->
tokenCount = result.tokens.size();
924 for (
size_t j = 0; j < result.tokens.size(); j++) {
925 const auto& token = result.tokens[j];
926 lineResult->
tokens[j].
startIndex = tml::mapByteToUtf16(map, token.startIndex);
927 lineResult->
tokens[j].
endIndex = tml::mapByteToUtf16(map, token.endIndex);
931 lineResult->
tokens[j].
scopes =
new char*[token.scopes.size()];
932 for (
size_t k = 0; k < token.scopes.size(); k++) {
933 lineResult->
tokens[j].
scopes[k] = stringToCString(token.scopes[k]);
953 Grammar* gram =
static_cast<Grammar*
>(grammar);
954 static thread_local std::string scopeName;
955 scopeName = gram->getScopeName();
956 return scopeName.c_str();
965 IOnigLib* lib =
static_cast<IOnigLib*
>(onigLib);
C language API for TextMateLib.
Helper class to manage theme resources and provide C API implementation.
Abstract interface representing the parsing state at the end of a line.
const StateStack * INITIAL
Initial parsing state for the first line of a document.
std::string ScopeName
Semantic name identifying a scope (e.g., "source.javascript", "comment.line")
void * TextMateGrammar
Handle to a grammar definition for a specific language.
void * TextMateTheme
Handle to a theme object containing color schemes.
void * TextMateRegistry
Handle to the grammar registry managing loaded grammars and themes.
void * TextMateStateStack
Handle to a parsing state stack (immutable, used for incremental tokenization)
void * TextMateOnigLib
Handle to the Oniguruma regex library instance.
int textmate_registry_add_grammar_from_json(TextMateRegistry registry, const char *jsonContent)
Register a grammar from a JSON string.
TextMateOnigLib textmate_oniglib_create()
Initialize the Oniguruma regular expression library.
void textmate_registry_dispose(TextMateRegistry registry)
Free a registry and all its resources.
int textmate_registry_add_grammar_from_file(TextMateRegistry registry, const char *grammarPath)
Register a grammar from a JSON file.
TextMateRegistry textmate_registry_create(TextMateOnigLib onigLib)
Create a new grammar registry.
TextMateGrammar textmate_registry_load_grammar(TextMateRegistry registry, const char *scopeName)
Load a grammar by scope name.
void textmate_registry_set_injections(TextMateRegistry registry, const char *scopeName, const char **injections, int32_t injectionCount)
Set grammar injection rules for a scope.
uint32_t textmate_theme_get_default_background(TextMateTheme theme)
Get the default/fallback background color for the entire theme.
uint32_t textmate_theme_get_background(TextMateTheme theme, const char *scopePath, uint32_t defaultColor)
Get the background color for a scope path.
void textmate_theme_dispose(TextMateTheme theme)
Free a theme object and release resources.
uint32_t textmate_theme_get_foreground(TextMateTheme theme, const char *scopePath, uint32_t defaultColor)
Get the foreground color for a scope path.
int32_t textmate_theme_get_font_style(TextMateTheme theme, const char *scopePath, int32_t defaultStyle)
Get the font style flags for a scope path.
#define TEXTMATE_FONT_STYLE_NONE
Font style flag constants for textmate_theme_get_font_style()
uint32_t textmate_theme_get_default_foreground(TextMateTheme theme)
Get the default/fallback foreground color for the entire theme.
#define TEXTMATE_FONT_STYLE_BOLD
Bold text.
#define TEXTMATE_FONT_STYLE_UNDERLINE
Underlined text.
#define TEXTMATE_FONT_STYLE_ITALIC
Italic text.
TextMateTheme textmate_theme_load_from_json(const char *jsonContent)
Load a theme from a JSON string.
TextMateTheme textmate_theme_load_from_file(const char *themePath)
Load a theme from a JSON file.
void textmate_free_tokenize_result(TextMateTokenizeResult *result)
Free a line tokenization result.
const char * textmate_grammar_get_scope_name(TextMateGrammar grammar)
Get the scope name (language identifier) of a grammar.
TextMateTokenizeMultiLinesResult * textmate_tokenize_lines(TextMateGrammar grammar, const char **lines, int32_t lineCount, TextMateStateStack initialState)
Tokenize multiple lines in a single call.
void textmate_oniglib_dispose(TextMateOnigLib onigLib)
Free the Oniguruma library.
TextMateStateStack textmate_get_initial_state()
Get the initial parsing state.
TextMateTokenizeResult2 * textmate_tokenize_line2(TextMateGrammar grammar, const char *lineText, TextMateStateStack prevState)
Tokenize a single line of text with encoded tokens (more efficient)
void textmate_free_tokenize_lines_result(TextMateTokenizeMultiLinesResult *result)
Free a batch tokenization result.
TextMateTokenizeResult * textmate_tokenize_line(TextMateGrammar grammar, const char *lineText, TextMateStateStack prevState)
Tokenize a single line of text with decoded scopes.
void textmate_free_tokenize_result2(TextMateTokenizeResult2 *result)
Free an encoded line tokenization result.
TextMateTokenizeMultiLinesResult * textmate_tokenize_lines_utf16(TextMateGrammar grammar, const char **lines, int32_t lineCount, TextMateStateStack initialState)
Tokenize multiple lines in a single call, returning UTF-16 indices.
TextMateTokenizeResult2 * textmate_tokenize_line2_utf16(TextMateGrammar grammar, const char *lineText, TextMateStateStack prevState)
Tokenize a single line with encoded tokens, returning UTF-16 indices.
TextMateTokenizeResult * textmate_tokenize_line_utf16(TextMateGrammar grammar, const char *lineText, TextMateStateStack prevState)
Tokenize a single line with decoded scopes, returning UTF-16 indices.
Represents a single token in tokenized text.
int32_t startIndex
Start position in the line (0-based)
int32_t endIndex
End position (exclusive)
char ** scopes
Array of scope strings (e.g., "keyword.control", "string.quoted.double")
int32_t scopeDepth
Number of scopes in the scope hierarchy.
Result from batch tokenizing multiple lines.
int32_t lineCount
Number of lines tokenized.
TextMateTokenizeResult ** lineResults
Array of results, one per line.
Result from tokenizing a single line with encoded tokens.
int32_t tokenCount
Number of tokens in the array.
TextMateStateStack ruleStack
State at end of line (pass to next line's tokenization)
uint32_t * tokens
Array of encoded tokens.
int32_t stoppedEarly
Non-zero if tokenization stopped before end (time limit hit)
Result from tokenizing a single line with decoded tokens.
int32_t stoppedEarly
Non-zero if tokenization stopped before end (time limit hit)
TextMateStateStack ruleStack
State at end of line (pass to next line's tokenization)
TextMateToken * tokens
Array of tokens found in this line.
int32_t tokenCount
Number of tokens in the array.