aboutsummaryrefslogtreecommitdiff
path: root/www/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/index.js')
-rw-r--r--www/index.js128
1 files changed, 128 insertions, 0 deletions
diff --git a/www/index.js b/www/index.js
new file mode 100644
index 0000000..8a4971d
--- /dev/null
+++ b/www/index.js
@@ -0,0 +1,128 @@
1import {SynNode} from "cstea";
2import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
3import {Decoration, DecorationSet} from "@codemirror/view"
4import {StateField, StateEffect} from "@codemirror/state"
5import {oneDark, oneDarkTheme, oneDarkHighlightStyle} from "@codemirror/theme-one-dark"
6import file from '!raw-loader!../flake.nix'
7
8let cst = document.getElementById('cst');
9let view = new EditorView({
10 state: EditorState.create({
11 doc: file,
12 extensions: [
13 basicSetup,
14 EditorView.updateListener.of((v) => {
15 if (v.docChanged) {
16 doRender()
17 }
18 }),
19 oneDark,
20 oneDarkTheme,
21 oneDarkHighlightStyle.extension
22 ]
23 }),
24 parent: document.getElementById('source-code'),
25})
26
27const doHighlight = StateEffect.define()
28
29const highlightField = StateField.define({
30 create() {
31 return Decoration.none;
32 },
33 update(highlight, tr) {
34 for (let e of tr.effects) if (e.is(doHighlight)) {
35 return (Decoration.none).update({
36 add: [hlMark.range(e.value.from, e.value.to)]
37 });
38 }
39 return Decoration.none;
40 },
41 provide: f => EditorView.decorations.from(f)
42})
43
44const hlMark = Decoration.mark({class: "cm-highlight"})
45
46const hlTheme = EditorView.baseTheme({
47 ".cm-highlight": {
48 backgroundColor: "#ff3299aa"
49 }
50})
51
52function highlightArea(view, textRange) {
53 let effects = [doHighlight.of({from: textRange.start(), to: textRange.end()})];
54 if (!view.state.field(highlightField, false)) {
55 effects.push(StateEffect.appendConfig.of([highlightField, hlTheme]));
56 }
57 view.dispatch({effects});
58}
59
60function render_cst(synRoot) {
61 let nodeDiv = document.createElement("div");
62 nodeDiv.className = "syntax-node";
63 let r = synRoot.range();
64 let synText = synTextHtml(synRoot);
65 synText.onmouseover = () => {
66 highlightArea(view, r);
67 let sourceFile = view.state.doc.toString();
68 view.scrollPosIntoView(r.start());
69 }
70 nodeDiv.appendChild(synText);
71 if (!synRoot.is_token()) {
72 synRoot.children().forEach(node => {
73 nodeDiv.appendChild(render_cst(node));
74 });
75 }
76 return nodeDiv;
77}
78
79function synTextHtml(node) {
80 let kind = document.createElement("span");
81 kind.innerText = ` ${node.kind()} `
82 kind.className = "kind";
83
84 let text = document.createElement("span");
85 text.innerText = ` ${node.text()} `
86 text.className = "token-text";
87
88 let range = document.createElement("span");
89 range.innerText = ` ${node.range().to_string()} `
90 range.className = "range";
91
92 let d = document.createElement("div");
93 d.appendChild(kind);
94 d.appendChild(text);
95 d.appendChild(range);
96
97 return d;
98}
99
100function wrap(s, tag) {
101 let t = document.createElement(tag);
102 t.innerText = s;
103 return t;
104}
105
106function render_err(errorList) {
107 let errDiv = document.createElement("div");
108 errDiv.className = "syntax-err";
109 errorList.forEach(err => {
110 let sourceFile = view.state.doc.toString();
111 errDiv.appendChild(wrap(err.to_string(), "pre"));
112 // highlightArea(view, err.range());
113 });
114 return errDiv;
115}
116
117function doRender() {
118 let sourceFile = view.state.doc.toString();
119 cst.innerHTML = "";
120 try {
121 let synRoot = SynNode.from_str(sourceFile);
122 cst.appendChild(render_cst(synRoot));
123 } catch (synError) {
124 cst.appendChild(render_err(synError));
125 }
126}
127
128doRender();