codemirror-rails 3.20 → 3.21

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/lib/codemirror/rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/codemirror.js +240 -135
  4. data/vendor/assets/javascripts/codemirror/addons/comment/comment.js +6 -2
  5. data/vendor/assets/javascripts/codemirror/addons/comment/continuecomment.js +1 -1
  6. data/vendor/assets/javascripts/codemirror/addons/dialog/dialog.js +1 -0
  7. data/vendor/assets/javascripts/codemirror/addons/edit/closebrackets.js +5 -3
  8. data/vendor/assets/javascripts/codemirror/addons/edit/closetag.js +10 -6
  9. data/vendor/assets/javascripts/codemirror/addons/edit/continuelist.js +2 -0
  10. data/vendor/assets/javascripts/codemirror/addons/edit/matchbrackets.js +1 -1
  11. data/vendor/assets/javascripts/codemirror/addons/fold/comment-fold.js +3 -1
  12. data/vendor/assets/javascripts/codemirror/addons/fold/foldcode.js +13 -2
  13. data/vendor/assets/javascripts/codemirror/addons/fold/foldgutter.js +1 -1
  14. data/vendor/assets/javascripts/codemirror/addons/fold/indent-fold.js +2 -2
  15. data/vendor/assets/javascripts/codemirror/addons/fold/xml-fold.js +6 -0
  16. data/vendor/assets/javascripts/codemirror/addons/hint/anyword-hint.js +3 -5
  17. data/vendor/assets/javascripts/codemirror/addons/hint/css-hint.js +29 -33
  18. data/vendor/assets/javascripts/codemirror/addons/hint/javascript-hint.js +1 -1
  19. data/vendor/assets/javascripts/codemirror/addons/hint/pig-hint.js +1 -1
  20. data/vendor/assets/javascripts/codemirror/addons/hint/python-hint.js +1 -5
  21. data/vendor/assets/javascripts/codemirror/addons/hint/show-hint.js +58 -9
  22. data/vendor/assets/javascripts/codemirror/addons/hint/sql-hint.js +58 -17
  23. data/vendor/assets/javascripts/codemirror/addons/hint/xml-hint.js +5 -5
  24. data/vendor/assets/javascripts/codemirror/addons/lint/lint.js +1 -1
  25. data/vendor/assets/javascripts/codemirror/addons/merge/merge.js +6 -1
  26. data/vendor/assets/javascripts/codemirror/addons/mode/multiplex.js +5 -3
  27. data/vendor/assets/javascripts/codemirror/addons/runmode/runmode-standalone.js +26 -11
  28. data/vendor/assets/javascripts/codemirror/addons/runmode/runmode.js +1 -1
  29. data/vendor/assets/javascripts/codemirror/addons/runmode/runmode.node.js +22 -11
  30. data/vendor/assets/javascripts/codemirror/addons/search/search.js +22 -9
  31. data/vendor/assets/javascripts/codemirror/addons/search/searchcursor.js +48 -24
  32. data/vendor/assets/javascripts/codemirror/addons/selection/active-line.js +15 -9
  33. data/vendor/assets/javascripts/codemirror/addons/tern/tern.js +3 -3
  34. data/vendor/assets/javascripts/codemirror/addons/wrap/hardwrap.js +21 -9
  35. data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +12 -1
  36. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +110 -28
  37. data/vendor/assets/javascripts/codemirror/modes/clike.js +28 -9
  38. data/vendor/assets/javascripts/codemirror/modes/coffeescript.js +3 -4
  39. data/vendor/assets/javascripts/codemirror/modes/css.js +341 -297
  40. data/vendor/assets/javascripts/codemirror/modes/erlang.js +302 -179
  41. data/vendor/assets/javascripts/codemirror/modes/gfm.js +10 -5
  42. data/vendor/assets/javascripts/codemirror/modes/gherkin.js +45 -50
  43. data/vendor/assets/javascripts/codemirror/modes/haml.js +0 -4
  44. data/vendor/assets/javascripts/codemirror/modes/haskell.js +5 -3
  45. data/vendor/assets/javascripts/codemirror/modes/htmlembedded.js +0 -2
  46. data/vendor/assets/javascripts/codemirror/modes/htmlmixed.js +0 -2
  47. data/vendor/assets/javascripts/codemirror/modes/javascript.js +43 -30
  48. data/vendor/assets/javascripts/codemirror/modes/jinja2.js +13 -3
  49. data/vendor/assets/javascripts/codemirror/modes/less.js +7 -6
  50. data/vendor/assets/javascripts/codemirror/modes/markdown.js +231 -45
  51. data/vendor/assets/javascripts/codemirror/modes/{ocaml.js → mllike.js} +88 -13
  52. data/vendor/assets/javascripts/codemirror/modes/pegjs.js +5 -9
  53. data/vendor/assets/javascripts/codemirror/modes/php.js +6 -7
  54. data/vendor/assets/javascripts/codemirror/modes/python.js +6 -0
  55. data/vendor/assets/javascripts/codemirror/modes/r.js +5 -1
  56. data/vendor/assets/javascripts/codemirror/modes/rpm-spec.js +1 -1
  57. data/vendor/assets/javascripts/codemirror/modes/ruby.js +3 -1
  58. data/vendor/assets/javascripts/codemirror/modes/smalltalk.js +4 -2
  59. data/vendor/assets/javascripts/codemirror/modes/smartymixed.js +0 -2
  60. data/vendor/assets/javascripts/codemirror/modes/sql.js +5 -4
  61. data/vendor/assets/javascripts/codemirror/modes/xml.js +87 -100
  62. data/vendor/assets/stylesheets/codemirror/themes/mbo.css +1 -1
  63. data/vendor/assets/stylesheets/codemirror/themes/midnight.css +1 -1
  64. data/vendor/assets/stylesheets/codemirror/themes/pastel-on-dark.css +49 -0
  65. data/vendor/assets/stylesheets/codemirror/themes/the-matrix.css +1 -1
  66. metadata +3 -2
