codemirror-rails 2.2.1 → 2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/README.md +8 -0
  2. data/codemirror-rails.gemspec +1 -1
  3. data/lib/codemirror/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/codemirror.js +842 -422
  5. data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +2 -2
  6. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +722 -32
  7. data/vendor/assets/javascripts/codemirror/modes/clike.js +31 -9
  8. data/vendor/assets/javascripts/codemirror/modes/clojure.js +14 -14
  9. data/vendor/assets/javascripts/codemirror/modes/coffeescript.js +59 -37
  10. data/vendor/assets/javascripts/codemirror/modes/css.js +1 -1
  11. data/vendor/assets/javascripts/codemirror/modes/diff.js +24 -5
  12. data/vendor/assets/javascripts/codemirror/modes/ecl.js +203 -0
  13. data/vendor/assets/javascripts/codemirror/modes/erlang.js +251 -0
  14. data/vendor/assets/javascripts/codemirror/modes/gfm.js +40 -4
  15. data/vendor/assets/javascripts/codemirror/modes/go.js +170 -0
  16. data/vendor/assets/javascripts/codemirror/modes/htmlembedded.js +1 -1
  17. data/vendor/assets/javascripts/codemirror/modes/htmlmixed.js +4 -2
  18. data/vendor/assets/javascripts/codemirror/modes/javascript.js +10 -9
  19. data/vendor/assets/javascripts/codemirror/modes/less.js +232 -0
  20. data/vendor/assets/javascripts/codemirror/modes/markdown.js +52 -49
  21. data/vendor/assets/javascripts/codemirror/modes/mysql.js +188 -0
  22. data/vendor/assets/javascripts/codemirror/modes/pascal.js +2 -46
  23. data/vendor/assets/javascripts/codemirror/modes/perl.js +1 -1
  24. data/vendor/assets/javascripts/codemirror/modes/php.js +55 -25
  25. data/vendor/assets/javascripts/codemirror/modes/pig.js +172 -0
  26. data/vendor/assets/javascripts/codemirror/modes/properties.js +63 -0
  27. data/vendor/assets/javascripts/codemirror/modes/python.js +37 -32
  28. data/vendor/assets/javascripts/codemirror/modes/rpm-spec.js +1 -1
  29. data/vendor/assets/javascripts/codemirror/modes/rst.js +1 -1
  30. data/vendor/assets/javascripts/codemirror/modes/ruby.js +14 -14
  31. data/vendor/assets/javascripts/codemirror/modes/rust.js +36 -15
  32. data/vendor/assets/javascripts/codemirror/modes/scheme.js +74 -46
  33. data/vendor/assets/javascripts/codemirror/modes/shell.js +103 -0
  34. data/vendor/assets/javascripts/codemirror/modes/smalltalk.js +16 -16
  35. data/vendor/assets/javascripts/codemirror/modes/smarty.js +148 -0
  36. data/vendor/assets/javascripts/codemirror/modes/stex.js +21 -6
  37. data/vendor/assets/javascripts/codemirror/modes/tiddlywiki.js +55 -45
  38. data/vendor/assets/javascripts/codemirror/modes/tiki.js +316 -0
  39. data/vendor/assets/javascripts/codemirror/modes/vbscript.js +26 -0
  40. data/vendor/assets/javascripts/codemirror/modes/verilog.js +194 -0
  41. data/vendor/assets/javascripts/codemirror/modes/xml.js +89 -16
  42. data/vendor/assets/javascripts/codemirror/modes/xmlpure.js +5 -0
  43. data/vendor/assets/javascripts/codemirror/modes/xquery.js +448 -0
  44. data/vendor/assets/javascripts/codemirror/utils/closetag.js +146 -0
  45. data/vendor/assets/javascripts/codemirror/utils/dialog.js +63 -0
  46. data/vendor/assets/javascripts/codemirror/utils/foldcode.js +196 -0
  47. data/vendor/assets/javascripts/codemirror/utils/formatting.js +297 -0
  48. data/vendor/assets/javascripts/codemirror/utils/javascript-hint.js +134 -0
  49. data/vendor/assets/javascripts/codemirror/utils/loadmode.js +51 -0
  50. data/vendor/assets/javascripts/codemirror/utils/match-highlighter.js +44 -0
  51. data/vendor/assets/javascripts/codemirror/utils/multiplex.js +72 -0
  52. data/vendor/assets/javascripts/codemirror/{overlay.js → utils/overlay.js} +3 -2
  53. data/vendor/assets/javascripts/codemirror/utils/pig-hint.js +123 -0
  54. data/vendor/assets/javascripts/codemirror/utils/runmode.js +49 -0
  55. data/vendor/assets/javascripts/codemirror/utils/search.js +118 -0
  56. data/vendor/assets/javascripts/codemirror/utils/searchcursor.js +117 -0
  57. data/vendor/assets/javascripts/codemirror/utils/simple-hint.js +72 -0
  58. data/vendor/assets/stylesheets/codemirror.css +69 -5
  59. data/vendor/assets/stylesheets/codemirror/modes/tiddlywiki.css +14 -21
  60. data/vendor/assets/stylesheets/codemirror/modes/tiki.css +26 -0
  61. data/vendor/assets/stylesheets/codemirror/themes/ambiance.css +81 -0
  62. data/vendor/assets/stylesheets/codemirror/themes/blackboard.css +25 -0
  63. data/vendor/assets/stylesheets/codemirror/themes/cobalt.css +1 -1
  64. data/vendor/assets/stylesheets/codemirror/themes/eclipse.css +1 -1
  65. data/vendor/assets/stylesheets/codemirror/themes/elegant.css +2 -2
  66. data/vendor/assets/stylesheets/codemirror/themes/erlang-dark.css +21 -0
  67. data/vendor/assets/stylesheets/codemirror/themes/lesser-dark.css +44 -0
  68. data/vendor/assets/stylesheets/codemirror/themes/monokai.css +1 -1
  69. data/vendor/assets/stylesheets/codemirror/themes/neat.css +3 -3
  70. data/vendor/assets/stylesheets/codemirror/themes/night.css +1 -1
  71. data/vendor/assets/stylesheets/codemirror/themes/rubyblue.css +2 -2
  72. data/vendor/assets/stylesheets/codemirror/themes/vibrant-ink.css +27 -0
  73. data/vendor/assets/stylesheets/codemirror/themes/xq-dark.css +46 -0
  74. data/vendor/assets/stylesheets/codemirror/utils/dialog.css +23 -0
  75. data/vendor/assets/stylesheets/codemirror/utils/simple-hint.css +16 -0
  76. metadata +41 -10
  77. data/vendor/assets/javascripts/codemirror/runmode.js +0 -27
  78. data/vendor/assets/stylesheets/codemirror/modes/clike.css +0 -7
  79. data/vendor/assets/stylesheets/codemirror/modes/markdown.css +0 -10
  80. data/vendor/assets/stylesheets/codemirror/modes/rst.css +0 -75
  81. data/vendor/assets/stylesheets/codemirror/themes/default.css +0 -19
