codemirror-rails 3.19 → 3.20

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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/lib/codemirror/rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/codemirror.js +61 -27
  4. data/vendor/assets/javascripts/codemirror/addons/dialog/dialog.js +42 -1
  5. data/vendor/assets/javascripts/codemirror/addons/display/fullscreen.js +2 -1
  6. data/vendor/assets/javascripts/codemirror/addons/display/placeholder.js +0 -6
  7. data/vendor/assets/javascripts/codemirror/addons/edit/closetag.js +13 -11
  8. data/vendor/assets/javascripts/codemirror/addons/edit/matchbrackets.js +2 -1
  9. data/vendor/assets/javascripts/codemirror/addons/fold/foldgutter.js +4 -4
  10. data/vendor/assets/javascripts/codemirror/addons/fold/indent-fold.js +25 -21
  11. data/vendor/assets/javascripts/codemirror/addons/hint/javascript-hint.js +1 -0
  12. data/vendor/assets/javascripts/codemirror/addons/hint/show-hint.js +20 -8
  13. data/vendor/assets/javascripts/codemirror/addons/tern/tern.js +7 -7
  14. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +3 -4
  15. data/vendor/assets/javascripts/codemirror/modes/coffeescript.js +13 -8
  16. data/vendor/assets/javascripts/codemirror/modes/css.js +3 -3
  17. data/vendor/assets/javascripts/codemirror/modes/go.js +1 -1
  18. data/vendor/assets/javascripts/codemirror/modes/haskell.js +1 -1
  19. data/vendor/assets/javascripts/codemirror/modes/htmlmixed.js +1 -1
  20. data/vendor/assets/javascripts/codemirror/modes/javascript.js +216 -79
  21. data/vendor/assets/javascripts/codemirror/modes/julia.js +262 -0
  22. data/vendor/assets/javascripts/codemirror/modes/less.js +4 -2
  23. data/vendor/assets/javascripts/codemirror/modes/markdown.js +16 -7
  24. data/vendor/assets/javascripts/codemirror/modes/pegjs.js +103 -0
  25. data/vendor/assets/javascripts/codemirror/modes/pig.js +1 -1
  26. data/vendor/assets/stylesheets/codemirror/themes/ambiance.css +1 -1
  27. data/vendor/assets/stylesheets/codemirror/themes/mbo.css +3 -1
  28. metadata +3 -1
@@ -1,26 +1,30 @@
1
1
  CodeMirror.registerHelper("fold", "indent", function(cm, start) {
2
- var lastLine = cm.lastLine(),
3
- tabSize = cm.getOption("tabSize"),
4
- firstLine = cm.getLine(start.line),
5
- myIndent = CodeMirror.countColumn(firstLine, null, tabSize);
6
-
7
- function foldEnded(curColumn, prevColumn) {
8
- return curColumn < myIndent ||
9
- (curColumn == myIndent && prevColumn >= myIndent) ||
10
- (curColumn > myIndent && i == lastLine);
11
- }
12
-
13
- for (var i = start.line + 1; i <= lastLine; i++) {
14
- var curColumn = CodeMirror.countColumn(cm.getLine(i), null, tabSize);
15
- var prevColumn = CodeMirror.countColumn(cm.getLine(i-1), null, tabSize);
16
-
17
- if (foldEnded(curColumn, prevColumn)) {
18
- var lastFoldLineNumber = curColumn > myIndent && i == lastLine ? i : i-1;
19
- var lastFoldLine = cm.getLine(lastFoldLineNumber);
20
- return {from: CodeMirror.Pos(start.line, firstLine.length),
21
- to: CodeMirror.Pos(lastFoldLineNumber, lastFoldLine.length)};
2
+ var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line);
3
+ if (!/\S/.test(firstLine)) return;
4
+ var getIndent = function(lineNum) {
5
+ return CodeMirror.countColumn(lineNum, null, tabSize);
6
+ };
7
+ var myIndent = getIndent(firstLine);
8
+ var lastLineInFold = null;
9
+ // Go through lines until we find a line that definitely doesn't belong in
10
+ // the block we're folding, or to the end.
11
+ for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) {
12
+ var curLine = cm.getLine(i);
13
+ var curIndent = getIndent(curLine);
14
+ if (curIndent > myIndent) {
15
+ // Lines with a greater indent are considered part of the block.
16
+ lastLineInFold = i;
17
+ } else if (!/\S/.test(curLine)) {
18
+ // Empty lines might be breaks within the block we're trying to fold.
19
+ } else {
20
+ // A non-empty line at an indent equal to or less than ours marks the
21
+ // start of another block.
22
+ break;
22
23
  }
23
24
  }
25
+ if (lastLineInFold) return {
26
+ from: CodeMirror.Pos(start.line, firstLine.length),
27
+ to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length)
28
+ };
24
29
  });
