codemirror-rails 3.13 → 3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/codemirror/rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/codemirror.js +328 -250
  4. data/vendor/assets/javascripts/codemirror/addons/comment/comment.js +7 -6
  5. data/vendor/assets/javascripts/codemirror/addons/edit/closebrackets.js +33 -7
  6. data/vendor/assets/javascripts/codemirror/addons/edit/matchbrackets.js +14 -10
  7. data/vendor/assets/javascripts/codemirror/addons/edit/trailingspace.js +15 -0
  8. data/vendor/assets/javascripts/codemirror/addons/fold/brace-fold.js +70 -17
  9. data/vendor/assets/javascripts/codemirror/addons/fold/foldcode.js +56 -20
  10. data/vendor/assets/javascripts/codemirror/addons/fold/xml-fold.js +135 -39
  11. data/vendor/assets/javascripts/codemirror/addons/hint/html-hint.js +324 -571
  12. data/vendor/assets/javascripts/codemirror/addons/hint/show-hint.js +199 -109
  13. data/vendor/assets/javascripts/codemirror/addons/hint/xml-hint.js +60 -113
  14. data/vendor/assets/javascripts/codemirror/addons/lint/coffeescript-lint.js +24 -0
  15. data/vendor/assets/javascripts/codemirror/addons/merge/merge.js +431 -0
  16. data/vendor/assets/javascripts/codemirror/addons/mode/multiplex.js +7 -1
  17. data/vendor/assets/javascripts/codemirror/addons/search/match-highlighter.js +46 -20
  18. data/vendor/assets/javascripts/codemirror/addons/search/search.js +1 -1
  19. data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +370 -13
  20. data/vendor/assets/javascripts/codemirror/keymaps/extra.js +43 -0
  21. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +535 -214
  22. data/vendor/assets/javascripts/codemirror/modes/clike.js +56 -0
  23. data/vendor/assets/javascripts/codemirror/modes/javascript.js +19 -14
  24. data/vendor/assets/javascripts/codemirror/modes/markdown.js +2 -2
  25. data/vendor/assets/javascripts/codemirror/modes/ruby.js +67 -16
  26. data/vendor/assets/javascripts/codemirror/modes/smarty.js +167 -110
  27. data/vendor/assets/javascripts/codemirror/modes/sql.js +97 -15
  28. data/vendor/assets/javascripts/codemirror/modes/xml.js +14 -18
  29. data/vendor/assets/stylesheets/codemirror.css +0 -1
  30. data/vendor/assets/stylesheets/codemirror/addons/merge/merge.css +82 -0
  31. metadata +7 -2
@@ -1,5 +1,5 @@
1
1
  CodeMirror.multiplexingMode = function(outer /*, others */) {
2
- // Others should be {open, close, mode [, delimStyle]} objects
2
+ // Others should be {open, close, mode [, delimStyle] [, innerStyle]} objects
3
3
  var others = Array.prototype.slice.call(arguments, 1);
4
4
  var n_others = others.length;
5
5
 
@@ -58,6 +58,12 @@ CodeMirror.multiplexingMode = function(outer /*, others */) {
58
58
  if (found > -1) stream.string = oldContent;
59
59
  var cur = stream.current(), found = cur.indexOf(curInner.close);
60
60
  if (found > -1) stream.backUp(cur.length - found);
61
+
62
+ if (curInner.innerStyle) {
63
+ if (innerToken) innerToken = innerToken + ' ' + curInner.innerStyle;
64
+ else innerToken = curInner.innerStyle;
65
+ }
66
+
61
67
  return innerToken;
62
68
  }
63
69
  },
@@ -5,35 +5,49 @@
5
5
  // document.
6
6
  //
7
7
  // The option can be set to true to simply enable it, or to a
