codemirror-rails 3.16 → 3.17

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -0
  3. data/lib/codemirror/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/codemirror.js +104 -48
  5. data/vendor/assets/javascripts/codemirror/addons/comment/continuecomment.js +25 -15
  6. data/vendor/assets/javascripts/codemirror/addons/edit/closetag.js +28 -27
  7. data/vendor/assets/javascripts/codemirror/addons/edit/matchtags.js +10 -5
  8. data/vendor/assets/javascripts/codemirror/addons/hint/css-hint.js +50 -0
  9. data/vendor/assets/javascripts/codemirror/addons/hint/html-hint.js +0 -0
  10. data/vendor/assets/javascripts/codemirror/addons/hint/show-hint.js +10 -7
  11. data/vendor/assets/javascripts/codemirror/addons/lint/css-lint.js +17 -0
  12. data/vendor/assets/javascripts/codemirror/addons/lint/lint.js +1 -1
  13. data/vendor/assets/javascripts/codemirror/addons/merge/merge.js +32 -6
  14. data/vendor/assets/javascripts/codemirror/addons/runmode/runmode-standalone.js +1 -1
  15. data/vendor/assets/javascripts/codemirror/addons/runmode/runmode.js +1 -1
  16. data/vendor/assets/javascripts/codemirror/addons/runmode/runmode.node.js +1 -1
  17. data/vendor/assets/javascripts/codemirror/addons/search/searchcursor.js +2 -2
  18. data/vendor/assets/javascripts/codemirror/addons/tern/tern.js +29 -6
  19. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +13 -0
  20. data/vendor/assets/javascripts/codemirror/modes/coffeescript.js +1 -1
  21. data/vendor/assets/javascripts/codemirror/modes/css.js +15 -13
  22. data/vendor/assets/javascripts/codemirror/modes/d.js +0 -0
  23. data/vendor/assets/javascripts/codemirror/modes/dtd.js +127 -0
  24. data/vendor/assets/javascripts/codemirror/modes/fortran.js +173 -0
  25. data/vendor/assets/javascripts/codemirror/modes/haskell.js +5 -1
  26. data/vendor/assets/javascripts/codemirror/modes/less.js +47 -49
  27. data/vendor/assets/javascripts/codemirror/modes/octave.js +118 -0
  28. data/vendor/assets/javascripts/codemirror/modes/sql.js +8 -5
  29. data/vendor/assets/javascripts/codemirror/modes/toml.js +71 -0
  30. data/vendor/assets/stylesheets/codemirror.css +7 -3
  31. data/vendor/assets/stylesheets/codemirror/addons/display/fullscreen.css +1 -1
  32. data/vendor/assets/stylesheets/codemirror/addons/lint/lint.css +3 -27
  33. data/vendor/assets/stylesheets/codemirror/themes/solarized.css +2 -24
  34. metadata +24 -21
  35. data/vendor/assets/javascripts/codemirror/addons/edit/continuecomment.js +0 -44
  36. data/vendor/assets/javascripts/codemirror/addons/merge/dep/diff_match_patch.js +0 -50
  37. data/vendor/assets/javascripts/codemirror/modes/scss_test.js +0 -80
