-
Notifications
You must be signed in to change notification settings - Fork 0
/
eval-region.js
188 lines (162 loc) · 5.65 KB
/
eval-region.js
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
import { Prec } from '@codemirror/state'
import { keymap } from '@codemirror/view'
import { syntaxTree } from "@codemirror/language"
import { props } from "@nextjournal/lezer-clojure"
import { evalString, sciInit } from "./sci"
import { NodeProp } from "@lezer/common"
// Node props are marked in the grammar and distinguish categories of nodes
// primitive collection
const collProp = props.coll
// prefix collection - a prefix token that wraps the next element
const prefixCollProp = props.prefixColl
// the prefix edge itself
const prefixEdgeProp = props.prefixEdge
// prefix form - pair of [metadata, target]
const prefixContainerProp = props.prefixContainer
// edges at the beginning/end of collections, + "same" edges (string quotes)
const startEdgeProp = NodeProp.closedBy
const endEdgeProp = NodeProp.openedBy
const sameEdgeProp = props.sameEdge
function up(node) {
return node.parent;
}
function isTopType(nodeType) {
return nodeType.isTop;
}
function isTop(node) {
return isTopType(node.type);
}
function mainSelection(state) {
return state.selection.asSingle().ranges[0]
}
function tree(state, pos, dir) {
switch (arguments["length"]) {
case 1:
return syntaxTree(state);
case 2:
return syntaxTree(state).resolveInner(pos);
case 3:
return syntaxTree(state).resolveInner(pos, dir);
}
}
function nearestTouching(state, pos) {
const L = tree(state, pos, -1)
const R = tree(state, pos, 1)
const mid = tree(state, pos)
return L
}
function isTerminalType(nodeType) {
if (isTopType(nodeType || nodeType.prefixCollProp.prop() ||
nodeType.collProp.prop() || nodeType.name == "Meta" ||
nodeType.name == "TaggedLiteral" || nodeType.name == "ConstructorCall")) {
return false
} else {
return true
}
}
function children(parent, from, dir) {
let child = parent.childBefore(from)
return children(parent, child.from).unshift(child)
}
function parents(node, p) {
if (isTop(node)) return p;
return parents(up(node), p.concat(node));
}
function rangeStr(state, selection) {
return state.doc.slice(selection.from, selection.to).toString()
}
// Return node or its highest parent that ends at the cursor position
function uppermostEdge(pos, node) {
const p = parents(node, []).filter(n => pos == n.to && pos == node.to);
return p[p.length - 1] || node
}
function isTerminal(node, pos) {
return isTerminalType(node.type) ||
pos === node.from || pos === node.to
}
function nodeAtCursor(state) {
const pos = mainSelection(state).from
const n = nearestTouching(state, pos)
return uppermostEdge(pos, n)
}
let posAtFormEnd = 0
function topLevelNode(state) {
const pos = mainSelection(state).from
const p = parents(nearestTouching(state, pos), [])
if (p.length === 0) {
return nodeAtCursor(state)
} else {
return p[p.length - 1]
}
}
function cursorNodeString(state) {
return rangeStr(state, nodeAtCursor(state))
}
function topLevelString(state) {
return rangeStr(state, topLevelNode(state))
}
let ctx = sciInit()
let evalResult = ""
let codeTail = ""
let codeBeforeEval = ""
let posBeforeEval = 0
function updateEditor(view, text, pos) {
const doc = view.state.doc.toString()
codeBeforeEval = doc
const end = doc.length
view.dispatch({
changes: {from: 0, to: end, insert: text},
selection: {anchor: pos, head: pos}
})
}
function evalAtCursor(view) {
const doc = view.state.doc.toString()
codeBeforeEval = doc
posBeforeEval = view.state.selection.main.head
const codeBeforeCursor = codeBeforeEval.slice(0, posBeforeEval)
const codeAfterCursor = codeBeforeEval.slice(posBeforeEval, codeBeforeEval.length)
evalResult = evalString(ctx, cursorNodeString(view.state))
const codeWithResult = codeBeforeCursor + " => " + evalResult + " " + codeAfterCursor
updateEditor(view, codeWithResult, posBeforeEval)
view.dispatch({selection: {anchor: posBeforeEval, head: posBeforeEval}})
return true
}
function clearEval(view) {
if (evalResult.length != 0) {
evalResult = ""
updateEditor(view, codeBeforeEval, posBeforeEval)
}
}
function evalTopLevel(view) {
posAtFormEnd = topLevelNode(view.state).to
const doc = view.state.doc.toString()
posBeforeEval = view.state.selection.main.head
codeBeforeEval = doc
const codeBeforeFormEnd = codeBeforeEval.slice(0, posAtFormEnd)
const codeAfterFormEnd = codeBeforeEval.slice(posAtFormEnd, codeBeforeEval.length)
evalResult = evalString(ctx, topLevelString(view.state))
const codeWithResult = codeBeforeFormEnd + " => " + evalResult + " " + codeAfterFormEnd
updateEditor(view, codeWithResult, posBeforeEval)
//view.dispatch({selection: {anchor: posBeforeEval, head: posBeforeEval}})
//console.log("evalTopLevel>", evalString(ctx, topLevelString(view.state)))
return true
}
function evalCell(view) {
const doc = view.state.doc.toString()
evalResult = evalString(ctx, view.state.doc.text.join(" "))
const codeWithResult = doc + "\n" + " => " + evalResult
updateEditor(view, codeWithResult, posBeforeEval)
//console.log("evalCell>", evalString(ctx, view.state.doc.text.join(" ")))
return true
}
export function evalExtension() {
return Prec.highest(keymap.of(
[{key: "Shift-Enter", run: evalCell},
{key: "Ctrl-Enter", mac: "Cmd-Enter", run: evalAtCursor},
{key: "Alt-Enter", run: evalTopLevel},
{key: "Escape", run: clearEval},
{key: "ArrowLeft", run: clearEval},
{key: "ArrowRight", run: clearEval},
{key: "ArrowUp", run: clearEval},
{key: "ArrowDown", run: clearEval}]))
}