8
- // {minChars, style} object to explicitly configure it. minChars is
9
- // the minimum amount of characters that should be selected for the
10
- // behavior to occur, and style is the token style to apply to the
11
- // matches. This will be prefixed by "cm-" to create an actual CSS
12
- // class name.
8
+ // {minChars, style, showToken} object to explicitly configure it.
9
+ // minChars is the minimum amount of characters that should be
10
+ // selected for the behavior to occur, and style is the token style to
11
+ // apply to the matches. This will be prefixed by "cm-" to create an
12
+ // actual CSS class name. showToken, when enabled, will cause the
13
+ // current token to be highlighted when nothing is selected.
13
14
 
14
15
  (function() {
15
16
  var DEFAULT_MIN_CHARS = 2;
16
17
  var DEFAULT_TOKEN_STYLE = "matchhighlight";
17
18
 
18
19
  function State(options) {
19
- this.minChars = typeof options == "object" && options.minChars || DEFAULT_MIN_CHARS;
20
- this.style = typeof options == "object" && options.style || DEFAULT_TOKEN_STYLE;
21
- this.overlay = null;
20
+ if (typeof options == "object") {
21
+ this.minChars = options.minChars;
22
+ this.style = options.style;
23
+ this.showToken = options.showToken;
24
+ }
25
+ if (this.style == null) this.style = DEFAULT_TOKEN_STYLE;
26
+ if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS;
27
+ this.overlay = this.timeout = null;
22
28
  }
23
29
 
24
30
  CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
25
- var prev = old && old != CodeMirror.Init;
26
- if (val && !prev) {
27
- cm.state.matchHighlighter = new State(val);
28
- cm.on("cursorActivity", highlightMatches);
29
- } else if (!val && prev) {
31
+ if (old && old != CodeMirror.Init) {
30
32
  var over = cm.state.matchHighlighter.overlay;
31
33
  if (over) cm.removeOverlay(over);
34
+ clearTimeout(cm.state.matchHighlighter.timeout);
32
35
  cm.state.matchHighlighter = null;
33
- cm.off("cursorActivity", highlightMatches);
36
+ cm.off("cursorActivity", cursorActivity);
37
+ }
38
+ if (val) {
39
+ cm.state.matchHighlighter = new State(val);
40
+ highlightMatches(cm);
41
+ cm.on("cursorActivity", cursorActivity);
34
42
  }
35
43
  });
36
44
 
45
+ function cursorActivity(cm) {
46
+ var state = cm.state.matchHighlighter;
47
+ clearTimeout(state.timeout);
48
+ state.timeout = setTimeout(function() {highlightMatches(cm);}, 100);
49
+ }
50
+
37
51
  function highlightMatches(cm) {
38
52
  cm.operation(function() {
39
53
  var state = cm.state.matchHighlighter;
@@ -42,17 +56,29 @@
42
56
  state.overlay = null;
43
57
  }
44
58
 
45
- if (!cm.somethingSelected()) return;
59
+ if (!cm.somethingSelected() && state.showToken) {
60
+ var tok = cm.getTokenAt(cm.getCursor()).string;
61
+ if (/\w/.test(tok))
62
+ cm.addOverlay(state.overlay = makeOverlay(tok, true, state.style));
63
+ return;
64
+ }
65
+ if (cm.getCursor("head").line != cm.getCursor("anchor").line) return;
46
66
  var selection = cm.getSelection().replace(/^\s+|\s+$/g, "");
47
- if (selection.length < state.minChars) return;
48
-
49
- cm.addOverlay(state.overlay = makeOverlay(selection, state.style));
67
+ if (selection.length >= state.minChars)
68
+ cm.addOverlay(state.overlay = makeOverlay(selection, false, state.style));
50
69
  });
51
70
  }
52
71
 