@@ -27,9 +27,9 @@
27
27
  if (val && (old == CodeMirror.Init || !old)) {
28
28
  var map = {name: "autoCloseTags"};
29
29
  if (typeof val != "object" || val.whenClosing)
30
- map["'/'"] = function(cm) { return autoCloseTag(cm, '/'); };
30
+ map["'/'"] = function(cm) { return autoCloseSlash(cm); };
31
31
  if (typeof val != "object" || val.whenOpening)
32
- map["'>'"] = function(cm) { return autoCloseTag(cm, '>'); };
32
+ map["'>'"] = function(cm) { return autoCloseGT(cm); };
33
33
  cm.addKeyMap(map);
34
34
  } else if (!val && (old != CodeMirror.Init && old)) {
35
35
  cm.removeKeyMap("autoCloseTags");
@@ -41,40 +41,41 @@
41
41
  var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
42
42
  "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
43
43
 
44
- function autoCloseTag(cm, ch) {
44
+ function autoCloseGT(cm) {
45
45
  var pos = cm.getCursor(), tok = cm.getTokenAt(pos);
46
46
  var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
47
- if (inner.mode.name != "xml") return CodeMirror.Pass;
47
+ if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
48
48
 
49
49
  var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html";
50
50
  var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
51
51
  var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
52
52
 
53
- if (ch == ">" && state.tagName) {
54
- var tagName = state.tagName;
55
- if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
56
- var lowerTagName = tagName.toLowerCase();
57
- // Don't process the '>' at the end of an end-tag or self-closing tag
58
- if (tok.type == "tag" && state.type == "closeTag" ||
59
- tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
60
- dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1)
61
- return CodeMirror.Pass;
53
+ var tagName = state.tagName;
54
+ if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
55
+ var lowerTagName = tagName.toLowerCase();
56
+ // Don't process the '>' at the end of an end-tag or self-closing tag
57
+ if (tok.type == "tag" && state.type == "closeTag" ||
58
+ tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
59
+ dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1)
60
+ return CodeMirror.Pass;
62
61
 
63
- var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1;
64
- var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1);
65
- cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "</" + tagName + ">",
66
- {head: curPos, anchor: curPos});
67
- if (doIndent) {
68
- cm.indentLine(pos.line + 1);
69
- cm.indentLine(pos.line + 2);
70
- }
71
- return;
72
- } else if (ch == "/" && tok.string == "<") {
73
- var tagName = state.context && state.context.tagName;
74
- if (tagName) cm.replaceSelection("/" + tagName + ">", "end");
75
- return;
62
+ var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1;
63
+ var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1);
64
+ cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "</" + tagName + ">",
65
+ {head: curPos, anchor: curPos});
66
+ if (doIndent) {
67
+ cm.indentLine(pos.line + 1);
68
+ cm.indentLine(pos.line + 2);
76
69
  }
77
- return CodeMirror.Pass;
70
+ }
71
+
72
+ function autoCloseSlash(cm) {
73
+ var pos = cm.getCursor(), tok = cm.getTokenAt(pos);
74
+ var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
75
+ if (tok.string.charAt(0) != "<" || inner.mode.name != "xml") return CodeMirror.Pass;
76
+
77
+ var tagName = state.context && state.context.tagName;
78
+ if (tagName) cm.replaceSelection("/" + tagName + ">", "end");
78
79
  }
79
80
 
80
81
  function indexOf(collection, elt) {
@@ -8,6 +8,7 @@
8
8
  clear(cm);
9
9
  }
10
10
  if (val) {
11
+ cm.state.matchBothTags = typeof val == "object" && val.bothTags;
11
12
  cm.on("cursorActivity", doMatchTags);
12
13
  cm.on("viewportChange", maybeUpdateMatch);
13
14
  doMatchTags(cm);
@@ -15,23 +16,27 @@
15
16
  });
16
17
 
17
18
  function clear(cm) {
18
- if (cm.state.matchedTag) {
19
- cm.state.matchedTag.clear();
20
- cm.state.matchedTag = null;
21
- }
19
+ if (cm.state.tagHit) cm.state.tagHit.clear();
20
+ if (cm.state.tagOther) cm.state.tagOther.clear();
21
+ cm.state.tagHit = cm.state.tagOther = null;
22
22
  }
23
23
 
