From 73177af78f61975a0eaef9a88758d59db4ccc473 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 12 Jun 2021 21:01:51 +0530 Subject: syntax tree <-> dom tree --- www/index.html | 15 ++++++++++--- www/index.js | 69 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 70 insertions(+), 14 deletions(-) (limited to 'www') diff --git a/www/index.html b/www/index.html index 01ba57d..1404993 100644 --- a/www/index.html +++ b/www/index.html @@ -9,6 +9,17 @@ display: grid; grid-template-columns: 1fr 1fr; grid-gap: 20px; +} +.syntax-node { + padding: 0px; + padding-left: 20px; +} +.syntax-err { + color: red; +} +pre { + padding: 0px; + margin: 0px } @@ -16,9 +27,7 @@
-
-
-        
+
diff --git a/www/index.js b/www/index.js index 2665873..52253c0 100644 --- a/www/index.js +++ b/www/index.js @@ -1,5 +1,7 @@ import {SynNode, put_cst} from "cstea"; import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup" +import {Decoration, DecorationSet} from "@codemirror/view" +import {StateField, StateEffect} from "@codemirror/state" import {rust} from "@codemirror/lang-rust" let cst = document.getElementById('cst'); @@ -18,32 +20,77 @@ let view = new EditorView({ parent: document.getElementById('source-code') }) +const doHighlight = StateEffect.define() + +const highlightField = StateField.define({ + create() { + return Decoration.none; + }, + update(highlight, tr) { + for (let e of tr.effects) if (e.is(doHighlight)) { + return (Decoration.none).update({ + add: [hlMark.range(e.value.from, e.value.to)] + }); + } + }, + provide: f => EditorView.decorations.from(f) +}) + +const hlMark = Decoration.mark({class: "cm-highlight"}) + +const hlTheme = EditorView.baseTheme({ + ".cm-highlight": { textDecoration: "underline 3px red" } +}) + +function highlightArea(view, textRange) { + let effects = [doHighlight.of({from: textRange.start(), to: textRange.end()})]; + if (!view.state.field(highlightField, false)) { + effects.push(StateEffect.appendConfig.of([highlightField, hlTheme])); + } + view.dispatch({effects}); +} + function render_cst(synRoot) { - cst.innerText += "\n" + synRoot.to_string(); + let nodeDiv = document.createElement("div"); + nodeDiv.className = "syntax-node"; + let r = synRoot.range(); + let synText = wrap(synRoot.text() + synRoot.range().to_string(), "pre"); + synText.onmouseover = () => { + console.log(r.to_string()); + highlightArea(view, r); + } + nodeDiv.appendChild(synText); if (!synRoot.is_token()) { synRoot.children().forEach(node => { - render_cst(node); - return; + nodeDiv.appendChild(render_cst(node)); }); - } else { - return; } + return nodeDiv; +} + +function wrap(s, tag) { + let t = document.createElement(tag); + t.innerText = s; + return t; } function render_err(errorList) { - cst.innerText = ""; + let errDiv = document.createElement("div"); + errDiv.className = "syntax-err"; errorList.forEach(err => { - cst.innerText += "\n" + err.to_string(); + errDiv.appendChild(wrap(err.to_string(), "pre")); + highlightArea(view, err.range()); }); + return errDiv; } function doRender() { let sourceFile = view.state.doc.toString();; - cst.innerText = ""; + cst.innerHTML = ""; try { - var synRoot = SynNode.from_str(sourceFile); - render_cst(synRoot); + let synRoot = SynNode.from_str(sourceFile); + cst.appendChild(render_cst(synRoot)); } catch (synError) { - render_err(synError) + cst.appendChild(render_err(synError)); } } -- cgit v1.2.3