1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
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');
let view = new EditorView({
state: EditorState.create({
extensions: [
basicSetup,
rust(),
EditorView.updateListener.of((v) => {
if (v.docChanged) {
doRender()
}
})
]
}),
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)]
});
}
return Decoration.none;
},
provide: f => EditorView.decorations.from(f)
})
const hlMark = Decoration.mark({class: "cm-highlight"})
const hlTheme = EditorView.baseTheme({
".cm-highlight": {
backgroundColor: "#ff3299aa"
}
})
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) {
let nodeDiv = document.createElement("div");
nodeDiv.className = "syntax-node";
let r = synRoot.range();
let synText = wrap(synRoot.kind() + " @ " + r.to_string() + " " +synRoot.text(), "pre");
synText.onmouseover = () => {
highlightArea(view, r);
}
nodeDiv.appendChild(synText);
if (!synRoot.is_token()) {
synRoot.children().forEach(node => {
nodeDiv.appendChild(render_cst(node));
});
}
return nodeDiv;
}
function wrap(s, tag) {
let t = document.createElement(tag);
t.innerText = s;
return t;
}
function render_err(errorList) {
let errDiv = document.createElement("div");
errDiv.className = "syntax-err";
errorList.forEach(err => {
errDiv.appendChild(wrap(err.to_string(), "pre"));
// highlightArea(view, err.range());
});
return errDiv;
}
function doRender() {
let sourceFile = view.state.doc.toString();;
cst.innerHTML = "";
try {
let synRoot = SynNode.from_str(sourceFile);
cst.appendChild(render_cst(synRoot));
} catch (synError) {
cst.appendChild(render_err(synError));
}
}
|