24
24
  function doMatchTags(cm) {
25
25
  cm.state.failedTagMatch = false;
26
26
  cm.operation(function() {
27
27
  clear(cm);
28
+ if (cm.somethingSelected()) return;
28
29
  var cur = cm.getCursor(), range = cm.getViewport();
29
30
  range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
30
31
  var match = CodeMirror.findMatchingTag(cm, cur, range);
31
32
  if (!match) return;
33
+ if (cm.state.matchBothTags) {
34
+ var hit = match.at == "open" ? match.open : match.close;
35
+ if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
36
+ }
32
37
  var other = match.at == "close" ? match.open : match.close;
33
38
  if (other)
34
- cm.state.matchedTag = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
39
+ cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
35
40
  else
36
41
  cm.state.failedTagMatch = true;
37
42
  });
@@ -0,0 +1,50 @@
1
+ (function () {
2
+ "use strict";
3
+
4
+ function getHints(cm) {
5
+ var cur = cm.getCursor(), token = cm.getTokenAt(cur);
6
+ var inner = CodeMirror.innerMode(cm.getMode(), token.state);
7
+ if (inner.mode.name != "css") return;
8
+
9
+ // If it's not a 'word-style' token, ignore the token.
10
+ if (!/^[\w$_-]*$/.test(token.string)) {
11
+ token = {
12
+ start: cur.ch, end: cur.ch, string: "", state: token.state,
13
+ type: null
14
+ };
15
+ var stack = token.state.stack;
16
+ var lastToken = stack && stack.length > 0 ? stack[stack.length - 1] : "";
17
+ if (token.string == ":" || lastToken.indexOf("property") == 0)
18
+ token.type = "variable";
19
+ else if (token.string == "{" || lastToken.indexOf("rule") == 0)
20
+ token.type = "property";
21
+ }
22
+
23
+ if (!token.type)
24
+ return;
25
+
26
+ var spec = CodeMirror.resolveMode("text/css");
27
+ var keywords = null;
28
+ if (token.type.indexOf("property") == 0)
29
+ keywords = spec.propertyKeywords;
30
+ else if (token.type.indexOf("variable") == 0)
31
+ keywords = spec.valueKeywords;
32
+
33
+ if (!keywords)
34
+ return;
35
+
36
+ var result = [];
37
+ for (var name in keywords) {
38
+ if (name.indexOf(token.string) == 0 /* > -1 */)
39
+ result.push(name);
40
+ }
41
+
42
+ return {
43
+ list: result,
44
+ from: CodeMirror.Pos(cur.line, token.start),
45
+ to: CodeMirror.Pos(cur.line, token.end)
46
+ };
47
+ }
48
+
49
+ CodeMirror.registerHelper("hint", "css", getHints);
50
+ })();
@@ -111,10 +111,10 @@
111
111
  var baseMap = {
112
112
  Up: function() {handle.moveFocus(-1);},
113
113
  Down: function() {handle.moveFocus(1);},
114
- PageUp: function() {handle.moveFocus(-handle.menuSize());},
115
- PageDown: function() {handle.moveFocus(handle.menuSize());},
114
+ PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
115
+ PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
116
116
  Home: function() {handle.setFocus(0);},
117
- End: function() {handle.setFocus(handle.length);},
117
+ End: function() {handle.setFocus(handle.length - 1);},
118
118
  Enter: handle.pick,
119
119
  Tab: handle.pick,
120
120
  Esc: handle.close
@@ -167,6 +167,7 @@
167
167
  // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
168
168
  var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
169
169
  var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
170
+ (options.container || document.body).appendChild(hints);
170
171
  var box = hints.getBoundingClientRect();
171
172
  var overlapX = box.right - winW, overlapY = box.bottom - winH;
172
173
  if (overlapX > 0) {
@@ -187,10 +188,9 @@
187
188
  }
188
189
  hints.style.top = (top = pos.bottom - overlapY) + "px";
189
190
  }
190
- (options.container || document.body).appendChild(hints);
191
191
 
192
192
  cm.addKeyMap(this.keyMap = buildKeyMap(options, {
193
- moveFocus: function(n) { widget.changeActive(widget.selectedHint + n); },
193
+ moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
194
194
  setFocus: function(n) { widget.changeActive(n); },
195
195
  menuSize: function() { return widget.screenAmount(); },
196
196
  length: completions.length,
@@ -250,8 +250,11 @@
250
250
  this.completion.pick(this.data, this.selectedHint);
251
251
  },
252
252
 
253
- changeActive: function(i) {
254
- i = Math.max(0, Math.min(i, this.data.list.length - 1));
253
+ changeActive: function(i, avoidWrap) {
254
+ if (i >= this.data.list.length)
255
+ i = avoidWrap ? this.data.list.length - 1 : 0;
256
+ else if (i < 0)
257
+ i = avoidWrap ? 0 : this.data.list.length - 1;
255
258
  if (this.selectedHint == i) return;
256
259
  var node = this.hints.childNodes[this.selectedHint];
257
260
  node.className = node.className.replace(" CodeMirror-hint-active", "");
@@ -0,0 +1,17 @@
1
+ // Depends on csslint.js from https://github.com/stubbornella/csslint
2
+
3
+ CodeMirror.registerHelper("lint", "css", function(text) {
4
+ var found = [];
5
+ var results = CSSLint.verify(text), messages = results.messages, message = null;
6
+ for ( var i = 0; i < messages.length; i++) {
7
+ message = messages[i];
8
+ var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col;
9
+ found.push({
10
+ from: CodeMirror.Pos(startLine, startCol),
11
+ to: CodeMirror.Pos(endLine, endCol),
12
+ message: message.message,
13
+ severity : message.type
14
+ });
15
+ }
16
+ return found;
17
+ });
@@ -112,7 +112,7 @@
112
112
  if (options.async)
113
113
  options.getAnnotations(cm, updateLinting, options);
114
114
  else
115
- updateLinting(cm, options.getAnnotations(cm.getValue()));
115
+ updateLinting(cm, options.getAnnotations(cm.getValue(), options));
116
116
  }
117
117
 
118
118
  function updateLinting(cm, annotationsNotSorted) {
@@ -31,9 +31,17 @@
31
31
  this.diff = getDiff(orig, options.value);
32
32
  this.diffOutOfDate = false;
33
33
 
34
+ this.showDifferences = options.showDifferences !== false;
34
35
  this.forceUpdate = registerUpdate(this);
35
36
  setScrollLock(this, true, false);
36
37
  registerScroll(this);
38
+ },
39
+ setShowDifferences: function(val) {
40
+ val = val !== false;
41
+ if (val != this.showDifferences) {
42
+ this.showDifferences = val;
43
+ this.forceUpdate("full");
44
+ }
37
45
  }
38
46
  };
39
47
 
@@ -41,26 +49,38 @@
41
49
  var edit = {from: 0, to: 0, marked: []};
42
50
  var orig = {from: 0, to: 0, marked: []};
43
51
  var debounceChange;
44
- function update() {
52
+ function update(mode) {
53
+ if (mode == "full") {
54
+ if (dv.svg) clear(dv.svg);
55
+ clear(dv.copyButtons);
56
+ clearMarks(dv.edit, edit.marked, dv.classes);
57
+ clearMarks(dv.orig, orig.marked, dv.classes);
58
+ edit.from = edit.to = orig.from = orig.to = 0;
59
+ }
45
60
  if (dv.diffOutOfDate) {
46
61
  dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue());
47
62
  dv.diffOutOfDate = false;
63
+ CodeMirror.signal(dv.edit, "updateDiff", dv.diff);
64
+ }
65
+ if (dv.showDifferences) {
66
+ updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes);
67
+ updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes);
48
68
  }
49
- updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes);
50
- updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes);
51
69
  drawConnectors(dv);
