Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagecell
Path: blob/master/js/editor.js
447 views
1
import $ from "jquery";
2
import sagecell from "./sagecell";
3
import utils from "./utils";
4
import MultiSockJS from "./multisockjs";
5
import CodeMirror from "codemirror/lib/codemirror";
6
7
// These are imported just for their side-effects
8
import "codemirror/addon/display/autorefresh";
9
import "codemirror/addon/display/fullscreen";
10
import "codemirror/addon/edit/matchbrackets";
11
import "codemirror/addon/fold/foldcode";
12
import "codemirror/addon/fold/foldgutter";
13
import "codemirror/addon/fold/brace-fold";
14
import "codemirror/addon/fold/xml-fold";
15
import "codemirror/addon/fold/comment-fold";
16
import "codemirror/addon/fold/indent-fold";
17
import "codemirror/addon/hint/show-hint";
18
import "codemirror/addon/runmode/runmode";
19
import "codemirror/addon/runmode/colorize";
20
import "codemirror/mode/css/css";
21
import "codemirror/mode/htmlmixed/htmlmixed";
22
import "codemirror/mode/javascript/javascript";
23
import "codemirror/mode/python/python";
24
import "codemirror/mode/r/r";
25
import "codemirror/mode/xml/xml";
26
27
var ce = utils.createElement;
28
29
function makeMsg(msg_type, content) {
30
return {
31
header: {
32
msg_id: utils.uuid(),
33
session: utils.uuid(),
34
msg_type: msg_type,
35
username: "",
36
},
37
content: content,
38
parent_header: {},
39
metadata: {},
40
};
41
}
42
43
var callbacks = {};
44
45
function completerMsg(msg, callback) {
46
function sendMsg() {
47
callbacks[msg.header.msg_id] = callback;
48
completer.send(JSON.stringify(msg));
49
}
50
if (completer === undefined) {
51
var completer = new MultiSockJS(null, "complete");
52
completer.onmessage = function (event) {
53
var data = JSON.parse(event.data);
54
var cb = callbacks[data.parent_header.msg_id];
55
delete callbacks[data.parent_header.msg_id];
56
cb(data);
57
};
58
completer.onopen = sendMsg;
59
} else {
60
sendMsg();
61
}
62
}
63
64
var openedDialog = null;
65
66
function closeDialog() {
67
if (openedDialog) {
68
openedDialog.dialog("destroy");
69
openedDialog = null;
70
}
71
}
72
73
function showInfo(msg, cm) {
74
if (!msg.content.found) {
75
return;
76
}
77
var d = ce("pre");
78
d.innerHTML = utils.fixConsole(msg.content.data["text/plain"]);
79
closeDialog();
80
openedDialog = $(d).dialog({
81
width: 700,
82
height: 300,
83
position: {
84
my: "left top",
85
at: "left+5px bottom+5px",
86
of: cm.display.cursorDiv.parentNode,
87
collision: "none",
88
},
89
appendTo: $(cm.display.wrapper).parents(".sagecell").first(),
90
close: closeDialog,
91
});
92
cm.focus();
93
}
94
95
function requestInfo(cm) {
96
var cur = cm.getCursor();
97
var line = cm.getLine(cur.line).substr(0, cur.ch);
98
var detail = cur.ch > 1 && line[cur.ch - 2] === "?" ? 1 : 0;
99
var oname = line.match(/([a-z_][a-z_\d.]*)(\?\??|\()$/i);
100
if (oname === null) {
101
return;
102
}
103
var cb = function (data) {
104
showInfo(data, cm);
105
};
106
var kernel = sagecell.kernels[cm.k];
107
if (kernel && kernel.session.linked && kernel.shell_channel.send) {
108
var msg = kernel._get_msg("object_info_request", {
109
oname: oname[1],
110
detail_level: detail,
111
});
112
kernel.shell_channel.send(JSON.stringify(msg));
113
kernel.set_callbacks_for_msg(msg.header.msg_id, {
114
inspect_request: cb,
115
});
116
} else {
117
completerMsg(
118
makeMsg("object_info_request", {
119
oname: oname[1],
120
detail_level: detail,
121
}),
122
cb
123
);
124
}
125
}
126
127
function render(editorType, inputLocation, collapse) {
128
var commands = inputLocation.find(".sagecell_commands");
129
var editorData;
130
if (collapse !== undefined) {
131
var header, code;
132
var accordion = ce("div", {}, [
133
(header = ce("h3", {}, ["Code"])),
134
(code = document.createElement("div")),
135
]);
136
header.style.paddingLeft = "2.2em";
137
$(accordion).insertBefore(commands);
138
$(accordion).accordion({
139
active: collapse ? false : header,
140
collapsible: true,
141
header: header,
142
});
143
}
144
commands.on("keypress", function (event) {
145
if (event.which === 13 && event.shiftKey) {
146
event.preventDefault();
147
}
148
});
149
commands.on("keyup", function (event) {
150
if (event.which === 13 && event.shiftKey) {
151
inputLocation.find(".sagecell_evalButton").click();
152
}
153
});
154
if (editorType === "textarea") {
155
editorData = {};
156
} else if (editorType === "textarea-readonly") {
157
editorData = {};
158
commands.attr("readonly", "readonly");
159
} else {
160
var readOnly = false;
161
if (editorType === "codemirror-readonly") {
162
readOnly = true;
163
} else {
164
editorType = "codemirror";
165
}
166
var langSelect = inputLocation.find(".sagecell_language select");
167
var mode = langSelect[0].value;
168
CodeMirror.commands.autocomplete = function (cm) {
169
CodeMirror.showHint(
170
cm,
171
function (cm, callback) {
172
var cur = cm.getCursor();
173
var kernel = sagecell.kernels[cm.k];
174
var cb = function (data) {
175
if (data.content) {
176
data = data.content;
177
}
178
callback({
179
list: data.matches,
180
from: CodeMirror.Pos(cur.line, data.cursor_start),
181
to: cur,
182
});
183
};
184
var mode = langSelect[0].value;
185
if (
186
(mode === "sage" || mode === "python") &&
187
kernel &&
188
kernel.session.linked &&
189
kernel.shell_channel.send
190
) {
191
kernel.complete(cm.getLine(cur.line), cur.ch, {
192
complete_reply: cb,
193
});
194
} else {
195
completerMsg(
196
makeMsg("complete_request", {
197
text: "",
198
line: cm.getLine(cur.line),
199
cursor_pos: cur.ch,
200
mode: mode,
201
}),
202
cb
203
);
204
}
205
},
206
{ async: true }
207
);
208
};
209
var fullscreen = $(
210
ce("button", {
211
title: "Toggle full-screen editor (F11)",
212
type: "button",
213
class: "sagecell_fullScreen sagecell_icon-resize-full",
214
})
215
);
216
var fullscreenToggle = function (cm) {
217
cm.setOption("fullScreen", !cm.getOption("fullScreen"));
218
fullscreen.toggleClass("sagecell_fullScreenEnabled");
219
fullscreen.toggleClass(
220
"sagecell_icon-resize-full sagecell_icon-resize-small"
221
);
222
};
223
224
editorData = CodeMirror.fromTextArea(commands.get(0), {
225
autoRefresh: true,
226
mode: sagecell.modes[mode],
227
viewportMargin: Infinity,
228
indentUnit: 4,
229
lineNumbers: true,
230
matchBrackets: true,
231
readOnly: readOnly,
232
foldGutter: true,
233
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
234
screenReaderLabel: "Sagecell editor",
235
extraKeys: {
236
Tab: function (cm) {
237
var cur = cm.getCursor();
238
var line = cm.getLine(cur.line).substr(0, cur.ch);
239
var mode = langSelect[0].value;
240
if (
241
(mode === "sage" || mode === "python") &&
242
cur.ch > 0 &&
243
(line[cur.ch - 1] === "?" || line[cur.ch - 1] === "(")
244
) {
245
requestInfo(cm);
246
} else if (line.match(/^ *$/)) {
247
CodeMirror.commands.indentMore(cm);
248
} else {
249
closeDialog();
250
CodeMirror.commands.autocomplete(cm);
251
}
252
},
253
"Shift-Tab": "indentLess",
254
"Shift-Enter": closeDialog,
255
F11: function (cm) {
256
fullscreenToggle(cm);
257
},
258
Esc: function (cm) {
259
if (openedDialog) {
260
closeDialog();
261
} else if (cm.getOption("fullScreen")) {
262
fullscreenToggle(cm);
263
}
264
},
265
},
266
});
267
editorData.on("keyup", function (cm, event) {
268
cm.save();
269
if (event.which === 13 && event.shiftKey) {
270
inputLocation.find(".sagecell_evalButton").click();
271
if (cm.getOption("fullScreen")) {
272
fullscreenToggle(cm);
273
}
274
}
275
});
276
$(accordion).on("accordionactivate", function () {
277
editorData.refresh();
278
});
279
$(editorData.getWrapperElement()).prepend(fullscreen);
280
fullscreen.on("click", function () {
281
fullscreenToggle(editorData);
282
editorData.focus();
283
});
284
}
285
return [editorType, editorData];
286
}
287
288
export default {
289
render: render,
290
};
291
292