53
- function makeOverlay(query, style) {
72
+ function boundariesAround(stream) {
73
+ return (stream.start || /.\b./.test(stream.string.slice(stream.start - 1, stream.start + 1))) &&
74
+ (stream.pos == stream.string.length || /.\b./.test(stream.string.slice(stream.pos - 1, stream.pos + 1)));
75
+ }
76
+
77
+ function makeOverlay(query, wordBoundaries, style) {
54
78
  return {token: function(stream) {
55
- if (stream.match(query)) return style;
79
+ if (stream.match(query) &&
80
+ (!wordBoundaries || boundariesAround(stream)))
81
+ return style;
56
82
  stream.next();
57
83
  stream.skipTo(query.charAt(0)) || stream.skipToEnd();
58
84
  }};
@@ -55,7 +55,7 @@
55
55
  if (!query || state.query) return;
56
56
  state.query = parseQuery(query);
57
57
  cm.removeOverlay(state.overlay);
58
- state.overlay = searchOverlay(query);
58
+ state.overlay = searchOverlay(state.query);
59
59
  cm.addOverlay(state.overlay);
60
60
  state.posFrom = state.posTo = cm.getCursor();
61
61
  findNext(cm, rev);
@@ -1,30 +1,387 @@
1
- // TODO number prefixes
2
1
  (function() {
3
- // Really primitive kill-ring implementation.
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
+
4
9
  var killRing = [];
5
10
  function addToRing(str) {
6
11
  killRing.push(str);
7
12
  if (killRing.length > 50) killRing.shift();
8
13
  }
9
- function getFromRing() { return killRing[killRing.length - 1] || ""; }
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)] || ""; }
10
19
  function popFromRing() { if (killRing.length > 1) killRing.pop(); return getFromRing(); }
11
20
 
12
- CodeMirror.keyMap.emacs = {
13
- "Ctrl-X": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-X");},
14
- "Ctrl-W": function(cm) {addToRing(cm.getSelection()); cm.replaceSelection("");},
15
- "Ctrl-Alt-W": function(cm) {addToRing(cm.getSelection()); cm.replaceSelection("");},
16
- "Alt-W": function(cm) {addToRing(cm.getSelection());},
17
- "Ctrl-Y": function(cm) {cm.replaceSelection(getFromRing());},
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 getInput(cm, msg, f) {
205
+ if (cm.openDialog)
206
+ cm.openDialog(msg + ": <input type=\"text\" style=\"width: 10em\"/>", f, {bottom: true});
207
+ else
208
+ f(prompt(msg, ""));
209
+ }
210
+
211
+ function operateOnWord(cm, op) {
212
+ var start = cm.getCursor(), end = cm.findPosH(start, 1, "word");
213
+ cm.replaceRange(op(cm.getRange(start, end)), start, end);
214
+ cm.setCursor(end);
215
+ }
216
+
217
+ function toEnclosingExpr(cm) {
218
+ var pos = cm.getCursor(), line = pos.line, ch = pos.ch;
219
+ var stack = [];
220
+ while (line >= cm.firstLine()) {
221
+ var text = cm.getLine(line);
222
+ for (var i = ch == null ? text.length : ch; i > 0;) {
223
+ var ch = text.charAt(--i);
224
+ if (ch == ")")
225
+ stack.push("(");
226
+ else if (ch == "]")
227
+ stack.push("[");
228
+ else if (ch == "}")
229
+ stack.push("{");
230
+ else if (/[\(\{\[]/.test(ch) && (!stack.length || stack.pop() != ch))
231
+ return cm.extendSelection(Pos(line, i));
232
+ }
233
+ --line; ch = null;
234
+ }
235
+ }
236
+
237
+ // Actual keymap
238
+
239
+ var keyMap = CodeMirror.keyMap.emacs = {
240
+ "Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"));},
241
+ "Ctrl-K": repeated(function(cm) {
242
+ var start = cm.getCursor(), end = cm.clipPos(Pos(start.line));
243
+ var text = cm.getRange(start, end);
244
+ if (!/\S/.test(text)) {
245
+ text += "\n";
246
+ end = Pos(start.line + 1, 0);
247
+ }
248
+ kill(cm, start, end, true, text);
249
+ }),
250
+ "Alt-W": function(cm) {
251
+ addToRing(cm.getSelection());
252
+ },
253
+ "Ctrl-Y": function(cm) {
254
+ var start = cm.getCursor();
255
+ cm.replaceRange(getFromRing(getPrefix(cm)), start, start, "paste");
256
+ cm.setSelection(start, cm.getCursor());
257
+ },
18
258
  "Alt-Y": function(cm) {cm.replaceSelection(popFromRing());},
19
- "Ctrl-/": "undo", "Shift-Ctrl--": "undo", "Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd",
259
+
260
+ "Ctrl-Space": setMark, "Ctrl-Shift-2": setMark,
261
+
262
+ "Ctrl-F": move(byChar, 1), "Ctrl-B": move(byChar, -1),
263
+ "Right": move(byChar, 1), "Left": move(byChar, -1),
264
+ "Ctrl-D": function(cm) { killTo(cm, byChar, 1); },
265
+ "Delete": function(cm) { killTo(cm, byChar, 1); },
266
+ "Ctrl-H": function(cm) { killTo(cm, byChar, -1); },
267
+ "Backspace": function(cm) { killTo(cm, byChar, -1); },
268
+
269
+ "Alt-F": move(byWord, 1), "Alt-B": move(byWord, -1),
270
+ "Alt-D": function(cm) { killTo(cm, byWord, 1); },
271
+ "Alt-Backspace": function(cm) { killTo(cm, byWord, -1); },
272
+
273
+ "Ctrl-N": move(byLine, 1), "Ctrl-P": move(byLine, -1),
274
+ "Down": move(byLine, 1), "Up": move(byLine, -1),
275
+ "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
276
+ "End": "goLineEnd", "Home": "goLineStart",
277
+
278
+ "Alt-V": move(byPage, -1), "Ctrl-V": move(byPage, 1),
279
+ "PageUp": move(byPage, -1), "PageDown": move(byPage, 1),
280
+
281
+ "Ctrl-Up": move(byParagraph, -1), "Ctrl-Down": move(byParagraph, 1),
282
+
283
+ "Alt-A": move(bySentence, -1), "Alt-E": move(bySentence, 1),
284
+ "Alt-K": function(cm) { killTo(cm, bySentence, 1); },
285
+
286
+ "Ctrl-Alt-K": function(cm) { killTo(cm, byExpr, 1); },
287
+ "Ctrl-Alt-Backspace": function(cm) { killTo(cm, byExpr, -1); },
288
+ "Ctrl-Alt-F": move(byExpr, 1), "Ctrl-Alt-B": move(byExpr, -1),
289
+
290
+ "Shift-Ctrl-Alt-2": function(cm) {
291
+ cm.setSelection(findEnd(cm, byExpr, 1), cm.getCursor());
292
+ },
293
+ "Ctrl-Alt-T": function(cm) {
294
+ var leftStart = byExpr(cm, cm.getCursor(), -1), leftEnd = byExpr(cm, leftStart, 1);
295
+ var rightEnd = byExpr(cm, leftEnd, 1), rightStart = byExpr(cm, rightEnd, -1);
296
+ cm.replaceRange(cm.getRange(rightStart, rightEnd) + cm.getRange(leftEnd, rightStart) +
297
+ cm.getRange(leftStart, leftEnd), leftStart, rightEnd);
298
+ },
299
+ "Ctrl-Alt-U": repeated(toEnclosingExpr),
300
+
301
+ "Alt-Space": function(cm) {
302
+ var pos = cm.getCursor(), from = pos.ch, to = pos.ch, text = cm.getLine(pos.line);
303
+ while (from && /\s/.test(text.charAt(from - 1))) --from;
304
+ while (to < text.length && /\s/.test(text.charAt(to))) ++to;
305
+ cm.replaceRange(" ", Pos(pos.line, from), Pos(pos.line, to));
306
+ },
307
+ "Ctrl-O": repeated(function(cm) { cm.replaceSelection("\n", "start"); }),
308
+ "Ctrl-T": repeated(function(cm) {
309
+ var pos = cm.getCursor();
310
+ if (pos.ch < cm.getLine(pos.line).length) pos = Pos(pos.line, pos.ch + 1);
311
+ var from = cm.findPosH(pos, -2, "char");
312
+ var range = cm.getRange(from, pos);
313
+ if (range.length != 2) return;
314
+ cm.setSelection(from, pos);
315
+ cm.replaceSelection(range.charAt(1) + range.charAt(0), "end");
316
+ }),
317
+
318
+ "Alt-C": repeated(function(cm) {
319
+ operateOnWord(cm, function(w) {
320
+ var letter = w.search(/\w/);
321
+ if (letter == -1) return w;
322
+ return w.slice(0, letter) + w.charAt(letter).toUpperCase() + w.slice(letter + 1).toLowerCase();
323
+ });
324
+ }),
325
+ "Alt-U": repeated(function(cm) {
326
+ operateOnWord(cm, function(w) { return w.toUpperCase(); });
327
+ }),
328
+ "Alt-L": repeated(function(cm) {
329
+ operateOnWord(cm, function(w) { return w.toLowerCase(); });
330
+ }),
331
+
332
+ "Alt-;": "toggleComment",
333
+
334
+ "Ctrl-/": repeated("undo"), "Shift-Ctrl--": repeated("undo"),
335
+ "Ctrl-Z": repeated("undo"), "Cmd-Z": repeated("undo"),
336
+ "Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd",
20
337
  "Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": "clearSearch", "Shift-Alt-5": "replace",
21
- "Ctrl-Z": "undo", "Cmd-Z": "undo", "Alt-/": "autocomplete", "Alt-V": "goPageUp",
338
+ "Alt-/": "autocomplete",
22
339
  "Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto",
23
- fallthrough: ["basic", "emacsy"]
340
+
341
+ "Alt-G": function(cm) {cm.setOption("keyMap", "emacs-Alt-G");},
342
+ "Ctrl-X": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-X");},
343
+ "Ctrl-Q": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-Q");},
344
+ "Ctrl-U": addPrefixMap
24
345
  };
25
346
 
26
347
  CodeMirror.keyMap["emacs-Ctrl-X"] = {
27
- "Ctrl-S": "save", "Ctrl-W": "save", "S": "saveAll", "F": "open", "U": "undo", "K": "close",
348
+ "Tab": function(cm) {
349
+ cm.indentSelection(getPrefix(cm, true) || cm.getOption("indentUnit"));
350
+ },
351
+ "Ctrl-X": function(cm) {
352
+ cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
353
+ },
354
+
355
+ "Ctrl-S": "save", "Ctrl-W": "save", "S": "saveAll", "F": "open", "U": repeated("undo"), "K": "close",
356
+ "Delete": function(cm) { kill(cm, cm.getCursor(), sentenceEnd(cm, 1), true); },
357
+ auto: "emacs", nofallthrough: true, disableInput: true
358
+ };
359
+
360
+ CodeMirror.keyMap["emacs-Alt-G"] = {
361
+ "G": function(cm) {
362
+ var prefix = getPrefix(cm, true);
363
+ if (prefix != null && prefix > 0) return cm.setCursor(prefix - 1);
364
+
365
+ getInput(cm, "Goto line", function(str) {
366
+ var num;
367
+ if (str && !isNaN(num = Number(str)) && num == num|0 && num > 0)
368
+ cm.setCursor(num - 1);
369
+ });
370
+ },
371
+ auto: "emacs", nofallthrough: true, disableInput: true
372
+ };
373
+
374
+ CodeMirror.keyMap["emacs-Ctrl-Q"] = {
375
+ "Tab": repeated("insertTab"),
28
376
  auto: "emacs", nofallthrough: true
29
377
  };
378
+
379
+ var prefixMap = {"Ctrl-G": clearPrefix};
380
+ function regPrefix(d) {
381
+ prefixMap[d] = function(cm) { addPrefix(cm, d); };
382
+ keyMap["Ctrl-" + d] = function(cm) { addPrefix(cm, d); };
383
+ prefixPreservingKeys["Ctrl-" + d] = true;
384
+ }
385
+ for (var i = 0; i < 10; ++i) regPrefix(String(i));
386
+ regPrefix("-");
30
387
  })();