From acdc8b899340b6b8c621602c3c5dfce3eb71bfd2 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 18 Aug 2023 21:06:43 -0400 Subject: [PATCH] Highlighting characters. --- static/script.js | 70 +++++++++++++++++++++++++++++++++++++++++++----- static/style.css | 13 ++++++++- 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/static/script.js b/static/script.js index ecd4550..f9318f5 100644 --- a/static/script.js +++ b/static/script.js @@ -14,6 +14,7 @@ function abortableFetch(request, options) { } function clearOutput() { + clearActiveAstNode(); outputElement.innerHTML = ""; astTreeElement.innerHTML = ""; } @@ -34,7 +35,18 @@ function renderSourceBox(response) { for (let line of lines) { let wrappedLine = document.createElement("code"); - wrappedLine.textContent = line ? line : "\n"; + if (line !== "" && line !== null) { + for (let chr of line) { + // Please forgive me + let wrappedCharacter = document.createElement("span"); + wrappedCharacter.textContent = chr; + wrappedLine.appendChild(wrappedCharacter); + } + } else { + let wrappedCharacter = document.createElement("span"); + wrappedCharacter.textContent = "\n"; + wrappedLine.appendChild(wrappedCharacter); + } outputElement.appendChild(wrappedLine); } } @@ -53,12 +65,46 @@ function renderAstNode(originalSource, depth, astNode) { nodeElem.innerText = `${astNode.name}: ${escapedSource}`; nodeElem.style.marginLeft = `${depth * 20}px`; + nodeElem.dataset.startLine = astNode.position.start_line; + nodeElem.dataset.endLine = astNode.position.end_line; + nodeElem.dataset.startCharacter = astNode.position.start_character; + nodeElem.dataset.endCharacter = astNode.position.end_character; + + nodeElem.addEventListener("click", () => { + setActiveAstNode(nodeElem, originalSource); + }); + astTreeElement.appendChild(nodeElem); for (let child of astNode.children) { renderAstNode(originalSource, depth + 1, child); } } +function clearActiveAstNode() { + for (let elem of document.querySelectorAll("#ast-tree .ast_node.highlighted")) { + elem.classList.remove("highlighted"); + } + for (let elem of document.querySelectorAll("#parse-output > code.highlighted")) { + elem.classList.remove("highlighted"); + } + for (let elem of document.querySelectorAll("#parse-output > code > span")) { + elem.classList.remove("highlighted"); + } +} + +function setActiveAstNode(elem, originalSource) { + clearActiveAstNode(); + elem.classList.add("highlighted"); + let startLine = parseInt(elem.dataset.startLine, 10); + let endLine = parseInt(elem.dataset.endLine, 10); + let startCharacter = parseInt(elem.dataset.startCharacter, 10); + let endCharacter = parseInt(elem.dataset.endCharacter, 10); + for (let line = startLine; line < endLine; ++line) { + highlightLine("parse-output", line - 1); + } + highlightCharacters("parse-output", originalSource, startCharacter, endCharacter); +} + inputElement.addEventListener("input", async () => { let orgSource = inputElement.value; if (inFlightRequest != null) { @@ -86,12 +132,24 @@ inputElement.addEventListener("input", async () => { function highlightLine(htmlName, lineOffset) { const childOffset = lineOffset + 1; - const codeLineElement = document.querySelector(`.${htmlName} > code:nth-child(${childOffset})`); + const codeLineElement = document.querySelector(`#${htmlName} > code:nth-child(${childOffset})`); codeLineElement?.classList.add("highlighted") } -function unhighlightLine(htmlName, lineOffset) { - const childOffset = lineOffset + 1; - const codeLineElement = document.querySelector(`.${htmlName} > code:nth-child(${childOffset})`); - codeLineElement?.classList.remove("highlighted") +function highlightCharacters(htmlName, originalSource, startCharacter, endCharacter) { + let sourceBefore = originalSource.slice(0, startCharacter - 1); + let precedingLineBreak = sourceBefore.lastIndexOf("\n"); + let characterIndexOnLine = precedingLineBreak !== -1 ? startCharacter - precedingLineBreak - 1 : startCharacter; + let lineNumber = (sourceBefore.match(/\r?\n/g) || '').length + 1; + + for (let characterIndex = startCharacter; characterIndex < endCharacter; ++characterIndex) { + document.querySelector(`#${htmlName} > code:nth-child(${lineNumber}) > span:nth-child(${characterIndexOnLine})`)?.classList.add("highlighted"); + if (originalSource[characterIndex - 1] == "\n") { + ++lineNumber; + characterIndexOnLine = 1; + } else { + ++characterIndexOnLine; + } + } + } diff --git a/static/style.css b/static/style.css index 7272846..151a40d 100644 --- a/static/style.css +++ b/static/style.css @@ -47,13 +47,18 @@ h7 { } .code_block > code.highlighted { - background: #307351ff; + /* We aren't using this because we are going to highlight individual characters, but we still need to set the highlighted class on the code elem so the line numbers on the left get highlighted to make empty lines more obvious. */ + /* background: #307351ff; */ } .code_block > code.highlighted::before { background: #307351ff; } +.code_block > code > span.highlighted { + background: #307351ff; +} + .output_container { display: flex; flex-direction: row; @@ -68,8 +73,14 @@ h7 { } .ast_node { + cursor: pointer; background: #eeeeee; margin-bottom: 5px; border: 1px solid #000000; padding: 2px; } + +.ast_node.highlighted { + background: #307351ff; + color: #ffffff; +}