let tree; (async () => { const scriptURL = document.currentScript.getAttribute('src'); const codeInput = document.getElementById('code-input'); const languageSelect = document.getElementById('language-select'); const loggingCheckbox = document.getElementById('logging-checkbox'); const outputContainer = document.getElementById('output-container'); const outputContainerScroll = document.getElementById('output-container-scroll'); const updateTimeSpan = document.getElementById('update-time'); const demoContainer = document.getElementById('playground-container'); const languagesByName = {}; await Promise.all([ codeInput.value = await fetch(scriptURL).then(r => r.text()), TreeSitter.init() ]); const parser = new TreeSitter(); const codeEditor = CodeMirror.fromTextArea(codeInput, { lineNumbers: true, showCursorWhenSelecting: true }); const cluster = new Clusterize({ rows: [], noDataText: null, contentElem: outputContainer, scrollElem: outputContainerScroll }); const renderTreeOnCodeChange = debounce(renderTree, 50); let languageName = languageSelect.value; let treeRows = null; let treeRowHighlightedIndex = -1; let parseCount = 0; let isRendering = 0; codeEditor.on('changes', handleCodeChange); codeEditor.on('cursorActivity', debounce(handleCursorMovement, 150)); loggingCheckbox.addEventListener('change', handleLoggingChange); languageSelect.addEventListener('change', handleLanguageChange); outputContainer.addEventListener('click', handleTreeClick); await handleLanguageChange() demoContainer.style.visibility = 'visible'; async function handleLanguageChange() { const newLanguageName = languageSelect.value; if (!languagesByName[newLanguageName]) { const url = `${LANGUAGE_BASE_URL}/tree-sitter-${newLanguageName}.wasm` languageSelect.disabled = true; try { languagesByName[newLanguageName] = await TreeSitter.Language.load(url); } catch (e) { console.error(e); languageSelect.value = languageName; return } finally { languageSelect.disabled = false; } } tree = null; languageName = newLanguageName; parser.setLanguage(languagesByName[newLanguageName]); handleCodeChange(); } async function handleCodeChange(editor, changes) { let start; if (tree && changes) { start = performance.now(); for (const change of changes) { const edit = treeEditForEditorChange(change); tree.edit(edit); } } else { start = performance.now(); } const newTree = parser.parse(codeEditor.getValue() + '\n', tree); const duration = (performance.now() - start).toFixed(1); updateTimeSpan.innerText = `${duration} ms`; if (tree) tree.delete(); tree = newTree; parseCount++; renderTreeOnCodeChange(); } async function renderTree() { isRendering++; const cursor = tree.walk(); let currentRenderCount = parseCount; let row = ''; let rows = []; let finishedRow = false; let visitedChildren = false; let indentLevel = 0; for (let i = 0;; i++) { if (i > 0 && i % 10000 === 0) { await new Promise(r => setTimeout(r, 0)); if (parseCount !== currentRenderCount) { cursor.delete(); isRendering--; return; } } let displayName; if (cursor.nodeIsMissing) { displayName = `MISSING ${cursor.nodeType}` } else if (cursor.nodeIsNamed) { displayName = cursor.nodeType; } if (visitedChildren) { if (displayName) { finishedRow = true; } if (cursor.gotoNextSibling()) { visitedChildren = false; } else if (cursor.gotoParent()) { visitedChildren = true; indentLevel--; } else { break; } } else { if (displayName) { if (finishedRow) { row += ''; rows.push(row); finishedRow = false; } const start = cursor.startPosition; const end = cursor.endPosition; const id = cursor.nodeId; row = `