in playground/tree-sitter/concrete-syntax.js [573:687]
window.playground.highlightConcreteSyntaxMatches = function(matches) {
debugLog('highlightConcreteSyntaxMatches function called with:', matches);
// Clear existing highlights first
const codeEditor = window.playground.codeEditor;
if (!codeEditor) return;
// Clear previous concrete syntax highlights
codeEditor.getAllMarks().forEach(mark => {
if (mark.className && mark.className.startsWith('concrete-syntax')) {
mark.clear();
}
});
if (!matches || matches.length === 0) return;
// Use playground's color scheme for consistency
const colors = window.playground.LIGHT_COLORS || [
"#0550ae", "#ab5000", "#116329", "#844708", "#6639ba",
"#7d4e00", "#0969da", "#1a7f37", "#cf222e", "#8250df"
];
debugLog('Highlighting matches:', matches);
matches.forEach((match, index) => {
debugLog('Match', index, ':', match);
// Handle both snake_case and camelCase field names
const range = match.range;
const startPoint = range.startPoint || range.start_point;
const endPoint = range.endPoint || range.end_point;
if (range && startPoint && endPoint) {
const from = {
line: startPoint.row,
ch: startPoint.column
};
const to = {
line: endPoint.row,
ch: endPoint.column
};
// Highlight the full match with primary color
codeEditor.markText(from, to, {
css: `background-color: ${colors[0]}20; border: 1px solid ${colors[0]}60;`,
title: `Concrete Syntax Match ${index + 1}`
});
// Highlight captured variables with different colors
const captures = match.matches;
if (captures && typeof captures === 'object') {
let colorIndex = 1;
debugLog('Match captures:', captures);
// Handle both Map and plain object
const captureEntries = captures instanceof Map ?
Array.from(captures.entries()) :
Object.entries(captures);
captureEntries.forEach(([key, captureData]) => {
debugLog('Capture:', key, captureData);
if (key !== '*' && captureData) {
const captureColor = colors[colorIndex % colors.length];
// Check if captureData has range information (CapturedNode structure)
const captureRange = captureData.range;
const captureStartPoint = captureRange?.startPoint || captureRange?.start_point;
const captureEndPoint = captureRange?.endPoint || captureRange?.end_point;
if (captureRange && captureStartPoint && captureEndPoint) {
// Use exact range from capture data
const captureFrom = {
line: captureStartPoint.row,
ch: captureStartPoint.column
};
const captureTo = {
line: captureEndPoint.row,
ch: captureEndPoint.column
};
debugLog(`Highlighting capture ${key} from (${captureFrom.line},${captureFrom.ch}) to (${captureTo.line},${captureTo.ch})`);
codeEditor.markText(captureFrom, captureTo, {
css: `background-color: ${captureColor}30; border: 1px solid ${captureColor}; border-radius: 2px;`,
title: `${key}: ${captureData.text || captureData}`
});
} else if (typeof captureData === 'string') {
// Fallback: find the capture text within the match
const matchText = match.matchedString || match.matched_string || '';
const captureStart = matchText.indexOf(captureData);
if (captureStart >= 0) {
const captureFrom = {
line: from.line,
ch: from.ch + captureStart
};
const captureTo = {
line: from.line,
ch: from.ch + captureStart + captureData.length
};
debugLog(`Highlighting string capture ${key} from (${captureFrom.line},${captureFrom.ch}) to (${captureTo.line},${captureTo.ch})`);
codeEditor.markText(captureFrom, captureTo, {
css: `background-color: ${captureColor}30; border: 1px solid ${captureColor}; border-radius: 2px;`,
title: `${key}: ${captureData}`
});
}
}
colorIndex++;
}
});
}
}
});
};