1#include "syntax_highlighter.h"
11uint64_t SyntaxHighlighter::getCurrentTimeMs() {
12 auto now = std::chrono::high_resolution_clock::now();
13 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
14 now.time_since_epoch()
16 return static_cast<uint64_t
>(ms.count());
23SyntaxHighlighter::SyntaxHighlighter(
24 std::shared_ptr<IGrammar> gram,
32 createdAtMs(getCurrentTimeMs()),
33 lastUpdateMs(createdAtMs) {
36 throw std::invalid_argument(
"Grammar cannot be null");
39 throw std::invalid_argument(
"Theme cannot be null");
44 uint64_t sessionId = SessionManager::createSession(grammar);
45 session = SessionManager::getSession(sessionId);
48 throw std::runtime_error(
"Failed to create session");
53 cache.reset(
new HighlighterCache());
57SyntaxHighlighter::~SyntaxHighlighter() {
61 SessionManager::releaseSession(session->getSessionId());
69void SyntaxHighlighter::setDocument(
const std::vector<std::string>& lines) {
71 throw std::runtime_error(
"Session is null");
74 session->setLines(lines);
75 lastUpdateMs = getCurrentTimeMs();
83void SyntaxHighlighter::editLine(
int lineIndex,
const std::string& newContent) {
85 throw std::runtime_error(
"Session is null");
88 if (lineIndex < 0 || lineIndex >= session->getLineCount()) {
89 throw std::out_of_range(
"Line index out of range");
93 std::vector<std::string> newLines = {newContent};
94 session->edit(newLines, lineIndex, 1);
95 lastUpdateMs = getCurrentTimeMs();
99 cache->invalidateLine(lineIndex);
103void SyntaxHighlighter::insertLines(
int startIndex,
const std::vector<std::string>& lines) {
105 throw std::runtime_error(
"Session is null");
108 if (startIndex < 0 || startIndex > session->getLineCount()) {
109 throw std::out_of_range(
"Insert index out of range");
112 session->add(lines, startIndex);
113 lastUpdateMs = getCurrentTimeMs();
117 cache->invalidateRange(startIndex, session->getLineCount() - 1);
121void SyntaxHighlighter::removeLines(
int startIndex,
int count) {
123 throw std::runtime_error(
"Session is null");
126 if (startIndex < 0 || count < 0 || startIndex + count > session->getLineCount()) {
127 throw std::out_of_range(
"Remove range out of range");
130 session->remove(startIndex, count);
131 lastUpdateMs = getCurrentTimeMs();
135 cache->invalidateRange(startIndex, std::max(0, session->getLineCount() - 1));
139int SyntaxHighlighter::getLineCount()
const {
143 return session->getLineCount();
150HighlightedToken SyntaxHighlighter::tokenToHighlighted(
const IToken& token) {
151 HighlightedToken highlighted;
152 highlighted.startIndex = token.startIndex;
153 highlighted.endIndex = token.endIndex;
154 highlighted.scopes = token.scopes;
157 ScopeStack* scopeStack = buildScopeStack(token.scopes);
159 if (scopeStack && theme) {
161 StyleAttributes* styleAttrs = theme->match(scopeStack);
165 auto colorMap = theme->getColorMap();
168 if (styleAttrs->foregroundId >= 0 && styleAttrs->foregroundId <
static_cast<int>(colorMap.size())) {
169 highlighted.foregroundColor = colorMap[styleAttrs->foregroundId];
173 if (styleAttrs->backgroundId >= 0 && styleAttrs->backgroundId <
static_cast<int>(colorMap.size())) {
174 highlighted.backgroundColor = colorMap[styleAttrs->backgroundId];
178 highlighted.fontStyle = styleAttrs->fontStyle;
188 if (!token.scopes.empty()) {
189 highlighted.debugInfo = token.scopes[0];
190 for (
size_t i = 1; i < token.scopes.size(); ++i) {
191 highlighted.debugInfo +=
" " + token.scopes[i];
198ScopeStack* SyntaxHighlighter::buildScopeStack(
const std::vector<std::string>& scopes) {
199 if (scopes.empty()) {
205 ScopeStack* result = ScopeStack::from(scopes);
213HighlightedLine SyntaxHighlighter::getHighlightedLine(
int lineIndex) {
215 throw std::runtime_error(
"Session is null");
218 if (lineIndex < 0 || lineIndex >= session->getLineCount()) {
219 throw std::out_of_range(
"Line index out of range");
223 uint64_t version = lastUpdateMs;
225 HighlightedLine* cached = cache->getCachedLine(lineIndex, version);
231 HighlightedLine result;
232 result.lineIndex = lineIndex;
233 result.version = version;
236 const SessionLine* sessionLine = session->getLine(lineIndex);
238 result.isComplete =
false;
242 result.content = sessionLine->content;
243 result.isComplete = sessionLine->cached;
246 result.tokens.reserve(sessionLine->tokens.size());
247 for (
const auto& token : sessionLine->tokens) {
248 result.tokens.push_back(tokenToHighlighted(token));
253 cache->cacheLine(result, version);
259std::vector<HighlightedLine> SyntaxHighlighter::getHighlightedRange(
264 throw std::runtime_error(
"Session is null");
267 int lineCount = session->getLineCount();
268 if (startIndex < 0 || endIndex >= lineCount || startIndex > endIndex) {
269 throw std::out_of_range(
"Range out of bounds");
272 std::vector<HighlightedLine> results;
273 results.reserve(endIndex - startIndex + 1);
275 for (
int i = startIndex; i <= endIndex; ++i) {
276 results.push_back(getHighlightedLine(i));
282std::vector<IToken> SyntaxHighlighter::getLineTokens(
int lineIndex) {
284 throw std::runtime_error(
"Session is null");
287 if (lineIndex < 0 || lineIndex >= session->getLineCount()) {
288 throw std::out_of_range(
"Line index out of range");
291 const std::vector<IToken>* tokens = session->getLineTokens(lineIndex);
293 return std::vector<IToken>();
303void SyntaxHighlighter::setTheme(Theme* newTheme) {
305 throw std::invalid_argument(
"Theme cannot be null");
309 lastUpdateMs = getCurrentTimeMs();
317Theme* SyntaxHighlighter::getTheme()
const {
325void SyntaxHighlighter::clearCache() {
331void SyntaxHighlighter::invalidateCacheRange(
int startIndex,
int endIndex) {
336 if (startIndex < 0 || endIndex < 0 || startIndex > endIndex) {
337 throw std::invalid_argument(
"Invalid cache range");
340 cache->invalidateRange(startIndex, endIndex);
347SyntaxHighlightingMetadata SyntaxHighlighter::getMetadata()
const {
348 SyntaxHighlightingMetadata metadata;
351 SessionMetadata sessionMeta = session->getMetadata();
352 metadata.sessionId = sessionMeta.createdAtMs;
353 metadata.lineCount = sessionMeta.lineCount;
354 metadata.cachedLineCount = sessionMeta.cachedLineCount;
358 auto colorMap = theme->getColorMap();
359 metadata.themeColorCount =
static_cast<int>(colorMap.size());
362 metadata.lastUpdateMs = lastUpdateMs;
365 metadata.cachedLineCount = cache->getCachedLineCount();
371SessionImpl* SyntaxHighlighter::getSession()
const {
372 return session.get();
379HighlightedLine* HighlighterCache::getCachedLine(
int lineIndex, uint64_t version) {
380 auto it = lineCache.find(lineIndex);
381 if (it != lineCache.end() && it->second.version == version) {
382 return &it->second.line;
387void HighlighterCache::cacheLine(
const HighlightedLine& line, uint64_t version) {
388 lineCache[line.lineIndex] = {line, version};
391void HighlighterCache::invalidateLine(
int lineIndex) {
392 lineCache.erase(lineIndex);
395void HighlighterCache::invalidateRange(
int startIndex,
int endIndex) {
396 auto it = lineCache.begin();
397 while (it != lineCache.end()) {
398 if (it->first >= startIndex && it->first <= endIndex) {
399 it = lineCache.erase(it);
406void HighlighterCache::clear() {
410size_t HighlighterCache::getCachedLineCount()
const {
411 return lineCache.size();