TextMateLib 1.0
Modern C++ implementation of the TextMate syntax highlighting engine
Loading...
Searching...
No Matches
session.cpp
1#include "session.h"
2#include <algorithm>
3#include <chrono>
4#include <cstring>
5#include <vector>
6#include <stdint.h>
7
8namespace tml {
9
10// ============================================================================
11// SessionLine Implementation
12// ============================================================================
13
14SessionLine::~SessionLine() {
15 if (state != nullptr) {
16 // State stacks are managed by the grammar/tokenizer
17 // They should be released through the proper mechanism
18 state = nullptr;
19 }
20}
21
22// ============================================================================
23// TextMateSession Implementation
24// ============================================================================
25
26SessionImpl::SessionImpl(uint64_t id, std::shared_ptr<IGrammar> gram)
27 : sessionId(id),
28 grammar(gram),
29 referenceCount(1),
30 nextVersion(1) {
31 auto now = std::chrono::system_clock::now();
32 createdAtMs = std::chrono::duration_cast<std::chrono::milliseconds>(
33 now.time_since_epoch()
34 ).count();
35 lastAccessMs = createdAtMs;
36}
37
38SessionImpl::~SessionImpl() {
39 clearCache();
40}
41
42void SessionImpl::retain() {
43 referenceCount++;
44}
45
46void SessionImpl::release() {
47 if (referenceCount > 0) {
48 referenceCount--;
49 }
50}
51
52void SessionImpl::updateAccessTime() {
53 auto now = std::chrono::system_clock::now();
54 lastAccessMs = std::chrono::duration_cast<std::chrono::milliseconds>(
55 now.time_since_epoch()
56 ).count();
57}
58
59bool SessionImpl::isExpired(uint64_t currentTimeMs, uint64_t maxAgeMs) const {
60 return (currentTimeMs - createdAtMs) > maxAgeMs;
61}
62
63bool SessionImpl::isStateEqual(StateStack* state1, StateStack* state2) const {
64 if (state1 == state2) {
65 return true;
66 }
67 if (state1 == nullptr || state2 == nullptr) {
68 return false;
69 }
70 // Compare state stacks by converting to string representation
71 // This is a simple comparison; more sophisticated comparison may be needed
72 return std::string(reinterpret_cast<char*>(state1)) ==
73 std::string(reinterpret_cast<char*>(state2));
74}
75
76void SessionImpl::invalidateFrom(int32_t startIndex) {
77 if (startIndex < 0 || startIndex >= static_cast<int32_t>(lines.size())) {
78 return;
79 }
80
81 for (int32_t i = startIndex; i < static_cast<int32_t>(lines.size()); i++) {
82 lines[i].cached = false;
83 lines[i].tokens.clear();
84 if (lines[i].state != nullptr) {
85 lines[i].state = nullptr;
86 }
87 lines[i].version = nextVersion;
88 }
89 nextVersion++;
90}
91
92int32_t SessionImpl::setLines(const std::vector<std::string>& newLines) {
93 updateAccessTime();
94
95 // Clear existing lines
96 clearCache();
97 lines.clear();
98
99 // Initialize new lines
100 lines.resize(newLines.size());
101 for (size_t i = 0; i < newLines.size(); i++) {
102 lines[i].content = newLines[i];
103 lines[i].cached = false;
104 lines[i].state = nullptr;
105 lines[i].version = nextVersion;
106 }
107
108 // Tokenize all lines with initial state
109 retokenizeLines(0, static_cast<int32_t>(lines.size()) - 1);
110
111 return 0;
112}
113
114void SessionImpl::retokenizeLines(int32_t startIndex, int32_t endIndex) {
115 if (!grammar || startIndex < 0 || startIndex > endIndex) {
116 return;
117 }
118
119 endIndex = std::min(endIndex, static_cast<int32_t>(lines.size()) - 1);
120
121 // Get initial state for first line to retokenize
122 StateStack* state = nullptr;
123 if (startIndex > 0 && lines[startIndex - 1].cached && lines[startIndex - 1].state) {
124 state = lines[startIndex - 1].state;
125 }
126
127 // Retokenize with early stopping when state stabilizes
128 for (int32_t i = startIndex; i <= endIndex; i++) {
129 ITokenizeLineResult result = grammar->tokenizeLine(lines[i].content, state);
130
131 lines[i].tokens = result.tokens;
132 lines[i].state = result.ruleStack;
133 lines[i].cached = true;
134 lines[i].version = nextVersion;
135
136 // Check if we should stop cascading (state has stabilized)
137 if (i < static_cast<int32_t>(lines.size()) - 1) {
138 if (lines[i + 1].cached &&
139 isStateEqual(result.ruleStack, lines[i + 1].state)) {
140 // State matches expected, can stop here
141 break;
142 }
143 }
144
145 state = result.ruleStack;
146 }
147}
148
149int32_t SessionImpl::edit(
150 const std::vector<std::string>& newLines,
151 int32_t startIndex,
152 int32_t replaceCount
153) {
154 updateAccessTime();
155
156 // Validate parameters
157 if (startIndex < 0 || startIndex > static_cast<int32_t>(lines.size())) {
158 return 1; // Error
159 }
160
161 int32_t endIndex = startIndex + replaceCount - 1;
162 if (endIndex >= static_cast<int32_t>(lines.size())) {
163 endIndex = static_cast<int32_t>(lines.size()) - 1;
164 }
165
166 // Replace lines in the buffer
167 if (replaceCount > 0 && startIndex < static_cast<int32_t>(lines.size())) {
168 for (int32_t i = 0; i < static_cast<int32_t>(newLines.size()); i++) {
169 int32_t targetIdx = startIndex + i;
170 if (targetIdx < static_cast<int32_t>(lines.size())) {
171 lines[targetIdx].content = newLines[i];
172 }
173 }
174 }
175
176 // Invalidate cache from start
177 invalidateFrom(startIndex);
178
179 // Retokenize starting from the edited line
180 if (startIndex < static_cast<int32_t>(lines.size())) {
181 retokenizeLines(startIndex, static_cast<int32_t>(lines.size()) - 1);
182 }
183
184 return 0;
185}
186
187int32_t SessionImpl::add(
188 const std::vector<std::string>& newLines,
189 int32_t insertIndex
190) {
191 updateAccessTime();
192
193 // Validate parameters
194 if (insertIndex < 0 || insertIndex > static_cast<int32_t>(lines.size())) {
195 return 1; // Error
196 }
197
198 // Insert new lines at the specified position
199 lines.insert(
200 lines.begin() + insertIndex,
201 SessionLine()
202 );
203
204 for (size_t i = 0; i < newLines.size(); i++) {
205 int32_t targetIdx = insertIndex + i;
206 if (targetIdx < static_cast<int32_t>(lines.size())) {
207 lines[targetIdx].content = newLines[i];
208 lines[targetIdx].cached = false;
209 lines[targetIdx].state = nullptr;
210 }
211 }
212
213 // Invalidate cache from insertion point
214 invalidateFrom(insertIndex);
215
216 // Retokenize from insertion point
217 if (insertIndex < static_cast<int32_t>(lines.size())) {
218 retokenizeLines(insertIndex, static_cast<int32_t>(lines.size()) - 1);
219 }
220
221 return 0;
222}
223
224int32_t SessionImpl::remove(
225 int32_t startIndex,
226 int32_t removeCount
227) {
228 updateAccessTime();
229
230 // Validate parameters
231 if (startIndex < 0 || startIndex >= static_cast<int32_t>(lines.size()) ||
232 removeCount <= 0) {
233 return 1; // Error
234 }
235
236 int32_t endIndex = std::min(
237 static_cast<int32_t>(lines.size()),
238 startIndex + removeCount
239 );
240
241 // Remove lines from buffer
242 lines.erase(lines.begin() + startIndex, lines.begin() + endIndex);
243
244 // Invalidate cache from removal point
245 if (startIndex < static_cast<int32_t>(lines.size())) {
246 invalidateFrom(startIndex);
247
248 // Retokenize from removal point
249 retokenizeLines(startIndex, static_cast<int32_t>(lines.size()) - 1);
250 }
251
252 return 0;
253}
254
255const SessionLine* SessionImpl::getLine(int32_t lineIndex) const {
256 if (lineIndex < 0 || lineIndex >= static_cast<int32_t>(lines.size())) {
257 return nullptr;
258 }
259 return &lines[lineIndex];
260}
261
262const std::vector<IToken>* SessionImpl::getLineTokens(int32_t lineIndex) const {
263 const SessionLine* line = getLine(lineIndex);
264 if (line == nullptr || !line->cached) {
265 return nullptr;
266 }
267 return &line->tokens;
268}
269
270StateStack* SessionImpl::getLineState(int32_t lineIndex) const {
271 const SessionLine* line = getLine(lineIndex);
272 if (line == nullptr || !line->cached) {
273 return nullptr;
274 }
275 return line->state;
276}
277
278void SessionImpl::getTokensRange(
279 int32_t startIndex,
280 int32_t endIndex,
281 std::vector<SessionLine>& results
282) const {
283 results.clear();
284
285 if (startIndex < 0 || endIndex >= static_cast<int32_t>(lines.size()) ||
286 startIndex > endIndex) {
287 return;
288 }
289
290 for (int32_t i = startIndex; i <= endIndex; i++) {
291 if (i < static_cast<int32_t>(lines.size())) {
292 results.push_back(lines[i]);
293 }
294 }
295}
296
297void SessionImpl::invalidateRange(int32_t startIndex, int32_t endIndex) {
298 updateAccessTime();
299
300 if (startIndex < 0 || startIndex >= static_cast<int32_t>(lines.size())) {
301 return;
302 }
303
304 if (endIndex < 0 || endIndex >= static_cast<int32_t>(lines.size())) {
305 endIndex = static_cast<int32_t>(lines.size()) - 1;
306 }
307
308 for (int32_t i = startIndex; i <= endIndex; i++) {
309 lines[i].cached = false;
310 lines[i].tokens.clear();
311 if (lines[i].state != nullptr) {
312 lines[i].state = nullptr;
313 }
314 lines[i].version = nextVersion;
315 }
316 nextVersion++;
317
318 // Retokenize the range
319 if (startIndex < static_cast<int32_t>(lines.size())) {
320 retokenizeLines(startIndex, endIndex);
321 }
322}
323
324void SessionImpl::clearCache() {
325 for (auto& line : lines) {
326 line.cached = false;
327 line.tokens.clear();
328 if (line.state != nullptr) {
329 line.state = nullptr;
330 }
331 }
332 nextVersion++;
333}
334
335uint64_t SessionImpl::calculateMemoryUsage() const {
336 uint64_t usage = sizeof(SessionImpl);
337
338 // Account for lines vector
339 usage += lines.capacity() * sizeof(SessionLine);
340
341 // Account for token data
342 for (const auto& line : lines) {
343 usage += line.content.capacity();
344 usage += line.tokens.capacity() * sizeof(IToken);
345 for (const auto& token : line.tokens) {
346 for (const auto& scope : token.scopes) {
347 usage += scope.capacity();
348 }
349 }
350 }
351
352 return usage;
353}
354
355int32_t SessionImpl::countCachedLines() const {
356 int32_t count = 0;
357 for (const auto& line : lines) {
358 if (line.cached) {
359 count++;
360 }
361 }
362 return count;
363}
364
365SessionMetadata SessionImpl::getMetadata() const {
366 SessionMetadata metadata;
367 metadata.createdAtMs = createdAtMs;
368 metadata.referenceCount = referenceCount;
369 metadata.lineCount = static_cast<int32_t>(lines.size());
370 metadata.cachedLineCount = countCachedLines();
371 metadata.memoryUsageBytes = calculateMemoryUsage();
372 return metadata;
373}
374
375// ============================================================================
376// SessionManager Implementation
377// ============================================================================
378
379std::map<uint64_t, std::shared_ptr<SessionImpl>> SessionManager::sessions;
380uint64_t SessionManager::nextSessionId = 1;
381uint32_t SessionManager::operationCount = 0;
382
383uint64_t SessionManager::createSession(std::shared_ptr<IGrammar> grammar) {
384 if (!grammar) {
385 return 0;
386 }
387
388 uint64_t sessionId = nextSessionId++;
389 auto session = std::make_shared<SessionImpl>(sessionId, grammar);
390 sessions[sessionId] = session;
391
392 // Periodic cleanup
393 triggerPeriodicCleanup(60000); // 60 seconds
394
395 return sessionId;
396}
397
398std::shared_ptr<SessionImpl> SessionManager::getSession(uint64_t sessionId) {
399 auto it = sessions.find(sessionId);
400 if (it != sessions.end()) {
401 return it->second;
402 }
403 return nullptr;
404}
405
406void SessionManager::retainSession(uint64_t sessionId) {
407 auto session = getSession(sessionId);
408 if (session) {
409 session->retain();
410 }
411}
412
413void SessionManager::releaseSession(uint64_t sessionId) {
414 auto session = getSession(sessionId);
415 if (session) {
416 session->release();
417 }
418}
419
420void SessionManager::disposeSession(uint64_t sessionId) {
421 auto it = sessions.find(sessionId);
422 if (it != sessions.end()) {
423 it->second->release();
424 if (it->second->getRefCount() == 0) {
425 sessions.erase(it);
426 }
427 }
428}
429
430void SessionManager::cleanupExpired(int32_t maxAgeMs) {
431 auto now = std::chrono::system_clock::now();
432 uint64_t currentTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(
433 now.time_since_epoch()
434 ).count();
435
436 for (auto it = sessions.begin(); it != sessions.end();) {
437 if (it->second->isExpired(currentTimeMs, maxAgeMs)) {
438 it = sessions.erase(it);
439 } else {
440 ++it;
441 }
442 }
443}
444
445void SessionManager::triggerPeriodicCleanup(int32_t maxAgeMs) {
446 operationCount++;
447 if (operationCount % CLEANUP_INTERVAL == 0) {
448 cleanupExpired(maxAgeMs);
449 }
450}
451
452size_t SessionManager::getSessionCount() {
453 return sessions.size();
454}
455
456} // namespace tml
457
458// ============================================================================
459// C API Wrapper Layer (extern "C")
460// ============================================================================
461
462// Opaque types from c_api.h (not defining TextMateSession yet)
463typedef void* TextMateGrammar;
464typedef void* TextMateStateStack;
465typedef void* TextMateOnigLib;
466typedef void* TextMateTheme;
467
468// Token structure
469typedef struct {
470 int32_t startIndex;
471 int32_t endIndex;
472 int32_t scopeDepth;
473 char** scopes;
475
476// Tokenize result structure
477typedef struct {
478 TextMateToken* tokens;
479 int32_t tokenCount;
480 TextMateStateStack ruleStack;
481 int32_t stoppedEarly;
483
484// Session opaque handle
485typedef uint64_t TextMateSession;
486
487// Session line representation
488typedef struct {
489 TextMateToken* tokens;
490 int32_t tokenCount;
491 TextMateStateStack state;
492 uint64_t version;
493} TextMateSessionLine;
494
495// Session lines result
496typedef struct {
497 TextMateSessionLine* lines;
498 int32_t lineCount;
499} TextMateSessionLinesResult;
500
501// Session metadata
502typedef struct {
503 uint64_t createdAtMs;
504 uint32_t referenceCount;
505 int32_t lineCount;
506 int32_t cachedLineCount;
507 uint64_t memoryUsageBytes;
508} TextMateSessionMetadata;
509
510extern "C" {
511
512using namespace tml;
513
514// ============================================================================
515// Session Lifecycle (from session_c_api.h)
516// ============================================================================
517
518TextMateSession textmate_session_create(TextMateGrammar grammar) {
519 if (!grammar) {
520 return 0;
521 }
522
523 // Grammar is a void* that we need to cast to IGrammar*
524 auto grammarPtr = static_cast<IGrammar*>(grammar);
525 auto grammarSharedPtr = std::shared_ptr<IGrammar>(grammarPtr, [](IGrammar*) {
526 // Don't delete - ownership remains with the registry
527 });
528
529 return SessionManager::createSession(grammarSharedPtr);
530}
531
532void textmate_session_retain(TextMateSession session) {
533 if (session == 0) return;
534 SessionManager::retainSession(session);
535}
536
537void textmate_session_release(TextMateSession session) {
538 if (session == 0) return;
539 SessionManager::releaseSession(session);
540}
541
542void textmate_session_dispose(TextMateSession session) {
543 if (session == 0) return;
544 SessionManager::disposeSession(session);
545}
546
547// ============================================================================
548// Session State Management
549// ============================================================================
550
551int textmate_session_set_lines(
552 TextMateSession session,
553 const char** lines,
554 int32_t lineCount
555) {
556 if (session == 0 || !lines || lineCount < 0) {
557 return 1;
558 }
559
560 auto sessionPtr = SessionManager::getSession(session);
561 if (!sessionPtr) {
562 return 1;
563 }
564
565 std::vector<std::string> lineVec;
566 for (int32_t i = 0; i < lineCount; i++) {
567 lineVec.push_back(lines[i] ? std::string(lines[i]) : std::string(""));
568 }
569
570 return sessionPtr->setLines(lineVec);
571}
572
573int32_t textmate_session_get_line_count(TextMateSession session) {
574 if (session == 0) {
575 return 0;
576 }
577
578 auto sessionPtr = SessionManager::getSession(session);
579 if (!sessionPtr) {
580 return 0;
581 }
582
583 return sessionPtr->getLineCount();
584}
585
586// ============================================================================
587// Incremental Tokenization Operations
588// ============================================================================
589
590int textmate_session_edit(
591 TextMateSession session,
592 const char** lines,
593 int32_t lineCount,
594 int32_t startIndex,
595 int32_t replaceCount
596) {
597 if (session == 0 || !lines || lineCount < 0) {
598 return 1;
599 }
600
601 auto sessionPtr = SessionManager::getSession(session);
602 if (!sessionPtr) {
603 return 1;
604 }
605
606 std::vector<std::string> lineVec;
607 for (int32_t i = 0; i < lineCount; i++) {
608 lineVec.push_back(lines[i] ? std::string(lines[i]) : std::string(""));
609 }
610
611 return sessionPtr->edit(lineVec, startIndex, replaceCount);
612}
613
614int textmate_session_add(
615 TextMateSession session,
616 const char** lines,
617 int32_t lineCount,
618 int32_t insertIndex
619) {
620 if (session == 0 || !lines || lineCount < 0) {
621 return 1;
622 }
623
624 auto sessionPtr = SessionManager::getSession(session);
625 if (!sessionPtr) {
626 return 1;
627 }
628
629 std::vector<std::string> lineVec;
630 for (int32_t i = 0; i < lineCount; i++) {
631 lineVec.push_back(lines[i] ? std::string(lines[i]) : std::string(""));
632 }
633
634 return sessionPtr->add(lineVec, insertIndex);
635}
636
637int textmate_session_remove(
638 TextMateSession session,
639 int32_t startIndex,
640 int32_t removeCount
641) {
642 if (session == 0) {
643 return 1;
644 }
645
646 auto sessionPtr = SessionManager::getSession(session);
647 if (!sessionPtr) {
648 return 1;
649 }
650
651 return sessionPtr->remove(startIndex, removeCount);
652}
653
654// ============================================================================
655// Query Operations
656// ============================================================================
657
658TextMateTokenizeResult* textmate_session_get_line_tokens(
659 TextMateSession session,
660 int32_t lineIndex
661) {
662 if (session == 0) {
663 return nullptr;
664 }
665
666 auto sessionPtr = SessionManager::getSession(session);
667 if (!sessionPtr) {
668 return nullptr;
669 }
670
671 const auto* tokens = sessionPtr->getLineTokens(lineIndex);
672 if (!tokens) {
673 return nullptr;
674 }
675
676 // Allocate result structure
677 auto* result = new TextMateTokenizeResult();
678 result->tokenCount = static_cast<int32_t>(tokens->size());
679
680 if (result->tokenCount > 0) {
681 result->tokens = new TextMateToken[result->tokenCount];
682
683 for (int32_t i = 0; i < result->tokenCount; i++) {
684 const auto& token = (*tokens)[i];
685 result->tokens[i].startIndex = token.startIndex;
686 result->tokens[i].endIndex = token.endIndex;
687 result->tokens[i].scopeDepth = static_cast<int32_t>(token.scopes.size());
688
689 // Allocate scope array
690 result->tokens[i].scopes = new char*[token.scopes.size()];
691 for (size_t j = 0; j < token.scopes.size(); j++) {
692 size_t scopeLen = token.scopes[j].length();
693 result->tokens[i].scopes[j] = new char[scopeLen + 1];
694 std::strcpy(result->tokens[i].scopes[j], token.scopes[j].c_str());
695 }
696 }
697 } else {
698 result->tokens = nullptr;
699 }
700
701 result->ruleStack = sessionPtr->getLineState(lineIndex);
702 result->stoppedEarly = 0;
703
704 return result;
705}
706
707TextMateStateStack textmate_session_get_line_state(
708 TextMateSession session,
709 int32_t lineIndex
710) {
711 if (session == 0) {
712 return nullptr;
713 }
714
715 auto sessionPtr = SessionManager::getSession(session);
716 if (!sessionPtr) {
717 return nullptr;
718 }
719
720 return sessionPtr->getLineState(lineIndex);
721}
722
723TextMateSessionLinesResult* textmate_session_get_tokens_range(
724 TextMateSession session,
725 int32_t startIndex,
726 int32_t endIndex
727) {
728 if (session == 0) {
729 return nullptr;
730 }
731
732 auto sessionPtr = SessionManager::getSession(session);
733 if (!sessionPtr) {
734 return nullptr;
735 }
736
737 auto* result = new TextMateSessionLinesResult();
738 std::vector<SessionLine> lines;
739 sessionPtr->getTokensRange(startIndex, endIndex, lines);
740
741 result->lineCount = static_cast<int32_t>(lines.size());
742
743 if (result->lineCount > 0) {
744 result->lines = new TextMateSessionLine[result->lineCount];
745
746 for (int32_t i = 0; i < result->lineCount; i++) {
747 const auto& line = lines[i];
748 result->lines[i].tokenCount = static_cast<int32_t>(line.tokens.size());
749 result->lines[i].state = line.state;
750 result->lines[i].version = line.version;
751
752 if (result->lines[i].tokenCount > 0) {
753 result->lines[i].tokens = new TextMateToken[result->lines[i].tokenCount];
754
755 for (int32_t j = 0; j < result->lines[i].tokenCount; j++) {
756 const auto& token = line.tokens[j];
757 result->lines[i].tokens[j].startIndex = token.startIndex;
758 result->lines[i].tokens[j].endIndex = token.endIndex;
759 result->lines[i].tokens[j].scopeDepth = static_cast<int32_t>(token.scopes.size());
760
761 result->lines[i].tokens[j].scopes = new char*[token.scopes.size()];
762 for (size_t k = 0; k < token.scopes.size(); k++) {
763 size_t scopeLen = token.scopes[k].length();
764 result->lines[i].tokens[j].scopes[k] = new char[scopeLen + 1];
765 std::strcpy(result->lines[i].tokens[j].scopes[k], token.scopes[k].c_str());
766 }
767 }
768 } else {
769 result->lines[i].tokens = nullptr;
770 }
771 }
772 } else {
773 result->lines = nullptr;
774 }
775
776 return result;
777}
778
779void textmate_session_free_tokens_result(
781) {
782 if (!result) {
783 return;
784 }
785
786 if (result->tokens) {
787 for (int32_t i = 0; i < result->tokenCount; i++) {
788 if (result->tokens[i].scopes) {
789 for (int j = 0; j < result->tokens[i].scopeDepth; j++) {
790 delete[] result->tokens[i].scopes[j];
791 }
792 delete[] result->tokens[i].scopes;
793 }
794 }
795 delete[] result->tokens;
796 }
797
798 delete result;
799}
800
801void textmate_session_free_lines_result(
802 TextMateSessionLinesResult* result
803) {
804 if (!result) {
805 return;
806 }
807
808 if (result->lines) {
809 for (int32_t i = 0; i < result->lineCount; i++) {
810 if (result->lines[i].tokens) {
811 for (int32_t j = 0; j < result->lines[i].tokenCount; j++) {
812 if (result->lines[i].tokens[j].scopes) {
813 for (int k = 0; k < result->lines[i].tokens[j].scopeDepth; k++) {
814 delete[] result->lines[i].tokens[j].scopes[k];
815 }
816 delete[] result->lines[i].tokens[j].scopes;
817 }
818 }
819 delete[] result->lines[i].tokens;
820 }
821 }
822 delete[] result->lines;
823 }
824
825 delete result;
826}
827
828// ============================================================================
829// Maintenance Operations
830// ============================================================================
831
832void textmate_session_invalidate_range(
833 TextMateSession session,
834 int32_t startIndex,
835 int32_t endIndex
836) {
837 if (session == 0) {
838 return;
839 }
840
841 auto sessionPtr = SessionManager::getSession(session);
842 if (!sessionPtr) {
843 return;
844 }
845
846 sessionPtr->invalidateRange(startIndex, endIndex);
847}
848
849void textmate_session_clear_cache(TextMateSession session) {
850 if (session == 0) {
851 return;
852 }
853
854 auto sessionPtr = SessionManager::getSession(session);
855 if (!sessionPtr) {
856 return;
857 }
858
859 sessionPtr->clearCache();
860}
861
862void textmate_session_cleanup_expired(int32_t maxAgeMs) {
863 SessionManager::cleanupExpired(maxAgeMs);
864}
865
866TextMateSessionMetadata textmate_session_get_metadata(
867 TextMateSession session
868) {
869 TextMateSessionMetadata metadata = {0, 0, 0, 0, 0};
870
871 if (session == 0) {
872 return metadata;
873 }
874
875 auto sessionPtr = SessionManager::getSession(session);
876 if (!sessionPtr) {
877 return metadata;
878 }
879
880 auto cppMetadata = sessionPtr->getMetadata();
881 metadata.createdAtMs = cppMetadata.createdAtMs;
882 metadata.referenceCount = cppMetadata.referenceCount;
883 metadata.lineCount = cppMetadata.lineCount;
884 metadata.cachedLineCount = cppMetadata.cachedLineCount;
885 metadata.memoryUsageBytes = cppMetadata.memoryUsageBytes;
886
887 return metadata;
888}
889
890} // extern "C"
Abstract interface representing the parsing state at the end of a line.
Definition types.h:55
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 * 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
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
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 tokenizing a single line with decoded tokens.
Definition c_api.h:72
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