-
Notifications
You must be signed in to change notification settings - Fork 128
/
eval-selection.ts
120 lines (109 loc) · 3.35 KB
/
eval-selection.ts
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
import generate from '@babel/generator';
import * as t from '@babel/types';
import * as monaco from 'monaco-editor';
import { evalCode } from '../sandbox';
export function registerEvalSelection(
editor: monaco.editor.IStandaloneCodeEditor,
): monaco.IDisposable {
const codeAction = monaco.languages.registerCodeActionProvider('javascript', {
provideCodeActions(_model, range) {
if (range.isEmpty()) return;
return {
actions: [
{
title: 'Evaluate and replace (value)',
kind: 'refactor',
command: {
id: 'editor.action.evaluate-expression',
title: 'Evaluate and replace (value)',
},
},
{
title: 'Evaluate and replace (raw)',
kind: 'refactor',
command: {
id: 'editor.action.evaluate-raw',
title: 'Evaluate and replace (raw)',
},
},
],
dispose: () => {},
};
},
});
const evalValuesCommand = monaco.editor.registerCommand(
'editor.action.evaluate-expression',
// eslint-disable-next-line @typescript-eslint/no-misused-promises
evalValues,
);
const evalRawCommand = monaco.editor.registerCommand(
'editor.action.evaluate-raw',
// eslint-disable-next-line @typescript-eslint/no-misused-promises
evalRaw,
);
const evalValueAction = editor.addAction({
id: 'editor.action.evaluate-expression',
label: 'Evaluate and replace selection (value)',
precondition: 'editorHasSelection',
keybindings: [monaco.KeyMod.Shift | monaco.KeyCode.Enter],
run: evalValues,
});
const evalRawAction = editor.addAction({
id: 'editor.action.evaluate-raw',
label: 'Evaluate and replace selection (raw)',
precondition: 'editorHasSelection',
keybindings: [
monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.Enter,
],
run: evalRaw,
});
async function evalValues() {
const selections = editor.getSelections();
if (!selections) return;
await evalSelections(
selections,
(value) => generate(t.valueToNode(value)).code,
);
}
async function evalRaw() {
const selections = editor.getSelections();
if (!selections) return;
await evalSelections(selections, (value) => {
if (typeof value !== 'string') {
console.error(value);
throw new Error('Evaluated value must be a string');
}
return value;
});
}
async function evalSelections(
ranges: monaco.Range[],
mapper: (value: unknown) => string,
) {
if (ranges.some((range) => range.isEmpty())) return;
const expressions = ranges
.map((range) => {
const value = editor.getModel()!.getValueInRange(range);
return `eval(${JSON.stringify(value)})`;
})
.join(',');
// New lines are added so line comments don't mess up the rest of the code
const code = `[\n${expressions}\n]`;
const values = (await evalCode(code)) as unknown[];
const edits = ranges.map((range, index) => ({
range,
text: mapper(values[index]),
}));
editor.pushUndoStop();
editor.executeEdits('evaluate-expression', edits);
}
return {
dispose() {
codeAction.dispose();
evalValuesCommand.dispose();
evalValueAction.dispose();
evalRawCommand.dispose();
evalRawAction.dispose();
},
};
}