@@ -1,45 +1,24 @@
1
- // block; "begin", "case", "fun", "if", "receive", "try": closed by "end"
2
- // block internal; "after", "catch", "of"
3
- // guard; "when", closed by "->"
4
- // "->" opens a clause, closed by ";" or "."
5
- // "<<" opens a binary, closed by ">>"
6
- // "," appears in arglists, lists, tuples and terminates lines of code
7
- // "." resets indentation to 0
8
- // obsolete; "cond", "let", "query"
1
+ /*jshint unused:true, eqnull:true, curly:true, bitwise:true */
2
+ /*jshint undef:true, latedef:true, trailing:true */
3
+ /*global CodeMirror:true */
4
+
5
+ // erlang mode.
6
+ // tokenizer -> token types -> CodeMirror styles
7
+ // tokenizer maintains a parse stack
8
+ // indenter uses the parse stack
9
+
10
+ // TODO indenter:
11
+ // bit syntax
12
+ // old guard/bif/conversion clashes (e.g. "float/1")
13
+ // type/spec/opaque
9
14
 
10
15
  CodeMirror.defineMIME("text/x-erlang", "erlang");
11
16
 
12
17
  CodeMirror.defineMode("erlang", function(cmCfg) {
18
+ "use strict";
13
19
 
14
- function rval(state,_stream,type) {
15
- // distinguish between "." as terminator and record field operator
16
- state.in_record = (type == "record");
17
-
18
- // erlang -> CodeMirror tag
19
- switch (type) {
20
- case "atom": return "atom";
21
- case "attribute": return "attribute";
22
- case "boolean": return "special";
23
- case "builtin": return "builtin";
24
- case "comment": return "comment";
25
- case "fun": return "meta";
26
- case "function": return "tag";
27
- case "guard": return "property";
28
- case "keyword": return "keyword";
29
- case "macro": return "variable-2";
30
- case "number": return "number";
31
- case "operator": return "operator";
32
- case "record": return "bracket";
33
- case "string": return "string";
34
- case "type": return "def";
35
- case "variable": return "variable";
36
- case "error": return "error";
37
- case "separator": return null;
38
- case "open_paren": return null;
39
- case "close_paren": return null;
40
- default: return null;
41
- }
42
- }
20
+ /////////////////////////////////////////////////////////////////////////////
21
+ // constants
43
22
 
44
23
  var typeWords = [
45
24
  "-type", "-spec", "-export_type", "-opaque"];
@@ -48,23 +27,23 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
48
27
  "after","begin","catch","case","cond","end","fun","if",
49
28
  "let","of","query","receive","try","when"];
50
29
 
51
- var separatorRE = /[\->\.,:;]/;
30
+ var separatorRE = /[\->,;]/;
52
31
  var separatorWords = [
53
- "->",";",":",".",","];
32
+ "->",";",","];
54
33
 