52
70
  }
53
71
  function set(slow) {
54
72
  clearTimeout(debounceChange);
55
73
  debounceChange = setTimeout(update, slow == true ? 250 : 100);
56
74
  }
57
- dv.edit.on("change", function() {
75
+ function change() {
58
76
  if (!dv.diffOutOfDate) {
59
77
  dv.diffOutOfDate = true;
60
78
  edit.from = edit.to = orig.from = orig.to = 0;
61
79
  }
62
80
  set(true);
63
- });
81
+ }
82
+ dv.edit.on("change", change);
83
+ dv.orig.on("change", change);
64
84
  dv.edit.on("viewportChange", set);
65
85
  dv.orig.on("viewportChange", set);
66
86
  update();
@@ -211,6 +231,8 @@
211
231
  // Updating the gap between editor and original
212
232
 
213
233
  function drawConnectors(dv) {
234
+ if (!dv.showDifferences) return;
235
+
214
236
  if (dv.svg) {
215
237
  clear(dv.svg);
216
238
  var w = dv.gap.offsetWidth;
@@ -322,7 +344,11 @@
322
344
  constuctor: MergeView,
323
345
  editor: function() { return this.edit; },
324
346
  rightOriginal: function() { return this.right && this.right.orig; },
325
- leftOriginal: function() { return this.left && this.left.orig; }
347
+ leftOriginal: function() { return this.left && this.left.orig; },
348
+ setShowDifferences: function(val) {
349
+ if (this.right) this.right.setShowDifferences(val);
350
+ if (this.left) this.left.setShowDifferences(val);
351
+ }
326
352
  };
327
353
 
328
354
  // Operations on diffs
@@ -125,7 +125,7 @@ CodeMirror.runMode = function (string, modespec, callback, options) {
125
125
  var stream = new CodeMirror.StringStream(lines[i]);
126
126
  while (!stream.eol()) {
127
127
  var style = mode.token(stream, state);
128
- callback(stream.current(), style, i, stream.start);
128
+ callback(stream.current(), style, i, stream.start, state);
129
129
  stream.start = stream.pos;
130
130
  }
131
131
  }
@@ -49,7 +49,7 @@ CodeMirror.runMode = function(string, modespec, callback, options) {
49
49
  var stream = new CodeMirror.StringStream(lines[i]);
50
50
  while (!stream.eol()) {
51
51
  var style = mode.token(stream, state);
52
- callback(stream.current(), style, i, stream.start);
52
+ callback(stream.current(), style, i, stream.start, state);
53
53
  stream.start = stream.pos;
54
54
  }
55
55
  }
@@ -96,7 +96,7 @@ exports.runMode = function(string, modespec, callback) {
96
96
  var stream = new exports.StringStream(lines[i]);
97
97
  while (!stream.eol()) {
98
98
  var style = mode.token(stream, state);
99
- callback(stream.current(), style, i, stream.start);
99
+ callback(stream.current(), style, i, stream.start, state);
100
100
  stream.start = stream.pos;
101
101
  }
102
102
  }
@@ -69,8 +69,8 @@
69
69
  this.matches = function(reverse, pos) {
70
70
  var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(doc.getLine(ln));
71
71
  var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
72
- if (reverse ? offsetA >= pos.ch || offsetA != match.length
73
- : offsetA <= pos.ch || offsetA != line.length - match.length)
72
+ if (reverse ? offsetA > pos.ch || offsetA != match.length
73
+ : offsetA < pos.ch || offsetA != line.length - match.length)
74
74
  return;
75
75
  for (;;) {
76
76
  if (reverse ? !ln : ln == doc.lineCount() - 1) return;
@@ -24,6 +24,9 @@
24
24
  // no tip should be shown. By default the docstring is shown.
25
25
  // * typeTip: Like completionTip, but for the tooltips shown for type
26
26
  // queries.
27
+ // * responseFilter: A function(doc, query, request, error, data) that
28
+ // will be applied to the Tern responses before treating them
29
+ //
27
30
  //
28
31
  // It is possible to run the Tern server in a web worker by specifying
29
32
  // these additional options:
@@ -102,8 +105,16 @@
102
105
 
103
106
  rename: function(cm) { rename(this, cm); },
104
107
 
105
- request: function(cm, query, c) {
106
- this.server.request(buildRequest(this, findDoc(this, cm.getDoc()), query), c);
108
+ request: function (cm, query, c) {
109
+ var self = this;
110
+ var doc = findDoc(this, cm.getDoc());
111
+ var request = buildRequest(this, doc, query);
112
+
113
+ this.server.request(request, function (error, data) {
114
+ if (!error && self.options.responseFilter)
115
+ data = self.options.responseFilter(doc, query, request, error, data);
116
+ c(error, data);
117
+ });
107
118
  }
108
119
  };
109
120
 
@@ -233,12 +244,24 @@
233
244
  closeArgHints(ts);
234
245
 
235
246
  if (cm.somethingSelected()) return;
236
- var lex = cm.getTokenAt(cm.getCursor()).state.lexical;
247
+ var state = cm.getTokenAt(cm.getCursor()).state;
248
+ var inner = CodeMirror.innerMode(cm.getMode(), state);
249
+ if (inner.mode.name != "javascript") return;
250
+ var lex = inner.state.lexical;
237
251
  if (lex.info != "call") return;
238
252
 
239
- var ch = lex.column, pos = lex.pos || 0;
240
- for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line)
241
- if (cm.getLine(line).charAt(ch) == "(") {found = true; break;}
253
+ var ch, pos = lex.pos || 0, tabSize = cm.getOption("tabSize");
254
+ for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {
255
+ var str = cm.getLine(line), extra = 0;
256
+ for (var pos = 0;;) {
257
+ var tab = str.indexOf("\t", pos);
258
+ if (tab == -1) break;
259
+ extra += tabSize - (tab + extra) % tabSize - 1;
260
+ pos = tab + 1;
261
+ }
262
+ ch = lex.column - extra;
263
+ if (str.charAt(ch) == "(") {found = true; break;}
264
+ }
242
265
  if (!found) return;
243
266
 
244
267
  var start = Pos(line, ch);
@@ -40,6 +40,10 @@
40
40
  * TODO: Implement the remaining special marks. They have more complex
41
41
  * behavior.
42
42
  *
43
+ * Events:
44
+ * 'vim-mode-change' - raised on the editor anytime the current mode changes,
45
+ * Event object: {mode: "visual", subMode: "linewise"}
46
+ *
43
47
  * Code structure:
44
48
  * 1. Default keymap
45
49
  * 2. Variable declarations and short basic helpers
@@ -318,6 +322,7 @@
318
322
  CodeMirror.defineOption('vimMode', false, function(cm, val) {
319
323
  if (val) {
320
324
  cm.setOption('keyMap', 'vim');
325
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
321
326
  cm.on('beforeSelectionChange', beforeSelectionChange);
322
327
  maybeInitVimState(cm);
323
328
  } else if (cm.state.vim) {
@@ -579,6 +584,7 @@
579
584
  !cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) {
580
585
  vim.visualMode = true;
581
586
  vim.visualLine = false;
587
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});
582
588
  cm.on('mousedown', exitVisualMode);
583
589
  }
584
590
  if (key != '0' || (key == '0' && vim.inputState.getRepeat() === 0)) {
@@ -1651,8 +1657,10 @@
1651
1657
  // Handle Replace-mode as a special case of insert mode.
1652
1658
  cm.toggleOverwrite(true);
1653
1659
  cm.setOption('keyMap', 'vim-replace');
1660
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "replace"});
1654
1661
  } else {
1655
1662
  cm.setOption('keyMap', 'vim-insert');
1663
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "insert"});
1656
1664
  }
