3#include "parseRawGrammar.h"
4#include "parseRawTheme.h"
6#include "utf16_utils.h"
13#include <rapidjson/document.h>
14#include <rapidjson/error/en.h>
17using namespace rapidjson;
20static std::string readFileContents(
const char* filepath) {
21 std::ifstream file(filepath);
22 if (!file.is_open()) {
25 std::stringstream buffer;
26 buffer << file.rdbuf();
31static char* stringToCString(
const std::string& str) {
32 char* cstr =
new char[str.length() + 1];
33 std::strcpy(cstr, str.c_str());
42static uint32_t hexColorToUint32(
const std::string& hexColor) {
43 if (hexColor.empty() || hexColor[0] !=
'#') {
47 std::string hex = hexColor.substr(1);
50 if (hex.length() == 6) {
54 else if (hex.length() != 8) {
59 uint32_t value = std::stoul(hex,
nullptr, 16);
68static int32_t fontStyleStringToFlags(
const std::string& fontStyle) {
71 if (fontStyle.find(
"italic") != std::string::npos) {
74 if (fontStyle.find(
"bold") != std::string::npos) {
77 if (fontStyle.find(
"underline") != std::string::npos) {
85static void freeScopeStack(ScopeStack* stack) {
87 ScopeStack* parent = stack->parent;
94static std::vector<ScopeName> parseScopePath(
const char* scopePath) {
95 std::vector<ScopeName> scopes;
96 if (!scopePath || !scopePath[0]) {
100 std::istringstream iss(scopePath);
102 while (iss >> scope) {
103 scopes.push_back(scope);
110static StyleAttributes* matchAllScopes(Theme* theme,
const std::vector<ScopeName>& scopes) {
111 if (scopes.empty()) {
115 StyleAttributes* merged =
nullptr;
119 for (
size_t i = 0; i < scopes.size(); i++) {
121 ScopeStack* scopeStack =
nullptr;
122 for (
size_t j = 0; j <= i; j++) {
123 scopeStack =
new ScopeStack(scopeStack, scopes[j]);
127 StyleAttributes* attrs = theme->match(scopeStack);
130 freeScopeStack(scopeStack);
138 if (attrs->fontStyle !=
static_cast<int>(FontStyle::NotSet)) {
139 merged->fontStyle = attrs->fontStyle;
141 if (attrs->foregroundId != 0) {
142 merged->foregroundId = attrs->foregroundId;
144 if (attrs->backgroundId != 0) {
145 merged->backgroundId = attrs->backgroundId;
169 std::string content = readFileContents(themePath);
170 if (content.empty()) {
174 Theme* theme = parseJSONTheme(content);
179 StyleAttributes* defaults = theme->getDefaults();
198 Theme* theme = parseJSONTheme(jsonContent);
203 StyleAttributes* defaults = theme->getDefaults();
218 const char* scopePath,
219 uint32_t defaultColor
227 auto colorMap = managed->theme->getColorMap();
230 if (!scopePath || !scopePath[0]) {
231 if (managed->defaults) {
232 int fgId = managed->defaults->foregroundId;
233 if (fgId > 0 && fgId <
static_cast<int>(colorMap.size())) {
234 return hexColorToUint32(colorMap[fgId]);
241 std::vector<ScopeName> scopes = parseScopePath(scopePath);
242 if (scopes.empty()) {
243 if (managed->defaults) {
244 int fgId = managed->defaults->foregroundId;
245 if (fgId > 0 && fgId <
static_cast<int>(colorMap.size())) {
246 return hexColorToUint32(colorMap[fgId]);
253 StyleAttributes* attrs = matchAllScopes(managed->theme, scopes);
257 int fgId = attrs->foregroundId;
260 if (fgId > 0 && fgId <
static_cast<int>(colorMap.size())) {
261 return hexColorToUint32(colorMap[fgId]);
266 if (managed->defaults) {
267 int fgId = managed->defaults->foregroundId;
268 if (fgId > 0 && fgId <
static_cast<int>(colorMap.size())) {
269 return hexColorToUint32(colorMap[fgId]);
281 const char* scopePath,
282 uint32_t defaultColor
290 auto colorMap = managed->theme->getColorMap();
293 if (!scopePath || !scopePath[0]) {
294 if (managed->defaults) {
295 int bgId = managed->defaults->backgroundId;
296 if (bgId > 0 && bgId <
static_cast<int>(colorMap.size())) {
297 return hexColorToUint32(colorMap[bgId]);
304 std::vector<ScopeName> scopes = parseScopePath(scopePath);
305 if (scopes.empty()) {
306 if (managed->defaults) {
307 int bgId = managed->defaults->backgroundId;
308 if (bgId > 0 && bgId <
static_cast<int>(colorMap.size())) {
309 return hexColorToUint32(colorMap[bgId]);
316 StyleAttributes* attrs = matchAllScopes(managed->theme, scopes);
320 int bgId = attrs->backgroundId;
323 if (bgId > 0 && bgId <
static_cast<int>(colorMap.size())) {
324 return hexColorToUint32(colorMap[bgId]);
329 if (managed->defaults) {
330 int bgId = managed->defaults->backgroundId;
331 if (bgId > 0 && bgId <
static_cast<int>(colorMap.size())) {
332 return hexColorToUint32(colorMap[bgId]);
344 const char* scopePath,
355 if (!scopePath || !scopePath[0]) {
356 if (managed->defaults) {
357 return managed->defaults->fontStyle;
363 std::vector<ScopeName> scopes = parseScopePath(scopePath);
364 if (scopes.empty()) {
365 if (managed->defaults) {
366 return managed->defaults->fontStyle;
372 StyleAttributes* attrs = matchAllScopes(managed->theme, scopes);
376 int fontStyle = attrs->fontStyle;
380 if (fontStyle >= 0) {
386 if (managed->defaults) {
387 return managed->defaults->fontStyle;
403 if (managed->defaults) {
404 auto colorMap = managed->theme->getColorMap();
405 int fgId = managed->defaults->foregroundId;
407 if (fgId > 0 && fgId <
static_cast<int>(colorMap.size())) {
408 return hexColorToUint32(colorMap[fgId]);
424 if (managed->defaults) {
425 auto colorMap = managed->theme->getColorMap();
426 int bgId = managed->defaults->backgroundId;
428 if (bgId > 0 && bgId <
static_cast<int>(colorMap.size())) {
429 return hexColorToUint32(colorMap[bgId]);
448 IOnigLib* onigLib =
new DefaultOnigLib();
456class ManagedRegistry {
459 std::map<std::string, IRawGrammar*> preloadedGrammars;
460 std::map<std::string, std::vector<std::string>> injections;
462 ManagedRegistry(IOnigLib* onigLib) {
463 RegistryOptions options;
464 options.onigLib = onigLib;
467 options.loadGrammar = [
this](
const ScopeName& scopeName) -> IRawGrammar* {
468 auto it = preloadedGrammars.find(scopeName);
469 if (it != preloadedGrammars.end()) {
476 options.getInjections = [
this](
const ScopeName& scopeName) -> std::vector<ScopeName> {
477 auto it = injections.find(scopeName);
478 if (it != injections.end()) {
481 return std::vector<ScopeName>();
484 registry =
new Registry(options);
498 ManagedRegistry* managed =
new ManagedRegistry(
static_cast<IOnigLib*
>(onigLib));
508 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
516 const char* grammarPath
518 if (!registry || !grammarPath) {
523 std::string content = readFileContents(grammarPath);
524 if (content.empty()) {
528 std::string pathStr = grammarPath;
529 IRawGrammar* rawGrammar = parseRawGrammar(content, &pathStr);
534 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
535 managed->preloadedGrammars[rawGrammar->scopeName] = rawGrammar;
546 const char* jsonContent
548 if (!registry || !jsonContent) {
553 IRawGrammar* rawGrammar = parseRawGrammar(jsonContent,
nullptr);
558 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
559 managed->preloadedGrammars[rawGrammar->scopeName] = rawGrammar;
570 const char* scopeName,
571 const char** injections,
572 int32_t injectionCount
574 if (!registry || !scopeName || !injections) {
579 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
580 std::vector<std::string> injectionsList;
581 for (int32_t i = 0; i < injectionCount; i++) {
583 injectionsList.push_back(injections[i]);
586 managed->injections[scopeName] = injectionsList;
595 const char* themeJsonContent
597 if (!registry || !themeJsonContent) {
602 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
603 IRawTheme* rawTheme = parseRawTheme(themeJsonContent);
604 if (!rawTheme)
return 0;
605 managed->registry->setTheme(rawTheme);
619 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
620 std::vector<std::string> colors = managed->registry->getColorMap();
628 result->
colorCount =
static_cast<int32_t
>(colors.size());
629 result->colors =
new char*[colors.size()]();
631 for (
size_t i = 0; i < colors.size(); i++) {
632 result->colors[i] = stringToCString(colors[i]);
635 return result.release();
644 const char* scopeName
646 if (!registry || !scopeName) {
651 ManagedRegistry* managed =
static_cast<ManagedRegistry*
>(registry);
652 Grammar* grammar = managed->registry->loadGrammar(scopeName);
667 const char* lineText,
670 if (!grammar || !lineText) {
675 Grammar* gram =
static_cast<Grammar*
>(grammar);
678 ITokenizeLineResult result = gram->tokenizeLine(lineText, state);
686 cResult->
tokenCount =
static_cast<int32_t
>(result.tokens.size());
689 cResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
692 for (
size_t i = 0; i < result.tokens.size(); i++) {
693 const IToken& token = result.tokens[i];
694 cResult->tokens[i].
startIndex = token.startIndex;
695 cResult->tokens[i].endIndex = token.endIndex;
696 cResult->tokens[i].scopeDepth =
static_cast<int32_t
>(token.scopes.size());
699 cResult->tokens[i].scopes =
new char*[token.scopes.size()]();
700 for (
size_t j = 0; j < token.scopes.size(); j++) {
701 cResult->tokens[i].scopes[j] = stringToCString(token.scopes[j]);
705 return cResult.release();
714 const char* lineText,
717 if (!grammar || !lineText) {
722 Grammar* gram =
static_cast<Grammar*
>(grammar);
725 ITokenizeLineResult2 result = gram->tokenizeLine2(lineText, state);
731 cResult->
tokenCount =
static_cast<int32_t
>(result.tokens.size());
732 cResult->tokens =
new uint32_t[result.tokens.size()];
734 cResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
737 for (
size_t i = 0; i < result.tokens.size(); i++) {
738 cResult->tokens[i] = result.tokens[i];
741 return cResult.release();
751 for (
int i = 0; i < result->
tokenCount; i++) {
782 if (!grammar || !lines || lineCount <= 0) {
787 Grammar* g =
static_cast<Grammar*
>(grammar);
800 for (int32_t i = 0; i < lineCount; i++) {
801 std::string lineText(lines[i]);
802 auto result = g->tokenizeLine(lineText, state);
805 state = result.ruleStack;
810 lineResult->
tokenCount =
static_cast<int32_t
>(result.tokens.size());
811 lineResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
815 lineResult->tokens =
new TextMateToken[result.tokens.size()]();
816 for (
size_t j = 0; j < result.tokens.size(); j++) {
817 const auto& token = result.tokens[j];
818 lineResult->tokens[j].
startIndex = token.startIndex;
819 lineResult->tokens[j].endIndex = token.endIndex;
820 lineResult->tokens[j].scopeDepth =
static_cast<int32_t
>(token.scopes.size());
823 lineResult->tokens[j].scopes =
new char*[token.scopes.size()]();
824 for (
size_t k = 0; k < token.scopes.size(); k++) {
825 lineResult->tokens[j].scopes[k] = stringToCString(token.scopes[k]);
829 batchResult->lineResults[i] = lineResult.release();
832 return batchResult.release();
843 for (int32_t i = 0; i < result->
lineCount; i++) {
859 if (!grammar || !lines || lineCount <= 0) {
864 Grammar* g =
static_cast<Grammar*
>(grammar);
876 for (int32_t i = 0; i < lineCount; i++) {
877 std::string lineText(lines[i]);
878 auto result = g->tokenizeLine2(lineText, state);
880 state = result.ruleStack;
884 lineResult->
tokenCount =
static_cast<int32_t
>(result.tokens.size());
885 lineResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
888 lineResult->tokens =
new uint32_t[result.tokens.size()];
889 for (
size_t j = 0; j < result.tokens.size(); j++) {
890 lineResult->tokens[j] = result.tokens[j];
893 batchResult->lineResults[i] = lineResult.release();
896 return batchResult.release();
906 for (int32_t i = 0; i < result->
lineCount; i++) {
919 for (int32_t i = 0; i < colorMap->
colorCount; i++) {
920 delete[] colorMap->
colors[i];
922 delete[] colorMap->
colors;
935 const char* lineText,
938 if (!grammar || !lineText) {
943 Grammar* gram =
static_cast<Grammar*
>(grammar);
946 ITokenizeLineResult result = gram->tokenizeLine(lineText, state);
949 auto map = tml::buildByteToUtf16Map(lineText, std::strlen(lineText));
957 cResult->
tokenCount =
static_cast<int32_t
>(result.tokens.size());
960 cResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
965 for (
size_t i = 0; i < result.tokens.size(); i++) {
966 const IToken& token = result.tokens[i];
967 cResult->tokens[i].
startIndex = tml::mapByteToUtf16(map, token.startIndex);
968 cResult->tokens[i].endIndex = tml::mapByteToUtf16(map, token.endIndex);
969 cResult->tokens[i].scopeDepth =
static_cast<int32_t
>(token.scopes.size());
972 cResult->tokens[i].scopes =
new char*[token.scopes.size()]();
973 for (
size_t j = 0; j < token.scopes.size(); j++) {
974 cResult->tokens[i].scopes[j] = stringToCString(token.scopes[j]);
978 return cResult.release();
987 const char* lineText,
990 if (!grammar || !lineText) {
995 Grammar* gram =
static_cast<Grammar*
>(grammar);
998 ITokenizeLineResult2 result = gram->tokenizeLine2(lineText, state);
1001 auto map = tml::buildByteToUtf16Map(lineText, std::strlen(lineText));
1007 cResult->
tokenCount =
static_cast<int32_t
>(result.tokens.size());
1008 cResult->tokens =
new uint32_t[result.tokens.size()];
1010 cResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
1014 for (
size_t i = 0; i < result.tokens.size(); i++) {
1017 cResult->tokens[i] = tml::mapByteToUtf16(map, result.tokens[i]);
1020 cResult->tokens[i] = result.tokens[i];
1024 return cResult.release();
1037 if (!grammar || !lines || lineCount <= 0) {
1042 Grammar* g =
static_cast<Grammar*
>(grammar);
1055 for (int32_t i = 0; i < lineCount; i++) {
1056 std::string lineText(lines[i]);
1057 auto result = g->tokenizeLine(lineText, state);
1060 state = result.ruleStack;
1063 auto map = tml::buildByteToUtf16Map(lineText.c_str(), lineText.size());
1068 lineResult->
tokenCount =
static_cast<int32_t
>(result.tokens.size());
1069 lineResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
1073 lineResult->tokens =
new TextMateToken[result.tokens.size()]();
1074 for (
size_t j = 0; j < result.tokens.size(); j++) {
1075 const auto& token = result.tokens[j];
1076 lineResult->tokens[j].
startIndex = tml::mapByteToUtf16(map, token.startIndex);
1077 lineResult->tokens[j].endIndex = tml::mapByteToUtf16(map, token.endIndex);
1078 lineResult->tokens[j].scopeDepth =
static_cast<int32_t
>(token.scopes.size());
1081 lineResult->tokens[j].scopes =
new char*[token.scopes.size()]();
1082 for (
size_t k = 0; k < token.scopes.size(); k++) {
1083 lineResult->tokens[j].scopes[k] = stringToCString(token.scopes[k]);
1087 batchResult->lineResults[i] = lineResult.release();
1090 return batchResult.release();
1103 if (!grammar || !lines || lineCount <= 0) {
1108 Grammar* g =
static_cast<Grammar*
>(grammar);
1120 for (int32_t i = 0; i < lineCount; i++) {
1121 std::string lineText(lines[i]);
1122 auto result = g->tokenizeLine2(lineText, state);
1124 state = result.ruleStack;
1126 auto map = tml::buildByteToUtf16Map(lineText.c_str(), lineText.size());
1130 lineResult->
tokenCount =
static_cast<int32_t
>(result.tokens.size());
1131 lineResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
1134 lineResult->tokens =
new uint32_t[result.tokens.size()];
1135 for (
size_t j = 0; j < result.tokens.size(); j++) {
1137 lineResult->tokens[j] = tml::mapByteToUtf16(map, result.tokens[j]);
1139 lineResult->tokens[j] = result.tokens[j];
1143 batchResult->lineResults[i] = lineResult.release();
1146 return batchResult.release();
1159 Grammar* gram =
static_cast<Grammar*
>(grammar);
1160 static thread_local std::string scopeName;
1161 scopeName = gram->getScopeName();
1162 return scopeName.c_str();
1171 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_set_theme(TextMateRegistry registry, const char *themeJsonContent)
Set the color theme on the registry.
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.
TextMateColorMap * textmate_registry_get_color_map(TextMateRegistry registry)
Get the color map from the registry after setting a theme.
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_result2(TextMateTokenizeMultiLinesResult2 *result)
Free a batch encoded tokenization result.
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_color_map(TextMateColorMap *colorMap)
Free a color map.
void textmate_free_tokenize_result2(TextMateTokenizeResult2 *result)
Free an encoded line tokenization result.
TextMateTokenizeMultiLinesResult2 * textmate_tokenize_lines2(TextMateGrammar grammar, const char **lines, int32_t lineCount, TextMateStateStack initialState)
Tokenize multiple lines with encoded tokens in a single call.
TextMateTokenizeMultiLinesResult2 * textmate_tokenize_lines2_utf16(TextMateGrammar grammar, const char **lines, int32_t lineCount, TextMateStateStack initialState)
Tokenize multiple lines with encoded tokens, returning UTF-16 indices.
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.
Theme color map returned by textmate_registry_get_color_map()
char ** colors
Array of hex color strings (e.g., "#RRGGBB" or "#RRGGBBAA")
int32_t colorCount
Number of colors in the array.
Represents a single token in tokenized text.
int32_t startIndex
Start position in the line (0-based)
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 with encoded tokens.
TextMateTokenizeResult2 ** lineResults
Array of encoded results, one per line.
int32_t lineCount
Number of lines tokenized.
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.
uint32_t * tokens
Array of encoded tokens.
Result from tokenizing a single line with decoded tokens.
TextMateToken * tokens
Array of tokens found in this line.
int32_t tokenCount
Number of tokens in the array.