TextMateLib 1.0
Modern C++ implementation of the TextMate syntax highlighting engine
Loading...
Searching...
No Matches
grammar.cpp
1#include "grammar.h"
2#include "tokenizeString.h"
3#include "encodedTokenAttributes.h"
4#include "matcher.h"
5#include <algorithm>
6#include <iostream>
7#include <unordered_set>
8
9namespace tml {
10
11// Static member initialization
12StateStackImpl* StateStackImpl::NULL_STATE = nullptr;
13
14// Active-arena state (see StackNodeArena in grammar.h). Single-threaded by design.
15static StackNodeArena* g_activeArena = nullptr;
16
17StackNodeArena* tmlGetActiveArena() { return g_activeArena; }
18void tmlSetActiveArena(StackNodeArena* arena) { g_activeArena = arena; }
19
20void StackNodeArena::clear() {
21 for (StateStackImpl* s : stacks) delete s;
22 for (AttributedScopeStack* a : scopes) delete a;
23 stacks.clear();
24 scopes.clear();
25}
26
27void StackNodeArena::sweepKeeping(const std::vector<StateStackImpl*>& roots) {
28 // Mark every node reachable from the surviving roots by walking parent chains. Node
29 // destructors never dereference parent/scope pointers, so freeing unmarked nodes in
30 // arena order cannot cause double-free or use-after-free regardless of ordering.
31 std::unordered_set<const StateStackImpl*> liveStacks;
32 std::unordered_set<const AttributedScopeStack*> liveScopes;
33
34 auto markScopes = [&](AttributedScopeStack* a) {
35 while (a && liveScopes.insert(a).second) a = a->parent;
36 };
37
38 for (StateStackImpl* root : roots) {
39 StateStackImpl* s = root;
40 while (s && liveStacks.insert(s).second) {
41 markScopes(s->nameScopesList);
42 markScopes(s->contentNameScopesList);
43 s = s->parent;
44 }
45 }
46
47 std::vector<StateStackImpl*> keptStacks;
48 keptStacks.reserve(liveStacks.size());
49 for (StateStackImpl* s : stacks) {
50 if (liveStacks.count(s)) keptStacks.push_back(s);
51 else delete s;
52 }
53 stacks.swap(keptStacks);
54
55 std::vector<AttributedScopeStack*> keptScopes;
56 keptScopes.reserve(liveScopes.size());
57 for (AttributedScopeStack* a : scopes) {
58 if (liveScopes.count(a)) keptScopes.push_back(a);
59 else delete a;
60 }
61 scopes.swap(keptScopes);
62}
63
64// BalancedBracketSelectors implementation
65
66BalancedBracketSelectors::BalancedBracketSelectors(
67 const std::vector<std::string>& balancedBracketSelectors,
68 const std::vector<std::string>& unbalancedBracketSelectors)
69 : _allowAny(false) {
70
71 if (balancedBracketSelectors.empty() && unbalancedBracketSelectors.empty()) {
72 _allowAny = true;
73 }
74
75 // Create matchers (simplified - full implementation would use createMatchers)
76 // For now, store the selectors
77}
78
79bool BalancedBracketSelectors::matchesAlways() const {
80 return _allowAny && _unbalancedBracketMatchers.empty();
81}
82
83bool BalancedBracketSelectors::matchesNever() const {
84 return !_allowAny && _balancedBracketMatchers.empty();
85}
86
87bool BalancedBracketSelectors::match(const std::vector<std::string>& scopes) const {
88 // Simple implementation: returns true if balanced brackets are enabled globally.
89 // Full implementation would match against specific scope selectors in _balancedBracketMatchers.
90 return _allowAny;
91}
92
93// AttributedScopeStack implementation
94
95AttributedScopeStack::AttributedScopeStack(
96 AttributedScopeStack* parent_,
97 const ScopeName& scopeName_,
98 EncodedTokenAttributes tokenAttributes_)
99 : parent(parent_), scopeName(scopeName_), tokenAttributes(tokenAttributes_) {
100 if (g_activeArena) g_activeArena->scopes.push_back(this);
101}
102
103AttributedScopeStack::~AttributedScopeStack() {
104 // Don't delete parent - it's managed separately
105}
106
107AttributedScopeStack* AttributedScopeStack::createRoot(
108 const std::string& scopeName,
109 EncodedTokenAttributes tokenAttributes) {
110 return new AttributedScopeStack(nullptr, scopeName, tokenAttributes);
111}
112
113AttributedScopeStack* AttributedScopeStack::createRootAndLookUpScopeName(
114 const std::string& scopeName,
115 EncodedTokenAttributes tokenAttributes,
116 Grammar* grammar) {
117
118 BasicScopeAttributes rawMetadata = grammar->getMetadataForScope(scopeName);
119
120 ScopeStack scopeStack(nullptr, scopeName);
121 StyleAttributes* rootStyle = grammar->getThemeProvider()->themeMatch(&scopeStack);
122
123 EncodedTokenAttributes scopeTokenAttributes = EncodedTokenAttributesHelper::set(
124 tokenAttributes,
125 rawMetadata.languageId,
126 rawMetadata.tokenType,
127 nullptr,
128 rootStyle ? rootStyle->fontStyle : static_cast<int>(FontStyle::NotSet),
129 rootStyle ? rootStyle->foregroundId : 0,
130 rootStyle ? rootStyle->backgroundId : 0
131 );
132
133 delete rootStyle;
134
135 return new AttributedScopeStack(nullptr, scopeName, scopeTokenAttributes);
136}
137
138AttributedScopeStack* AttributedScopeStack::push(
139 Grammar* grammar,
140 const std::string& scopeName) {
141
142 if (scopeName.empty()) {
143 return this;
144 }
145
146 BasicScopeAttributes rawMetadata = grammar->getMetadataForScope(scopeName);
147
148 std::vector<ScopeName> names = this->getScopeNames();
149 names.push_back(scopeName);
150 ScopeStack* scopeStack = ScopeStack::from(names);
151 StyleAttributes* themeData = grammar->getThemeProvider()->themeMatch(scopeStack);
152
153 EncodedTokenAttributes scopeTokenAttributes = EncodedTokenAttributesHelper::set(
154 this->tokenAttributes,
155 rawMetadata.languageId,
156 rawMetadata.tokenType,
157 nullptr,
158 themeData ? themeData->fontStyle : static_cast<int>(FontStyle::NotSet),
159 themeData ? themeData->foregroundId : 0,
160 themeData ? themeData->backgroundId : 0
161 );
162
163 delete themeData;
164 while (scopeStack) {
165 ScopeStack* p = scopeStack->parent;
166 delete scopeStack;
167 scopeStack = p;
168 }
169
170 return new AttributedScopeStack(this, scopeName, scopeTokenAttributes);
171}
172
173AttributedScopeStack* AttributedScopeStack::pushAttributed(
174 const std::string& scopePath,
175 Grammar* grammar) {
176
177 if (scopePath.empty()) {
178 return this;
179 }
180
181 // Check if scopePath contains spaces (multiple scopes)
182 if (scopePath.find(' ') == std::string::npos) {
183 // This is the common case and much faster - single scope
184 return _pushAttributed(this, scopePath, grammar);
185 }
186
187 // Split by spaces and push each scope
188 std::vector<std::string> scopes;
189 std::string currentScope;
190 for (char c : scopePath) {
191 if (c == ' ') {
192 if (!currentScope.empty()) {
193 scopes.push_back(currentScope);
194 currentScope.clear();
195 }
196 } else {
197 currentScope += c;
198 }
199 }
200 if (!currentScope.empty()) {
201 scopes.push_back(currentScope);
202 }
203
204 AttributedScopeStack* result = this;
205 for (const std::string& scope : scopes) {
206 result = _pushAttributed(result, scope, grammar);
207 }
208 return result;
209}
210
211AttributedScopeStack* AttributedScopeStack::_pushAttributed(
212 AttributedScopeStack* target,
213 const std::string& scopeName,
214 Grammar* grammar) {
215
216 if (scopeName.empty()) {
217 return target;
218 }
219
220 BasicScopeAttributes rawMetadata = grammar->getMetadataForScope(scopeName);
221
222 std::vector<ScopeName> names = target->getScopeNames();
223 names.push_back(scopeName);
224 ScopeStack* scopeStack = ScopeStack::from(names);
225 StyleAttributes* themeData = grammar->getThemeProvider()->themeMatch(scopeStack);
226
227 EncodedTokenAttributes metadata = EncodedTokenAttributesHelper::set(
228 target->tokenAttributes,
229 rawMetadata.languageId,
230 rawMetadata.tokenType,
231 nullptr,
232 themeData ? themeData->fontStyle : static_cast<int>(FontStyle::NotSet),
233 themeData ? themeData->foregroundId : 0,
234 themeData ? themeData->backgroundId : 0
235 );
236
237 delete themeData;
238 while (scopeStack) {
239 ScopeStack* p = scopeStack->parent;
240 delete scopeStack;
241 scopeStack = p;
242 }
243
244 return new AttributedScopeStack(target, scopeName, metadata);
245}
246
247std::vector<std::string> AttributedScopeStack::getScopeNames() const {
248 std::vector<std::string> result;
249 const AttributedScopeStack* current = this;
250 while (current) {
251 result.push_back(current->scopeName);
252 current = current->parent;
253 }
254 std::reverse(result.begin(), result.end());
255 return result;
256}
257
258bool AttributedScopeStack::equals(AttributedScopeStack* a, AttributedScopeStack* b) {
259 if (a == b) return true;
260 if (!a || !b) return false;
261
262 while (a && b) {
263 if (a->scopeName != b->scopeName || a->tokenAttributes != b->tokenAttributes) {
264 return false;
265 }
266 a = a->parent;
267 b = b->parent;
268 }
269
270 return a == nullptr && b == nullptr;
271}
272
273// StateStackImpl implementation
274
275StateStackImpl::StateStackImpl(
276 StateStackImpl* parent_,
277 RuleId ruleId_,
278 int enterPos_,
279 int anchorPos_,
280 bool beginRuleCapturedEOL_,
281 const std::string* endRule_,
282 AttributedScopeStack* nameScopesList_,
283 AttributedScopeStack* contentNameScopesList_)
284 : parent(parent_),
285 ruleId(ruleId_),
286 _enterPos(enterPos_),
287 _anchorPos(anchorPos_),
288 beginRuleCapturedEOL(beginRuleCapturedEOL_),
289 endRule(endRule_ ? new std::string(*endRule_) : nullptr),
290 nameScopesList(nameScopesList_),
291 contentNameScopesList(contentNameScopesList_) {
292
293 depth = parent ? parent->depth + 1 : 1;
294 if (g_activeArena) g_activeArena->stacks.push_back(this);
295}
296
297StateStackImpl::~StateStackImpl() {
298 delete endRule;
299 // Don't delete nameScopesList and contentNameScopesList - managed separately
300}
301
302StateStack* StateStackImpl::clone() {
303 return new StateStackImpl(
304 parent,
305 ruleId,
306 _enterPos,
307 _anchorPos,
308 beginRuleCapturedEOL,
309 endRule,
310 nameScopesList,
311 contentNameScopesList
312 );
313}
314
315bool StateStackImpl::equals(StateStack* other) {
316 if (this == other) return true;
317 if (!other) return false;
318
319 StateStackImpl* otherImpl = dynamic_cast<StateStackImpl*>(other);
320 if (!otherImpl) return false;
321
322 // Compare all fields
323 if (ruleIdToNumber(ruleId) != ruleIdToNumber(otherImpl->ruleId)) return false;
324 if (_enterPos != otherImpl->_enterPos) return false;
325
326 bool thisHasEndRule = (endRule != nullptr);
327 bool otherHasEndRule = (otherImpl->endRule != nullptr);
328 if (thisHasEndRule != otherHasEndRule) return false;
329 if (thisHasEndRule && *endRule != *otherImpl->endRule) return false;
330
331 if (!AttributedScopeStack::equals(nameScopesList, otherImpl->nameScopesList)) return false;
332 if (!AttributedScopeStack::equals(contentNameScopesList, otherImpl->contentNameScopesList)) return false;
333
334 // Compare parents recursively
335 if (parent == nullptr && otherImpl->parent == nullptr) return true;
336 if (parent == nullptr || otherImpl->parent == nullptr) return false;
337
338 return parent->equals(otherImpl->parent);
339}
340
341void StateStackImpl::reset() {
342 // Reset enter and anchor positions
343 StateStackImpl* el = this;
344 while (el) {
345 el->_enterPos = -1;
346 el->_anchorPos = -1;
347 el = el->parent;
348 }
349}
350
351StateStackImpl* StateStackImpl::push(
352 RuleId ruleId,
353 int enterPos,
354 int anchorPos,
355 bool beginRuleCapturedEOL,
356 const std::string* endRule,
357 AttributedScopeStack* nameScopesList,
358 AttributedScopeStack* contentNameScopesList) {
359
360 return new StateStackImpl(
361 this,
362 ruleId,
363 enterPos,
364 anchorPos,
365 beginRuleCapturedEOL,
366 endRule,
367 nameScopesList,
368 contentNameScopesList
369 );
370}
371
372StateStackImpl* StateStackImpl::pop() {
373 return this->parent;
374}
375
376StateStackImpl* StateStackImpl::safePop() {
377 if (this->parent) {
378 return this->parent;
379 }
380 return this;
381}
382
383Rule* StateStackImpl::getRule(Grammar* grammar) {
384 return grammar->getRule(this->ruleId);
385}
386
387StateStackImpl* StateStackImpl::withContentNameScopesList(AttributedScopeStack* contentNameScopesList) {
388 if (this->contentNameScopesList == contentNameScopesList) {
389 return this;
390 }
391 return this->parent->push(
392 this->ruleId,
393 this->_enterPos,
394 this->_anchorPos,
395 this->beginRuleCapturedEOL,
396 this->endRule,
397 this->nameScopesList,
398 contentNameScopesList
399 );
400}
401
402StateStackImpl* StateStackImpl::withEndRule(const std::string& endRule) {
403 if (this->endRule && *this->endRule == endRule) {
404 return this;
405 }
406 return new StateStackImpl(
407 this->parent,
408 this->ruleId,
409 this->_enterPos,
410 this->_anchorPos,
411 this->beginRuleCapturedEOL,
412 &endRule,
413 this->nameScopesList,
414 this->contentNameScopesList
415 );
416}
417
418bool StateStackImpl::hasSameRuleAs(StateStackImpl* other) {
419 StateStackImpl* el = this;
420 while (el && el->_enterPos == other->_enterPos) {
421 if (ruleIdToNumber(el->ruleId) == ruleIdToNumber(other->ruleId)) {
422 return true;
423 }
424 el = el->parent;
425 }
426 return false;
427}
428
429std::string StateStackImpl::toString() const {
430 std::string result = "StateStack[";
431 const StateStackImpl* current = this;
432 while (current) {
433 result += "Rule#" + std::to_string(ruleIdToNumber(current->ruleId));
434 if (current->parent) result += ", ";
435 current = current->parent;
436 }
437 result += "]";
438 return result;
439}
440
441// LineTokens implementation
442
443LineTokens::LineTokens(
444 bool emitBinaryTokens,
445 const std::string& lineText,
446 const std::vector<TokenTypeMatcher>& tokenTypeMatchers,
447 BalancedBracketSelectors* balancedBracketSelectors)
448 : _emitBinaryTokens(emitBinaryTokens),
449 _lineText(lineText),
450 _tokenTypeMatchers(tokenTypeMatchers),
451 _balancedBracketSelectors(balancedBracketSelectors),
452 _lastTokenEndIndex(0) {
453}
454
455void LineTokens::produce(StateStackImpl* stack, int endIndex) {
456 produceFromScopes(stack->contentNameScopesList, endIndex);
457}
458
459void LineTokens::produceFromScopes(AttributedScopeStack* scopesList, int endIndex) {
460 if (_lastTokenEndIndex >= endIndex) {
461 return;
462 }
463
464 if (_emitBinaryTokens) {
465 _binaryTokens.push_back(_lastTokenEndIndex);
466 _binaryTokens.push_back(scopesList->tokenAttributes);
467 _lastTokenEndIndex = endIndex;
468 } else {
469 IToken token;
470 token.startIndex = _lastTokenEndIndex;
471 token.endIndex = endIndex;
472 token.scopes = scopesList->getScopeNames();
473 _tokens.push_back(token);
474 _lastTokenEndIndex = endIndex;
475 }
476}
477
478std::vector<IToken> LineTokens::getResult(StateStackImpl* stack, int lineLength) {
479 // Remove token for newline if it exists
480 if (!_tokens.empty() && _tokens.back().startIndex == lineLength - 1) {
481 _tokens.pop_back();
482 }
483
484 // If no tokens, produce one for the entire line
485 if (_tokens.empty()) {
486 _lastTokenEndIndex = -1;
487 produce(stack, lineLength);
488 if (!_tokens.empty()) {
489 _tokens.back().startIndex = 0;
490 }
491 }
492
493 return _tokens;
494}
495
496std::vector<uint32_t> LineTokens::getBinaryResult(StateStackImpl* stack, int lineLength) {
497 return _binaryTokens;
498}
499
500// Grammar implementation
501
502Grammar::Grammar(
503 const ScopeName& rootScopeName,
504 IRawGrammar* grammar,
505 int initialLanguage,
506 const EmbeddedLanguagesMap* embeddedLanguages,
507 const TokenTypeMap* tokenTypes,
508 BalancedBracketSelectors* balancedBracketSelectors_,
509 IGrammarRepository* grammarRepository,
510 IThemeProvider* themeProvider,
511 IOnigLib* onigLib)
512 : _rootScopeName(rootScopeName),
513 _rootId(ruleIdFromNumber(-1)),
514 _lastRuleId(0),
515 _grammarRepository(grammarRepository),
516 _themeProvider(themeProvider),
517 _grammar(grammar),
518 _injections(nullptr),
519 balancedBracketSelectors(balancedBracketSelectors_),
520 _onigLib(onigLib) {
521
522 _ruleId2desc.push_back(nullptr); // Index 0 is null
523
524 _basicScopeAttributesProvider = new BasicScopeAttributesProvider(
525 initialLanguage,
526 embeddedLanguages
527 );
528
529 _grammar = initGrammar(grammar, nullptr);
530
531 // Build token type matchers
532 if (tokenTypes) {
533 for (const auto& pair : *tokenTypes) {
534 // Simple implementation: creates basic matchers without selector parsing.
535 // Full implementation would use createMatchers to parse scope selectors.
536 TokenTypeMatcher matcher;
537 matcher.type = pair.second;
538 _tokenTypeMatchers.push_back(matcher);
539 }
540 }
541}
542
543Grammar::~Grammar() {
544 dispose();
545}
546
547void Grammar::dispose() {
548 for (size_t i = 0; i < _ruleId2desc.size(); i++) {
549 auto* rule = _ruleId2desc[i];
550 if (rule) {
551 rule->dispose();
552 delete rule;
553 }
554 }
555 _ruleId2desc.clear();
556
557 if (_basicScopeAttributesProvider) {
558 delete _basicScopeAttributesProvider;
559 _basicScopeAttributesProvider = nullptr;
560 }
561 if (_injections) {
562 delete _injections;
563 _injections = nullptr;
564 }
565 if (balancedBracketSelectors) {
566 delete balancedBracketSelectors;
567 balancedBracketSelectors = nullptr;
568 }
569}
570
571OnigScanner* Grammar::createOnigScanner(const std::vector<std::string>& sources) {
572 return _onigLib->createOnigScanner(sources);
573}
574
575OnigString* Grammar::createOnigString(const std::string& str) {
576 return _onigLib->createOnigString(str);
577}
578
579BasicScopeAttributes Grammar::getMetadataForScope(const std::string& scope) {
580 return _basicScopeAttributesProvider->getBasicScopeAttributes(&scope);
581}
582
583Rule* Grammar::getRule(RuleId ruleId) {
584 int id = ruleIdToNumber(ruleId);
585 if (id >= 0 && id < static_cast<int>(_ruleId2desc.size())) {
586 return _ruleId2desc[id];
587 }
588 return nullptr;
589}
590
591RuleId Grammar::registerRule(Rule* rule) {
592 int id = ++_lastRuleId;
593 if (_ruleId2desc.size() <= static_cast<size_t>(id)) {
594 _ruleId2desc.resize(id + 1, nullptr);
595 }
596 _ruleId2desc[id] = rule;
597 return ruleIdFromNumber(id);
598}
599
600RuleId Grammar::allocateRuleId() {
601 int id = ++_lastRuleId;
602 if (_ruleId2desc.size() <= static_cast<size_t>(id)) {
603 _ruleId2desc.resize(id + 1, nullptr);
604 }
605 return ruleIdFromNumber(id);
606}
607
608void Grammar::setRule(RuleId ruleId, Rule* rule) {
609 int id = ruleIdToNumber(ruleId);
610 if (id >= 0 && id < static_cast<int>(_ruleId2desc.size())) {
611 _ruleId2desc[id] = rule;
612 }
613}
614
615IRawGrammar* Grammar::getExternalGrammar(const std::string& scopeName, IRawRepository* repository) {
616 auto it = _includedGrammars.find(scopeName);
617 if (it != _includedGrammars.end()) {
618 return it->second;
619 }
620
621 if (_grammarRepository) {
622 IRawGrammar* rawIncludedGrammar = _grammarRepository->lookup(scopeName);
623 if (rawIncludedGrammar) {
624 IRawRule* base = (repository && repository->baseRule) ? repository->baseRule : nullptr;
625 _includedGrammars[scopeName] = initGrammar(rawIncludedGrammar, base);
626 return _includedGrammars[scopeName];
627 } else {
628 }
629 } else {
630 }
631
632 return nullptr;
633}
634
635std::vector<Injection> Grammar::getInjections() {
636 if (_injections == nullptr) {
637 _injections = new std::vector<Injection>(_collectInjections());
638 }
639 return *_injections;
640}
641
642// Helper function: Check if two scope names match (exact or prefix match)
643static bool scopesAreMatching(const std::string& thisScopeName, const std::string& scopeName) {
644 if (thisScopeName.empty()) {
645 return false;
646 }
647 if (thisScopeName == scopeName) {
648 return true;
649 }
650 size_t len = scopeName.length();
651 return thisScopeName.length() > len &&
652 thisScopeName.substr(0, len) == scopeName &&
653 thisScopeName[len] == '.';
654}
655
656// Helper function: Match identifiers against scopes
657static bool nameMatcher(const std::vector<std::string>& identifiers,
658 const std::vector<std::string>& scopes) {
659 if (scopes.size() < identifiers.size()) {
660 return false;
661 }
662 size_t lastIndex = 0;
663 for (const auto& identifier : identifiers) {
664 bool found = false;
665 for (size_t i = lastIndex; i < scopes.size(); i++) {
666 if (scopesAreMatching(scopes[i], identifier)) {
667 lastIndex = i + 1;
668 found = true;
669 break;
670 }
671 }
672 if (!found) {
673 return false;
674 }
675 }
676 return true;
677}
678
679// Helper function: Collect injections from a single injection rule
680static void collectInjections(std::vector<Injection>& result,
681 const std::string& selector,
682 IRawRule* rule,
683 Grammar* grammar,
684 IRawGrammar* grammarDef) {
685 if (!rule) {
686 return;
687 }
688
689 // Create matchers from the selector
690 auto matchers = createMatchers<std::vector<std::string>>(selector, nameMatcher);
691
692 // Get the compiled rule ID
693 RuleId ruleId = RuleFactory::getCompiledRuleId(rule, grammar, grammarDef->repository);
694
695 // Add an injection for each matcher
696 for (const auto& matcherWithPriority : matchers) {
697 Injection injection;
698 injection.debugSelector = selector;
699 injection.matcher = matcherWithPriority.matcher;
700 injection.ruleId = ruleId;
701 injection.grammar = grammarDef;
702 injection.priority = matcherWithPriority.priority;
703 result.push_back(injection);
704 }
705}
706
707std::vector<Injection> Grammar::_collectInjections() {
708 std::vector<Injection> result;
709
710 // Get the current grammar
711 IRawGrammar* grammar = _grammar;
712 if (!grammar) {
713 return result;
714 }
715
716 // Add injections from the current grammar
717 if (grammar->injections) {
718 for (const auto& pair : *grammar->injections) {
719 const std::string& expression = pair.first;
720 IRawRule* rule = pair.second;
721 collectInjections(result, expression, rule, this, grammar);
722 }
723 }
724
725 // Add injection grammars contributed for the current scope
726 if (_grammarRepository) {
727 std::vector<std::string> injectionScopeNames = _grammarRepository->injections(_rootScopeName);
728 for (const auto& injectionScopeName : injectionScopeNames) {
729 IRawGrammar* injectionGrammar = getExternalGrammar(injectionScopeName, nullptr);
730 if (injectionGrammar) {
731 const std::string* selector = injectionGrammar->injectionSelector;
732 if (selector && !selector->empty()) {
733 // Use the injection grammar's $self rule which contains the patterns
734 // After initGrammar, the patterns are moved to repository->selfRule
735 IRawRule* injectionRule = (injectionGrammar->repository && injectionGrammar->repository->selfRule)
736 ? injectionGrammar->repository->selfRule
737 : injectionGrammar;
738 collectInjections(result, *selector, injectionRule, this, injectionGrammar);
739 }
740 }
741 }
742 }
743
744 // Sort by priority
745 std::sort(result.begin(), result.end(), [](const Injection& a, const Injection& b) {
746 return a.priority < b.priority;
747 });
748
749 return result;
750}
751
752ITokenizeLineResult Grammar::tokenizeLine(
753 const std::string& lineText,
754 StateStack* prevState,
755 int timeLimit) {
756
757 StateStackImpl* prevStateImpl = dynamic_cast<StateStackImpl*>(prevState);
758 TokenizeResult r = _tokenize(lineText, prevStateImpl, false, timeLimit);
759
760 ITokenizeLineResult result;
761 result.tokens = r.lineTokens->getResult(r.ruleStack, r.lineLength);
762 result.ruleStack = r.ruleStack;
763 result.stoppedEarly = r.stoppedEarly;
764
765 delete r.lineTokens;
766 return result;
767}
768
769ITokenizeLineResult2 Grammar::tokenizeLine2(
770 const std::string& lineText,
771 StateStack* prevState,
772 int timeLimit) {
773
774 StateStackImpl* prevStateImpl = dynamic_cast<StateStackImpl*>(prevState);
775 TokenizeResult r = _tokenize(lineText, prevStateImpl, true, timeLimit);
776
777 ITokenizeLineResult2 result;
778 result.tokens = r.lineTokens->getBinaryResult(r.ruleStack, r.lineLength);
779 result.ruleStack = r.ruleStack;
780 result.stoppedEarly = r.stoppedEarly;
781
782 delete r.lineTokens;
783 return result;
784}
785
786Grammar::TokenizeResult Grammar::_tokenize(
787 const std::string& lineText,
788 StateStackImpl* prevState,
789 bool emitBinaryTokens,
790 int timeLimit) {
791
792 // Initialize root rule if needed
793 if (ruleIdToNumber(_rootId) == -1) {
794 _rootId = RuleFactory::getCompiledRuleId(
795 _grammar->repository->selfRule,
796 this,
797 _grammar->repository
798 );
799 getInjections();
800 }
801
802 bool isFirstLine;
803 if (!prevState || prevState == StateStackImpl::NULL_STATE) {
804 isFirstLine = true;
805
806 BasicScopeAttributes rawDefaultMetadata =
807 _basicScopeAttributesProvider->getDefaultAttributes();
808 StyleAttributes* defaultStyle = _themeProvider->getDefaults();
809
810 EncodedTokenAttributes defaultMetadata = EncodedTokenAttributesHelper::set(
811 0,
812 rawDefaultMetadata.languageId,
813 rawDefaultMetadata.tokenType,
814 nullptr,
815 defaultStyle->fontStyle,
816 defaultStyle->foregroundId,
817 defaultStyle->backgroundId
818 );
819
820 Rule* rootRule = getRule(_rootId);
821 std::string* rootScopeName = rootRule ? rootRule->getName(nullptr, nullptr) : nullptr;
822
823 AttributedScopeStack* scopeList;
824 if (rootScopeName) {
825 scopeList = AttributedScopeStack::createRootAndLookUpScopeName(
826 *rootScopeName,
827 defaultMetadata,
828 this
829 );
830 delete rootScopeName;
831 } else {
832 scopeList = AttributedScopeStack::createRoot("unknown", defaultMetadata);
833 }
834
835 prevState = new StateStackImpl(
836 nullptr,
837 _rootId,
838 -1,
839 -1,
840 false,
841 nullptr,
842 scopeList,
843 scopeList
844 );
845 } else {
846 isFirstLine = false;
847 prevState->reset();
848 }
849
850 std::string lineTextWithNewline = lineText + "\n";
851 OnigString* onigLineText = createOnigString(lineTextWithNewline);
852 int lineLength = onigLineText->content().length();
853
854 LineTokens* lineTokens = new LineTokens(
855 emitBinaryTokens,
856 lineTextWithNewline,
857 _tokenTypeMatchers,
858 balancedBracketSelectors
859 );
860
861 StackElement resultStack = tokenizeString(
862 this,
863 onigLineText,
864 isFirstLine,
865 0,
866 prevState,
867 lineTokens,
868 true,
869 timeLimit
870 );
871
872 disposeOnigString(onigLineText);
873
874 TokenizeResult result;
875 result.lineLength = lineLength;
876 result.lineTokens = lineTokens;
877 result.ruleStack = resultStack.stack;
878 result.stoppedEarly = resultStack.stoppedEarly;
879
880 return result;
881}
882
883// Helper functions
884
885Grammar* createGrammar(
886 const ScopeName& scopeName,
887 IRawGrammar* grammar,
888 int initialLanguage,
889 const EmbeddedLanguagesMap* embeddedLanguages,
890 const TokenTypeMap* tokenTypes,
891 BalancedBracketSelectors* balancedBracketSelectors,
892 IGrammarRepository* grammarRepository,
893 IThemeProvider* themeProvider,
894 IOnigLib* onigLib) {
895
896 return new Grammar(
897 scopeName,
898 grammar,
899 initialLanguage,
900 embeddedLanguages,
901 tokenTypes,
902 balancedBracketSelectors,
903 grammarRepository,
904 themeProvider,
905 onigLib
906 );
907}
908
909IRawGrammar* initGrammar(IRawGrammar* grammar, IRawRule* base) {
910 // Create repository if it doesn't exist
911 if (!grammar->repository) {
912 grammar->repository = new IRawRepository();
913 }
914
915 // Create $self rule with grammar's patterns and scope name
916 IRawRule* selfRule = new IRawRule();
917 // Transfer ownership of patterns from grammar to $self rule
918 // This avoids double-free when both grammar and selfRule are destroyed
919 if (grammar->patterns && !grammar->patterns->empty()) {
920 selfRule->patterns = new std::vector<IRawRule*>(*grammar->patterns);
921 // Clear grammar->patterns so we don't have shared ownership
922 // The IRawRule* objects are now owned only by selfRule->patterns
923 grammar->patterns->clear();
924 }
925 // Set name to grammar's scopeName
926 selfRule->name = new std::string(grammar->scopeName);
927
928 grammar->repository->selfRule = selfRule;
929
930 // Create $base rule
931 grammar->repository->baseRule = base ? base : selfRule;
932
933 return grammar;
934}
935
936} // namespace tml
RuleId ruleIdFromNumber(int id)
Convert an integer to a RuleId.
Definition types.h:109
int ruleIdToNumber(RuleId id)
Convert a RuleId to its integer value.
Definition types.h:116
FontStyle
Font styling attributes (italic, bold, underline, strikethrough)
Definition types.h:159
@ NotSet
Type not determined or not applicable.
int32_t EncodedTokenAttributes
Compact 32-bit encoding of a token's attributes.
Definition types.h:128