25
-
26
30
  CodeMirror.indentRangeFinder = CodeMirror.fold.indent; // deprecated
@@ -21,6 +21,7 @@
21
21
  function scriptHint(editor, keywords, getToken, options) {
22
22
  // Find the token at the cursor
23
23
  var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
24
+ if (/\b(?:string|comment)\b/.test(token.type)) return;
24
25
  token.state = CodeMirror.innerMode(editor.getMode(), token.state).state;
25
26
 
26
27
  // If it's not a 'word-style' token, ignore the token.
@@ -1,6 +1,9 @@
1
1
  (function() {
2
2
  "use strict";
3
3
 
4
+ var HINT_ELEMENT_CLASS = "CodeMirror-hint";
5
+ var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
6
+
4
7
  CodeMirror.showHint = function(cm, getHints, options) {
5
8
  // We want a single cursor position.
6
9
  if (cm.somethingSelected()) return;
@@ -140,6 +143,13 @@
140
143
  return ourMap;
141
144
  }
142
145
 
146
+ function getHintElement(stopAt, el) {
147
+ while (el && el != stopAt) {
148
+ if (el.nodeName.toUpperCase() === "LI") return el;
149
+ el = el.parentNode;
150
+ }
151
+ }
152
+
143
153
  function Widget(completion, data) {
144
154
  this.completion = completion;
145
155
  this.data = data;
@@ -147,12 +157,12 @@
147
157
 
148
158
  var hints = this.hints = document.createElement("ul");
149
159
  hints.className = "CodeMirror-hints";
150
- this.selectedHint = 0;
160
+ this.selectedHint = options.getDefaultSelection ? options.getDefaultSelection(cm,options,data) : 0;
151
161
 
152
162
  var completions = data.list;
153
163
  for (var i = 0; i < completions.length; ++i) {
154
164
  var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
155
- var className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active");
165
+ var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
156
166
  if (cur.className != null) className = cur.className + " " + className;
157
167
  elt.className = className;
158
168
  if (cur.render) cur.render(elt, data, cur);
@@ -216,13 +226,15 @@
216
226
  });
217
227
 
218
228
  CodeMirror.on(hints, "dblclick", function(e) {
219
- var t = e.target || e.srcElement;
220
- if (t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
229
+ var t = getHintElement(hints, e.target || e.srcElement);
230
+ if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
221
231
  });
232
+
222
233
  CodeMirror.on(hints, "click", function(e) {
223
- var t = e.target || e.srcElement;
224
- if (t.hintId != null) widget.changeActive(t.hintId);
234
+ var t = getHintElement(hints, e.target || e.srcElement);
235
+ if (t && t.hintId != null) widget.changeActive(t.hintId);
225
236
  });
237
+
226
238
  CodeMirror.on(hints, "mousedown", function() {
227
239
  setTimeout(function(){cm.focus();}, 20);
228
240
  });
@@ -257,9 +269,9 @@
257
269
  i = avoidWrap ? 0 : this.data.list.length - 1;
258
270
  if (this.selectedHint == i) return;
259
271
  var node = this.hints.childNodes[this.selectedHint];
260
- node.className = node.className.replace(" CodeMirror-hint-active", "");
272
+ node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
261
273
  node = this.hints.childNodes[this.selectedHint = i];
262
- node.className += " CodeMirror-hint-active";
274
+ node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
263
275
  if (node.offsetTop < this.hints.scrollTop)
264
276
  this.hints.scrollTop = node.offsetTop - 3;
265
277
  else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
@@ -96,7 +96,7 @@
96
96
 
97
97
  getHint: function(cm, c) { return hint(this, cm, c); },
98
98
 
99
- showType: function(cm) { showType(this, cm); },
99
+ showType: function(cm, pos) { showType(this, cm, pos); },
100
100
 
101
101
  updateArgHints: function(cm) { updateArgHints(this, cm); },
102
102
 
@@ -106,10 +106,10 @@
106
106
 
107
107
  rename: function(cm) { rename(this, cm); },
108
108
 
109
- request: function (cm, query, c) {
109
+ request: function (cm, query, c, pos) {
110
110
  var self = this;
111
111
  var doc = findDoc(this, cm.getDoc());
112
- var request = buildRequest(this, doc, query);
112
+ var request = buildRequest(this, doc, query, pos);
113
113
 
114
114
  this.server.request(request, function (error, data) {
115
115
  if (!error && self.options.responseFilter)
@@ -221,7 +221,7 @@
221
221
 
222
222
  // Type queries
223
223
 
224
- function showType(ts, cm) {
224
+ function showType(ts, cm, pos) {
225
225
  ts.request(cm, "type", function(error, data) {
226
226
  if (error) return showError(ts, cm, error);
227
227
  if (ts.options.typeTip) {
@@ -236,7 +236,7 @@
236
236
  }
237
237
  }
238
238
  tempTooltip(cm, tip);
239
- });
239
+ }, pos);
240
240
  }
241
241
 
242
242
  // Maintaining argument hints
@@ -450,13 +450,13 @@
450
450
 
451
451
  // Generic request-building helper
452
452
 
453
- function buildRequest(ts, doc, query) {
453
+ function buildRequest(ts, doc, query, pos) {
454
454
  var files = [], offsetLines = 0, allowFragments = !query.fullDocs;
455
455
  if (!allowFragments) delete query.fullDocs;
456
456
  if (typeof query == "string") query = {type: query};
457
457
  query.lineCharPositions = true;
458
458
  if (query.end == null) {
459
- query.end = doc.doc.getCursor("end");
459
+ query.end = pos || doc.doc.getCursor("end");
460
460
  if (doc.doc.somethingSelected())
461
461
  query.start = doc.doc.getCursor("start");
462
462
  }
@@ -2726,10 +2726,9 @@
2726
2726
  return regexp;
2727
2727
  }
2728
2728
  function showConfirm(cm, text) {
2729
- if (cm.openConfirm) {
2730
- cm.openConfirm('<span style="color: red">' + text +
2731
- '</span> <button type="button">OK</button>', function() {},
2732
- {bottom: true});
2729
+ if (cm.openNotification) {
2730
+ cm.openNotification('<span style="color: red">' + text + '</span>',
2731
+ {bottom: true, duration: 5000});
2733
2732
  } else {
2734
2733
  alert(text);
2735
2734
  }
@@ -213,6 +213,8 @@ CodeMirror.defineMode("coffeescript", function(conf) {
213
213
  if (type !== "coffee") {
214
214
  align = null;
215
215
  alignOffset = stream.column() + stream.current().length;
216
+ } else if (state.scope.align) {
217
+ state.scope.align = false;
216
218
  }
217
219
  state.scope = {
218
220
  offset: offset,
@@ -268,7 +270,6 @@ CodeMirror.defineMode("coffeescript", function(conf) {
268
270
  }
269
271
  if (((current === "->" || current === "=>") &&
270
272
  !state.lambda &&
271
- state.scope.type == "coffee" &&
272
273
  !stream.peek())
273
274
  || style === "indent") {
274
275
  indent(stream, state);
@@ -292,9 +293,10 @@ CodeMirror.defineMode("coffeescript", function(conf) {
292
293
  }
293
294
  delimiter_index = "])}".indexOf(current);
294
295
  if (delimiter_index !== -1) {
295
- if (dedent(stream, state)) {
296
- return ERRORCLASS;
297
- }
296
+ while (state.scope.type == "coffee" && state.scope.prev)
297
+ state.scope = state.scope.prev;
298
+ if (state.scope.type == current)
299
+ state.scope = state.scope.prev;
298
300
  }
299
301
  if (state.dedent > 0 && stream.eol() && state.scope.type == "coffee") {
300
302
  if (state.scope.prev) state.scope = state.scope.prev;
@@ -333,11 +335,14 @@ CodeMirror.defineMode("coffeescript", function(conf) {
333
335
 
334
336
  indent: function(state, text) {
335
337
  if (state.tokenize != tokenBase) return 0;
336
- var closes = state.scope.type === (text && text.charAt(0));
337
- if (state.scope.align)
338
- return state.scope.alignOffset - (closes ? 1 : 0);
338
+ var scope = state.scope;
339
+ var closer = text && "])}".indexOf(text.charAt(0)) > -1;
340
+ if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
341
+ var closes = closer && scope.type === text.charAt(0);
342
+ if (scope.align)
343
+ return scope.alignOffset - (closes ? 1 : 0);
339
344
  else
340
- return (closes ? state.scope.prev : state.scope).offset;
345
+ return (closes ? scope.prev : scope).offset;
341
346
  },
342
347
 
343
348
  lineComment: "#",
@@ -262,7 +262,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
262
262
  // Pop off end of array until { is reached
263
263
  while(state.stack.length){
264
264
  var removed = state.stack.pop();
265
- if(removed.indexOf("{") > -1){
265
+ if(removed.indexOf("{") > -1 || removed == "block" || removed == "rule"){
266
266
  break;
267
267
  }
268
268
  }
@@ -609,8 +609,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
609
609
  }
610
610
  return ["variable", "variable"];
611
611
  },
612
- ",": function(_stream, state) {
613
- if (state.stack[state.stack.length - 1] == "propertyValue") {
612
+ ",": function(stream, state) {
613
+ if (state.stack[state.stack.length - 1] == "propertyValue" && stream.match(/^ *\$/, false)) {
614
614
  return ["operator", ";"];
615
615
  }
616
616
  },
@@ -158,7 +158,7 @@ CodeMirror.defineMode("go", function(config) {
158
158
  else return ctx.indented + (closing ? 0 : indentUnit);
159
159
  },
160
160
 
161
- electricChars: "{}:",
161
+ electricChars: "{}):",
162
162
  blockCommentStart: "/*",
163
163
  blockCommentEnd: "*/",
164
164
  lineComment: "//"
@@ -237,7 +237,7 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) {
237
237
  token: function(stream, state) {
238
238
  var t = state.f(stream, function(s) { state.f = s; });
239
239
  var w = stream.current();
240
- return (w in wellKnownWords) ? wellKnownWords[w] : t;
240
+ return wellKnownWords.hasOwnProperty(w) ? wellKnownWords[w] : t;
241
241
  },
242
242
 
243
243
  blockCommentStart: "{-",
@@ -44,7 +44,7 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
44
44
  if (close > -1) stream.backUp(cur.length - close);
45
45
  else if (m = cur.match(/<\/?$/)) {
46
46
  stream.backUp(cur.length);
47
- if (!stream.match(pat, false)) stream.match(cur[0]);
47
+ if (!stream.match(pat, false)) stream.match(cur);
48
48
  }
49
49
  return style;
50
50
  }
@@ -21,7 +21,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
21
21
  "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
22
22
  "in": operator, "typeof": operator, "instanceof": operator,
23
23
  "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
24
- "this": kw("this")
24
+ "this": kw("this"), "module": kw("module"), "class": kw("class"), "super": kw("atom"),
25
+ "yield": C, "export": kw("export"), "import": kw("import"), "extends": C
25
26
  };
26
27
 
27
28
  // Extend the 'normal' keywords with the TypeScript language extensions
@@ -30,7 +31,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
30
31
  var tsKeywords = {
31
32
  // object-like things
32
33
  "interface": kw("interface"),
33
- "class": kw("class"),
34
34
  "extends": kw("extends"),
35
35
  "constructor": kw("constructor"),
36
36
 
@@ -40,8 +40,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
40
40
  "protected": kw("protected"),
41
41
  "static": kw("static"),
42
42
 
43
- "super": kw("super"),
44
-
45
43
  // types
46
44
  "string": type, "number": type, "bool": type, "any": type
47
45
  };
@@ -56,11 +54,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
56
54
 
57
55
  var isOperatorChar = /[+\-*&%=<>!?|~^]/;
58
56
 
59
- function chain(stream, state, f) {
60
- state.tokenize = f;
61
- return f(stream, state);
62
- }
63
-
64
57
  function nextUntilUnescaped(stream, end) {
65
58
  var escaped = false, next;
66
59
  while ((next = stream.next()) != null) {
@@ -78,50 +71,51 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
78
71
  type = tp; content = cont;
79
72
  return style;
80
73
  }
81
- function jsTokenBase(stream, state) {
74
+ function tokenBase(stream, state) {
82
75
  var ch = stream.next();
83
- if (ch == '"' || ch == "'")
84
- return chain(stream, state, jsTokenString(ch));
85
- else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/))
76
+ if (ch == '"' || ch == "'") {
77
+ state.tokenize = tokenString(ch);
78
+ return state.tokenize(stream, state);
79
+ } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
86
80
  return ret("number", "number");
87
- else if (/[\[\]{}\(\),;\:\.]/.test(ch))
81
+ } else if (ch == "." && stream.match("..")) {
82
+ return ret("spread", "meta");
83
+ } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
88
84
  return ret(ch);
89
- else if (ch == "0" && stream.eat(/x/i)) {
85
+ } else if (ch == "=" && stream.eat(">")) {
86
+ return ret("=>");
87
+ } else if (ch == "0" && stream.eat(/x/i)) {
90
88
  stream.eatWhile(/[\da-f]/i);
91
89
  return ret("number", "number");
92
- }
93
- else if (/\d/.test(ch)) {
90
+ } else if (/\d/.test(ch)) {
94
91
  stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
95
92
  return ret("number", "number");
96
- }
97
- else if (ch == "/") {
93
+ } else if (ch == "/") {
98
94
  if (stream.eat("*")) {
99
- return chain(stream, state, jsTokenComment);
100
- }
101
- else if (stream.eat("/")) {
95
+ state.tokenize = tokenComment;
96
+ return tokenComment(stream, state);
97
+ } else if (stream.eat("/")) {
102
98
  stream.skipToEnd();
103
99
  return ret("comment", "comment");
104
- }
105
- else if (state.lastType == "operator" || state.lastType == "keyword c" ||
106
- /^[\[{}\(,;:]$/.test(state.lastType)) {
100
+ } else if (state.lastType == "operator" || state.lastType == "keyword c" ||
101
+ state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) {
107
102
  nextUntilUnescaped(stream, "/");
108
103
  stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
109
104
  return ret("regexp", "string-2");
110
- }
111
- else {
105
+ } else {
112
106
  stream.eatWhile(isOperatorChar);
113
107
  return ret("operator", null, stream.current());
114
108
  }
115
- }
116
- else if (ch == "#") {
109
+ } else if (ch == "`") {
110
+ state.tokenize = tokenQuasi;
111
+ return tokenQuasi(stream, state);
112
+ } else if (ch == "#") {
117
113
  stream.skipToEnd();
118
114
  return ret("error", "error");
119
- }
120
- else if (isOperatorChar.test(ch)) {
115
+ } else if (isOperatorChar.test(ch)) {
121
116
  stream.eatWhile(isOperatorChar);
122
117
  return ret("operator", null, stream.current());
123
- }
124
- else {
118
+ } else {
125
119
  stream.eatWhile(/[\w\$_]/);
126
120
  var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
127
121
  return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
@@ -129,19 +123,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
129
123
  }
130
124
  }
131
125
 
132
- function jsTokenString(quote) {
126
+ function tokenString(quote) {
133
127
  return function(stream, state) {
134
128
  if (!nextUntilUnescaped(stream, quote))
135
- state.tokenize = jsTokenBase;
129
+ state.tokenize = tokenBase;
136
130
  return ret("string", "string");
137
131
  };
138
132
  }
139
133
 
140
- function jsTokenComment(stream, state) {
134
+ function tokenComment(stream, state) {
141
135
  var maybeEnd = false, ch;
142
136
  while (ch = stream.next()) {
143
137
  if (ch == "/" && maybeEnd) {
144
- state.tokenize = jsTokenBase;
138
+ state.tokenize = tokenBase;
145
139
  break;
146
140
  }
147
141
  maybeEnd = (ch == "*");
@@ -149,6 +143,50 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
149
143
  return ret("comment", "comment");
150
144
  }
151
145
 
146
+ function tokenQuasi(stream, state) {
147
+ var escaped = false, next;
148
+ while ((next = stream.next()) != null) {
149
+ if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
150
+ state.tokenize = tokenBase;
151
+ break;
152
+ }
153
+ escaped = !escaped && next == "\\";
154
+ }
155
+ return ret("quasi", "string-2", stream.current());
156
+ }
157
+
158
+ var brackets = "([{}])";
159
+ // This is a crude lookahead trick to try and notice that we're
160
+ // parsing the argument patterns for a fat-arrow function before we
161
+ // actually hit the arrow token. It only works if the arrow is on
162
+ // the same line as the arguments and there's no strange noise
163
+ // (comments) in between. Fallback is to only notice when we hit the
164
+ // arrow, and not declare the arguments as locals for the arrow
165
+ // body.
166
+ function findFatArrow(stream, state) {
167
+ if (state.fatArrowAt) state.fatArrowAt = null;
168
+ var arrow = stream.string.indexOf("=>", stream.start);
169
+ if (arrow < 0) return;
170
+
171
+ var depth = 0, sawSomething = false;
172
+ for (var pos = arrow - 1; pos >= 0; --pos) {
173
+ var ch = stream.string.charAt(pos);
174
+ var bracket = brackets.indexOf(ch);
175
+ if (bracket >= 0 && bracket < 3) {
176
+ if (!depth) { ++pos; break; }
177
+ if (--depth == 0) break;
178
+ } else if (bracket >= 3 && bracket < 6) {
179
+ ++depth;
180
+ } else if (/[$\w]/.test(ch)) {
181
+ sawSomething = true;
182
+ } else if (sawSomething && !depth) {
183
+ ++pos;
184
+ break;
185
+ }
186
+ }
187
+ if (sawSomething && !depth) state.fatArrowAt = pos;
188
+ }
189
+
152
190
  // Parser
153
191
 
154
192
  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true};
@@ -165,6 +203,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
165
203
  function inScope(state, varname) {
166
204
  for (var v = state.localVars; v; v = v.next)
167
205
  if (v.name == varname) return true;
206
+ for (var cx = state.context; cx; cx = cx.prev) {
207
+ for (var v = cx.vars; v; v = v.next)
208
+ if (v.name == varname) return true;
209
+ }
168
210
  }
169
211
 
170
212
  function parseJS(state, style, type, content, stream) {
@@ -211,7 +253,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
211
253
  state.localVars = {name: varname, next: state.localVars};
212
254
  } else {
213
255
  if (inList(state.globalVars)) return;
214
- state.globalVars = {name: varname, next: state.globalVars};
256
+ if (parserConfig.globalVars)
257
+ state.globalVars = {name: varname, next: state.globalVars};
215
258
  }
216
259
  }
217
260
 
@@ -253,16 +296,15 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
253
296
  };
254
297
  }
255
298
 
256
- function statement(type) {
257
- if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
299
+ function statement(type, value) {
300
+ if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
258
301
  if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
259
302
  if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
260
303
  if (type == "{") return cont(pushlex("}"), block, poplex);
261
304
  if (type == ";") return cont();
262
305
  if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse);
263
306
  if (type == "function") return cont(functiondef);
264
- if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
265
- poplex, statement, poplex);
307
+ if (type == "for") return cont(pushlex("form"), forspec, poplex, statement, poplex);
266
308
  if (type == "variable") return cont(pushlex("stat"), maybelabel);
267
309
  if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
268
310
  block, poplex, poplex);
@@ -270,6 +312,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
270
312
  if (type == "default") return cont(expect(":"));
271
313
  if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
272
314
  statement, poplex, popcontext);
315
+ if (type == "module") return cont(pushlex("form"), pushcontext, afterModule, popcontext, poplex);
316
+ if (type == "class") return cont(pushlex("form"), className, objlit, poplex);
317
+ if (type == "export") return cont(pushlex("form"), afterExport, poplex);
318
+ if (type == "import") return cont(pushlex("form"), afterImport, poplex);
273
319
  return pass(pushlex("stat"), expression, expect(";"), poplex);
274
320
  }
275
321
  function expression(type) {
@@ -279,14 +325,20 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
279
325
  return expressionInner(type, true);
280
326
  }
281
327
  function expressionInner(type, noComma) {
328
+ if (cx.state.fatArrowAt == cx.stream.start) {
329
+ var body = noComma ? arrowBodyNoComma : arrowBody;
330
+ if (type == "(") return cont(pushcontext, commasep(pattern, ")"), expect("=>"), body, popcontext);
331
+ else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
332
+ }
333
+
282
334
  var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
283
335
  if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
284
336
  if (type == "function") return cont(functiondef);
285
337
  if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
286
- if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
287
- if (type == "operator") return cont(noComma ? expressionNoComma : expression);
288
- if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop);
289
- if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop);
338
+ if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
339
+ if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
340
+ if (type == "[") return cont(pushlex("]"), expressionNoComma, maybeArrayComprehension, poplex, maybeop);
341
+ if (type == "{") return cont(commasep(objprop, "}"), maybeop);
290
342
  return cont();
291
343
  }
292
344
  function maybeexpression(type) {
@@ -305,16 +357,40 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
305
357
  function maybeoperatorNoComma(type, value, noComma) {
306
358
  var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
307
359
  var expr = noComma == false ? expression : expressionNoComma;
360
+ if (value == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
308
361
  if (type == "operator") {
309
362
  if (/\+\+|--/.test(value)) return cont(me);
310
363
  if (value == "?") return cont(expression, expect(":"), expr);
311
364
  return cont(expr);
312
365
  }
366
+ if (type == "quasi") { cx.cc.push(me); return quasi(value); }
313
367
  if (type == ";") return;
314
- if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me);
368
+ if (type == "(") return cont(commasep(expressionNoComma, ")", "call"), me);
315
369
  if (type == ".") return cont(property, me);
316
370
  if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
317
371
  }
372
+ function quasi(value) {
373
+ if (!value) debugger;
374
+ if (value.slice(value.length - 2) != "${") return cont();
375
+ return cont(expression, continueQuasi);
376
+ }
377
+ function continueQuasi(type) {
378
+ if (type == "}") {
379
+ cx.marked = "string-2";
380
+ cx.state.tokenize = tokenQuasi;
381
+ return cont();
382
+ }
383
+ }
384
+ function arrowBody(type) {
385
+ findFatArrow(cx.stream, cx.state);
386
+ if (type == "{") return pass(statement);
387
+ return pass(expression);
388
+ }
389
+ function arrowBodyNoComma(type) {
390
+ findFatArrow(cx.stream, cx.state);
391
+ if (type == "{") return pass(statement);
392
+ return pass(expressionNoComma);
393
+ }
318
394
  function maybelabel(type) {
319
395
  if (type == ":") return cont(poplex, statement);
320
396
  return pass(maybeoperatorComma, expect(";"), poplex);
@@ -328,16 +404,21 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
328
404
  if (value == "get" || value == "set") return cont(getterSetter);
329
405
  } else if (type == "number" || type == "string") {
330
406
  cx.marked = type + " property";
407
+ } else if (type == "[") {
408
+ return cont(expression, expect("]"), afterprop);
331
409
  }
332
- if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expressionNoComma);
410
+ if (atomicTypes.hasOwnProperty(type)) return cont(afterprop);
333
411
  }
334
412
  function getterSetter(type) {
335
- if (type == ":") return cont(expression);
336
- if (type != "variable") return cont(expect(":"), expression);
413
+ if (type != "variable") return pass(afterprop);
337
414
  cx.marked = "property";
338
415
  return cont(functiondef);
339
416
  }
340
- function commasep(what, end) {
417
+ function afterprop(type) {
418
+ if (type == ":") return cont(expressionNoComma);
419
+ if (type == "(") return pass(functiondef);
420
+ }
421
+ function commasep(what, end, info) {
341
422
  function proceed(type) {
342
423
  if (type == ",") {
343
424
  var lex = cx.state.lexical;
@@ -349,7 +430,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
349
430
  }
350
431
  return function(type) {
351
432
  if (type == end) return cont();
352
- else return pass(what, proceed);
433
+ if (info === false) return pass(what, proceed);
434
+ return pass(pushlex(end, info), what, proceed, poplex);
353
435
  };
354
436
  }
355
437
  function block(type) {
@@ -357,67 +439,121 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
357
439
  return pass(statement, block);
358
440
  }
359
441
  function maybetype(type) {
360
- if (type == ":") return cont(typedef);
361
- return pass();
442
+ if (isTS && type == ":") return cont(typedef);
362
443
  }
363
444
  function typedef(type) {
364
445
  if (type == "variable"){cx.marked = "variable-3"; return cont();}
365
- return pass();
366
446
  }
367
- function vardef1(type, value) {
368
- if (type == "variable") {
447
+ function vardef() {
448
+ return pass(pattern, maybetype, maybeAssign, vardefCont);
449
+ }
450
+ function pattern(type, value) {
451
+ if (type == "variable") { register(value); return cont(); }
452
+ if (type == "[") return cont(commasep(pattern, "]"));
453
+ if (type == "{") return cont(commasep(proppattern, "}"));
454
+ }
455
+ function proppattern(type, value) {
456
+ if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
369
457
  register(value);
370
- return isTS ? cont(maybetype, vardef2) : cont(vardef2);
458
+ return cont(maybeAssign);
371
459
  }
372
- return pass();
460
+ if (type == "variable") cx.marked = "property";
461
+ return cont(expect(":"), pattern, maybeAssign);
462
+ }
463
+ function maybeAssign(_type, value) {
464
+ if (value == "=") return cont(expressionNoComma);
373
465
  }
374
- function vardef2(type, value) {
375
- if (value == "=") return cont(expressionNoComma, vardef2);
376
- if (type == ",") return cont(vardef1);
466
+ function vardefCont(type) {
467
+ if (type == ",") return cont(vardef);
377
468
  }
378
469
  function maybeelse(type, value) {
379
470
  if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex);
380
471
  }
472
+ function forspec(type) {
473
+ if (type == "(") return cont(pushlex(")"), forspec1, expect(")"));
474
+ }
381
475
  function forspec1(type) {
382
- if (type == "var") return cont(vardef1, expect(";"), forspec2);
476
+ if (type == "var") return cont(vardef, expect(";"), forspec2);
383
477
  if (type == ";") return cont(forspec2);
384
- if (type == "variable") return cont(formaybein);
478
+ if (type == "variable") return cont(formaybeinof);
385
479
  return pass(expression, expect(";"), forspec2);
386
480
  }
387
- function formaybein(_type, value) {
388
- if (value == "in") return cont(expression);
481
+ function formaybeinof(_type, value) {
482
+ if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
389
483
  return cont(maybeoperatorComma, forspec2);
390
484
  }
391
485
  function forspec2(type, value) {
392
486
  if (type == ";") return cont(forspec3);
393
- if (value == "in") return cont(expression);
487
+ if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
394
488
  return pass(expression, expect(";"), forspec3);
395
489
  }
396
490
  function forspec3(type) {
397
491
  if (type != ")") cont(expression);
398
492
  }
399
493
  function functiondef(type, value) {
494
+ if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
400
495
  if (type == "variable") {register(value); return cont(functiondef);}
401
- if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
496
+ if (type == "(") return cont(pushcontext, commasep(funarg, ")"), statement, popcontext);
497
+ }
498
+ function funarg(type) {
499
+ if (type == "spread") return cont(funarg);
500
+ return pass(pattern, maybetype);
501
+ }
502
+ function className(type, value) {
503
+ if (type == "variable") {register(value); return cont(classNameAfter);}
504
+ }
505
+ function classNameAfter(_type, value) {
506
+ if (value == "extends") return cont(expression);
507
+ }
508
+ function objlit(type) {
509
+ if (type == "{") return cont(commasep(objprop, "}"));
510
+ }
511
+ function afterModule(type, value) {
512
+ if (type == "string") return cont(statement);
513
+ if (type == "variable") { register(value); return cont(maybeFrom); }
514
+ }
515
+ function afterExport(_type, value) {
516
+ if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
517
+ if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
518
+ return pass(statement);
519
+ }
520
+ function afterImport(type) {
521
+ if (type == "string") return cont();
522
+ return pass(importSpec, maybeFrom);
523
+ }
524
+ function importSpec(type, value) {
525
+ if (type == "{") return cont(commasep(importSpec, "}"));
526
+ if (type == "variable") register(value);
527
+ return cont();
528
+ }
529
+ function maybeFrom(_type, value) {
530
+ if (value == "from") { cx.marked = "keyword"; return cont(expression); }
531
+ }
532
+ function maybeArrayComprehension(type) {
533
+ if (type == "for") return pass(comprehension);
534
+ if (type == ",") return cont(commasep(expressionNoComma, "]", false));
535
+ return pass(commasep(expressionNoComma, "]", false));
402
536
  }
403
- function funarg(type, value) {
404
- if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
537
+ function comprehension(type) {
538
+ if (type == "for") return cont(forspec, comprehension);
539
+ if (type == "if") return cont(expression, comprehension);
405
540
  }
406
541
 
407
542
  // Interface
408
543
 
409
544
  return {
410
545
  startState: function(basecolumn) {
411
- return {
412
- tokenize: jsTokenBase,
413
- lastType: null,
546
+ var state = {
547
+ tokenize: tokenBase,
548
+ lastType: "sof",
414
549
  cc: [],
415
550
  lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
416
551
  localVars: parserConfig.localVars,
417
- globalVars: parserConfig.globalVars,
418
552
  context: parserConfig.localVars && {vars: parserConfig.localVars},
419
553
  indented: 0
420
554
  };
555
+ if (parserConfig.globalVars) state.globalVars = parserConfig.globalVars;
556
+ return state;
421
557
  },
422
558
 
423
559
  token: function(stream, state) {
@@ -425,8 +561,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
425
561
  if (!state.lexical.hasOwnProperty("align"))
426
562
  state.lexical.align = false;
427
563
  state.indented = stream.indentation();
564
+ findFatArrow(stream, state);
428
565
  }
429
- if (state.tokenize != jsTokenComment && stream.eatSpace()) return null;
566
+ if (state.tokenize != tokenComment && stream.eatSpace()) return null;
430
567
  var style = state.tokenize(stream, state);
431
568
  if (type == "comment") return style;
432
569
  state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
@@ -434,21 +571,21 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
434
571
  },
435
572
 
436
573
  indent: function(state, textAfter) {
437
- if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
438
- if (state.tokenize != jsTokenBase) return 0;
574
+ if (state.tokenize == tokenComment) return CodeMirror.Pass;
575
+ if (state.tokenize != tokenBase) return 0;
439
576
  var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
440
577
  // Kludge to prevent 'maybelse' from blocking lexical scope pops
441
578
  for (var i = state.cc.length - 1; i >= 0; --i) {
442
579
  var c = state.cc[i];
443
580
  if (c == poplex) lexical = lexical.prev;
444
- else if (c != maybeelse || /^else\b/.test(textAfter)) break;
581
+ else if (c != maybeelse) break;
445
582
  }
446
583
  if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
447
584
  if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
448
585
  lexical = lexical.prev;
449
586
  var type = lexical.type, closing = firstChar == type;
450
587
 
451
- if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
588
+ if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
452
589
  else if (type == "form" && firstChar == "{") return lexical.indented;
453
590
  else if (type == "form") return lexical.indented + indentUnit;
454
591
  else if (type == "stat")