@@ -18,12 +18,12 @@
18
18
  "Alt-Y": function(cm) {cm.replaceSelection(popFromRing());},
19
19
  "Ctrl-/": "undo", "Shift-Ctrl--": "undo", "Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd",
20
20
  "Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": "clearSearch", "Shift-Alt-5": "replace",
21
- "Ctrl-Z": "undo", "Cmd-Z": "undo",
21
+ "Ctrl-Z": "undo", "Cmd-Z": "undo", "Alt-/": "autocomplete",
22
22
  fallthrough: ["basic", "emacsy"]
23
23
  };
24
24
 
25
25
  CodeMirror.keyMap["emacs-Ctrl-X"] = {
26
26
  "Ctrl-S": "save", "Ctrl-W": "save", "S": "saveAll", "F": "open", "U": "undo", "K": "close",
27
- auto: "emacs", catchall: function(cm) {/*ignore*/}
27
+ auto: "emacs", nofallthrough: true
28
28
  };
29
29
  })();
@@ -1,15 +1,96 @@
1
+ // Supported keybindings:
2
+ //
3
+ // Cursor movement:
4
+ // h, j, k, l
5
+ // e, E, w, W, b, B
6
+ // Ctrl-f, Ctrl-b
7
+ // Ctrl-n, Ctrl-p
8
+ // $, ^, 0
9
+ // G
10
+ // ge, gE
11
+ // gg
12
+ // f<char>, F<char>, t<char>, T<char>
13
+ // Ctrl-o, Ctrl-i TODO (FIXME - Ctrl-O wont work in Chrome)
14
+ // /, ?, n, N TODO (does not work)
15
+ // #, * TODO
16
+ //
17
+ // Entering insert mode:
18
+ // i, I, a, A, o, O
19
+ // s
20
+ // ce, cb (without support for number of actions like c3e - TODO)
21
+ // cc
22
+ // S, C TODO
23
+ // cf<char>, cF<char>, ct<char>, cT<char>
24
+ //
25
+ // Deleting text:
26
+ // x, X
27
+ // J
28
+ // dd, D
29
+ // de, db (without support for number of actions like d3e - TODO)
30
+ // df<char>, dF<char>, dt<char>, dT<char>
31
+ //
32
+ // Yanking and pasting:
33
+ // yy, Y
34
+ // p, P
35
+ // p'<char> TODO - test
36
+ // y'<char> TODO - test
37
+ // m<char> TODO - test
38
+ //
39
+ // Changing text in place:
40
+ // ~
41
+ // r<char>
42
+ //
43
+ // Visual mode:
44
+ // v, V TODO
45
+ //
46
+ // Misc:
47
+ // . TODO
48
+ //
49
+
1
50
  (function() {
2
51
  var count = "";
3
- function pushCountDigit(digit) { return function(cm) {count += digit;} }
4
- function popCount() { var i = parseInt(count); count = ""; return i || 1; }
52
+ var sdir = "f";
53
+ var buf = "";
54
+ var yank = 0;
55
+ var mark = [];
56
+ var reptTimes = 0;
57
+ function emptyBuffer() { buf = ""; }
58
+ function pushInBuffer(str) { buf += str; }
59
+ function pushCountDigit(digit) { return function(cm) {count += digit;}; }
60
+ function popCount() { var i = parseInt(count, 10); count = ""; return i || 1; }
61
+ function iterTimes(func) {
62
+ for (var i = 0, c = popCount(); i < c; ++i) func(i, i == c - 1);
63
+ }
5
64
  function countTimes(func) {
6
65
  if (typeof func == "string") func = CodeMirror.commands[func];
7
- return function(cm) { for (var i = 0, c = popCount(); i < c; ++i) func(cm); }
66
+ return function(cm) { iterTimes(function () { func(cm); }); };
8
67
  }
9
68
 
10
69
  function iterObj(o, f) {
11
70
  for (var prop in o) if (o.hasOwnProperty(prop)) f(prop, o[prop]);
12
71
  }
72
+ function iterList(l, f) {
73
+ for (var i in l) f(l[i]);
74
+ }
75
+ function toLetter(ch) {
76
+ // T -> t, Shift-T -> T, '*' -> *, "Space" -> " "
77
+ if (ch.slice(0, 6) == "Shift-") {
78
+ return ch.slice(0, 1);
79
+ } else {
80
+ if (ch == "Space") return " ";
81
+ if (ch.length == 3 && ch[0] == "'" && ch[2] == "'") return ch[1];
82
+ return ch.toLowerCase();
83
+ }
84
+ }
85
+ var SPECIAL_SYMBOLS = "~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;\"\'1234567890";
86
+ function toCombo(ch) {
87
+ // t -> T, T -> Shift-T, * -> '*', " " -> "Space"
88
+ if (ch == " ") return "Space";
89
+ var specialIdx = SPECIAL_SYMBOLS.indexOf(ch);
90
+ if (specialIdx != -1) return "'" + ch + "'";
91
+ if (ch.toLowerCase() == ch) return ch.toUpperCase();
92
+ return "Shift-" + ch.toUpperCase();
93
+ }
13
94
 
14
95
  var word = [/\w/, /[^\w\s]/], bigWord = [/\S/];
15
96
  function findWord(line, pos, dir, regexps) {
@@ -31,46 +112,655 @@
31
112
  }
32
113
  return {from: Math.min(start, end), to: Math.max(start, end)};
33
114
  }
34
- function moveToWord(cm, regexps, dir, where) {
35
- var cur = cm.getCursor(), ch = cur.ch, line = cm.getLine(cur.line), word;
36
- while (true) {
37
- word = findWord(line, ch, dir, regexps);
38
- ch = word[where == "end" ? "to" : "from"];
39
- if (ch == cur.ch && word.from != word.to) ch = word[dir < 0 ? "from" : "to"];
40
- else break;
115
+ function moveToWord(cm, regexps, dir, times, where) {
116
+ var cur = cm.getCursor();
117
+
118
+ for (var i = 0; i < times; i++) {
119
+ var line = cm.getLine(cur.line), startCh = cur.ch, word;
120
+ while (true) {
121
+ // If we're at start/end of line, start on prev/next respectivly
122
+ if (cur.ch == line.length && dir > 0) {
123
+ cur.line++;
124
+ cur.ch = 0;
125
+ line = cm.getLine(cur.line);
126
+ } else if (cur.ch == 0 && dir < 0) {
127
+ cur.line--;
128
+ cur.ch = line.length;
129
+ line = cm.getLine(cur.line);
130
+ }
131
+ if (!line) break;
132
+
133
+ // On to the actual searching
134
+ word = findWord(line, cur.ch, dir, regexps);
135
+ cur.ch = word[where == "end" ? "to" : "from"];
136
+ if (startCh == cur.ch && word.from != word.to) cur.ch = word[dir < 0 ? "from" : "to"];
137
+ else break;
138
+ }
139
+ }
140
+ return cur;
141
+ }
142
+ function joinLineNext(cm) {
143
+ var cur = cm.getCursor(), ch = cur.ch, line = cm.getLine(cur.line);
144
+ CodeMirror.commands.goLineEnd(cm);
145
+ if (cur.line != cm.lineCount()) {
146
+ CodeMirror.commands.goLineEnd(cm);
147
+ cm.replaceSelection(" ", "end");
148
+ CodeMirror.commands.delCharRight(cm);
149
+ }
150
+ }
151
+ function delTillMark(cm, cHar) {
152
+ var i = mark[cHar];
153
+ if (i === undefined) {
154
+ // console.log("Mark not set"); // TODO - show in status bar
155
+ return;
156
+ }
157
+ var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l;
158
+ cm.setCursor(start);
159
+ for (var c = start; c <= end; c++) {
160
+ pushInBuffer("\n"+cm.getLine(start));
161
+ cm.removeLine(start);
162
+ }
163
+ }
164
+ function yankTillMark(cm, cHar) {
165
+ var i = mark[cHar];
166
+ if (i === undefined) {
167
+ // console.log("Mark not set"); // TODO - show in status bar
168
+ return;
169
+ }
170
+ var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l;
171
+ for (var c = start; c <= end; c++) {
172
+ pushInBuffer("\n"+cm.getLine(c));
173
+ }
174
+ cm.setCursor(start);
175
+ }
176
+ function goLineStartText(cm) {
177
+ // Go to the start of the line where the text begins, or the end for whitespace-only lines
178
+ var cur = cm.getCursor(), firstNonWS = cm.getLine(cur.line).search(/\S/);
179
+ cm.setCursor(cur.line, firstNonWS == -1 ? line.length : firstNonWS, true);
180
+ }
181
+
182
+ function charIdxInLine(cm, cHar, motion_options) {
183
+ // Search for cHar in line.
184
+ // motion_options: {forward, inclusive}
185
+ // If inclusive = true, include it too.
186
+ // If forward = true, search forward, else search backwards.
187
+ // If char is not found on this line, do nothing
188
+ var cur = cm.getCursor(), line = cm.getLine(cur.line), idx;
189
+ var ch = toLetter(cHar), mo = motion_options;
190
+ if (mo.forward) {
191
+ idx = line.indexOf(ch, cur.ch + 1);
192
+ if (idx != -1 && mo.inclusive) idx += 1;
193
+ } else {
194
+ idx = line.lastIndexOf(ch, cur.ch);
195
+ if (idx != -1 && !mo.inclusive) idx += 1;
196
+ }
197
+ return idx;
198
+ }
199
+
200
+ function moveTillChar(cm, cHar, motion_options) {
201
+ // Move to cHar in line, as found by charIdxInLine.
202
+ var idx = charIdxInLine(cm, cHar, motion_options), cur = cm.getCursor();
203
+ if (idx != -1) cm.setCursor({line: cur.line, ch: idx});
204
+ }
205
+
206
+ function delTillChar(cm, cHar, motion_options) {
207
+ // delete text in this line, untill cHar is met,
208
+ // as found by charIdxInLine.
209
+ // If char is not found on this line, do nothing
210
+ var idx = charIdxInLine(cm, cHar, motion_options);
211
+ var cur = cm.getCursor();
212
+ if (idx !== -1) {
213
+ if (motion_options.forward) {
214
+ cm.replaceRange("", {line: cur.line, ch: cur.ch}, {line: cur.line, ch: idx});
215
+ } else {
216
+ cm.replaceRange("", {line: cur.line, ch: idx}, {line: cur.line, ch: cur.ch});
217
+ }
41
218
  }
42
- cm.setCursor(cur.line, word[where == "end" ? "to" : "from"], true);
43
219
  }
44
220
 
221
+ function enterInsertMode(cm) {
222
+ // enter insert mode: switch mode and cursor
223
+ popCount();
224
+ cm.setOption("keyMap", "vim-insert");
225
+ }
226
+
227
+ // main keymap
45
228
  var map = CodeMirror.keyMap.vim = {
46
- "0": function(cm) {count.length > 0 ? pushCountDigit("0")(cm) : CodeMirror.commands.goLineStart(cm);},
47
- "I": function(cm) {popCount(); cm.setOption("keyMap", "vim-insert");},
48
- "G": function(cm) {cm.setOption("keyMap", "vim-prefix-g");},
49
- catchall: function(cm) {/*ignore*/}
229
+ // Pipe (|); TODO: should be *screen* chars, so need a util function to turn tabs into spaces?
230
+ "'|'": function(cm) {
231
+ cm.setCursor(cm.getCursor().line, popCount() - 1, true);
232
+ },
233
+ "A": function(cm) {
234
+ cm.setCursor(cm.getCursor().line, cm.getCursor().ch+1, true);
235
+ enterInsertMode(cm);
236
+ },
237
+ "Shift-A": function(cm) { CodeMirror.commands.goLineEnd(cm); enterInsertMode(cm);},
238
+ "I": function(cm) { enterInsertMode(cm);},
239
+ "Shift-I": function(cm) { goLineStartText(cm); enterInsertMode(cm);},
240
+ "O": function(cm) {
241
+ CodeMirror.commands.goLineEnd(cm);
242
+ CodeMirror.commands.newlineAndIndent(cm);
243
+ enterInsertMode(cm);
244
+ },
245
+ "Shift-O": function(cm) {
246
+ CodeMirror.commands.goLineStart(cm);
247
+ cm.replaceSelection("\n", "start");
248
+ cm.indentLine(cm.getCursor().line);
249
+ enterInsertMode(cm);
250
+ },
251
+ "G": function(cm) { cm.setOption("keyMap", "vim-prefix-g");},
252
+ "Shift-D": function(cm) {
253
+ // commented out verions works, but I left original, cause maybe
254
+ // I don't know vim enouth to see what it does
255
+ /* var cur = cm.getCursor();
256
+ var f = {line: cur.line, ch: cur.ch}, t = {line: cur.line};
257
+ pushInBuffer(cm.getRange(f, t));
258
+ */
259
+ emptyBuffer();
260
+ mark["Shift-D"] = cm.getCursor(false).line;
261
+ cm.setCursor(cm.getCursor(true).line);
262
+ delTillMark(cm,"Shift-D"); mark = [];
263
+ },
264
+
265
+ "S": function (cm) {
266
+ countTimes(function (_cm) {
267
+ CodeMirror.commands.delCharRight(_cm);
268
+ })(cm);
269
+ enterInsertMode(cm);
270
+ },
271
+ "M": function(cm) {cm.setOption("keyMap", "vim-prefix-m"); mark = [];},
272
+ "Y": function(cm) {cm.setOption("keyMap", "vim-prefix-y"); emptyBuffer(); yank = 0;},
273
+ "Shift-Y": function(cm) {
274
+ emptyBuffer();
275
+ mark["Shift-D"] = cm.getCursor(false).line;
276
+ cm.setCursor(cm.getCursor(true).line);
277
+ yankTillMark(cm,"Shift-D"); mark = [];
278
+ },
279
+ "/": function(cm) {var f = CodeMirror.commands.find; f && f(cm); sdir = "f";},
280
+ "'?'": function(cm) {
281
+ var f = CodeMirror.commands.find;
282
+ if (f) { f(cm); CodeMirror.commands.findPrev(cm); sdir = "r"; }
283
+ },
284
+ "N": function(cm) {
285
+ var fn = CodeMirror.commands.findNext;
286
+ if (fn) sdir != "r" ? fn(cm) : CodeMirror.commands.findPrev(cm);
287
+ },
288
+ "Shift-N": function(cm) {
289
+ var fn = CodeMirror.commands.findNext;
290
+ if (fn) sdir != "r" ? CodeMirror.commands.findPrev(cm) : fn.findNext(cm);
291
+ },
292
+ "Shift-G": function(cm) {
293
+ count == "" ? cm.setCursor(cm.lineCount()) : cm.setCursor(parseInt(count, 10)-1);
294
+ popCount();
295
+ CodeMirror.commands.goLineStart(cm);
296
+ },
297
+ nofallthrough: true, style: "fat-cursor"
50
298
  };
51
- // Add bindings for number keys
52
- for (var i = 1; i < 10; ++i) map[i] = pushCountDigit(i);
299
+
300
+ // standard mode switching
301
+ iterList(["d", "t", "T", "f", "F", "c", "r"],
302
+ function (ch) {
303
+ CodeMirror.keyMap.vim[toCombo(ch)] = function (cm) {
304
+ cm.setOption("keyMap", "vim-prefix-" + ch);
305
+ emptyBuffer();
306
+ };
307
+ });
308
+
309
+ function addCountBindings(keyMap) {
310
+ // Add bindings for number keys
311
+ keyMap["0"] = function(cm) {
312
+ count.length > 0 ? pushCountDigit("0")(cm) : CodeMirror.commands.goLineStart(cm);
313
+ };
314
+ for (var i = 1; i < 10; ++i) keyMap[i] = pushCountDigit(i);
315
+ }
316
+ addCountBindings(CodeMirror.keyMap.vim);
317
+
318
+ // main num keymap
53
319
  // Add bindings that are influenced by number keys
54
- iterObj({"H": "goColumnLeft", "L": "goColumnRight", "J": "goLineDown", "K": "goLineUp",
55
- "Left": "goColumnLeft", "Right": "goColumnRight", "Down": "goLineDown", "Up": "goLineUp",
56
- "Backspace": "goCharLeft", "Space": "goCharRight",
57
- "B": function(cm) {moveToWord(cm, word, -1, "end");},
58
- "E": function(cm) {moveToWord(cm, word, 1, "end");},
59
- "W": function(cm) {moveToWord(cm, word, 1, "start");},
60
- "Shift-B": function(cm) {moveToWord(cm, bigWord, -1, "end");},
61
- "Shift-E": function(cm) {moveToWord(cm, bigWord, 1, "end");},
62
- "Shift-W": function(cm) {moveToWord(cm, bigWord, 1, "start");},
63
- "U": "undo", "Ctrl-R": "redo", "Shift-4": "goLineEnd"},
64
- function(key, cmd) { map[key] = countTimes(cmd); });
320
+ iterObj({
321
+ "Left": "goColumnLeft", "Right": "goColumnRight",
322
+ "Down": "goLineDown", "Up": "goLineUp", "Backspace": "goCharLeft",
323
+ "Space": "goCharRight",
324
+ "X": function(cm) {CodeMirror.commands.delCharRight(cm);},
325
+ "P": function(cm) {
326
+ var cur = cm.getCursor().line;
327
+ if (buf!= "") {
328
+ if (buf[0] == "\n") CodeMirror.commands.goLineEnd(cm);
329
+ cm.replaceRange(buf, cm.getCursor());
330
+ }
331
+ },
332
+ "Shift-X": function(cm) {CodeMirror.commands.delCharLeft(cm);},
333
+ "Shift-J": function(cm) {joinLineNext(cm);},
334
+ "Shift-P": function(cm) {
335
+ var cur = cm.getCursor().line;
336
+ if (buf!= "") {
337
+ CodeMirror.commands.goLineUp(cm);
338
+ CodeMirror.commands.goLineEnd(cm);
339
+ cm.replaceSelection(buf, "end");
340
+ }
341
+ cm.setCursor(cur+1);
342
+ },
343
+ "'~'": function(cm) {
344
+ var cur = cm.getCursor(), cHar = cm.getRange({line: cur.line, ch: cur.ch}, {line: cur.line, ch: cur.ch+1});
345
+ cHar = cHar != cHar.toLowerCase() ? cHar.toLowerCase() : cHar.toUpperCase();
346
+ cm.replaceRange(cHar, {line: cur.line, ch: cur.ch}, {line: cur.line, ch: cur.ch+1});
347
+ cm.setCursor(cur.line, cur.ch+1);
348
+ },
349
+ "Ctrl-B": function(cm) {CodeMirror.commands.goPageUp(cm);},
350
+ "Ctrl-F": function(cm) {CodeMirror.commands.goPageDown(cm);},
351
+ "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
352
+ "U": "undo", "Ctrl-R": "redo"
353
+ }, function(key, cmd) { map[key] = countTimes(cmd); });
354
+
355
+ // empty key maps
356
+ iterList([
357
+ "vim-prefix-d'",
358
+ "vim-prefix-y'",
359
+ "vim-prefix-df",
360
+ "vim-prefix-dF",
361
+ "vim-prefix-dt",
362
+ "vim-prefix-dT",
363
+ "vim-prefix-c",
364
+ "vim-prefix-cf",
365
+ "vim-prefix-cF",
366
+ "vim-prefix-ct",
367
+ "vim-prefix-cT",
368
+ "vim-prefix-",
369
+ "vim-prefix-f",
370
+ "vim-prefix-F",
371
+ "vim-prefix-t",
372
+ "vim-prefix-T",
373
+ "vim-prefix-r",
374
+ "vim-prefix-m"
375
+ ],
376
+ function (prefix) {
377
+ CodeMirror.keyMap[prefix] = {
378
+ auto: "vim",
379
+ nofallthrough: true,
380
+ style: "fat-cursor"
381
+ };
382
+ });
65
383
 
66
384
  CodeMirror.keyMap["vim-prefix-g"] = {
67
- "E": countTimes(function(cm) { moveToWord(cm, word, -1, "start");}),
68
- "Shift-E": countTimes(function(cm) { moveToWord(cm, bigWord, -1, "start");}),
69
- auto: "vim", catchall: function(cm) {/*ignore*/}
385
+ "E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, word, -1, 1, "start"));}),
386
+ "Shift-E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, bigWord, -1, 1, "start"));}),
387
+ "G": function (cm) { cm.setCursor({line: 0, ch: cm.getCursor().ch});},
388
+ auto: "vim", nofallthrough: true, style: "fat-cursor"
389
+ };
390
+
391
+ CodeMirror.keyMap["vim-prefix-d"] = {
392
+ "D": countTimes(function(cm) {
393
+ pushInBuffer("\n"+cm.getLine(cm.getCursor().line));
394
+ cm.removeLine(cm.getCursor().line);
395
+ cm.setOption("keyMap", "vim");
396
+ }),
397
+ "'": function(cm) {
398
+ cm.setOption("keyMap", "vim-prefix-d'");
399
+ emptyBuffer();
400
+ },
401
+ "B": function(cm) {
402
+ var cur = cm.getCursor();
403
+ var line = cm.getLine(cur.line);
404
+ var index = line.lastIndexOf(" ", cur.ch);
405
+
406
+ pushInBuffer(line.substring(index, cur.ch));
407
+ cm.replaceRange("", {line: cur.line, ch: index}, cur);
408
+ cm.setOption("keyMap", "vim");
409
+ },
410
+ nofallthrough: true, style: "fat-cursor"
411
+ };
412
+ // FIXME - does not work for bindings like "d3e"
413
+ addCountBindings(CodeMirror.keyMap["vim-prefix-d"]);
414
+
415
+ CodeMirror.keyMap["vim-prefix-c"] = {
416
+ "B": function (cm) {
417
+ countTimes("delWordLeft")(cm);
418
+ enterInsertMode(cm);
419
+ },
420
+ "C": function (cm) {
421
+ iterTimes(function (i, last) {
422
+ CodeMirror.commands.deleteLine(cm);
423
+ if (i) {
424
+ CodeMirror.commands.delCharRight(cm);
425
+ if (last) CodeMirror.commands.deleteLine(cm);
426
+ }
427
+ });
428
+ enterInsertMode(cm);
429
+ },
430
+ nofallthrough: true, style: "fat-cursor"
431
+ };
432
+
433
+ iterList(["vim-prefix-d", "vim-prefix-c", "vim-prefix-"], function (prefix) {
434
+ iterList(["f", "F", "T", "t"],
435
+ function (ch) {
436
+ CodeMirror.keyMap[prefix][toCombo(ch)] = function (cm) {
437
+ cm.setOption("keyMap", prefix + ch);
438
+ emptyBuffer();
439
+ };
440
+ });
441
+ });
442
+
443
+ var MOTION_OPTIONS = {
444
+ "t": {inclusive: false, forward: true},
445
+ "f": {inclusive: true, forward: true},
446
+ "T": {inclusive: false, forward: false},
447
+ "F": {inclusive: true, forward: false}
448
+ };
449
+
450
+ function setupPrefixBindingForKey(m) {
451
+ CodeMirror.keyMap["vim-prefix-m"][m] = function(cm) {
452
+ mark[m] = cm.getCursor().line;
453
+ };
454
+ CodeMirror.keyMap["vim-prefix-d'"][m] = function(cm) {
455
+ delTillMark(cm,m);
456
+ };
457
+ CodeMirror.keyMap["vim-prefix-y'"][m] = function(cm) {
458
+ yankTillMark(cm,m);
459
+ };
460
+ CodeMirror.keyMap["vim-prefix-r"][m] = function (cm) {
461
+ var cur = cm.getCursor();
462
+ cm.replaceRange(toLetter(m),
463
+ {line: cur.line, ch: cur.ch},
464
+ {line: cur.line, ch: cur.ch + 1});
465
+ CodeMirror.commands.goColumnLeft(cm);
466
+ };
467
+ // all commands, related to motions till char in line
468
+ iterObj(MOTION_OPTIONS, function (ch, options) {
469
+ CodeMirror.keyMap["vim-prefix-" + ch][m] = function(cm) {
470
+ moveTillChar(cm, m, options);
471
+ };
472
+ CodeMirror.keyMap["vim-prefix-d" + ch][m] = function(cm) {
473
+ delTillChar(cm, m, options);
474
+ };
475
+ CodeMirror.keyMap["vim-prefix-c" + ch][m] = function(cm) {
476
+ delTillChar(cm, m, options);
477
+ enterInsertMode(cm);
478
+ };
479
+ });
480
+ }
481
+ for (var i = 65; i < 65 + 26; i++) { // uppercase alphabet char codes
482
+ var ch = String.fromCharCode(i);
483
+ setupPrefixBindingForKey(toCombo(ch));
484
+ setupPrefixBindingForKey(toCombo(ch.toLowerCase()));
485
+ }
486
+ iterList(SPECIAL_SYMBOLS, function (ch) {
487
+ setupPrefixBindingForKey(toCombo(ch));
488
+ });
489
+ setupPrefixBindingForKey("Space");
490
+
491
+ CodeMirror.keyMap["vim-prefix-y"] = {
492
+ "Y": countTimes(function(cm) { pushInBuffer("\n"+cm.getLine(cm.getCursor().line+yank)); yank++; }),
493
+ "'": function(cm) {cm.setOption("keyMap", "vim-prefix-y'"); emptyBuffer();},
494
+ nofallthrough: true, style: "fat-cursor"
70
495
  };
71
496
 
72
497
  CodeMirror.keyMap["vim-insert"] = {
73
- "Esc": function(cm) {cm.setOption("keyMap", "vim");},
498
+ // TODO: override navigation keys so that Esc will cancel automatic indentation from o, O, i_<CR>
499
+ "Esc": function(cm) {
500
+ cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true);
501
+ cm.setOption("keyMap", "vim");
502
+ },
503
+ "Ctrl-N": "autocomplete",
504
+ "Ctrl-P": "autocomplete",
74
505
  fallthrough: ["default"]
75
506
  };
507
+
508
+ function findMatchedSymbol(cm, cur, symb) {
509
+ var line = cur.line;
510
+ var symb = symb ? symb : cm.getLine(line)[cur.ch];
511
+
512
+ // Are we at the opening or closing char
513
+ var forwards = ['(', '[', '{'].indexOf(symb) != -1;
514
+
515
+ var reverseSymb = (function(sym) {
516
+ switch (sym) {
517
+ case '(' : return ')';
518
+ case '[' : return ']';
519
+ case '{' : return '}';
520
+ case ')' : return '(';
521
+ case ']' : return '[';
522
+ case '}' : return '{';
523
+ default : return null;
524
+ }
525
+ })(symb);
526
+
527
+ // Couldn't find a matching symbol, abort
528
+ if (reverseSymb == null) return cur;
529
+
530
+ // Tracking our imbalance in open/closing symbols. An opening symbol wii be
531
+ // the first thing we pick up if moving forward, this isn't true moving backwards
532
+ var disBal = forwards ? 0 : 1;
533
+
534
+ while (true) {
535
+ if (line == cur.line) {
536
+ // First pass, do some special stuff
537
+ var currLine = forwards ? cm.getLine(line).substr(cur.ch).split('') : cm.getLine(line).substr(0,cur.ch).split('').reverse();
538
+ } else {
539
+ var currLine = forwards ? cm.getLine(line).split('') : cm.getLine(line).split('').reverse();
540
+ }
541
+
542
+ for (var index = 0; index < currLine.length; index++) {
543
+ if (currLine[index] == symb) disBal++;
544
+ else if (currLine[index] == reverseSymb) disBal--;
545
+
546
+ if (disBal == 0) {
547
+ if (forwards && cur.line == line) return {line: line, ch: index + cur.ch};
548
+ else if (forwards) return {line: line, ch: index};
549
+ else return {line: line, ch: currLine.length - index - 1 };
550
+ }
551
+ }
552
+
553
+ if (forwards) line++;
554
+ else line--;
555
+ }
556
+ }
557
+
558
+ function selectCompanionObject(cm, revSymb, inclusive) {
559
+ var cur = cm.getCursor();
560
+
561
+ var end = findMatchedSymbol(cm, cur, revSymb);
562
+ var start = findMatchedSymbol(cm, end);
563
+ start.ch += inclusive ? 1 : 0;
564
+ end.ch += inclusive ? 0 : 1;
565
+
566
+ return {start: start, end: end};
567
+ }
568
+
569
+ // These are our motion commands to be used for navigation and selection with
570
+ // certian other commands. All should return a cursor object.
571
+ var motionList = ['B', 'E', 'J', 'K', 'H', 'L', 'W', 'Shift-W', "'^'", "'$'", "'%'", 'Esc'];
572
+
573
+ motions = {
574
+ 'B': function(cm, times) { return moveToWord(cm, word, -1, times); },
575
+ 'Shift-B': function(cm, times) { return moveToWord(cm, bigWord, -1, times); },
576
+ 'E': function(cm, times) { return moveToWord(cm, word, 1, times, 'end'); },
577
+ 'Shift-E': function(cm, times) { return moveToWord(cm, bigWord, 1, times, 'end'); },
578
+ 'J': function(cm, times) {
579
+ var cur = cm.getCursor();
580
+ return {line: cur.line+times, ch : cur.ch};
581
+ },
582
+
583
+ 'K': function(cm, times) {
584
+ var cur = cm.getCursor();
585
+ return {line: cur.line-times, ch: cur.ch};
586
+ },
587
+
588
+ 'H': function(cm, times) {
589
+ var cur = cm.getCursor();
590
+ return {line: cur.line, ch: cur.ch-times};
591
+ },
592
+
593
+ 'L': function(cm, times) {
594
+ var cur = cm.getCursor();
595
+ return {line: cur.line, ch: cur.ch+times};
596
+ },
597
+ 'W': function(cm, times) { return moveToWord(cm, word, 1, times); },
598
+ 'Shift-W': function(cm, times) { return moveToWord(cm, bigWord, 1, times); },
599
+ "'^'": function(cm, times) {
600
+ var cur = cm.getCursor();
601
+ var line = cm.getLine(cur.line).split('');
602
+
603
+ // Empty line :o
604
+ if (line.length == 0) return cur;
605
+
606
+ for (var index = 0; index < line.length; index++) {
607
+ if (line[index].match(/[^\s]/)) return {line: cur.line, ch: index};
608
+ }
609
+ },
610
+ "'$'": function(cm) {
611
+ var cur = cm.getCursor();
612
+ var line = cm.getLine(cur.line);
613
+ return {line: cur.line, ch: line.length};
614
+ },
615
+ "'%'": function(cm) { return findMatchedSymbol(cm, cm.getCursor()); },
616
+ "Esc" : function(cm) {
617
+ cm.setOption('vim');
618
+ reptTimes = 0;
619
+
620
+ return cm.getCursor();
621
+ }
622
+ };
623
+
624
+ // Map our movement actions each operator and non-operational movement
625
+ motionList.forEach(function(key, index, array) {
626
+ CodeMirror.keyMap['vim-prefix-d'][key] = function(cm) {
627
+ // Get our selected range
628
+ var start = cm.getCursor();
629
+ var end = motions[key](cm, reptTimes ? reptTimes : 1);
630
+
631
+ // Set swap var if range is of negative length
632
+ if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true;
633
+
634
+ // Take action, switching start and end if swap var is set
635
+ pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end));
636
+ cm.replaceRange("", swap ? end : start, swap ? start : end);
637
+
638
+ // And clean up
639
+ reptTimes = 0;
640
+ cm.setOption("keyMap", "vim");
641
+ };
642
+
643
+ CodeMirror.keyMap['vim-prefix-c'][key] = function(cm) {
644
+ var start = cm.getCursor();
645
+ var end = motions[key](cm, reptTimes ? reptTimes : 1);
646
+
647
+ if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true;
648
+ pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end));
649
+ cm.replaceRange("", swap ? end : start, swap ? start : end);
650
+
651
+ reptTimes = 0;
652
+ cm.setOption('keyMap', 'vim-insert');
653
+ };
654
+
655
+ CodeMirror.keyMap['vim-prefix-y'][key] = function(cm) {
656
+ var start = cm.getCursor();
657
+ var end = motions[key](cm, reptTimes ? reptTimes : 1);
658
+
659
+ if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true;
660
+ pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end));
661
+
662
+ reptTimes = 0;
663
+ cm.setOption("keyMap", "vim");
664
+ };
665
+
666
+ CodeMirror.keyMap['vim'][key] = function(cm) {
667
+ var cur = motions[key](cm, reptTimes ? reptTimes : 1);
668
+ cm.setCursor(cur.line, cur.ch);
669
+
670
+ reptTimes = 0;
671
+ };
672
+ });
673
+
674
+ var nums = [1,2,3,4,5,6,7,8,9];
675
+ nums.forEach(function(key, index, array) {
676
+ CodeMirror.keyMap['vim'][key] = function (cm) {
677
+ reptTimes = (reptTimes * 10) + key;
678
+ };
679
+ CodeMirror.keyMap['vim-prefix-d'][key] = function (cm) {
680
+ reptTimes = (reptTimes * 10) + key;
681
+ };
682
+ CodeMirror.keyMap['vim-prefix-y'][key] = function (cm) {
683
+ reptTimes = (reptTimes * 10) + key;
684
+ };
685
+ CodeMirror.keyMap['vim-prefix-c'][key] = function (cm) {
686
+ reptTimes = (reptTimes * 10) + key;
687
+ };
688
+ });
689
+
690
+ // Create our keymaps for each operator and make xa and xi where x is an operator
691
+ // change to the corrosponding keymap
692
+ var operators = ['d', 'y', 'c'];
693
+ operators.forEach(function(key, index, array) {
694
+ CodeMirror.keyMap['vim-prefix-'+key+'a'] = {
695
+ auto: 'vim', nofallthrough: true, style: "fat-cursor"
696
+ };
697
+ CodeMirror.keyMap['vim-prefix-'+key+'i'] = {
698
+ auto: 'vim', nofallthrough: true, style: "fat-cursor"
699
+ };
700
+
701
+ CodeMirror.keyMap['vim-prefix-'+key]['A'] = function(cm) {
702
+ reptTimes = 0;
703
+ cm.setOption('keyMap', 'vim-prefix-' + key + 'a');
704
+ };
705
+
706
+ CodeMirror.keyMap['vim-prefix-'+key]['I'] = function(cm) {
707
+ reptTimes = 0;
708
+ cm.setOption('keyMap', 'vim-prefix-' + key + 'i');
709
+ };
710
+ });
711
+
712
+ function regexLastIndexOf(string, pattern, startIndex) {
713
+ for (var i = startIndex == null ? string.length : startIndex; i >= 0; --i)
714
+ if (pattern.test(string.charAt(i))) return i;
715
+ return -1;
716
+ }
717
+
718
+ // Create our text object functions. They work similar to motions but they
719
+ // return a start cursor as well
720
+ var textObjectList = ['W', 'Shift-[', 'Shift-9', '['];
721
+ var textObjects = {
722
+ 'W': function(cm, inclusive) {
723
+ var cur = cm.getCursor();
724
+ var line = cm.getLine(cur.line);
725
+
726
+ var line_to_char = new String(line.substring(0, cur.ch));
727
+ var start = regexLastIndexOf(line_to_char, /[^a-zA-Z0-9]/) + 1;
728
+ var end = motions["E"](cm, 1) ;
729
+
730
+ end.ch += inclusive ? 1 : 0 ;
731
+ return {start: {line: cur.line, ch: start}, end: end };
732
+ },
733
+ 'Shift-[': function(cm, inclusive) { return selectCompanionObject(cm, '}', inclusive); },
734
+ 'Shift-9': function(cm, inclusive) { return selectCompanionObject(cm, ')', inclusive); },
735
+ '[': function(cm, inclusive) { return selectCompanionObject(cm, ']', inclusive); }
736
+ };
737
+
738
+ // One function to handle all operation upon text objects. Kinda funky but it works
739
+ // better than rewriting this code six times
740
+ function textObjectManipulation(cm, object, remove, insert, inclusive) {
741
+ // Object is the text object, delete object if remove is true, enter insert
742
+ // mode if insert is true, inclusive is the difference between a and i
743
+ var tmp = textObjects[object](cm, inclusive);
744
+ var start = tmp.start;
745
+ var end = tmp.end;
746
+
747
+ if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true ;
748
+
749
+ pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end));
750
+ if (remove) cm.replaceRange("", swap ? end : start, swap ? start : end);
751
+ if (insert) cm.setOption('keyMap', 'vim-insert');
752
+ }
753
+
754
+ // And finally build the keymaps up from the text objects
755
+ for (var i = 0; i < textObjectList.length; ++i) {
756
+ var object = textObjectList[i];
757
+ (function(object) {
758
+ CodeMirror.keyMap['vim-prefix-di'][object] = function(cm) { textObjectManipulation(cm, object, true, false, false); };
759
+ CodeMirror.keyMap['vim-prefix-da'][object] = function(cm) { textObjectManipulation(cm, object, true, false, true); };
760
+ CodeMirror.keyMap['vim-prefix-yi'][object] = function(cm) { textObjectManipulation(cm, object, false, false, false); };
761
+ CodeMirror.keyMap['vim-prefix-ya'][object] = function(cm) { textObjectManipulation(cm, object, false, false, true); };
762
+ CodeMirror.keyMap['vim-prefix-ci'][object] = function(cm) { textObjectManipulation(cm, object, true, true, false); };
763
+ CodeMirror.keyMap['vim-prefix-ca'][object] = function(cm) { textObjectManipulation(cm, object, true, true, true); };
764
+ })(object)
765
+ }
76
766
  })();