@opentui/core
Version:
OpenTUI is a TypeScript library for building terminal user interfaces (TUIs)
863 lines (854 loc) • 31.6 kB
JavaScript
// @bun
var __require = import.meta.require;
// src/lib/tree-sitter/parser.worker.ts
import { Parser, Query, Language } from "web-tree-sitter";
import { mkdir as mkdir2 } from "fs/promises";
import * as path2 from "path";
// src/lib/tree-sitter/download-utils.ts
import { mkdir, writeFile } from "fs/promises";
import * as path from "path";
class DownloadUtils {
static hashUrl(url) {
let hash = 0;
for (let i = 0;i < url.length; i++) {
const char = url.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
return Math.abs(hash).toString(16);
}
static async downloadOrLoad(source, cacheDir, cacheSubdir, fileExtension, useHashForCache = true, filetype) {
const isUrl = source.startsWith("http://") || source.startsWith("https://");
if (isUrl) {
let cacheFileName;
if (useHashForCache) {
const hash = this.hashUrl(source);
cacheFileName = filetype ? `${filetype}-${hash}${fileExtension}` : `${hash}${fileExtension}`;
} else {
cacheFileName = path.basename(source);
}
const cacheFile = path.join(cacheDir, cacheSubdir, cacheFileName);
await mkdir(path.dirname(cacheFile), { recursive: true });
try {
const cachedContent = await Bun.file(cacheFile).arrayBuffer();
if (cachedContent.byteLength > 0) {
console.log(`Loaded from cache: ${cacheFile} (${source})`);
return { content: cachedContent, filePath: cacheFile };
}
} catch (error) {}
try {
console.log(`Downloading from URL: ${source}`);
const response = await fetch(source);
if (!response.ok) {
return { error: `Failed to fetch from ${source}: ${response.statusText}` };
}
const content = await response.arrayBuffer();
try {
await writeFile(cacheFile, Buffer.from(content));
console.log(`Cached: ${source}`);
} catch (cacheError) {
console.warn(`Failed to cache: ${cacheError}`);
}
return { content, filePath: cacheFile };
} catch (error) {
return { error: `Error downloading from ${source}: ${error}` };
}
} else {
try {
console.log(`Loading from local path: ${source}`);
const content = await Bun.file(source).arrayBuffer();
return { content, filePath: source };
} catch (error) {
return { error: `Error loading from local path ${source}: ${error}` };
}
}
}
static async downloadToPath(source, targetPath) {
const isUrl = source.startsWith("http://") || source.startsWith("https://");
await mkdir(path.dirname(targetPath), { recursive: true });
if (isUrl) {
try {
console.log(`Downloading from URL: ${source}`);
const response = await fetch(source);
if (!response.ok) {
return { error: `Failed to fetch from ${source}: ${response.statusText}` };
}
const content = await response.arrayBuffer();
await writeFile(targetPath, Buffer.from(content));
console.log(`Downloaded: ${source} -> ${targetPath}`);
return { content, filePath: targetPath };
} catch (error) {
return { error: `Error downloading from ${source}: ${error}` };
}
} else {
try {
console.log(`Copying from local path: ${source}`);
const content = await Bun.file(source).arrayBuffer();
await writeFile(targetPath, Buffer.from(content));
return { content, filePath: targetPath };
} catch (error) {
return { error: `Error copying from local path ${source}: ${error}` };
}
}
}
static async fetchHighlightQueries(sources, cacheDir, filetype) {
const queryPromises = sources.map((source) => this.fetchHighlightQuery(source, cacheDir, filetype));
const queryResults = await Promise.all(queryPromises);
const validQueries = queryResults.filter((query) => query.trim().length > 0);
return validQueries.join(`
`);
}
static async fetchHighlightQuery(source, cacheDir, filetype) {
const result = await this.downloadOrLoad(source, cacheDir, "queries", ".scm", true, filetype);
if (result.error) {
console.error(`Error fetching highlight query from ${source}:`, result.error);
return "";
}
if (result.content) {
return new TextDecoder().decode(result.content);
}
return "";
}
}
// src/lib/tree-sitter/parser.worker.ts
import { isMainThread } from "worker_threads";
// src/lib/bunfs.ts
import { basename as basename2, join as join2 } from "path";
function isBunfsPath(path2) {
return path2.includes("$bunfs") || /^B:[\\/]~BUN/i.test(path2);
}
function getBunfsRootPath() {
return process.platform === "win32" ? "B:\\~BUN\\root" : "/$bunfs/root";
}
function normalizeBunfsPath(fileName) {
return join2(getBunfsRootPath(), basename2(fileName));
}
// src/lib/tree-sitter/parser.worker.ts
var self = globalThis;
class ParserWorker {
bufferParsers = new Map;
filetypeParserOptions = new Map;
filetypeParsers = new Map;
filetypeParserPromises = new Map;
reusableParsers = new Map;
reusableParserPromises = new Map;
initializePromise;
performance;
dataPath;
tsDataPath;
initialized = false;
constructor() {
this.performance = {
averageParseTime: 0,
parseTimes: [],
averageQueryTime: 0,
queryTimes: []
};
}
async fetchQueries(sources, filetype) {
if (!this.tsDataPath) {
return "";
}
return DownloadUtils.fetchHighlightQueries(sources, this.tsDataPath, filetype);
}
async initialize({ dataPath }) {
if (this.initializePromise) {
return this.initializePromise;
}
this.initializePromise = new Promise(async (resolve, reject) => {
this.dataPath = dataPath;
this.tsDataPath = path2.join(dataPath, "tree-sitter");
try {
await mkdir2(path2.join(this.tsDataPath, "languages"), { recursive: true });
await mkdir2(path2.join(this.tsDataPath, "queries"), { recursive: true });
let { default: treeWasm } = await import("web-tree-sitter/tree-sitter.wasm", {
with: { type: "wasm" }
});
if (isBunfsPath(treeWasm)) {
treeWasm = normalizeBunfsPath(path2.parse(treeWasm).base);
}
await Parser.init({
locateFile() {
return treeWasm;
}
});
this.initialized = true;
resolve();
} catch (error) {
reject(error);
}
});
return this.initializePromise;
}
addFiletypeParser(filetypeParser) {
this.filetypeParserOptions.set(filetypeParser.filetype, filetypeParser);
}
async createQueries(filetypeParser, language) {
try {
const highlightQueryContent = await this.fetchQueries(filetypeParser.queries.highlights, filetypeParser.filetype);
if (!highlightQueryContent) {
console.error("Failed to fetch highlight queries for:", filetypeParser.filetype);
return;
}
const highlightsQuery = new Query(language, highlightQueryContent);
const result = {
highlights: highlightsQuery
};
if (filetypeParser.queries.injections && filetypeParser.queries.injections.length > 0) {
const injectionQueryContent = await this.fetchQueries(filetypeParser.queries.injections, filetypeParser.filetype);
if (injectionQueryContent) {
result.injections = new Query(language, injectionQueryContent);
}
}
return result;
} catch (error) {
console.error("Error creating queries for", filetypeParser.filetype, filetypeParser.queries);
console.error(error);
return;
}
}
async loadLanguage(languageSource) {
if (!this.initialized || !this.tsDataPath) {
return;
}
const result = await DownloadUtils.downloadOrLoad(languageSource, this.tsDataPath, "languages", ".wasm", false);
if (result.error) {
console.error(`Error loading language ${languageSource}:`, result.error);
return;
}
if (!result.filePath) {
return;
}
const normalizedPath = result.filePath.replaceAll("\\", "/");
try {
const language = await Language.load(normalizedPath);
return language;
} catch (error) {
console.error(`Error loading language from ${normalizedPath}:`, error);
return;
}
}
async resolveFiletypeParser(filetype) {
if (this.filetypeParsers.has(filetype)) {
return this.filetypeParsers.get(filetype);
}
if (this.filetypeParserPromises.has(filetype)) {
return this.filetypeParserPromises.get(filetype);
}
const loadingPromise = this.loadFiletypeParser(filetype);
this.filetypeParserPromises.set(filetype, loadingPromise);
try {
const result = await loadingPromise;
if (result) {
this.filetypeParsers.set(filetype, result);
}
return result;
} finally {
this.filetypeParserPromises.delete(filetype);
}
}
async loadFiletypeParser(filetype) {
const filetypeParserOptions = this.filetypeParserOptions.get(filetype);
if (!filetypeParserOptions) {
return;
}
const language = await this.loadLanguage(filetypeParserOptions.wasm);
if (!language) {
return;
}
const queries = await this.createQueries(filetypeParserOptions, language);
if (!queries) {
console.error("Failed to create queries for:", filetype);
return;
}
const filetypeParser = {
...filetypeParserOptions,
queries,
language
};
return filetypeParser;
}
async preloadParser(filetype) {
return this.resolveFiletypeParser(filetype);
}
async getReusableParser(filetype) {
if (this.reusableParsers.has(filetype)) {
return this.reusableParsers.get(filetype);
}
if (this.reusableParserPromises.has(filetype)) {
return this.reusableParserPromises.get(filetype);
}
const creationPromise = this.createReusableParser(filetype);
this.reusableParserPromises.set(filetype, creationPromise);
try {
const result = await creationPromise;
if (result) {
this.reusableParsers.set(filetype, result);
}
return result;
} finally {
this.reusableParserPromises.delete(filetype);
}
}
async createReusableParser(filetype) {
const filetypeParser = await this.resolveFiletypeParser(filetype);
if (!filetypeParser) {
return;
}
const parser = new Parser;
parser.setLanguage(filetypeParser.language);
const reusableState = {
parser,
filetypeParser,
queries: filetypeParser.queries
};
return reusableState;
}
async handleInitializeParser(bufferId, version, content, filetype, messageId) {
const filetypeParser = await this.resolveFiletypeParser(filetype);
if (!filetypeParser) {
self.postMessage({
type: "PARSER_INIT_RESPONSE",
bufferId,
messageId,
hasParser: false,
warning: `No parser available for filetype ${filetype}`
});
return;
}
const parser = new Parser;
parser.setLanguage(filetypeParser.language);
const tree = parser.parse(content);
if (!tree) {
self.postMessage({
type: "PARSER_INIT_RESPONSE",
bufferId,
messageId,
hasParser: false,
error: "Failed to parse buffer"
});
return;
}
const parserState = {
parser,
tree,
queries: filetypeParser.queries,
filetype,
content,
injectionMapping: filetypeParser.injectionMapping
};
this.bufferParsers.set(bufferId, parserState);
self.postMessage({
type: "PARSER_INIT_RESPONSE",
bufferId,
messageId,
hasParser: true
});
const highlights = await this.initialQuery(parserState);
self.postMessage({
type: "HIGHLIGHT_RESPONSE",
bufferId,
version,
...highlights
});
}
async initialQuery(parserState) {
const query = parserState.queries.highlights;
const matches = query.captures(parserState.tree.rootNode);
let injectionRanges = new Map;
if (parserState.queries.injections) {
const injectionResult = await this.processInjections(parserState);
matches.push(...injectionResult.captures);
injectionRanges = injectionResult.injectionRanges;
}
return this.getHighlights(parserState, matches, injectionRanges);
}
getNodeText(node, content) {
return content.substring(node.startIndex, node.endIndex);
}
async processInjections(parserState) {
const injectionMatches = [];
const injectionRanges = new Map;
if (!parserState.queries.injections) {
return { captures: injectionMatches, injectionRanges };
}
const content = parserState.content;
const injectionCaptures = parserState.queries.injections.captures(parserState.tree.rootNode);
const languageGroups = new Map;
const injectionMapping = parserState.injectionMapping;
for (const capture of injectionCaptures) {
const captureName = capture.name;
if (captureName === "injection.content" || captureName.includes("injection")) {
const nodeType = capture.node.type;
let targetLanguage;
if (injectionMapping?.nodeTypes && injectionMapping.nodeTypes[nodeType]) {
targetLanguage = injectionMapping.nodeTypes[nodeType];
} else if (nodeType === "code_fence_content") {
const parent = capture.node.parent;
if (parent) {
const infoString = parent.children.find((child) => child.type === "info_string");
if (infoString) {
const languageNode = infoString.children.find((child) => child.type === "language");
if (languageNode) {
const languageName = this.getNodeText(languageNode, content);
if (injectionMapping?.infoStringMap && injectionMapping.infoStringMap[languageName]) {
targetLanguage = injectionMapping.infoStringMap[languageName];
} else {
targetLanguage = languageName;
}
}
}
}
}
if (targetLanguage) {
if (!languageGroups.has(targetLanguage)) {
languageGroups.set(targetLanguage, []);
}
languageGroups.get(targetLanguage).push({ node: capture.node, name: capture.name });
}
}
}
for (const [language, captures] of languageGroups.entries()) {
const injectedParser = await this.getReusableParser(language);
if (!injectedParser) {
console.warn(`No parser found for injection language: ${language}`);
continue;
}
if (!injectionRanges.has(language)) {
injectionRanges.set(language, []);
}
const parser = injectedParser.parser;
for (const { node: injectionNode } of captures) {
try {
injectionRanges.get(language).push({
start: injectionNode.startIndex,
end: injectionNode.endIndex
});
const injectionContent = this.getNodeText(injectionNode, content);
const tree = parser.parse(injectionContent);
if (tree) {
const matches = injectedParser.queries.highlights.captures(tree.rootNode);
for (const match of matches) {
const offsetCapture = {
name: match.name,
patternIndex: match.patternIndex,
_injectedQuery: injectedParser.queries.highlights,
node: {
...match.node,
startPosition: {
row: match.node.startPosition.row + injectionNode.startPosition.row,
column: match.node.startPosition.row === 0 ? match.node.startPosition.column + injectionNode.startPosition.column : match.node.startPosition.column
},
endPosition: {
row: match.node.endPosition.row + injectionNode.startPosition.row,
column: match.node.endPosition.row === 0 ? match.node.endPosition.column + injectionNode.startPosition.column : match.node.endPosition.column
},
startIndex: match.node.startIndex + injectionNode.startIndex,
endIndex: match.node.endIndex + injectionNode.startIndex
}
};
injectionMatches.push(offsetCapture);
}
tree.delete();
}
} catch (error) {
console.error(`Error processing injection for language ${language}:`, error);
}
}
}
return { captures: injectionMatches, injectionRanges };
}
editToRange(edit) {
return {
startPosition: {
column: edit.startPosition.column,
row: edit.startPosition.row
},
endPosition: {
column: edit.newEndPosition.column,
row: edit.newEndPosition.row
},
startIndex: edit.startIndex,
endIndex: edit.newEndIndex
};
}
async handleEdits(bufferId, content, edits) {
const parserState = this.bufferParsers.get(bufferId);
if (!parserState) {
return { warning: "No parser state found for buffer" };
}
parserState.content = content;
for (const edit of edits) {
parserState.tree.edit(edit);
}
const startParse = performance.now();
const newTree = parserState.parser.parse(content, parserState.tree);
const endParse = performance.now();
const parseTime = endParse - startParse;
this.performance.parseTimes.push(parseTime);
if (this.performance.parseTimes.length > 10) {
this.performance.parseTimes.shift();
}
this.performance.averageParseTime = this.performance.parseTimes.reduce((acc, time) => acc + time, 0) / this.performance.parseTimes.length;
if (!newTree) {
return { error: "Failed to parse buffer" };
}
const changedRanges = parserState.tree.getChangedRanges(newTree);
parserState.tree = newTree;
const startQuery = performance.now();
const matches = [];
if (changedRanges.length === 0) {
edits.forEach((edit) => {
const range = this.editToRange(edit);
changedRanges.push(range);
});
}
for (const range of changedRanges) {
let node = parserState.tree.rootNode.descendantForPosition(range.startPosition, range.endPosition);
if (!node) {
continue;
}
if (node.equals(parserState.tree.rootNode)) {
const rangeCaptures = parserState.queries.highlights.captures(node, {
startIndex: range.startIndex - 100,
endIndex: range.endIndex + 1000
});
matches.push(...rangeCaptures);
continue;
}
while (node && !this.nodeContainsRange(node, range)) {
node = node.parent;
}
if (!node) {
node = parserState.tree.rootNode;
}
const nodeCaptures = parserState.queries.highlights.captures(node);
matches.push(...nodeCaptures);
}
let injectionRanges = new Map;
if (parserState.queries.injections) {
const injectionResult = await this.processInjections(parserState);
matches.push(...injectionResult.captures);
injectionRanges = injectionResult.injectionRanges;
}
const endQuery = performance.now();
const queryTime = endQuery - startQuery;
this.performance.queryTimes.push(queryTime);
if (this.performance.queryTimes.length > 10) {
this.performance.queryTimes.shift();
}
this.performance.averageQueryTime = this.performance.queryTimes.reduce((acc, time) => acc + time, 0) / this.performance.queryTimes.length;
return this.getHighlights(parserState, matches, injectionRanges);
}
nodeContainsRange(node, range) {
return node.startPosition.row <= range.startPosition.row && node.endPosition.row >= range.endPosition.row && (node.startPosition.row < range.startPosition.row || node.startPosition.column <= range.startPosition.column) && (node.endPosition.row > range.endPosition.row || node.endPosition.column >= range.endPosition.column);
}
getHighlights(parserState, matches, injectionRanges) {
const lineHighlights = new Map;
const droppedHighlights = new Map;
for (const match of matches) {
const node = match.node;
const startLine = node.startPosition.row;
const endLine = node.endPosition.row;
const highlight = {
startCol: node.startPosition.column,
endCol: node.endPosition.column,
group: match.name
};
if (!lineHighlights.has(startLine)) {
lineHighlights.set(startLine, new Map);
droppedHighlights.set(startLine, new Map);
}
if (lineHighlights.get(startLine)?.has(node.id)) {
droppedHighlights.get(startLine)?.set(node.id, lineHighlights.get(startLine)?.get(node.id));
}
lineHighlights.get(startLine)?.set(node.id, highlight);
if (startLine !== endLine) {
for (let line = startLine + 1;line <= endLine; line++) {
if (!lineHighlights.has(line)) {
lineHighlights.set(line, new Map);
}
const hl = {
startCol: 0,
endCol: node.endPosition.column,
group: match.name
};
lineHighlights.get(line)?.set(node.id, hl);
}
}
}
return {
highlights: Array.from(lineHighlights.entries()).map(([line, lineHighlights2]) => ({
line,
highlights: Array.from(lineHighlights2.values()),
droppedHighlights: droppedHighlights.get(line) ? Array.from(droppedHighlights.get(line).values()) : []
}))
};
}
getSimpleHighlights(matches, injectionRanges) {
const highlights = [];
const flatInjectionRanges = [];
for (const [lang, ranges] of injectionRanges.entries()) {
for (const range of ranges) {
flatInjectionRanges.push({ ...range, lang });
}
}
for (const match of matches) {
const node = match.node;
let isInjection = false;
let injectionLang;
let containsInjection = false;
for (const injRange of flatInjectionRanges) {
if (node.startIndex >= injRange.start && node.endIndex <= injRange.end) {
isInjection = true;
injectionLang = injRange.lang;
break;
} else if (node.startIndex <= injRange.start && node.endIndex >= injRange.end) {
containsInjection = true;
break;
}
}
const matchQuery = match._injectedQuery;
const patternProperties = matchQuery?.setProperties?.[match.patternIndex];
const concealValue = patternProperties?.conceal ?? match.setProperties?.conceal;
const concealLines = patternProperties?.conceal_lines ?? match.setProperties?.conceal_lines;
const meta = {};
if (isInjection && injectionLang) {
meta.isInjection = true;
meta.injectionLang = injectionLang;
}
if (containsInjection) {
meta.containsInjection = true;
}
if (concealValue !== undefined) {
meta.conceal = concealValue;
}
if (concealLines !== undefined) {
meta.concealLines = concealLines;
}
if (Object.keys(meta).length > 0) {
highlights.push([node.startIndex, node.endIndex, match.name, meta]);
} else {
highlights.push([node.startIndex, node.endIndex, match.name]);
}
}
highlights.sort((a, b) => a[0] - b[0]);
return highlights;
}
async handleResetBuffer(bufferId, version, content) {
const parserState = this.bufferParsers.get(bufferId);
if (!parserState) {
return { warning: "No parser state found for buffer" };
}
parserState.content = content;
const newTree = parserState.parser.parse(content);
if (!newTree) {
return { error: "Failed to parse buffer during reset" };
}
parserState.tree = newTree;
const matches = parserState.queries.highlights.captures(parserState.tree.rootNode);
let injectionRanges = new Map;
if (parserState.queries.injections) {
const injectionResult = await this.processInjections(parserState);
matches.push(...injectionResult.captures);
injectionRanges = injectionResult.injectionRanges;
}
return this.getHighlights(parserState, matches, injectionRanges);
}
disposeBuffer(bufferId) {
const parserState = this.bufferParsers.get(bufferId);
if (!parserState) {
return;
}
parserState.tree.delete();
parserState.parser.delete();
this.bufferParsers.delete(bufferId);
}
async handleOneShotHighlight(content, filetype, messageId) {
const reusableState = await this.getReusableParser(filetype);
if (!reusableState) {
self.postMessage({
type: "ONESHOT_HIGHLIGHT_RESPONSE",
messageId,
hasParser: false,
warning: `No parser available for filetype ${filetype}`
});
return;
}
const parseContent = filetype === "markdown" && content.endsWith("```") ? content + `
` : content;
const tree = reusableState.parser.parse(parseContent);
if (!tree) {
self.postMessage({
type: "ONESHOT_HIGHLIGHT_RESPONSE",
messageId,
hasParser: false,
error: "Failed to parse content"
});
return;
}
try {
const matches = reusableState.filetypeParser.queries.highlights.captures(tree.rootNode);
let injectionRanges = new Map;
if (reusableState.filetypeParser.queries.injections) {
const parserState = {
parser: reusableState.parser,
tree,
queries: reusableState.filetypeParser.queries,
filetype,
content,
injectionMapping: reusableState.filetypeParser.injectionMapping
};
const injectionResult = await this.processInjections(parserState);
matches.push(...injectionResult.captures);
injectionRanges = injectionResult.injectionRanges;
}
const highlights = this.getSimpleHighlights(matches, injectionRanges);
self.postMessage({
type: "ONESHOT_HIGHLIGHT_RESPONSE",
messageId,
hasParser: true,
highlights
});
} finally {
tree.delete();
}
}
async updateDataPath(dataPath) {
this.dataPath = dataPath;
this.tsDataPath = path2.join(dataPath, "tree-sitter");
try {
await mkdir2(path2.join(this.tsDataPath, "languages"), { recursive: true });
await mkdir2(path2.join(this.tsDataPath, "queries"), { recursive: true });
} catch (error) {
throw new Error(`Failed to update data path: ${error}`);
}
}
async clearCache() {
if (!this.dataPath || !this.tsDataPath) {
throw new Error("No data path configured");
}
const { rm } = await import("fs/promises");
try {
const treeSitterPath = path2.join(this.dataPath, "tree-sitter");
await rm(treeSitterPath, { recursive: true, force: true });
await mkdir2(path2.join(treeSitterPath, "languages"), { recursive: true });
await mkdir2(path2.join(treeSitterPath, "queries"), { recursive: true });
this.filetypeParsers.clear();
this.filetypeParserPromises.clear();
this.reusableParsers.clear();
this.reusableParserPromises.clear();
} catch (error) {
throw new Error(`Failed to clear cache: ${error}`);
}
}
}
if (!isMainThread) {
let logMessage = function(type, ...args) {
self.postMessage({
type: "WORKER_LOG",
logType: type,
data: args
});
};
const worker = new ParserWorker;
console.log = (...args) => logMessage("log", ...args);
console.error = (...args) => logMessage("error", ...args);
console.warn = (...args) => logMessage("warn", ...args);
self.onmessage = async (e) => {
const { type, bufferId, version, content, filetype, edits, filetypeParser, messageId, dataPath } = e.data;
try {
switch (type) {
case "INIT":
try {
await worker.initialize({ dataPath });
self.postMessage({ type: "INIT_RESPONSE" });
} catch (error) {
self.postMessage({
type: "INIT_RESPONSE",
error: error instanceof Error ? error.stack || error.message : String(error)
});
}
break;
case "ADD_FILETYPE_PARSER":
worker.addFiletypeParser(filetypeParser);
break;
case "PRELOAD_PARSER":
const maybeParser = await worker.preloadParser(filetype);
self.postMessage({ type: "PRELOAD_PARSER_RESPONSE", messageId, hasParser: !!maybeParser });
break;
case "INITIALIZE_PARSER":
await worker.handleInitializeParser(bufferId, version, content, filetype, messageId);
break;
case "HANDLE_EDITS":
const response = await worker.handleEdits(bufferId, content, edits);
if (response.highlights && response.highlights.length > 0) {
self.postMessage({ type: "HIGHLIGHT_RESPONSE", bufferId, version, ...response });
} else if (response.warning) {
self.postMessage({ type: "WARNING", bufferId, warning: response.warning });
} else if (response.error) {
self.postMessage({ type: "ERROR", bufferId, error: response.error });
}
break;
case "GET_PERFORMANCE":
self.postMessage({ type: "PERFORMANCE_RESPONSE", performance: worker.performance, messageId });
break;
case "RESET_BUFFER":
const resetResponse = await worker.handleResetBuffer(bufferId, version, content);
if (resetResponse.highlights && resetResponse.highlights.length > 0) {
self.postMessage({ type: "HIGHLIGHT_RESPONSE", bufferId, version, ...resetResponse });
} else if (resetResponse.warning) {
self.postMessage({ type: "WARNING", bufferId, warning: resetResponse.warning });
} else if (resetResponse.error) {
self.postMessage({ type: "ERROR", bufferId, error: resetResponse.error });
}
break;
case "DISPOSE_BUFFER":
worker.disposeBuffer(bufferId);
self.postMessage({ type: "BUFFER_DISPOSED", bufferId });
break;
case "ONESHOT_HIGHLIGHT":
await worker.handleOneShotHighlight(content, filetype, messageId);
break;
case "UPDATE_DATA_PATH":
try {
await worker.updateDataPath(dataPath);
self.postMessage({ type: "UPDATE_DATA_PATH_RESPONSE", messageId });
} catch (error) {
self.postMessage({
type: "UPDATE_DATA_PATH_RESPONSE",
messageId,
error: error instanceof Error ? error.message : String(error)
});
}
break;
case "CLEAR_CACHE":
try {
await worker.clearCache();
self.postMessage({ type: "CLEAR_CACHE_RESPONSE", messageId });
} catch (error) {
self.postMessage({
type: "CLEAR_CACHE_RESPONSE",
messageId,
error: error instanceof Error ? error.message : String(error)
});
}
break;
default:
self.postMessage({
type: "ERROR",
bufferId,
error: `Unknown message type: ${type}`
});
}
} catch (error) {
self.postMessage({
type: "ERROR",
bufferId,
error: error instanceof Error ? error.stack || error.message : String(error)
});
}
};
}
//# debugId=A12A2CF1481F706164756E2164756E21
//# sourceMappingURL=parser.worker.js.map