TextMateLib 1.0
Modern C++ implementation of the TextMate syntax highlighting engine
Loading...
Searching...
No Matches
c_api.cpp
1#include "c_api.h"
2#include "main.h"
3#include "parseRawGrammar.h"
4#include "parseRawTheme.h"
5#include "theme.h"
6#include "utf16_utils.h"
7#include <string>
8#include <cstring>
9#include <fstream>
10#include <sstream>
11#include <cctype>
12#include <rapidjson/document.h>
13#include <rapidjson/error/en.h>
14
15using namespace tml;
16using namespace rapidjson;
17
18// Helper function to read file contents
19static std::string readFileContents(const char* filepath) {
20 std::ifstream file(filepath);
21 if (!file.is_open()) {
22 return "";
23 }
24 std::stringstream buffer;
25 buffer << file.rdbuf();
26 return buffer.str();
27}
28
29// Helper function to convert std::string to C string (caller must free)
30static char* stringToCString(const std::string& str) {
31 char* cstr = new char[str.length() + 1];
32 std::strcpy(cstr, str.c_str());
33 return cstr;
34}
35
36// ============================================================================
37// Theme Helper Functions
38// ============================================================================
39
40// Convert hex color string (#RRGGBB or #RRGGBBAA) to uint32_t (0xRRGGBBAA)
41static uint32_t hexColorToUint32(const std::string& hexColor) {
42 if (hexColor.empty() || hexColor[0] != '#') {
43 return 0;
44 }
45
46 std::string hex = hexColor.substr(1);
47
48 // Handle 6-char (#RRGGBB) - add full opacity
49 if (hex.length() == 6) {
50 hex += "FF";
51 }
52 // Handle 8-char (#RRGGBBAA) - convert to RGBA format
53 else if (hex.length() != 8) {
54 return 0;
55 }
56
57 try {
58 uint32_t value = std::stoul(hex, nullptr, 16);
59 // Convert from #RRGGBBAA to 0xRRGGBBAA
60 return value;
61 } catch (...) {
62 return 0;
63 }
64}
65
66// Convert font style string to flags
67static int32_t fontStyleStringToFlags(const std::string& fontStyle) {
68 int32_t flags = TEXTMATE_FONT_STYLE_NONE;
69
70 if (fontStyle.find("italic") != std::string::npos) {
72 }
73 if (fontStyle.find("bold") != std::string::npos) {
75 }
76 if (fontStyle.find("underline") != std::string::npos) {
78 }
79
80 return flags;
81}
82
83// Helper to free ScopeStack linked list created by ScopeStack::from()
84static void freeScopeStack(ScopeStack* stack) {
85 while (stack) {
86 ScopeStack* parent = stack->parent;
87 delete stack;
88 stack = parent;
89 }
90}
91
92// Parse space-separated scope path into vector of scope names
93static std::vector<ScopeName> parseScopePath(const char* scopePath) {
94 std::vector<ScopeName> scopes;
95 if (!scopePath || !scopePath[0]) {
96 return scopes;
97 }
98
99 std::istringstream iss(scopePath);
100 std::string scope;
101 while (iss >> scope) {
102 scopes.push_back(scope);
103 }
104 return scopes;
105}
106
107// Match all scopes in the stack against the theme and merge results
108// This iterates from outermost to innermost scope, with inner scopes overwriting outer ones
109static StyleAttributes* matchAllScopes(Theme* theme, const std::vector<ScopeName>& scopes) {
110 if (scopes.empty()) {
111 return nullptr;
112 }
113
114 StyleAttributes* merged = nullptr;
115
116 // Iterate through all scopes from outermost to innermost
117 // Build a progressively deeper scope stack for each match
118 for (size_t i = 0; i < scopes.size(); i++) {
119 // Create a scope stack up to this scope
120 ScopeStack* scopeStack = nullptr;
121 for (size_t j = 0; j <= i; j++) {
122 scopeStack = new ScopeStack(scopeStack, scopes[j]);
123 }
124
125 // Match against theme
126 StyleAttributes* attrs = theme->match(scopeStack);
127
128 // Clean up scope stack
129 freeScopeStack(scopeStack);
130
131 // Merge results
132 if (attrs) {
133 if (!merged) {
134 merged = attrs;
135 } else {
136 // Inner scope attributes override outer ones (if set)
137 if (attrs->fontStyle != static_cast<int>(FontStyle::NotSet)) {
138 merged->fontStyle = attrs->fontStyle;
139 }
140 if (attrs->foregroundId != 0) {
141 merged->foregroundId = attrs->foregroundId;
142 }
143 if (attrs->backgroundId != 0) {
144 merged->backgroundId = attrs->backgroundId;
145 }
146 delete attrs;
147 }
148 }
149 }
150
151 return merged;
152}
153
154// Parse JSON theme and create Theme object
155// Use the shared parseJSONTheme from parseRawTheme.h
156// (Implementation moved to parseRawTheme.cpp to avoid duplication)
157
158// ============================================================================
159// Theme C API Implementation
160// ============================================================================
161
163 if (!themePath) {
164 return nullptr;
165 }
166
167 try {
168 std::string content = readFileContents(themePath);
169 if (content.empty()) {
170 return nullptr;
171 }
172
173 Theme* theme = parseJSONTheme(content);
174 if (!theme) {
175 return nullptr;
176 }
177
178 StyleAttributes* defaults = theme->getDefaults();
179 if (!defaults) {
180 delete theme;
181 return nullptr;
182 }
183
184 auto managed = new ManagedTheme(theme, defaults);
185 return static_cast<TextMateTheme>(managed);
186 } catch (...) {
187 return nullptr;
188 }
189}
190
192 if (!jsonContent) {
193 return nullptr;
194 }
195
196 try {
197 Theme* theme = parseJSONTheme(jsonContent);
198 if (!theme) {
199 return nullptr;
200 }
201
202 StyleAttributes* defaults = theme->getDefaults();
203 if (!defaults) {
204 delete theme;
205 return nullptr;
206 }
207
208 auto managed = new ManagedTheme(theme, defaults);
209 return static_cast<TextMateTheme>(managed);
210 } catch (...) {
211 return nullptr;
212 }
213}
214
216 TextMateTheme theme,
217 const char* scopePath,
218 uint32_t defaultColor
219) {
220 if (!theme) {
221 return defaultColor;
222 }
223
224 try {
225 auto managed = static_cast<ManagedTheme*>(theme);
226 auto colorMap = managed->theme->getColorMap();
227
228 // If no scope path provided, return default foreground
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]);
234 }
235 }
236 return defaultColor;
237 }
238
239 // Parse scope path
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]);
246 }
247 }
248 return defaultColor;
249 }
250
251 // Match all scopes against theme and merge results
252 StyleAttributes* attrs = matchAllScopes(managed->theme, scopes);
253
254 // Get color from matched attributes
255 if (attrs) {
256 int fgId = attrs->foregroundId;
257 delete attrs;
258
259 if (fgId > 0 && fgId < static_cast<int>(colorMap.size())) {
260 return hexColorToUint32(colorMap[fgId]);
261 }
262 }
263
264 // Fall back to default foreground
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]);
269 }
270 }
271
272 return defaultColor;
273 } catch (...) {
274 return defaultColor;
275 }
276}
277
279 TextMateTheme theme,
280 const char* scopePath,
281 uint32_t defaultColor
282) {
283 if (!theme) {
284 return defaultColor;
285 }
286
287 try {
288 auto managed = static_cast<ManagedTheme*>(theme);
289 auto colorMap = managed->theme->getColorMap();
290
291 // If no scope path provided, return default background
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]);
297 }
298 }
299 return defaultColor;
300 }
301
302 // Parse scope path
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]);
309 }
310 }
311 return defaultColor;
312 }
313
314 // Match all scopes against theme and merge results
315 StyleAttributes* attrs = matchAllScopes(managed->theme, scopes);
316
317 // Get color from matched attributes
318 if (attrs) {
319 int bgId = attrs->backgroundId;
320 delete attrs;
321
322 if (bgId > 0 && bgId < static_cast<int>(colorMap.size())) {
323 return hexColorToUint32(colorMap[bgId]);
324 }
325 }
326
327 // Fall back to default background
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]);
332 }
333 }
334
335 return defaultColor;
336 } catch (...) {
337 return defaultColor;
338 }
339}
340
342 TextMateTheme theme,
343 const char* scopePath,
344 int32_t defaultStyle
345) {
346 if (!theme) {
347 return defaultStyle;
348 }
349
350 try {
351 auto managed = static_cast<ManagedTheme*>(theme);
352
353 // If no scope path provided, return default font style
354 if (!scopePath || !scopePath[0]) {
355 if (managed->defaults) {
356 return managed->defaults->fontStyle;
357 }
358 return defaultStyle;
359 }
360
361 // Parse scope path
362 std::vector<ScopeName> scopes = parseScopePath(scopePath);
363 if (scopes.empty()) {
364 if (managed->defaults) {
365 return managed->defaults->fontStyle;
366 }
367 return defaultStyle;
368 }
369
370 // Match all scopes against theme and merge results
371 StyleAttributes* attrs = matchAllScopes(managed->theme, scopes);
372
373 // Get font style from matched attributes
374 if (attrs) {
375 int fontStyle = attrs->fontStyle;
376 delete attrs;
377
378 // FontStyle::NotSet is -1, only return if explicitly set
379 if (fontStyle >= 0) {
380 return fontStyle;
381 }
382 }
383
384 // Fall back to default font style
385 if (managed->defaults) {
386 return managed->defaults->fontStyle;
387 }
388
389 return defaultStyle;
390 } catch (...) {
391 return defaultStyle;
392 }
393}
394
396 if (!theme) {
397 return 0xFFFFFFFF; // White
398 }
399
400 try {
401 auto managed = static_cast<ManagedTheme*>(theme);
402 if (managed->defaults) {
403 auto colorMap = managed->theme->getColorMap();
404 int fgId = managed->defaults->foregroundId;
405
406 if (fgId > 0 && fgId < static_cast<int>(colorMap.size())) {
407 return hexColorToUint32(colorMap[fgId]);
408 }
409 }
410 return 0xFFFFFFFF; // White fallback
411 } catch (...) {
412 return 0xFFFFFFFF;
413 }
414}
415
417 if (!theme) {
418 return 0x000000FF; // Black
419 }
420
421 try {
422 auto managed = static_cast<ManagedTheme*>(theme);
423 if (managed->defaults) {
424 auto colorMap = managed->theme->getColorMap();
425 int bgId = managed->defaults->backgroundId;
426
427 if (bgId > 0 && bgId < static_cast<int>(colorMap.size())) {
428 return hexColorToUint32(colorMap[bgId]);
429 }
430 }
431 return 0x000000FF; // Black fallback
432 } catch (...) {
433 return 0x000000FF;
434 }
435}
436
438 if (theme) {
439 auto managed = static_cast<ManagedTheme*>(theme);
440 delete managed;
441 }
442}
443
444// Initialize Oniguruma library
446 try {
447 IOnigLib* onigLib = new DefaultOnigLib();
448 return static_cast<TextMateOnigLib>(onigLib);
449 } catch (...) {
450 return nullptr;
451 }
452}
453
454// Helper class to manage registry with internal grammar storage
455class ManagedRegistry {
456public:
457 Registry* registry;
458 std::map<std::string, IRawGrammar*> preloadedGrammars;
459 std::map<std::string, std::vector<std::string>> injections;
460
461 ManagedRegistry(IOnigLib* onigLib) {
462 RegistryOptions options;
463 options.onigLib = onigLib;
464
465 // Set up loadGrammar callback to return from preloaded grammars
466 options.loadGrammar = [this](const ScopeName& scopeName) -> IRawGrammar* {
467 auto it = preloadedGrammars.find(scopeName);
468 if (it != preloadedGrammars.end()) {
469 return it->second;
470 }
471 return nullptr;
472 };
473
474 // Set up getInjections callback to return configured injections
475 options.getInjections = [this](const ScopeName& scopeName) -> std::vector<ScopeName> {
476 auto it = injections.find(scopeName);
477 if (it != injections.end()) {
478 return it->second;
479 }
480 return std::vector<ScopeName>();
481 };
482
483 registry = new Registry(options);
484 }
485
486 ~ManagedRegistry() {
487 if (registry) {
488 delete registry;
489 }
490 // Note: Don't delete preloaded grammars as they're owned by the registry now
491 }
492};
493
494// Create registry with Oniguruma library
496 try {
497 ManagedRegistry* managed = new ManagedRegistry(static_cast<IOnigLib*>(onigLib));
498 return static_cast<TextMateRegistry>(managed);
499 } catch (...) {
500 return nullptr;
501 }
502}
503
504// Dispose registry
506 if (registry) {
507 ManagedRegistry* managed = static_cast<ManagedRegistry*>(registry);
508 delete managed;
509 }
510}
511
512// Add grammar to registry from JSON file (does not return Grammar, just registers it)
514 TextMateRegistry registry,
515 const char* grammarPath
516) {
517 if (!registry || !grammarPath) {
518 return 0;
519 }
520
521 try {
522 std::string content = readFileContents(grammarPath);
523 if (content.empty()) {
524 return 0;
525 }
526
527 std::string pathStr = grammarPath;
528 IRawGrammar* rawGrammar = parseRawGrammar(content, &pathStr);
529 if (!rawGrammar) {
530 return 0;
531 }
532
533 ManagedRegistry* managed = static_cast<ManagedRegistry*>(registry);
534 managed->preloadedGrammars[rawGrammar->scopeName] = rawGrammar;
535
536 return 1; // Success
537 } catch (...) {
538 return 0;
539 }
540}
541
542// Add grammar to registry from JSON string (does not return Grammar, just registers it)
544 TextMateRegistry registry,
545 const char* jsonContent
546) {
547 if (!registry || !jsonContent) {
548 return 0;
549 }
550
551 try {
552 IRawGrammar* rawGrammar = parseRawGrammar(jsonContent, nullptr);
553 if (!rawGrammar) {
554 return 0;
555 }
556
557 ManagedRegistry* managed = static_cast<ManagedRegistry*>(registry);
558 managed->preloadedGrammars[rawGrammar->scopeName] = rawGrammar;
559
560 return 1; // Success
561 } catch (...) {
562 return 0;
563 }
564}
565
566// Set grammar injections for a scope (call before loading the grammar)
568 TextMateRegistry registry,
569 const char* scopeName,
570 const char** injections,
571 int32_t injectionCount
572) {
573 if (!registry || !scopeName || !injections) {
574 return;
575 }
576
577 try {
578 ManagedRegistry* managed = static_cast<ManagedRegistry*>(registry);
579 std::vector<std::string> injectionsList;
580 for (int32_t i = 0; i < injectionCount; i++) {
581 if (injections[i]) {
582 injectionsList.push_back(injections[i]);
583 }
584 }
585 managed->injections[scopeName] = injectionsList;
586 } catch (...) {
587 // Ignore errors
588 }
589}
590
591// Load grammar by scope name (after grammars have been added to registry)
593 TextMateRegistry registry,
594 const char* scopeName
595) {
596 if (!registry || !scopeName) {
597 return nullptr;
598 }
599
600 try {
601 ManagedRegistry* managed = static_cast<ManagedRegistry*>(registry);
602 Grammar* grammar = managed->registry->loadGrammar(scopeName);
603 return static_cast<TextMateGrammar>(grammar);
604 } catch (...) {
605 return nullptr;
606 }
607}
608
609// Get INITIAL state
613
614// Tokenize a line of text
616 TextMateGrammar grammar,
617 const char* lineText,
618 TextMateStateStack prevState
619) {
620 if (!grammar || !lineText) {
621 return nullptr;
622 }
623
624 try {
625 Grammar* gram = static_cast<Grammar*>(grammar);
626 StateStack* state = static_cast<StateStack*>(prevState);
627
628 ITokenizeLineResult result = gram->tokenizeLine(lineText, state);
629
630 // Allocate result structure
632 cResult->tokenCount = result.tokens.size();
633 cResult->tokens = new TextMateToken[cResult->tokenCount];
634 cResult->ruleStack = static_cast<TextMateStateStack>(result.ruleStack);
635 cResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
636
637 // Convert tokens
638 for (int i = 0; i < cResult->tokenCount; i++) {
639 const IToken& token = result.tokens[i];
640 cResult->tokens[i].startIndex = token.startIndex;
641 cResult->tokens[i].endIndex = token.endIndex;
642 cResult->tokens[i].scopeDepth = token.scopes.size();
643
644 // Allocate scope strings
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]);
648 }
649 }
650
651 return cResult;
652 } catch (...) {
653 return nullptr;
654 }
655}
656
657// Tokenize a line of text with encoded tokens
659 TextMateGrammar grammar,
660 const char* lineText,
661 TextMateStateStack prevState
662) {
663 if (!grammar || !lineText) {
664 return nullptr;
665 }
666
667 try {
668 Grammar* gram = static_cast<Grammar*>(grammar);
669 StateStack* state = static_cast<StateStack*>(prevState);
670
671 ITokenizeLineResult2 result = gram->tokenizeLine2(lineText, state);
672
673 // Allocate result structure
675 cResult->tokenCount = result.tokens.size();
676 cResult->tokens = new uint32_t[cResult->tokenCount];
677 cResult->ruleStack = static_cast<TextMateStateStack>(result.ruleStack);
678 cResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
679
680 // Copy tokens
681 for (int i = 0; i < cResult->tokenCount; i++) {
682 cResult->tokens[i] = result.tokens[i];
683 }
684
685 return cResult;
686 } catch (...) {
687 return nullptr;
688 }
689}
690
691// Free tokenize result
693 if (result) {
694 if (result->tokens) {
695 for (int i = 0; i < result->tokenCount; i++) {
696 if (result->tokens[i].scopes) {
697 for (int j = 0; j < result->tokens[i].scopeDepth; j++) {
698 delete[] result->tokens[i].scopes[j];
699 }
700 delete[] result->tokens[i].scopes;
701 }
702 }
703 delete[] result->tokens;
704 }
705 delete result;
706 }
707}
708
709// Free tokenize result2
711 if (result) {
712 if (result->tokens) {
713 delete[] result->tokens;
714 }
715 delete result;
716 }
717}
718
719// Batch tokenize multiple lines (Phase 2 optimization)
721 TextMateGrammar grammar,
722 const char** lines,
723 int32_t lineCount,
724 TextMateStateStack initialState
725) {
726 if (!grammar || !lines || lineCount <= 0) {
727 return nullptr;
728 }
729
730 try {
731 Grammar* g = static_cast<Grammar*>(grammar);
732 StateStack* state = static_cast<StateStack*>(initialState);
733
734 // Allocate result structure
736 batchResult->lineCount = lineCount;
737 batchResult->lineResults = new TextMateTokenizeResult*[lineCount];
738
739 // Tokenize each line, propagating state
740 for (int32_t i = 0; i < lineCount; i++) {
741 std::string lineText(lines[i]);
742 auto result = g->tokenizeLine(lineText, state);
743
744 // Update state for next line
745 state = result.ruleStack;
746
747 // Allocate result for this line
749 lineResult->tokenCount = result.tokens.size();
750 lineResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
751 lineResult->ruleStack = static_cast<TextMateStateStack>(result.ruleStack);
752
753 // Allocate and populate tokens
754 lineResult->tokens = new TextMateToken[lineResult->tokenCount];
755 for (size_t j = 0; j < result.tokens.size(); j++) {
756 const auto& token = result.tokens[j];
757 lineResult->tokens[j].startIndex = token.startIndex;
758 lineResult->tokens[j].endIndex = token.endIndex;
759 lineResult->tokens[j].scopeDepth = token.scopes.size();
760
761 // Allocate scope array
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]);
765 }
766 }
767
768 batchResult->lineResults[i] = lineResult;
769 }
770
771 return batchResult;
772 } catch (...) {
773 return nullptr;
774 }
775}
776
777// Free batch tokenize result
779 if (result) {
780 // Free each line result
781 for (int32_t i = 0; i < result->lineCount; i++) {
783 }
784 delete[] result->lineResults;
785 delete result;
786 }
787}
788
789// ============================================================================
790// UTF-16 Tokenization API
791// ============================================================================
792
793// Tokenize a line of text with UTF-16 code unit indices
795 TextMateGrammar grammar,
796 const char* lineText,
797 TextMateStateStack prevState
798) {
799 if (!grammar || !lineText) {
800 return nullptr;
801 }
802
803 try {
804 Grammar* gram = static_cast<Grammar*>(grammar);
805 StateStack* state = static_cast<StateStack*>(prevState);
806
807 ITokenizeLineResult result = gram->tokenizeLine(lineText, state);
808
809 // Build byte-offset to UTF-16 index map
810 auto map = tml::buildByteToUtf16Map(lineText, std::strlen(lineText));
811
812 // Allocate result structure
814 cResult->tokenCount = result.tokens.size();
815 cResult->tokens = new TextMateToken[cResult->tokenCount];
816 cResult->ruleStack = static_cast<TextMateStateStack>(result.ruleStack);
817 cResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
818
819 // Convert tokens with UTF-16 indices
820 // Note: the tokenizer may internally append '\n', so token indices
821 // can exceed strlen(lineText). Use mapByteToUtf16 for safe lookup.
822 for (int i = 0; i < cResult->tokenCount; i++) {
823 const IToken& token = result.tokens[i];
824 cResult->tokens[i].startIndex = tml::mapByteToUtf16(map, token.startIndex);
825 cResult->tokens[i].endIndex = tml::mapByteToUtf16(map, token.endIndex);
826 cResult->tokens[i].scopeDepth = token.scopes.size();
827
828 // Allocate scope strings
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]);
832 }
833 }
834
835 return cResult;
836 } catch (...) {
837 return nullptr;
838 }
839}
840
841// Tokenize a line of text with encoded tokens and UTF-16 indices
843 TextMateGrammar grammar,
844 const char* lineText,
845 TextMateStateStack prevState
846) {
847 if (!grammar || !lineText) {
848 return nullptr;
849 }
850
851 try {
852 Grammar* gram = static_cast<Grammar*>(grammar);
853 StateStack* state = static_cast<StateStack*>(prevState);
854
855 ITokenizeLineResult2 result = gram->tokenizeLine2(lineText, state);
856
857 // Build byte-offset to UTF-16 index map
858 auto map = tml::buildByteToUtf16Map(lineText, std::strlen(lineText));
859
860 // Allocate result structure
862 cResult->tokenCount = result.tokens.size();
863 cResult->tokens = new uint32_t[cResult->tokenCount];
864 cResult->ruleStack = static_cast<TextMateStateStack>(result.ruleStack);
865 cResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
866
867 // Copy tokens, converting start offsets from UTF-8 byte to UTF-16
868 // Encoded tokens are pairs: [startIndex, metadata, startIndex, metadata, ...]
869 for (int i = 0; i < cResult->tokenCount; i++) {
870 if (i % 2 == 0) {
871 // Even indices are start offsets
872 cResult->tokens[i] = tml::mapByteToUtf16(map, result.tokens[i]);
873 } else {
874 // Odd indices are metadata — pass through
875 cResult->tokens[i] = result.tokens[i];
876 }
877 }
878
879 return cResult;
880 } catch (...) {
881 return nullptr;
882 }
883}
884
885// Batch tokenize multiple lines with UTF-16 indices
887 TextMateGrammar grammar,
888 const char** lines,
889 int32_t lineCount,
890 TextMateStateStack initialState
891) {
892 if (!grammar || !lines || lineCount <= 0) {
893 return nullptr;
894 }
895
896 try {
897 Grammar* g = static_cast<Grammar*>(grammar);
898 StateStack* state = static_cast<StateStack*>(initialState);
899
900 // Allocate result structure
902 batchResult->lineCount = lineCount;
903 batchResult->lineResults = new TextMateTokenizeResult*[lineCount];
904
905 // Tokenize each line, propagating state
906 for (int32_t i = 0; i < lineCount; i++) {
907 std::string lineText(lines[i]);
908 auto result = g->tokenizeLine(lineText, state);
909
910 // Update state for next line
911 state = result.ruleStack;
912
913 // Build byte-offset to UTF-16 index map for this line
914 auto map = tml::buildByteToUtf16Map(lineText.c_str(), lineText.size());
915
916 // Allocate result for this line
918 lineResult->tokenCount = result.tokens.size();
919 lineResult->stoppedEarly = result.stoppedEarly ? 1 : 0;
920 lineResult->ruleStack = static_cast<TextMateStateStack>(result.ruleStack);
921
922 // Allocate and populate tokens with UTF-16 indices
923 lineResult->tokens = new TextMateToken[lineResult->tokenCount];
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);
928 lineResult->tokens[j].scopeDepth = token.scopes.size();
929
930 // Allocate scope array
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]);
934 }
935 }
936
937 batchResult->lineResults[i] = lineResult;
938 }
939
940 return batchResult;
941 } catch (...) {
942 return nullptr;
943 }
944}
945
946// Get scope name from grammar
948 if (!grammar) {
949 return nullptr;
950 }
951
952 try {
953 Grammar* gram = static_cast<Grammar*>(grammar);
954 static thread_local std::string scopeName;
955 scopeName = gram->getScopeName();
956 return scopeName.c_str();
957 } catch (...) {
958 return nullptr;
959 }
960}
961
962// Dispose Oniguruma library
964 if (onigLib) {
965 IOnigLib* lib = static_cast<IOnigLib*>(onigLib);
966 delete lib;
967 }
968}
C language API for TextMateLib.
Helper class to manage theme resources and provide C API implementation.
Definition theme.h:209
Abstract interface representing the parsing state at the end of a line.
Definition types.h:55
const StateStack * INITIAL
Initial parsing state for the first line of a document.
Definition main.cpp:6
std::string ScopeName
Semantic name identifying a scope (e.g., "source.javascript", "comment.line")
Definition types.h:20
void * TextMateGrammar
Handle to a grammar definition for a specific language.
Definition c_api.h:38
void * TextMateTheme
Handle to a theme object containing color schemes.
Definition c_api.h:35
void * TextMateRegistry
Handle to the grammar registry managing loaded grammars and themes.
Definition c_api.h:47
void * TextMateStateStack
Handle to a parsing state stack (immutable, used for incremental tokenization)
Definition c_api.h:41
void * TextMateOnigLib
Handle to the Oniguruma regex library instance.
Definition c_api.h:44
int textmate_registry_add_grammar_from_json(TextMateRegistry registry, const char *jsonContent)
Register a grammar from a JSON string.
Definition c_api.cpp:543
TextMateOnigLib textmate_oniglib_create()
Initialize the Oniguruma regular expression library.
Definition c_api.cpp:445
void textmate_registry_dispose(TextMateRegistry registry)
Free a registry and all its resources.
Definition c_api.cpp:505
int textmate_registry_add_grammar_from_file(TextMateRegistry registry, const char *grammarPath)
Register a grammar from a JSON file.
Definition c_api.cpp:513
TextMateRegistry textmate_registry_create(TextMateOnigLib onigLib)
Create a new grammar registry.
Definition c_api.cpp:495
TextMateGrammar textmate_registry_load_grammar(TextMateRegistry registry, const char *scopeName)
Load a grammar by scope name.
Definition c_api.cpp:592
void textmate_registry_set_injections(TextMateRegistry registry, const char *scopeName, const char **injections, int32_t injectionCount)
Set grammar injection rules for a scope.
Definition c_api.cpp:567
uint32_t textmate_theme_get_default_background(TextMateTheme theme)
Get the default/fallback background color for the entire theme.
Definition c_api.cpp:416
uint32_t textmate_theme_get_background(TextMateTheme theme, const char *scopePath, uint32_t defaultColor)
Get the background color for a scope path.
Definition c_api.cpp:278
void textmate_theme_dispose(TextMateTheme theme)
Free a theme object and release resources.
Definition c_api.cpp:437
uint32_t textmate_theme_get_foreground(TextMateTheme theme, const char *scopePath, uint32_t defaultColor)
Get the foreground color for a scope path.
Definition c_api.cpp:215
int32_t textmate_theme_get_font_style(TextMateTheme theme, const char *scopePath, int32_t defaultStyle)
Get the font style flags for a scope path.
Definition c_api.cpp:341
#define TEXTMATE_FONT_STYLE_NONE
Font style flag constants for textmate_theme_get_font_style()
Definition c_api.h:155
uint32_t textmate_theme_get_default_foreground(TextMateTheme theme)
Get the default/fallback foreground color for the entire theme.
Definition c_api.cpp:395
#define TEXTMATE_FONT_STYLE_BOLD
Bold text.
Definition c_api.h:157
#define TEXTMATE_FONT_STYLE_UNDERLINE
Underlined text.
Definition c_api.h:158
#define TEXTMATE_FONT_STYLE_ITALIC
Italic text.
Definition c_api.h:156
TextMateTheme textmate_theme_load_from_json(const char *jsonContent)
Load a theme from a JSON string.
Definition c_api.cpp:191
TextMateTheme textmate_theme_load_from_file(const char *themePath)
Load a theme from a JSON file.
Definition c_api.cpp:162
void textmate_free_tokenize_result(TextMateTokenizeResult *result)
Free a line tokenization result.
Definition c_api.cpp:692
const char * textmate_grammar_get_scope_name(TextMateGrammar grammar)
Get the scope name (language identifier) of a grammar.
Definition c_api.cpp:947
TextMateTokenizeMultiLinesResult * textmate_tokenize_lines(TextMateGrammar grammar, const char **lines, int32_t lineCount, TextMateStateStack initialState)
Tokenize multiple lines in a single call.
Definition c_api.cpp:720
void textmate_oniglib_dispose(TextMateOnigLib onigLib)
Free the Oniguruma library.
Definition c_api.cpp:963
TextMateStateStack textmate_get_initial_state()
Get the initial parsing state.
Definition c_api.cpp:610
TextMateTokenizeResult2 * textmate_tokenize_line2(TextMateGrammar grammar, const char *lineText, TextMateStateStack prevState)
Tokenize a single line of text with encoded tokens (more efficient)
Definition c_api.cpp:658
void textmate_free_tokenize_lines_result(TextMateTokenizeMultiLinesResult *result)
Free a batch tokenization result.
Definition c_api.cpp:778
TextMateTokenizeResult * textmate_tokenize_line(TextMateGrammar grammar, const char *lineText, TextMateStateStack prevState)
Tokenize a single line of text with decoded scopes.
Definition c_api.cpp:615
void textmate_free_tokenize_result2(TextMateTokenizeResult2 *result)
Free an encoded line tokenization result.
Definition c_api.cpp:710
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.
Definition c_api.cpp:886
TextMateTokenizeResult2 * textmate_tokenize_line2_utf16(TextMateGrammar grammar, const char *lineText, TextMateStateStack prevState)
Tokenize a single line with encoded tokens, returning UTF-16 indices.
Definition c_api.cpp:842
TextMateTokenizeResult * textmate_tokenize_line_utf16(TextMateGrammar grammar, const char *lineText, TextMateStateStack prevState)
Tokenize a single line with decoded scopes, returning UTF-16 indices.
Definition c_api.cpp:794
Represents a single token in tokenized text.
Definition c_api.h:58
int32_t startIndex
Start position in the line (0-based)
Definition c_api.h:59
int32_t endIndex
End position (exclusive)
Definition c_api.h:60
char ** scopes
Array of scope strings (e.g., "keyword.control", "string.quoted.double")
Definition c_api.h:62
int32_t scopeDepth
Number of scopes in the scope hierarchy.
Definition c_api.h:61
Result from batch tokenizing multiple lines.
Definition c_api.h:100
int32_t lineCount
Number of lines tokenized.
Definition c_api.h:102
TextMateTokenizeResult ** lineResults
Array of results, one per line.
Definition c_api.h:101
Result from tokenizing a single line with encoded tokens.
Definition c_api.h:86
int32_t tokenCount
Number of tokens in the array.
Definition c_api.h:88
TextMateStateStack ruleStack
State at end of line (pass to next line's tokenization)
Definition c_api.h:89
uint32_t * tokens
Array of encoded tokens.
Definition c_api.h:87
int32_t stoppedEarly
Non-zero if tokenization stopped before end (time limit hit)
Definition c_api.h:90
Result from tokenizing a single line with decoded tokens.
Definition c_api.h:72
int32_t stoppedEarly
Non-zero if tokenization stopped before end (time limit hit)
Definition c_api.h:76
TextMateStateStack ruleStack
State at end of line (pass to next line's tokenization)
Definition c_api.h:75
TextMateToken * tokens
Array of tokens found in this line.
Definition c_api.h:73
int32_t tokenCount
Number of tokens in the array.
Definition c_api.h:74