1657
1665
  if (!vimGlobalState.macroModeState.inReplay) {
1658
1666
  // Only record if not replaying.
@@ -1694,6 +1702,7 @@
1694
1702
  } else {
1695
1703
  cm.setSelection(curStart, curEnd);
1696
1704
  }
1705
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : ""});
1697
1706
  } else {
1698
1707
  curStart = cm.getCursor('anchor');
1699
1708
  curEnd = cm.getCursor('head');
@@ -1706,10 +1715,12 @@
1706
1715
  curEnd.ch = cursorIsBefore(curStart, curEnd) ?
1707
1716
  lineLength(cm, curEnd.line) : 0;
1708
1717
  cm.setSelection(curStart, curEnd);
1718
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: "linewise"});
1709
1719
  } else if (vim.visualLine && !actionArgs.linewise) {
1710
1720
  // v pressed in linewise visual mode. Switch to characterwise visual
1711
1721
  // mode instead of exiting visual mode.
1712
1722
  vim.visualLine = false;
1723
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});
1713
1724
  } else {
1714
1725
  exitVisualMode(cm);
1715
1726
  }
@@ -2022,6 +2033,7 @@
2022
2033
  // it's not supposed to be.
2023
2034
  cm.setCursor(clipCursorToContent(cm, selectionEnd));
2024
2035
  }
2036
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
2025
2037
  }
2026
2038
 
2027
2039
  // Remove any trailing newlines from the selection. For
@@ -3444,6 +3456,7 @@
3444
3456
  vim.insertMode = false;
3445
3457
  cm.setOption('keyMap', 'vim');
3446
3458
  cm.toggleOverwrite(false); // exit replace mode if we were in it.
3459
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
3447
3460
  }
3448
3461
 
3449
3462
  CodeMirror.keyMap['vim-insert'] = {