55
- var operatorWords = [
34
+ var operatorAtomWords = [
56
35
  "and","andalso","band","bnot","bor","bsl","bsr","bxor",
57
36
  "div","not","or","orelse","rem","xor"];
58
37
 
59
- var symbolRE = /[\+\-\*\/<>=\|:!]/;
60
- var symbolWords = [
61
- "+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-","!"];
38
+ var operatorSymbolRE = /[\+\-\*\/<>=\|:!]/;
39
+ var operatorSymbolWords = [
40
+ "=","+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-","!"];
62
41
 
63
- var openParenRE = /[<\(\[\{]/;
42
+ var openParenRE = /[<\(\[\{]/;
64
43
  var openParenWords = [
65
44
  "<<","(","[","{"];
66
45
 
67
- var closeParenRE = /[>\)\]\}]/;
46
+ var closeParenRE = /[>\)\]\}]/;
68
47
  var closeParenWords = [
69
48
  "}","]",")",">>"];
70
49
 
@@ -99,23 +78,25 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
99
78
  "term_to_binary","time","throw","tl","trunc","tuple_size",
100
79
  "tuple_to_list","unlink","unregister","whereis"];
101
80
 
102
- // [Ø-Þ] [À-Ö]
103
- // [ß-ö] [ø-ÿ]
81
+ // upper case: [A-Z] [Ø-Þ] [À-Ö]
82
+ // lower case: [a-z] [ß-ö] [ø-ÿ]
104
83
  var anumRE = /[\w@Ø-ÞÀ-Öß-öø-ÿ]/;
105
84
  var escapesRE =
