Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
malwaredllc
GitHub Repository: malwaredllc/byob
Path: blob/master/web-gui/buildyourownbotnet/assets/js/codemirror/keymap/emacs.js
1293 views
1
(function() {
2
"use strict";
3
4
var Pos = CodeMirror.Pos;
5
function posEq(a, b) { return a.line == b.line && a.ch == b.ch; }
6
7
// Kill 'ring'
8
9
var killRing = [];
10
function addToRing(str) {
11
killRing.push(str);
12
if (killRing.length > 50) killRing.shift();
13
}
14
function growRingTop(str) {
15
if (!killRing.length) return addToRing(str);
16
killRing[killRing.length - 1] += str;
17
}
18
function getFromRing(n) { return killRing[killRing.length - (n ? Math.min(n, 1) : 1)] || ""; }
19
function popFromRing() { if (killRing.length > 1) killRing.pop(); return getFromRing(); }
20
21
var lastKill = null;
22
23
function kill(cm, from, to, mayGrow, text) {
24
if (text == null) text = cm.getRange(from, to);
25
26
if (mayGrow && lastKill && lastKill.cm == cm && posEq(from, lastKill.pos) && cm.isClean(lastKill.gen))
27
growRingTop(text);
28
else
29
addToRing(text);
30
cm.replaceRange("", from, to, "+delete");
31
32
if (mayGrow) lastKill = {cm: cm, pos: from, gen: cm.changeGeneration()};
33
else lastKill = null;
34
}
35
36
// Boundaries of various units
37
38
function byChar(cm, pos, dir) {
39
return cm.findPosH(pos, dir, "char", true);
40
}
41
42
function byWord(cm, pos, dir) {
43
return cm.findPosH(pos, dir, "word", true);
44
}
45
46
function byLine(cm, pos, dir) {
47
return cm.findPosV(pos, dir, "line", cm.doc.sel.goalColumn);
48
}
49
50
function byPage(cm, pos, dir) {
51
return cm.findPosV(pos, dir, "page", cm.doc.sel.goalColumn);
52
}
53
54
function byParagraph(cm, pos, dir) {
55
var no = pos.line, line = cm.getLine(no);
56
var sawText = /\S/.test(dir < 0 ? line.slice(0, pos.ch) : line.slice(pos.ch));
57
var fst = cm.firstLine(), lst = cm.lastLine();
58
for (;;) {
59
no += dir;
60
if (no < fst || no > lst)
61
return cm.clipPos(Pos(no - dir, dir < 0 ? 0 : null));
62
line = cm.getLine(no);
63
var hasText = /\S/.test(line);
64
if (hasText) sawText = true;
65
else if (sawText) return Pos(no, 0);
66
}
67
}
68
69
function bySentence(cm, pos, dir) {
70
var line = pos.line, ch = pos.ch;
71
var text = cm.getLine(pos.line), sawWord = false;
72
for (;;) {
73
var next = text.charAt(ch + (dir < 0 ? -1 : 0));
74
if (!next) { // End/beginning of line reached
75
if (line == (dir < 0 ? cm.firstLine() : cm.lastLine())) return Pos(line, ch);
76
text = cm.getLine(line + dir);
77
if (!/\S/.test(text)) return Pos(line, ch);
78
line += dir;
79
ch = dir < 0 ? text.length : 0;
80
continue;
81
}
82
if (sawWord && /[!?.]/.test(next)) return Pos(line, ch + (dir > 0 ? 1 : 0));
83
if (!sawWord) sawWord = /\w/.test(next);
84
ch += dir;
85
}
86
}
87
88
function byExpr(cm, pos, dir) {
89
var wrap;
90
if (cm.findMatchingBracket && (wrap = cm.findMatchingBracket(pos, true))
91
&& wrap.match && (wrap.forward ? 1 : -1) == dir)
92
return dir > 0 ? Pos(wrap.to.line, wrap.to.ch + 1) : wrap.to;
93
94
for (var first = true;; first = false) {
95
var token = cm.getTokenAt(pos);
96
var after = Pos(pos.line, dir < 0 ? token.start : token.end);
97
if (first && dir > 0 && token.end == pos.ch || !/\w/.test(token.string)) {
98
var newPos = cm.findPosH(after, dir, "char");
99
if (posEq(after, newPos)) return pos;
100
else pos = newPos;
101
} else {
102
return after;
103
}
104
}
105
}
106
107
// Prefixes (only crudely supported)
108
109
function getPrefix(cm, precise) {
110
var digits = cm.state.emacsPrefix;
111
if (!digits) return precise ? null : 1;
112
clearPrefix(cm);
113
return digits == "-" ? -1 : Number(digits);
114
}
115
116
function repeated(cmd) {
117
var f = typeof cmd == "string" ? function(cm) { cm.execCommand(cmd); } : cmd;
118
return function(cm) {
119
var prefix = getPrefix(cm);
120
f(cm);
121
for (var i = 1; i < prefix; ++i) f(cm);
122
};
123
}
124
125
function findEnd(cm, by, dir) {
126
var pos = cm.getCursor(), prefix = getPrefix(cm);
127
if (prefix < 0) { dir = -dir; prefix = -prefix; }
128
for (var i = 0; i < prefix; ++i) {
129
var newPos = by(cm, pos, dir);
130
if (posEq(newPos, pos)) break;
131
pos = newPos;
132
}
133
return pos;
134
}
135
136
function move(by, dir) {
137
var f = function(cm) {
138
cm.extendSelection(findEnd(cm, by, dir));
139
};
140
f.motion = true;
141
return f;
142
}
143
144
function killTo(cm, by, dir) {
145
kill(cm, cm.getCursor(), findEnd(cm, by, dir), true);
146
}
147
148
function addPrefix(cm, digit) {
149
if (cm.state.emacsPrefix) {
150
if (digit != "-") cm.state.emacsPrefix += digit;
151
return;
152
}
153
// Not active yet
154
cm.state.emacsPrefix = digit;
155
cm.on("keyHandled", maybeClearPrefix);
156
cm.on("inputRead", maybeDuplicateInput);
157
}
158
159
var prefixPreservingKeys = {"Alt-G": true, "Ctrl-X": true, "Ctrl-Q": true, "Ctrl-U": true};
160
161
function maybeClearPrefix(cm, arg) {
162
if (!cm.state.emacsPrefixMap && !prefixPreservingKeys.hasOwnProperty(arg))
163
clearPrefix(cm);
164
}
165
166
function clearPrefix(cm) {
167
cm.state.emacsPrefix = null;
168
cm.off("keyHandled", maybeClearPrefix);
169
cm.off("inputRead", maybeDuplicateInput);
170
}
171
172
function maybeDuplicateInput(cm, event) {
173
var dup = getPrefix(cm);
174
if (dup > 1 && event.origin == "+input") {
175
var one = event.text.join("\n"), txt = "";
176
for (var i = 1; i < dup; ++i) txt += one;
177
cm.replaceSelection(txt, "end", "+input");
178
}
179
}
180
181
function addPrefixMap(cm) {
182
cm.state.emacsPrefixMap = true;
183
cm.addKeyMap(prefixMap);
184
cm.on("keyHandled", maybeRemovePrefixMap);
185
cm.on("inputRead", maybeRemovePrefixMap);
186
}
187
188
function maybeRemovePrefixMap(cm, arg) {
189
if (typeof arg == "string" && (/^\d$/.test(arg) || arg == "Ctrl-U")) return;
190
cm.removeKeyMap(prefixMap);
191
cm.state.emacsPrefixMap = false;
192
cm.off("keyHandled", maybeRemovePrefixMap);
193
cm.off("inputRead", maybeRemovePrefixMap);
194
}
195
196
// Utilities
197
198
function setMark(cm) {
199
cm.setCursor(cm.getCursor());
200
cm.setExtending(true);
201
cm.on("change", function() { cm.setExtending(false); });
202
}
203
204
function clearMark(cm) {
205
cm.setExtending(false);
206
cm.setCursor(cm.getCursor());
207
}
208
209
function getInput(cm, msg, f) {
210
if (cm.openDialog)
211
cm.openDialog(msg + ": <input type=\"text\" style=\"width: 10em\"/>", f, {bottom: true});
212
else
213
f(prompt(msg, ""));
214
}
215
216
function operateOnWord(cm, op) {
217
var start = cm.getCursor(), end = cm.findPosH(start, 1, "word");
218
cm.replaceRange(op(cm.getRange(start, end)), start, end);
219
cm.setCursor(end);
220
}
221
222
function toEnclosingExpr(cm) {
223
var pos = cm.getCursor(), line = pos.line, ch = pos.ch;
224
var stack = [];
225
while (line >= cm.firstLine()) {
226
var text = cm.getLine(line);
227
for (var i = ch == null ? text.length : ch; i > 0;) {
228
var ch = text.charAt(--i);
229
if (ch == ")")
230
stack.push("(");
231
else if (ch == "]")
232
stack.push("[");
233
else if (ch == "}")
234
stack.push("{");
235
else if (/[\(\{\[]/.test(ch) && (!stack.length || stack.pop() != ch))
236
return cm.extendSelection(Pos(line, i));
237
}
238
--line; ch = null;
239
}
240
}
241
242
function quit(cm) {
243
cm.execCommand("clearSearch");
244
clearMark(cm);
245
}
246
247
// Actual keymap
248
249
var keyMap = CodeMirror.keyMap.emacs = {
250
"Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"));},
251
"Ctrl-K": repeated(function(cm) {
252
var start = cm.getCursor(), end = cm.clipPos(Pos(start.line));
253
var text = cm.getRange(start, end);
254
if (!/\S/.test(text)) {
255
text += "\n";
256
end = Pos(start.line + 1, 0);
257
}
258
kill(cm, start, end, true, text);
259
}),
260
"Alt-W": function(cm) {
261
addToRing(cm.getSelection());
262
clearMark(cm);
263
},
264
"Ctrl-Y": function(cm) {
265
var start = cm.getCursor();
266
cm.replaceRange(getFromRing(getPrefix(cm)), start, start, "paste");
267
cm.setSelection(start, cm.getCursor());
268
},
269
"Alt-Y": function(cm) {cm.replaceSelection(popFromRing());},
270
271
"Ctrl-Space": setMark, "Ctrl-Shift-2": setMark,
272
273
"Ctrl-F": move(byChar, 1), "Ctrl-B": move(byChar, -1),
274
"Right": move(byChar, 1), "Left": move(byChar, -1),
275
"Ctrl-D": function(cm) { killTo(cm, byChar, 1); },
276
"Delete": function(cm) { killTo(cm, byChar, 1); },
277
"Ctrl-H": function(cm) { killTo(cm, byChar, -1); },
278
"Backspace": function(cm) { killTo(cm, byChar, -1); },
279
280
"Alt-F": move(byWord, 1), "Alt-B": move(byWord, -1),
281
"Alt-D": function(cm) { killTo(cm, byWord, 1); },
282
"Alt-Backspace": function(cm) { killTo(cm, byWord, -1); },
283
284
"Ctrl-N": move(byLine, 1), "Ctrl-P": move(byLine, -1),
285
"Down": move(byLine, 1), "Up": move(byLine, -1),
286
"Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
287
"End": "goLineEnd", "Home": "goLineStart",
288
289
"Alt-V": move(byPage, -1), "Ctrl-V": move(byPage, 1),
290
"PageUp": move(byPage, -1), "PageDown": move(byPage, 1),
291
292
"Ctrl-Up": move(byParagraph, -1), "Ctrl-Down": move(byParagraph, 1),
293
294
"Alt-A": move(bySentence, -1), "Alt-E": move(bySentence, 1),
295
"Alt-K": function(cm) { killTo(cm, bySentence, 1); },
296
297
"Ctrl-Alt-K": function(cm) { killTo(cm, byExpr, 1); },
298
"Ctrl-Alt-Backspace": function(cm) { killTo(cm, byExpr, -1); },
299
"Ctrl-Alt-F": move(byExpr, 1), "Ctrl-Alt-B": move(byExpr, -1),
300
301
"Shift-Ctrl-Alt-2": function(cm) {
302
cm.setSelection(findEnd(cm, byExpr, 1), cm.getCursor());
303
},
304
"Ctrl-Alt-T": function(cm) {
305
var leftStart = byExpr(cm, cm.getCursor(), -1), leftEnd = byExpr(cm, leftStart, 1);
306
var rightEnd = byExpr(cm, leftEnd, 1), rightStart = byExpr(cm, rightEnd, -1);
307
cm.replaceRange(cm.getRange(rightStart, rightEnd) + cm.getRange(leftEnd, rightStart) +
308
cm.getRange(leftStart, leftEnd), leftStart, rightEnd);
309
},
310
"Ctrl-Alt-U": repeated(toEnclosingExpr),
311
312
"Alt-Space": function(cm) {
313
var pos = cm.getCursor(), from = pos.ch, to = pos.ch, text = cm.getLine(pos.line);
314
while (from && /\s/.test(text.charAt(from - 1))) --from;
315
while (to < text.length && /\s/.test(text.charAt(to))) ++to;
316
cm.replaceRange(" ", Pos(pos.line, from), Pos(pos.line, to));
317
},
318
"Ctrl-O": repeated(function(cm) { cm.replaceSelection("\n", "start"); }),
319
"Ctrl-T": repeated(function(cm) {
320
var pos = cm.getCursor();
321
if (pos.ch < cm.getLine(pos.line).length) pos = Pos(pos.line, pos.ch + 1);
322
var from = cm.findPosH(pos, -2, "char");
323
var range = cm.getRange(from, pos);
324
if (range.length != 2) return;
325
cm.setSelection(from, pos);
326
cm.replaceSelection(range.charAt(1) + range.charAt(0), "end");
327
}),
328
329
"Alt-C": repeated(function(cm) {
330
operateOnWord(cm, function(w) {
331
var letter = w.search(/\w/);
332
if (letter == -1) return w;
333
return w.slice(0, letter) + w.charAt(letter).toUpperCase() + w.slice(letter + 1).toLowerCase();
334
});
335
}),
336
"Alt-U": repeated(function(cm) {
337
operateOnWord(cm, function(w) { return w.toUpperCase(); });
338
}),
339
"Alt-L": repeated(function(cm) {
340
operateOnWord(cm, function(w) { return w.toLowerCase(); });
341
}),
342
343
"Alt-;": "toggleComment",
344
345
"Ctrl-/": repeated("undo"), "Shift-Ctrl--": repeated("undo"),
346
"Ctrl-Z": repeated("undo"), "Cmd-Z": repeated("undo"),
347
"Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd",
348
"Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": quit, "Shift-Alt-5": "replace",
349
"Alt-/": "autocomplete",
350
"Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto",
351
352
"Alt-G": function(cm) {cm.setOption("keyMap", "emacs-Alt-G");},
353
"Ctrl-X": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-X");},
354
"Ctrl-Q": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-Q");},
355
"Ctrl-U": addPrefixMap
356
};
357
358
CodeMirror.keyMap["emacs-Ctrl-X"] = {
359
"Tab": function(cm) {
360
cm.indentSelection(getPrefix(cm, true) || cm.getOption("indentUnit"));
361
},
362
"Ctrl-X": function(cm) {
363
cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
364
},
365
366
"Ctrl-S": "save", "Ctrl-W": "save", "S": "saveAll", "F": "open", "U": repeated("undo"), "K": "close",
367
"Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); },
368
auto: "emacs", nofallthrough: true, disableInput: true
369
};
370
371
CodeMirror.keyMap["emacs-Alt-G"] = {
372
"G": function(cm) {
373
var prefix = getPrefix(cm, true);
374
if (prefix != null && prefix > 0) return cm.setCursor(prefix - 1);
375
376
getInput(cm, "Goto line", function(str) {
377
var num;
378
if (str && !isNaN(num = Number(str)) && num == num|0 && num > 0)
379
cm.setCursor(num - 1);
380
});
381
},
382
auto: "emacs", nofallthrough: true, disableInput: true
383
};
384
385
CodeMirror.keyMap["emacs-Ctrl-Q"] = {
386
"Tab": repeated("insertTab"),
387
auto: "emacs", nofallthrough: true
388
};
389
390
var prefixMap = {"Ctrl-G": clearPrefix};
391
function regPrefix(d) {
392
prefixMap[d] = function(cm) { addPrefix(cm, d); };
393
keyMap["Ctrl-" + d] = function(cm) { addPrefix(cm, d); };
394
prefixPreservingKeys["Ctrl-" + d] = true;
395
}
396
for (var i = 0; i < 10; ++i) regPrefix(String(i));
397
regPrefix("-");
398
})();
399
400