codemirror-rails 2.35 → 2.36

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.
@@ -1,6 +1,6 @@
1
1
  module Codemirror
2
2
  module Rails
3
- VERSION = '2.35'
4
- CODEMIRROR_VERSION = '2.35'
3
+ VERSION = '2.36'
4
+ CODEMIRROR_VERSION = '2.36'
5
5
  end
6
6
  end
@@ -1,4 +1,4 @@
1
- // CodeMirror version 2.35
1
+ // CodeMirror version 2.36
2
2
  //
3
3
  // All functions that need access to the editor's state live inside
4
4
  // the CodeMirror function. Below that, at the bottom of the file,
@@ -232,6 +232,7 @@ window.CodeMirror = (function() {
232
232
  var off = eltOffset(lineSpace);
233
233
  return coordsChar(coords.x - off.left, coords.y - off.top);
234
234
  },
235
+ defaultTextHeight: function() { return textHeight(); },
235
236
  markText: operation(markText),
236
237
  setBookmark: setBookmark,
237
238
  findMarksAt: findMarksAt,
@@ -346,6 +347,11 @@ window.CodeMirror = (function() {
346
347
  return {x: scroller.scrollLeft, y: scrollbar.scrollTop,
347
348
  height: scrollbar.scrollHeight, width: scroller.scrollWidth};
348
349
  },
350
+ scrollIntoView: function(pos) {
351
+ var coords = localCoords(pos ? clipPos(pos) : sel.inverted ? sel.from : sel.to);
352
+ scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
353
+ },
354
+
349
355
  setSize: function(width, height) {
350
356
  function interpret(val) {
351
357
  val = String(val);
@@ -395,7 +401,7 @@ window.CodeMirror = (function() {
395
401
  }
396
402
 
397
403
  function onScrollBar(e) {
398
- if (scrollbar.scrollTop != lastScrollTop) {
404
+ if (Math.abs(scrollbar.scrollTop - lastScrollTop) > 1) {
399
405
  lastScrollTop = scroller.scrollTop = scrollbar.scrollTop;
400
406
  updateDisplay([]);
401
407
  }
@@ -404,7 +410,7 @@ window.CodeMirror = (function() {
404
410
  function onScrollMain(e) {
405
411
  if (options.fixedGutter && gutter.style.left != scroller.scrollLeft + "px")
406
412
  gutter.style.left = scroller.scrollLeft + "px";
407
- if (scroller.scrollTop != lastScrollTop) {
413
+ if (Math.abs(scroller.scrollTop - lastScrollTop) > 1) {
408
414
  lastScrollTop = scroller.scrollTop;
409
415
  if (scrollbar.scrollTop != lastScrollTop)
410
416
  scrollbar.scrollTop = lastScrollTop;
@@ -637,7 +643,7 @@ window.CodeMirror = (function() {
637
643
  if (handled) {
638
644
  e_preventDefault(e);
639
645
  restartBlink();
640
- if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
646
+ if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
641
647
  }
642
648
  return handled;
643
649
  }
@@ -2174,7 +2180,7 @@ window.CodeMirror = (function() {
2174
2180
  keyMap.emacsy = {
2175
2181
  "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
2176
2182
  "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
2177
- "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
2183
+ "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft",
2178
2184
  "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
2179
2185
  };
2180
2186
 
@@ -2232,15 +2238,13 @@ window.CodeMirror = (function() {
2232
2238
  if (textarea.form) {
2233
2239
  // Deplorable hack to make the submit method do the right thing.
2234
2240
  var rmSubmit = connect(textarea.form, "submit", save, true);
2235
- if (typeof textarea.form.submit == "function") {
2236
- var realSubmit = textarea.form.submit;
2237
- textarea.form.submit = function wrappedSubmit() {
2238
- save();
2239
- textarea.form.submit = realSubmit;
2240
- textarea.form.submit();
2241
- textarea.form.submit = wrappedSubmit;
2242
- };
2243
- }
2241
+ var realSubmit = textarea.form.submit;
2242
+ textarea.form.submit = function wrappedSubmit() {
2243
+ save();
2244
+ textarea.form.submit = realSubmit;
2245
+ textarea.form.submit();
2246
+ textarea.form.submit = wrappedSubmit;
2247
+ };
2244
2248
  }
2245
2249
 
2246
2250
  textarea.style.display = "none";
@@ -3102,8 +3106,10 @@ window.CodeMirror = (function() {
3102
3106
  if (collection[i] == elt) return i;
3103
3107
  return -1;
3104
3108
  }
3109
+ var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
3105
3110
  function isWordChar(ch) {
3106
- return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase() || /[\u4E00-\u9FA5]/.test(ch);
3111
+ return /\w/.test(ch) || ch > "\x80" &&
3112
+ (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
3107
3113
  }
3108
3114
 
3109
3115
  // See if "".split is the broken IE version, if so, provide an
@@ -3159,7 +3165,7 @@ window.CodeMirror = (function() {
3159
3165
  for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
3160
3166
  })();
3161
3167
 
3162
- CodeMirror.version = "2.35";
3168
+ CodeMirror.version = "2.36";
3163
3169
 
3164
3170
  return CodeMirror;
3165
3171
  })();
@@ -18,7 +18,8 @@
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", "Alt-/": "autocomplete",
21
+ "Ctrl-Z": "undo", "Cmd-Z": "undo", "Alt-/": "autocomplete", "Alt-V": "goPageUp",
22
+ "Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto",
22
23
  fallthrough: ["basic", "emacsy"]
23
24
  };
24
25
 
@@ -17,7 +17,7 @@
17
17
  // Entering insert mode:
18
18
  // i, I, a, A, o, O
19
19
  // s
20
- // ce, cb (without support for number of actions like c3e - TODO)
20
+ // ce, cb
21
21
  // cc
22
22
  // S, C TODO
23
23
  // cf<char>, cF<char>, ct<char>, cT<char>
@@ -26,7 +26,7 @@
26
26
  // x, X
27
27
  // J
28
28
  // dd, D
29
- // de, db (without support for number of actions like d3e - TODO)
29
+ // de, db
30
30
  // df<char>, dF<char>, dt<char>, dT<char>
31
31
  //
32
32
  // Yanking and pasting:
@@ -48,22 +48,28 @@
48
48
  //
49
49
 
50
50
  (function() {
51
- var count = "";
52
51
  var sdir = "f";
53
52
  var buf = "";
54
- var yank = 0;
55
- var mark = [];
56
- var reptTimes = 0;
53
+ var mark = {};
54
+ var repeatCount = 0;
55
+ function isLine(cm, line) { return line >= 0 && line < cm.lineCount(); }
57
56
  function emptyBuffer() { buf = ""; }
58
57
  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; }
58
+ function pushRepeatCountDigit(digit) {return function(cm) {repeatCount = (repeatCount * 10) + digit}; }
59
+ function getCountOrOne() {
60
+ var i = repeatCount;
61
+ return i || 1;
62
+ }
63
+ function clearCount() {
64
+ repeatCount = 0;
65
+ }
61
66
  function iterTimes(func) {
62
- for (var i = 0, c = popCount(); i < c; ++i) func(i, i == c - 1);
67
+ for (var i = 0, c = getCountOrOne(); i < c; ++i) func(i, i == c - 1);
68
+ clearCount();
63
69
  }
64
70
  function countTimes(func) {
65
71
  if (typeof func == "string") func = CodeMirror.commands[func];
66
- return function(cm) { iterTimes(function () { func(cm); }); };
72
+ return function(cm) { iterTimes(function (i, last) { func(cm, i, last); }); };
67
73
  }
68
74
 
69
75
  function iterObj(o, f) {
@@ -93,50 +99,110 @@
93
99
  }
94
100
 
95
101
  var word = [/\w/, /[^\w\s]/], bigWord = [/\S/];
96
- function findWord(line, pos, dir, regexps) {
97
- var stop = 0, next = -1;
98
- if (dir > 0) { stop = line.length; next = 0; }
99
- var start = stop, end = stop;
100
- // Find bounds of next one.
101
- outer: for (; pos != stop; pos += dir) {
102
- for (var i = 0; i < regexps.length; ++i) {
103
- if (regexps[i].test(line.charAt(pos + next))) {
104
- start = pos;
105
- for (; pos != stop; pos += dir) {
106
- if (!regexps[i].test(line.charAt(pos + next))) break;
102
+ // Finds a word on the given line, and continue searching the next line if it can't find one.
103
+ function findWord(cm, lineNum, pos, dir, regexps) {
104
+ var line = cm.getLine(lineNum);
105
+ while (true) {
106
+ var stop = (dir > 0) ? line.length : -1;
107
+ var wordStart = stop, wordEnd = stop;
108
+ // Find bounds of next word.
109
+ for (; pos != stop; pos += dir) {
110
+ for (var i = 0; i < regexps.length; ++i) {
111
+ if (regexps[i].test(line.charAt(pos))) {
112
+ wordStart = pos;
113
+ // Advance to end of word.
114
+ for (; pos != stop && regexps[i].test(line.charAt(pos)); pos += dir) {}
115
+ wordEnd = (dir > 0) ? pos : pos + 1;
116
+ return {
117
+ from: Math.min(wordStart, wordEnd),
118
+ to: Math.max(wordStart, wordEnd),
119
+ line: lineNum};
107
120
  }
108
- end = pos;
109
- break outer;
110
121
  }
111
122
  }
123
+ // Advance to next/prev line.
124
+ lineNum += dir;
125
+ if (!isLine(cm, lineNum)) return null;
126
+ line = cm.getLine(lineNum);
127
+ pos = (dir > 0) ? 0 : line.length;
112
128
  }
113
- return {from: Math.min(start, end), to: Math.max(start, end)};
114
129
  }
115
- function moveToWord(cm, regexps, dir, times, where) {
130
+ /**
131
+ * @param {boolean} cm CodeMirror object.
132
+ * @param {regexp} regexps Regular expressions for word characters.
133
+ * @param {number} dir Direction, +/- 1.
134
+ * @param {number} times Number of times to advance word.
135
+ * @param {string} where Go to "start" or "end" of word, 'e' vs 'w'.
136
+ * @param {boolean} yank Whether we are finding words to yank. If true,
137
+ * do not go to the next line to look for the last word. This is to
138
+ * prevent deleting new line on 'dw' at the end of a line.
139
+ */
140
+ function moveToWord(cm, regexps, dir, times, where, yank) {
116
141
  var cur = cm.getCursor();
117
-
142
+ if (yank) {
143
+ where = 'start';
144
+ }
118
145
  for (var i = 0; i < times; i++) {
119
- var line = cm.getLine(cur.line), startCh = cur.ch, word;
146
+ var startCh = cur.ch, startLine = cur.line, word;
120
147
  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);
148
+ // Search and advance.
149
+ word = findWord(cm, cur.line, cur.ch, dir, regexps);
150
+ if (word) {
151
+ if (yank && times == 1 && dir == 1 && cur.line != word.line) {
152
+ // Stop at end of line of last word. Don't want to delete line return
153
+ // for dw if the last deleted word is at the end of a line.
154
+ cur.ch = cm.getLine(cur.line).length;
155
+ break;
156
+ } else {
157
+ // Move to the word we just found. If by moving to the word we end up
158
+ // in the same spot, then move an extra character and search again.
159
+ cur.line = word.line;
160
+ if (dir > 0 && where == 'end') {
161
+ // 'e'
162
+ if (startCh != word.to - 1 || startLine != word.line) {
163
+ cur.ch = word.to - 1;
164
+ break;
165
+ } else {
166
+ cur.ch = word.to;
167
+ }
168
+ } else if (dir > 0 && where == 'start') {
169
+ // 'w'
170
+ if (startCh != word.from || startLine != word.line) {
171
+ cur.ch = word.from;
172
+ break;
173
+ } else {
174
+ cur.ch = word.to;
175
+ }
176
+ } else if (dir < 0 && where == 'end') {
177
+ // 'ge'
178
+ if (startCh != word.to || startLine != word.line) {
179
+ cur.ch = word.to;
180
+ break;
181
+ } else {
182
+ cur.ch = word.from - 1;
183
+ }
184
+ } else if (dir < 0 && where == 'start') {
185
+ // 'b'
186
+ if (startCh != word.from || startLine != word.line) {
187
+ cur.ch = word.from;
188
+ break;
189
+ } else {
190
+ cur.ch = word.from - 1;
191
+ }
192
+ }
193
+ }
194
+ } else {
195
+ // No more words to be found. Move to end of document.
196
+ for (; isLine(cm, cur.line + dir); cur.line += dir) {}
197
+ cur.ch = (dir > 0) ? cm.getLine(cur.line).length : 0;
198
+ break;
130
199
  }
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
200
  }
139
201
  }
202
+ if (where == 'end' && yank) {
203
+ // Include the last character of the word for actions.
204
+ cur.ch++;
205
+ }
140
206
  return cur;
141
207
  }
142
208
  function joinLineNext(cm) {
@@ -157,7 +223,7 @@
157
223
  var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l;
158
224
  cm.setCursor(start);
159
225
  for (var c = start; c <= end; c++) {
160
- pushInBuffer("\n"+cm.getLine(start));
226
+ pushInBuffer("\n" + cm.getLine(start));
161
227
  cm.removeLine(start);
162
228
  }
163
229
  }
@@ -169,7 +235,7 @@
169
235
  }
170
236
  var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l;
171
237
  for (var c = start; c <= end; c++) {
172
- pushInBuffer("\n"+cm.getLine(c));
238
+ pushInBuffer("\n" + cm.getLine(c));
173
239
  }
174
240
  cm.setCursor(start);
175
241
  }
@@ -220,7 +286,7 @@
220
286
 
221
287
  function enterInsertMode(cm) {
222
288
  // enter insert mode: switch mode and cursor
223
- popCount();
289
+ clearCount();
224
290
  cm.setOption("keyMap", "vim-insert");
225
291
  }
226
292
 
@@ -238,7 +304,8 @@
238
304
  var map = CodeMirror.keyMap.vim = {
239
305
  // Pipe (|); TODO: should be *screen* chars, so need a util function to turn tabs into spaces?
240
306
  "'|'": function(cm) {
241
- cm.setCursor(cm.getCursor().line, popCount() - 1, true);
307
+ cm.setCursor(cm.getCursor().line, getCountOrOne() - 1, true);
308
+ clearCount();
242
309
  },
243
310
  "A": function(cm) {
244
311
  cm.setCursor(cm.getCursor().line, cm.getCursor().ch+1, true);
@@ -260,16 +327,26 @@
260
327
  },
261
328
  "G": function(cm) { cm.setOption("keyMap", "vim-prefix-g");},
262
329
  "Shift-D": function(cm) {
263
- // commented out verions works, but I left original, cause maybe
264
- // I don't know vim enouth to see what it does
265
- /* var cur = cm.getCursor();
266
- var f = {line: cur.line, ch: cur.ch}, t = {line: cur.line};
267
- pushInBuffer(cm.getRange(f, t));
268
- */
330
+ var cursor = cm.getCursor();
331
+ var lineN = cursor.line;
332
+ var line = cm.getLine(lineN);
333
+ cm.setLine(lineN, line.slice(0, cursor.ch));
334
+
269
335
  emptyBuffer();
270
- mark["Shift-D"] = cm.getCursor(false).line;
271
- cm.setCursor(cm.getCursor(true).line);
272
- delTillMark(cm,"Shift-D"); mark = [];
336
+ pushInBuffer(line.slice(cursor.ch));
337
+
338
+ if (repeatCount > 1) {
339
+ // we've already done it once
340
+ --repeatCount;
341
+ // the lines dissapear (ie, cursor stays on the same lineN),
342
+ // so only incremenet once
343
+ ++lineN;
344
+
345
+ iterTimes(function() {
346
+ pushInBuffer(cm.getLine(lineN));
347
+ cm.removeLine(lineN);
348
+ });
349
+ }
273
350
  },
274
351
 
275
352
  "S": function (cm) {
@@ -278,13 +355,11 @@
278
355
  })(cm);
279
356
  enterInsertMode(cm);
280
357
  },
281
- "M": function(cm) {cm.setOption("keyMap", "vim-prefix-m"); mark = [];},
282
- "Y": function(cm) {cm.setOption("keyMap", "vim-prefix-y"); emptyBuffer(); yank = 0;},
358
+ "M": function(cm) {cm.setOption("keyMap", "vim-prefix-m"); mark = {};},
359
+ "Y": function(cm) {cm.setOption("keyMap", "vim-prefix-y"); emptyBuffer();},
283
360
  "Shift-Y": function(cm) {
284
361
  emptyBuffer();
285
- mark["Shift-D"] = cm.getCursor(false).line;
286
- cm.setCursor(cm.getCursor(true).line);
287
- yankTillMark(cm,"Shift-D"); mark = [];
362
+ iterTimes(function(i) { pushInBuffer("\n" + cm.getLine(cm.getCursor().line + i)); });
288
363
  },
289
364
  "/": function(cm) {var f = CodeMirror.commands.find; f && f(cm); sdir = "f";},
290
365
  "'?'": function(cm) {
@@ -300,8 +375,8 @@
300
375
  if (fn) sdir != "r" ? CodeMirror.commands.findPrev(cm) : fn.findNext(cm);
301
376
  },
302
377
  "Shift-G": function(cm) {
303
- count == "" ? cm.setCursor(cm.lineCount()) : cm.setCursor(parseInt(count, 10)-1);
304
- popCount();
378
+ (repeatCount == 0) ? cm.setCursor(cm.lineCount()) : cm.setCursor(repeatCount - 1);
379
+ clearCount();
305
380
  CodeMirror.commands.goLineStart(cm);
306
381
  },
307
382
  "':'": function(cm) {
@@ -325,21 +400,9 @@
325
400
  };
326
401
  });
327
402
 
328
- function addCountBindings(keyMap) {
329
- // Add bindings for number keys
330
- keyMap["0"] = function(cm) {
331
- count.length > 0 ? pushCountDigit("0")(cm) : CodeMirror.commands.goLineStart(cm);
332
- };
333
- for (var i = 1; i < 10; ++i) keyMap[i] = pushCountDigit(i);
334
- }
335
- addCountBindings(CodeMirror.keyMap.vim);
336
-
337
403
  // main num keymap
338
404
  // Add bindings that are influenced by number keys
339
405
  iterObj({
340
- "Left": "goColumnLeft", "Right": "goColumnRight",
341
- "Down": "goLineDown", "Up": "goLineUp", "Backspace": "goCharLeft",
342
- "Space": "goCharRight",
343
406
  "X": function(cm) {CodeMirror.commands.delCharRight(cm);},
344
407
  "P": function(cm) {
345
408
  var cur = cm.getCursor().line;
@@ -401,15 +464,18 @@
401
464
  });
402
465
 
403
466
  CodeMirror.keyMap["vim-prefix-g"] = {
404
- "E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, word, -1, 1, "start"));}),
405
- "Shift-E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, bigWord, -1, 1, "start"));}),
406
- "G": function (cm) { cm.setCursor({line: 0, ch: cm.getCursor().ch});},
467
+ "E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, word, -1, 1, "end"));}),
468
+ "Shift-E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, bigWord, -1, 1, "end"));}),
469
+ "G": function (cm) {
470
+ cm.setCursor({line: repeatCount - 1, ch: cm.getCursor().ch});
471
+ clearCount();
472
+ },
407
473
  auto: "vim", nofallthrough: true, style: "fat-cursor"
408
474
  };
409
475
 
410
476
  CodeMirror.keyMap["vim-prefix-d"] = {
411
477
  "D": countTimes(function(cm) {
412
- pushInBuffer("\n"+cm.getLine(cm.getCursor().line));
478
+ pushInBuffer("\n" + cm.getLine(cm.getCursor().line));
413
479
  cm.removeLine(cm.getCursor().line);
414
480
  cm.setOption("keyMap", "vim");
415
481
  }),
@@ -428,8 +494,6 @@
428
494
  },
429
495
  nofallthrough: true, style: "fat-cursor"
430
496
  };
431
- // FIXME - does not work for bindings like "d3e"
432
- addCountBindings(CodeMirror.keyMap["vim-prefix-d"]);
433
497
 
434
498
  CodeMirror.keyMap["vim-prefix-c"] = {
435
499
  "B": function (cm) {
@@ -471,10 +535,10 @@
471
535
  mark[m] = cm.getCursor().line;
472
536
  };
473
537
  CodeMirror.keyMap["vim-prefix-d'"][m] = function(cm) {
474
- delTillMark(cm,m);
538
+ delTillMark(cm, m);
475
539
  };
476
540
  CodeMirror.keyMap["vim-prefix-y'"][m] = function(cm) {
477
- yankTillMark(cm,m);
541
+ yankTillMark(cm, m);
478
542
  };
479
543
  CodeMirror.keyMap["vim-prefix-r"][m] = function (cm) {
480
544
  var cur = cm.getCursor();
@@ -508,8 +572,8 @@
508
572
  setupPrefixBindingForKey("Space");
509
573
 
510
574
  CodeMirror.keyMap["vim-prefix-y"] = {
511
- "Y": countTimes(function(cm) {
512
- pushInBuffer("\n"+cm.getLine(cm.getCursor().line+yank)); yank++;
575
+ "Y": countTimes(function(cm, i, last) {
576
+ pushInBuffer("\n" + cm.getLine(cm.getCursor().line + i));
513
577
  cm.setOption("keyMap", "vim");
514
578
  }),
515
579
  "'": function(cm) {cm.setOption("keyMap", "vim-prefix-y'"); emptyBuffer();},
@@ -588,67 +652,107 @@
588
652
  return {start: start, end: end};
589
653
  }
590
654
 
591
- // These are our motion commands to be used for navigation and selection with
592
- // certian other commands. All should return a cursor object.
593
- var motionList = ['B', 'E', 'J', 'K', 'H', 'L', 'W', 'Shift-W', "'^'", "'$'", "'%'", 'Esc'];
655
+ // takes in a symbol and a cursor and tries to simulate text objects that have
656
+ // identical opening and closing symbols
657
+ // TODO support across multiple lines
658
+ function findBeginningAndEnd(cm, symb, inclusive) {
659
+ var cur = cm.getCursor();
660
+ var line = cm.getLine(cur.line);
661
+ var chars = line.split('');
662
+ var start = undefined;
663
+ var end = undefined;
664
+ var firstIndex = chars.indexOf(symb);
665
+
666
+ // the decision tree is to always look backwards for the beginning first,
667
+ // but if the cursor is in front of the first instance of the symb,
668
+ // then move the cursor forward
669
+ if (cur.ch < firstIndex) {
670
+ cur.ch = firstIndex;
671
+ cm.setCursor(cur.line, firstIndex+1);
672
+ }
673
+ // otherwise if the cursor is currently on the closing symbol
674
+ else if (firstIndex < cur.ch && chars[cur.ch] == symb) {
675
+ end = cur.ch; // assign end to the current cursor
676
+ --cur.ch; // make sure to look backwards
677
+ }
594
678
 
595
- motions = {
596
- 'B': function(cm, times) { return moveToWord(cm, word, -1, times); },
597
- 'Shift-B': function(cm, times) { return moveToWord(cm, bigWord, -1, times); },
598
- 'E': function(cm, times) { return moveToWord(cm, word, 1, times, 'end'); },
599
- 'Shift-E': function(cm, times) { return moveToWord(cm, bigWord, 1, times, 'end'); },
600
- 'J': function(cm, times) {
601
- var cur = cm.getCursor();
602
- return {line: cur.line+times, ch : cur.ch};
603
- },
679
+ // if we're currently on the symbol, we've got a start
680
+ if (chars[cur.ch] == symb && end == null)
681
+ start = cur.ch + 1; // assign start to ahead of the cursor
682
+ else {
683
+ // go backwards to find the start
684
+ for (var i = cur.ch; i > -1 && start == null; i--)
685
+ if (chars[i] == symb) start = i + 1;
686
+ }
604
687
 
605
- 'K': function(cm, times) {
606
- var cur = cm.getCursor();
607
- return {line: cur.line-times, ch: cur.ch};
608
- },
688
+ // look forwards for the end symbol
689
+ if (start != null && end == null) {
690
+ for (var i = start, len = chars.length; i < len && end == null; i++) {
691
+ if (chars[i] == symb) end = i;
692
+ }
693
+ }
609
694
 
610
- 'H': function(cm, times) {
611
- var cur = cm.getCursor();
612
- return {line: cur.line, ch: cur.ch-times};
613
- },
695
+ // nothing found
696
+ // FIXME still enters insert mode
697
+ if (start == null || end == null) return {
698
+ start: cur, end: cur
699
+ };
614
700
 
615
- 'L': function(cm, times) {
616
- var cur = cm.getCursor();
617
- return {line: cur.line, ch: cur.ch+times};
618
- },
619
- 'W': function(cm, times) { return moveToWord(cm, word, 1, times); },
620
- 'Shift-W': function(cm, times) { return moveToWord(cm, bigWord, 1, times); },
621
- "'^'": function(cm, times) {
622
- var cur = cm.getCursor();
623
- var line = cm.getLine(cur.line).split('');
701
+ // include the symbols
702
+ if (inclusive) {
703
+ --start; ++end;
704
+ }
624
705
 
625
- // Empty line :o
626
- if (line.length == 0) return cur;
706
+ return {
707
+ start: {line: cur.line, ch: start},
708
+ end: {line: cur.line, ch: end}
709
+ };
710
+ }
627
711
 
628
- for (var index = 0; index < line.length; index++) {
629
- if (line[index].match(/[^\s]/)) return {line: cur.line, ch: index};
712
+ function offsetCursor(cm, line, ch) {
713
+ var cur = cm.getCursor(); return {line: cur.line + line, ch: cur.ch + ch};
714
+ }
715
+
716
+ // These are the motion commands we use for navigation and selection with
717
+ // certain other commands. All should return a cursor object.
718
+ var motions = {
719
+ "J": function(cm, times) { return offsetCursor(cm, times, 0); },
720
+ "Down": function(cm, times) { return offsetCursor(cm, times, 0); },
721
+ "K": function(cm, times) { return offsetCursor(cm, -times, 0); },
722
+ "Up": function(cm, times) { return offsetCursor(cm, -times, 0); },
723
+ "L": function(cm, times) { return offsetCursor(cm, 0, times); },
724
+ "Right": function(cm, times) { return offsetCursor(cm, 0, times); },
725
+ "Space": function(cm, times) { return offsetCursor(cm, 0, times); },
726
+ "H": function(cm, times) { return offsetCursor(cm, 0, -times); },
727
+ "Left": function(cm, times) { return offsetCursor(cm, 0, -times); },
728
+ "Backspace": function(cm, times) { return offsetCursor(cm, 0, -times); },
729
+ "B": function(cm, times, yank) { return moveToWord(cm, word, -1, times, 'start', yank); },
730
+ "Shift-B": function(cm, times, yank) { return moveToWord(cm, bigWord, -1, times, 'start', yank); },
731
+ "E": function(cm, times, yank) { return moveToWord(cm, word, 1, times, 'end', yank); },
732
+ "Shift-E": function(cm, times, yank) { return moveToWord(cm, bigWord, 1, times, 'end', yank); },
733
+ "W": function(cm, times, yank) { return moveToWord(cm, word, 1, times, 'start', yank); },
734
+ "Shift-W": function(cm, times, yank) { return moveToWord(cm, bigWord, 1, times, 'start', yank); },
735
+ "'^'": function(cm, times) {
736
+ var cur = cm.getCursor(), line = cm.getLine(cur.line).split('');
737
+ for (var i = 0; i < line.length; i++) {
738
+ if (line[i].match(/[^\s]/)) return {line: cur.line, ch: index};
630
739
  }
740
+ return cur;
631
741
  },
632
742
  "'$'": function(cm) {
633
- var cur = cm.getCursor();
634
- var line = cm.getLine(cur.line);
635
- return {line: cur.line, ch: line.length};
743
+ var cur = cm.getCursor(), ch = cm.getLine(cur.line).length;
744
+ return {line: cur.line, ch: ch};
636
745
  },
637
746
  "'%'": function(cm) { return findMatchedSymbol(cm, cm.getCursor()); },
638
- "Esc" : function(cm) {
639
- cm.setOption('vim');
640
- reptTimes = 0;
641
-
642
- return cm.getCursor();
643
- }
747
+ "Esc" : function(cm) { cm.setOption("keyMap", "vim"); repeatCount = 0; return cm.getCursor(); }
644
748
  };
645
749
 
646
750
  // Map our movement actions each operator and non-operational movement
647
- iterList(motionList, function(key, index, array) {
751
+ iterObj(motions, function(key, motion) {
648
752
  CodeMirror.keyMap['vim-prefix-d'][key] = function(cm) {
649
753
  // Get our selected range
650
754
  var start = cm.getCursor();
651
- var end = motions[key](cm, reptTimes ? reptTimes : 1);
755
+ var end = motion(cm, repeatCount ? repeatCount : 1, true);
652
756
 
653
757
  // Set swap var if range is of negative length
654
758
  if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true;
@@ -658,56 +762,59 @@
658
762
  cm.replaceRange("", swap ? end : start, swap ? start : end);
659
763
 
660
764
  // And clean up
661
- reptTimes = 0;
765
+ repeatCount = 0;
662
766
  cm.setOption("keyMap", "vim");
663
767
  };
664
768
 
665
769
  CodeMirror.keyMap['vim-prefix-c'][key] = function(cm) {
666
770
  var start = cm.getCursor();
667
- var end = motions[key](cm, reptTimes ? reptTimes : 1);
771
+ var end = motion(cm, repeatCount ? repeatCount : 1, true);
668
772
 
669
773
  if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true;
670
774
  pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end));
671
775
  cm.replaceRange("", swap ? end : start, swap ? start : end);
672
776
 
673
- reptTimes = 0;
777
+ repeatCount = 0;
674
778
  cm.setOption('keyMap', 'vim-insert');
675
779
  };
676
780
 
677
781
  CodeMirror.keyMap['vim-prefix-y'][key] = function(cm) {
678
782
  var start = cm.getCursor();
679
- var end = motions[key](cm, reptTimes ? reptTimes : 1);
783
+ var end = motion(cm, repeatCount ? repeatCount : 1, true);
680
784
 
681
785
  if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true;
682
786
  pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end));
683
787
 
684
- reptTimes = 0;
788
+ repeatCount = 0;
685
789
  cm.setOption("keyMap", "vim");
686
790
  };
687
791
 
688
792
  CodeMirror.keyMap['vim'][key] = function(cm) {
689
- var cur = motions[key](cm, reptTimes ? reptTimes : 1);
793
+ var cur = motion(cm, repeatCount ? repeatCount : 1);
690
794
  cm.setCursor(cur.line, cur.ch);
691
795
 
692
- reptTimes = 0;
796
+ repeatCount = 0;
693
797
  };
694
798
  });
695
799
 
696
- var nums = [1,2,3,4,5,6,7,8,9];
697
- iterList(nums, function(key, index, array) {
698
- CodeMirror.keyMap['vim'][key] = function (cm) {
699
- reptTimes = (reptTimes * 10) + key;
700
- };
701
- CodeMirror.keyMap['vim-prefix-d'][key] = function (cm) {
702
- reptTimes = (reptTimes * 10) + key;
703
- };
704
- CodeMirror.keyMap['vim-prefix-y'][key] = function (cm) {
705
- reptTimes = (reptTimes * 10) + key;
706
- };
707
- CodeMirror.keyMap['vim-prefix-c'][key] = function (cm) {
708
- reptTimes = (reptTimes * 10) + key;
800
+ function addCountBindings(keyMapName) {
801
+ // Add bindings for number keys
802
+ keyMap = CodeMirror.keyMap[keyMapName];
803
+ keyMap["0"] = function(cm) {
804
+ if (repeatCount > 0) {
805
+ pushRepeatCountDigit(0)(cm);
806
+ } else {
807
+ CodeMirror.commands.goLineStart(cm);
808
+ }
709
809
  };
710
- });
810
+ for (var i = 1; i < 10; ++i) {
811
+ keyMap[i] = pushRepeatCountDigit(i);
812
+ }
813
+ }
814
+ addCountBindings('vim');
815
+ addCountBindings('vim-prefix-d');
816
+ addCountBindings('vim-prefix-y');
817
+ addCountBindings('vim-prefix-c');
711
818
 
712
819
  // Create our keymaps for each operator and make xa and xi where x is an operator
713
820
  // change to the corrosponding keymap
@@ -721,12 +828,12 @@
721
828
  };
722
829
 
723
830
  CodeMirror.keyMap['vim-prefix-'+key]['A'] = function(cm) {
724
- reptTimes = 0;
831
+ repeatCount = 0;
725
832
  cm.setOption('keyMap', 'vim-prefix-' + key + 'a');
726
833
  };
727
834
 
728
835
  CodeMirror.keyMap['vim-prefix-'+key]['I'] = function(cm) {
729
- reptTimes = 0;
836
+ repeatCount = 0;
730
837
  cm.setOption('keyMap', 'vim-prefix-' + key + 'i');
731
838
  };
732
839
  });
@@ -739,7 +846,7 @@
739
846
 
740
847
  // Create our text object functions. They work similar to motions but they
741
848
  // return a start cursor as well
742
- var textObjectList = ['W', 'Shift-[', 'Shift-9', '['];
849
+ var textObjectList = ['W', 'Shift-[', 'Shift-9', '[', "'", "Shift-'"];
743
850
  var textObjects = {
744
851
  'W': function(cm, inclusive) {
745
852
  var cur = cm.getCursor();
@@ -754,7 +861,9 @@
754
861
  },
755
862
  'Shift-[': function(cm, inclusive) { return selectCompanionObject(cm, '}', inclusive); },
756
863
  'Shift-9': function(cm, inclusive) { return selectCompanionObject(cm, ')', inclusive); },
757
- '[': function(cm, inclusive) { return selectCompanionObject(cm, ']', inclusive); }
864
+ '[': function(cm, inclusive) { return selectCompanionObject(cm, ']', inclusive); },
865
+ "'": function(cm, inclusive) { return findBeginningAndEnd(cm, "'", inclusive); },
866
+ "Shift-'": function(cm, inclusive) { return findBeginningAndEnd(cm, '"', inclusive); }
758
867
  };
759
868
 
760
869
  // One function to handle all operation upon text objects. Kinda funky but it works