106
85
  /[0-7]{1,3}|[bdefnrstv\\"']|\^[a-zA-Z]|x[0-9a-zA-Z]{2}|x{[0-9a-zA-Z]+}/;
107
86
 
108
- function tokenize(stream, state) {
87
+ /////////////////////////////////////////////////////////////////////////////
88
+ // tokenizer
109
89
 
90
+ function tokenizer(stream,state) {
110
91
  // in multi-line string
111
92
  if (state.in_string) {
112
- state.in_string = (!doubleQuote(stream));
93
+ state.in_string = (!doubleQuote(stream));
113
94
  return rval(state,stream,"string");
114
95
  }
115
96
 
116
97
  // in multi-line atom
117
98
  if (state.in_atom) {
118
- state.in_atom = (!singleQuote(stream));
99
+ state.in_atom = (!singleQuote(stream));
119
100
  return rval(state,stream,"atom");
120
101
  }
121
102
 
@@ -125,9 +106,9 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
125
106
  }
126
107
 
127
108
  // attributes and type specs
128
- if ((peekToken(state).token == "") &&
109
+ if (!peekToken(state) &&
129
110
  stream.match(/-\s*[a-zß-öø-ÿ][\wØ-ÞÀ-Öß-öø-ÿ]*/)) {
130
- if (isMember(stream.current(),typeWords)) {
111
+ if (is_member(stream.current(),typeWords)) {
131
112
  return rval(state,stream,"type");
132
113
  }else{
133
114
  return rval(state,stream,"attribute");
@@ -142,32 +123,43 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
142
123
  return rval(state,stream,"comment");
143
124
  }
144
125
 
126
+ // colon
127
+ if (ch == ":") {
128
+ return rval(state,stream,"colon");
129
+ }
130
+
145
131
  // macro
146
132
  if (ch == '?') {
133
+ stream.eatSpace();
147
134
  stream.eatWhile(anumRE);
148
135
  return rval(state,stream,"macro");
149
136
  }
150
137
 
151
138
  // record
152
139
  if (ch == "#") {
140
+ stream.eatSpace();
153
141
  stream.eatWhile(anumRE);
154
142
  return rval(state,stream,"record");
155
143
  }
156
144
 
157
145
  // dollar escape
158
- if ( ch == "$" ) {
146
+ if (ch == "$") {
159
147
  if (stream.next() == "\\" && !stream.match(escapesRE)) {
160
148
  return rval(state,stream,"error");
161
149
  }
162
150
  return rval(state,stream,"number");
163
151
  }
164
152
 
153
+ // dot
154
+ if (ch == ".") {
155
+ return rval(state,stream,"dot");
156
+ }
157
+
165
158
  // quoted atom
166
159
  if (ch == '\'') {
167
160
  if (!(state.in_atom = (!singleQuote(stream)))) {
168
161
  if (stream.match(/\s*\/\s*[0-9]/,false)) {
169
162
  stream.match(/\s*\/\s*[0-9]/,true);
170
- popToken(state);
171
163
  return rval(state,stream,"fun"); // 'f'/0 style fun
172
164
  }
173
165
  if (stream.match(/\s*\(/,false) || stream.match(/\s*:/,false)) {
@@ -195,34 +187,37 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
195
187
 
196
188
  if (stream.match(/\s*\/\s*[0-9]/,false)) {
197
189
  stream.match(/\s*\/\s*[0-9]/,true);
198
- popToken(state);
199
190
  return rval(state,stream,"fun"); // f/0 style fun
200
191
  }
201
192
 
202
193
  var w = stream.current();
203
194
 
204
- if (isMember(w,keywordWords)) {
205
- pushToken(state,stream);
195
+ if (is_member(w,keywordWords)) {
206
196
  return rval(state,stream,"keyword");
197
+ }else if (is_member(w,operatorAtomWords)) {
198
+ return rval(state,stream,"operator");
207
199
  }else if (stream.match(/\s*\(/,false)) {
208
200
  // 'put' and 'erlang:put' are bifs, 'foo:put' is not
209
- if (isMember(w,bifWords) &&
210
- (!isPrev(stream,":") || isPrev(stream,"erlang:"))) {
201
+ if (is_member(w,bifWords) &&
202
+ ((peekToken(state).token != ":") ||
203
+ (peekToken(state,2).token == "erlang"))) {
211
204
  return rval(state,stream,"builtin");
212
- }else if (isMember(w,guardWords)) {
205
+ }else if (is_member(w,guardWords)) {
213
206
  return rval(state,stream,"guard");
214
207
  }else{
215
208
  return rval(state,stream,"function");
216
209
  }
217
- }else if (isMember(w,operatorWords)) {
210
+ }else if (is_member(w,operatorAtomWords)) {
218
211
  return rval(state,stream,"operator");
219
- }else if (stream.match(/\s*:/,false)) {
212
+ }else if (lookahead(stream) == ":") {
220
213
  if (w == "erlang") {
221
214
  return rval(state,stream,"builtin");
222
215
  } else {
223
216
  return rval(state,stream,"function");
224
217
  }
225
- }else if (isMember(w,["true","false"])) {
218
+ }else if (is_member(w,["true","false"])) {
219
+ return rval(state,stream,"boolean");
220
+ }else if (is_member(w,["true","false"])) {
226
221
  return rval(state,stream,"boolean");
227
222
  }else{
228
223
  return rval(state,stream,"atom");
@@ -234,15 +229,25 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
234
229
  var radixRE = /[0-9a-zA-Z]/; // 36#zZ style int
235
230
  if (digitRE.test(ch)) {
236
231
  stream.eatWhile(digitRE);
237
- if (stream.eat('#')) {
238
- stream.eatWhile(radixRE); // 36#aZ style integer
239
- } else {
240
- if (stream.eat('.')) { // float
241
- stream.eatWhile(digitRE);
232
+ if (stream.eat('#')) { // 36#aZ style integer
233
+ if (!stream.eatWhile(radixRE)) {
234
+ stream.backUp(1); //"36#" - syntax error
242
235
  }
243
- if (stream.eat(/[eE]/)) {
244
- stream.eat(/[-+]/); // float with exponent
245
- stream.eatWhile(digitRE);
236
+ } else if (stream.eat('.')) { // float
237
+ if (!stream.eatWhile(digitRE)) {
238
+ stream.backUp(1); // "3." - probably end of function
239
+ } else {
240
+ if (stream.eat(/[eE]/)) { // float with exponent
241
+ if (stream.eat(/[-+]/)) {
242
+ if (!stream.eatWhile(digitRE)) {
243
+ stream.backUp(2); // "2e-" - syntax error
244
+ }
245
+ } else {
246
+ if (!stream.eatWhile(digitRE)) {
247
+ stream.backUp(1); // "2e" - syntax error
248
+ }
249
+ }
250
+ }
246
251
  }
247
252
  }
248
253
  return rval(state,stream,"number"); // normal integer
@@ -250,50 +255,35 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
250
255
 
251
256
  // open parens
252
257
  if (nongreedy(stream,openParenRE,openParenWords)) {
253
- pushToken(state,stream);
254
258
  return rval(state,stream,"open_paren");
255
259
  }
256
260
 
257
261
  // close parens
258
262
  if (nongreedy(stream,closeParenRE,closeParenWords)) {
259
- pushToken(state,stream);
260
263
  return rval(state,stream,"close_paren");
261
264
  }
262
265
 
263
266
  // separators
264
267
  if (greedy(stream,separatorRE,separatorWords)) {
265
- // distinguish between "." as terminator and record field operator
266
- if (!state.in_record) {
267
- pushToken(state,stream);
268
- }
269
268
  return rval(state,stream,"separator");
270
269
  }
271
270
 
272
271
  // operators
273
- if (greedy(stream,symbolRE,symbolWords)) {
272
+ if (greedy(stream,operatorSymbolRE,operatorSymbolWords)) {
274
273
  return rval(state,stream,"operator");
275
274
  }
276
275
 
277
276
  return rval(state,stream,null);
278
277
  }
279
278
 
280
- function isPrev(stream,string) {
281
- var start = stream.start;
282
- var len = string.length;
283
- if (len <= start) {
284
- var word = stream.string.slice(start-len,start);
285
- return word == string;
286
- }else{
287
- return false;
288
- }
289
- }
290
-
279
+ /////////////////////////////////////////////////////////////////////////////
280
+ // utilities
291
281
  function nongreedy(stream,re,words) {
292
282
  if (stream.current().length == 1 && re.test(stream.current())) {
293
283
  stream.backUp(1);
294
284
  while (re.test(stream.peek())) {
295
285
  stream.next();
296
- if (isMember(stream.current(),words)) {
286
+ if (is_member(stream.current(),words)) {
297
287
  return true;
298
288
  }
299
289
  }
@@ -308,7 +298,7 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
308
298
  stream.next();
309
299
  }
310
300
  while (0 < stream.current().length) {
311
- if (isMember(stream.current(),words)) {
301
+ if (is_member(stream.current(),words)) {
312
302
  return true;
313
303
  }else{
314
304
  stream.backUp(1);
@@ -339,144 +329,277 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
339
329
  return false;
340
330
  }
341
331
 
342
- function isMember(element,list) {
332
+ function lookahead(stream) {
333
+ var m = stream.match(/([\n\s]+|%[^\n]*\n)*(.)/,false);
334
+ return m ? m.pop() : "";
335
+ }
336
+
337
+ function is_member(element,list) {
343
338
  return (-1 < list.indexOf(element));
344
339
  }
345
340
 
346
- /////////////////////////////////////////////////////////////////////////////
347
- function myIndent(state,textAfter) {
348
- var indent = cmCfg.indentUnit;
349
- var token = (peekToken(state)).token;
350
- var wordAfter = takewhile(textAfter,/[^a-z]/);
341
+ function rval(state,stream,type) {
351
342
 
352
- if (state.in_string || state.in_atom) {
353
- return CodeMirror.Pass;
354
- }else if (token == "") {
355
- return 0;
356
- }else if (isMember(token,openParenWords)) {
357
- return (peekToken(state)).column+token.length;
358
- }else if (token == "when") {
359
- return (peekToken(state)).column+token.length+1;
360
- }else if (token == "fun" && wordAfter == "") {
361
- return (peekToken(state)).column+token.length;
362
- }else if (token == "->") {
363
- if (isMember(wordAfter,["end","after","catch"])) {
364
- return peekToken(state,2).column;
365
- }else if (peekToken(state,2).token == "fun") {
366
- return peekToken(state,2).column+indent;
367
- }else if (peekToken(state,2).token == "") {
368
- return indent;
369
- }else{
370
- return (peekToken(state)).indent+indent;
371
- }
372
- }else if (isMember(wordAfter,["after","catch","of"])) {
373
- return (peekToken(state)).indent;
374
- }else{
375
- return (peekToken(state)).column+indent;
343
+ // parse stack
344
+ pushToken(state,realToken(type,stream));
345
+
346
+ // map erlang token type to CodeMirror style class
347
+ // erlang -> CodeMirror tag
348
+ switch (type) {
349
+ case "atom": return "atom";
350
+ case "attribute": return "attribute";
351
+ case "boolean": return "special";
352
+ case "builtin": return "builtin";
353
+ case "close_paren": return null;
354
+ case "colon": return null;
355
+ case "comment": return "comment";
356
+ case "dot": return null;
357
+ case "error": return "error";
358
+ case "fun": return "meta";
359
+ case "function": return "tag";
360
+ case "guard": return "property";
361
+ case "keyword": return "keyword";
362
+ case "macro": return "variable-2";
363
+ case "number": return "number";
364
+ case "open_paren": return null;
365
+ case "operator": return "operator";
366
+ case "record": return "bracket";
367
+ case "separator": return null;
368
+ case "string": return "string";
369
+ case "type": return "def";
370
+ case "variable": return "variable";
371
+ default: return null;
376
372
  }
377
373
  }
378
374
 
379
- function takewhile(str,re) {
380
- var m = str.match(re);
381
- return m ? str.slice(0,m.index) : str;
375
+ function aToken(tok,col,ind,typ) {
376
+ return {token: tok,
377
+ column: col,
378
+ indent: ind,
379
+ type: typ};
382
380
  }
383
381
 
384
- function Token(stream) {
385
- this.token = stream ? stream.current() : "";
386
- this.column = stream ? stream.column() : 0;
387
- this.indent = stream ? stream.indentation() : 0;
382
+ function realToken(type,stream) {
383
+ return aToken(stream.current(),
384
+ stream.column(),
385
+ stream.indentation(),
386
+ type);
388
387
  }
389
388
 
390
- function popToken(state) {
391
- return state.tokenStack.pop();
389
+ function fakeToken(type) {
390
+ return aToken(type,0,0,type);
392
391
  }
393
392
 
394
393
  function peekToken(state,depth) {
395
394
  var len = state.tokenStack.length;
396
395
  var dep = (depth ? depth : 1);
396
+
397
397
  if (len < dep) {
398
- return new Token;
398
+ return false;
399
399
  }else{
400
400
  return state.tokenStack[len-dep];
401
401
  }
402
402
  }
403
403
 
404
- function pushToken(state,stream) {
405
- var token = stream.current();
406
- var prev_token = peekToken(state).token;
404
+ function pushToken(state,token) {
407
405
 
408
- if (token == ".") {
409
- state.tokenStack = [];
410
- return false;
411
- }else if(isMember(token,[",", ":", "of", "cond", "let", "query"])) {
412
- return false;
413
- }else if (drop_last(prev_token,token)) {
414
- return false;
415
- }else if (drop_both(prev_token,token)) {
416
- popToken(state);
417
- return false;
418
- }else if (drop_first(prev_token,token)) {
419
- popToken(state);
420
- return pushToken(state,stream);
421
- }else if (isMember(token,["after","catch"])) {
422
- return false;
406
+ if (!(token.type == "comment" || token.type == "whitespace")) {
407
+ state.tokenStack = maybe_drop_pre(state.tokenStack,token);
408
+ state.tokenStack = maybe_drop_post(state.tokenStack);
409
+ }
410
+ }
411
+
412
+ function maybe_drop_pre(s,token) {
413
+ var last = s.length-1;
414
+
415
+ if (0 < last && s[last].type === "record" && token.type === "dot") {
416
+ s.pop();
417
+ }else if (0 < last && s[last].type === "group") {
418
+ s.pop();
419
+ s.push(token);
423
420
  }else{
424
- state.tokenStack.push(new Token(stream));
425
- return true;
421
+ s.push(token);
426
422
  }
423
+ return s;
427
424
  }
428
425
 
429
- function drop_last(open, close) {
430
- switch(open+" "+close) {
431
- case "when ;": return true;
432
- default: return false;
426
+ function maybe_drop_post(s) {
427
+ var last = s.length-1;
428
+
429
+ if (s[last].type === "dot") {
430
+ return [];
431
+ }
432
+ if (s[last].type === "fun" && s[last-1].token === "fun") {
433
+ return s.slice(0,last-1);
434
+ }
435
+ switch (s[s.length-1].token) {
436
+ case "}": return d(s,{g:["{"]});
437
+ case "]": return d(s,{i:["["]});
438
+ case ")": return d(s,{i:["("]});
439
+ case ">>": return d(s,{i:["<<"]});
440
+ case "end": return d(s,{i:["begin","case","fun","if","receive","try"]});
441
+ case ",": return d(s,{e:["begin","try","when","->",
442
+ ",","(","[","{","<<"]});
443
+ case "->": return d(s,{r:["when"],
444
+ m:["try","if","case","receive"]});
445
+ case ";": return d(s,{E:["case","fun","if","receive","try","when"]});
446
+ case "catch":return d(s,{e:["try"]});
447
+ case "of": return d(s,{e:["case"]});
448
+ case "after":return d(s,{e:["receive","try"]});
449
+ default: return s;
433
450
  }
434
451
  }
435
452
 
436
- function drop_first(open, close) {
437
- switch (open+" "+close) {
438
- case "when ->": return true;
439
- case "-> end": return true;
440
- default: return false;
453
+ function d(stack,tt) {
454
+ // stack is a stack of Token objects.
455
+ // tt is an object; {type:tokens}
456
+ // type is a char, tokens is a list of token strings.
457
+ // The function returns (possibly truncated) stack.
458
+ // It will descend the stack, looking for a Token such that Token.token
459
+ // is a member of tokens. If it does not find that, it will normally (but
460
+ // see "E" below) return stack. If it does find a match, it will remove
461
+ // all the Tokens between the top and the matched Token.
462
+ // If type is "m", that is all it does.
463
+ // If type is "i", it will also remove the matched Token and the top Token.
464
+ // If type is "g", like "i", but add a fake "group" token at the top.
465
+ // If type is "r", it will remove the matched Token, but not the top Token.
466
+ // If type is "e", it will keep the matched Token but not the top Token.
467
+ // If type is "E", it behaves as for type "e", except if there is no match,
468
+ // in which case it will return an empty stack.
469
+
470
+ for (var type in tt) {
471
+ var len = stack.length-1;
472
+ var tokens = tt[type];
473
+ for (var i = len-1; -1 < i ; i--) {
474
+ if (is_member(stack[i].token,tokens)) {
475
+ var ss = stack.slice(0,i);
476
+ switch (type) {
477
+ case "m": return ss.concat(stack[i]).concat(stack[len]);
478
+ case "r": return ss.concat(stack[len]);
479
+ case "i": return ss;
480
+ case "g": return ss.concat(fakeToken("group"));
481
+ case "E": return ss.concat(stack[i]);
482
+ case "e": return ss.concat(stack[i]);
483
+ }
484
+ }
485
+ }
441
486
  }
487
+ return (type == "E" ? [] : stack);
442
488
  }
443
489
 
444
- function drop_both(open, close) {
445
- switch (open+" "+close) {
446
- case "( )": return true;
447
- case "[ ]": return true;
448
- case "{ }": return true;
449
- case "<< >>": return true;
450
- case "begin end": return true;
451
- case "case end": return true;
452
- case "fun end": return true;
453
- case "if end": return true;
454
- case "receive end": return true;
455
- case "try end": return true;
456
- case "-> catch": return true;
457
- case "-> after": return true;
458
- case "-> ;": return true;
459
- default: return false;
490
+ /////////////////////////////////////////////////////////////////////////////
491
+ // indenter
492
+
493
+ function indenter(state,textAfter) {
494
+ var t;
495
+ var unit = cmCfg.indentUnit;
496
+ var wordAfter = wordafter(textAfter);
497
+ var currT = peekToken(state,1);
498
+ var prevT = peekToken(state,2);
499
+
500
+ if (state.in_string || state.in_atom) {
501
+ return CodeMirror.Pass;
502
+ }else if (!prevT) {
503
+ return 0;
504
+ }else if (currT.token == "when") {
505
+ return currT.column+unit;
506
+ }else if (wordAfter === "when" && prevT.type === "function") {
507
+ return prevT.indent+unit;
508
+ }else if (wordAfter === "(" && currT.token === "fun") {
509
+ return currT.column+3;
510
+ }else if (wordAfter === "catch" && (t = getToken(state,["try"]))) {
511
+ return t.column;
512
+ }else if (is_member(wordAfter,["end","after","of"])) {
513
+ t = getToken(state,["begin","case","fun","if","receive","try"]);
514
+ return t ? t.column : CodeMirror.Pass;
515
+ }else if (is_member(wordAfter,closeParenWords)) {
516
+ t = getToken(state,openParenWords);
517
+ return t ? t.column : CodeMirror.Pass;
518
+ }else if (is_member(currT.token,[",","|","||"]) ||
519
+ is_member(wordAfter,[",","|","||"])) {
520
+ t = postcommaToken(state);
521
+ return t ? t.column+t.token.length : unit;
522
+ }else if (currT.token == "->") {
523
+ if (is_member(prevT.token, ["receive","case","if","try"])) {
524
+ return prevT.column+unit+unit;
525
+ }else{
526
+ return prevT.column+unit;
527
+ }
528
+ }else if (is_member(currT.token,openParenWords)) {
529
+ return currT.column+currT.token.length;
530
+ }else{
531
+ t = defaultToken(state);
532
+ return truthy(t) ? t.column+unit : 0;
460
533
  }
461
534
  }
462
535
 
536
+ function wordafter(str) {
537
+ var m = str.match(/,|[a-z]+|\}|\]|\)|>>|\|+|\(/);
538
+
539
+ return truthy(m) && (m.index === 0) ? m[0] : "";
540
+ }
541
+
542
+ function postcommaToken(state) {
543
+ var objs = state.tokenStack.slice(0,-1);
544
+ var i = getTokenIndex(objs,"type",["open_paren"]);
545
+
546
+ return truthy(objs[i]) ? objs[i] : false;
547
+ }
548
+
549
+ function defaultToken(state) {
550
+ var objs = state.tokenStack;
551
+ var stop = getTokenIndex(objs,"type",["open_paren","separator","keyword"]);
552
+ var oper = getTokenIndex(objs,"type",["operator"]);
553
+
554
+ if (truthy(stop) && truthy(oper) && stop < oper) {
555
+ return objs[stop+1];
556
+ } else if (truthy(stop)) {
557
+ return objs[stop];
558
+ } else {
559
+ return false;
560
+ }
561
+ }
562
+
563
+ function getToken(state,tokens) {
564
+ var objs = state.tokenStack;
565
+ var i = getTokenIndex(objs,"token",tokens);
566
+
567
+ return truthy(objs[i]) ? objs[i] : false;
568
+ }
569
+
570
+ function getTokenIndex(objs,propname,propvals) {
571
+
572
+ for (var i = objs.length-1; -1 < i ; i--) {
573
+ if (is_member(objs[i][propname],propvals)) {
574
+ return i;
575
+ }
576
+ }
577
+ return false;
578
+ }
579
+
580
+ function truthy(x) {
581
+ return (x !== false) && (x != null);
582
+ }
583
+
584
+ /////////////////////////////////////////////////////////////////////////////
585
+ // this object defines the mode
586
+
463
587
  return {
464
588
  startState:
465
589
  function() {
466
590
  return {tokenStack: [],
467
- in_record: false,
468
591
  in_string: false,
469
592
  in_atom: false};
470
593
  },
471
594
 
472
595
  token:
473
596
  function(stream, state) {
474
- return tokenize(stream, state);
597
+ return tokenizer(stream, state);
475
598
  },
476
599
 
477
600
  indent:
478
601
  function(state, textAfter) {
479
- return myIndent(state,textAfter);
602
+ return indenter(state,textAfter);
480
603
  },
481
604
 
482
605
  lineComment: "%"