codemirror-rails 3.16 → 3.17

Sign up to get free protection for your applications and to get access to all the features.
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'] = {