codemirror-rails 3.19 → 3.20

Sign up to get free protection for your applications and to get access to all the features.
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")