1
|
|
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others |
2
|
|
|
// Distributed under an MIT license: http://codemirror.net/LICENSE |
3
|
|
|
|
4
|
|
|
/*jshint unused:true, eqnull:true, curly:true, bitwise:true */ |
5
|
|
|
/*jshint undef:true, latedef:true, trailing:true */ |
6
|
|
|
/*global CodeMirror:true */ |
7
|
|
|
|
8
|
|
|
// erlang mode. |
9
|
|
|
// tokenizer -> token types -> CodeMirror styles |
10
|
|
|
// tokenizer maintains a parse stack |
11
|
|
|
// indenter uses the parse stack |
12
|
|
|
|
13
|
|
|
// TODO indenter: |
14
|
|
|
// bit syntax |
15
|
|
|
// old guard/bif/conversion clashes (e.g. "float/1") |
16
|
|
|
// type/spec/opaque |
17
|
|
|
|
18
|
|
|
(function(mod) { |
19
|
|
|
if (typeof exports == "object" && typeof module == "object") // CommonJS |
20
|
|
|
mod(require("../../lib/codemirror")); |
21
|
|
|
else if (typeof define == "function" && define.amd) // AMD |
|
|
|
|
22
|
|
|
define(["../../lib/codemirror"], mod); |
23
|
|
|
else // Plain browser env |
24
|
|
|
mod(CodeMirror); |
|
|
|
|
25
|
|
|
})(function(CodeMirror) { |
26
|
|
|
"use strict"; |
27
|
|
|
|
28
|
|
|
CodeMirror.defineMIME("text/x-erlang", "erlang"); |
29
|
|
|
|
30
|
|
|
CodeMirror.defineMode("erlang", function(cmCfg) { |
31
|
|
|
"use strict"; |
32
|
|
|
|
33
|
|
|
///////////////////////////////////////////////////////////////////////////// |
34
|
|
|
// constants |
35
|
|
|
|
36
|
|
|
var typeWords = [ |
37
|
|
|
"-type", "-spec", "-export_type", "-opaque"]; |
38
|
|
|
|
39
|
|
|
var keywordWords = [ |
40
|
|
|
"after","begin","catch","case","cond","end","fun","if", |
41
|
|
|
"let","of","query","receive","try","when"]; |
42
|
|
|
|
43
|
|
|
var separatorRE = /[\->,;]/; |
44
|
|
|
var separatorWords = [ |
45
|
|
|
"->",";",","]; |
46
|
|
|
|
47
|
|
|
var operatorAtomWords = [ |
48
|
|
|
"and","andalso","band","bnot","bor","bsl","bsr","bxor", |
49
|
|
|
"div","not","or","orelse","rem","xor"]; |
50
|
|
|
|
51
|
|
|
var operatorSymbolRE = /[\+\-\*\/<>=\|:!]/; |
52
|
|
|
var operatorSymbolWords = [ |
53
|
|
|
"=","+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-","!"]; |
54
|
|
|
|
55
|
|
|
var openParenRE = /[<\(\[\{]/; |
56
|
|
|
var openParenWords = [ |
57
|
|
|
"<<","(","[","{"]; |
58
|
|
|
|
59
|
|
|
var closeParenRE = /[>\)\]\}]/; |
60
|
|
|
var closeParenWords = [ |
61
|
|
|
"}","]",")",">>"]; |
62
|
|
|
|
63
|
|
|
var guardWords = [ |
64
|
|
|
"is_atom","is_binary","is_bitstring","is_boolean","is_float", |
65
|
|
|
"is_function","is_integer","is_list","is_number","is_pid", |
66
|
|
|
"is_port","is_record","is_reference","is_tuple", |
67
|
|
|
"atom","binary","bitstring","boolean","function","integer","list", |
68
|
|
|
"number","pid","port","record","reference","tuple"]; |
69
|
|
|
|
70
|
|
|
var bifWords = [ |
71
|
|
|
"abs","adler32","adler32_combine","alive","apply","atom_to_binary", |
72
|
|
|
"atom_to_list","binary_to_atom","binary_to_existing_atom", |
73
|
|
|
"binary_to_list","binary_to_term","bit_size","bitstring_to_list", |
74
|
|
|
"byte_size","check_process_code","contact_binary","crc32", |
75
|
|
|
"crc32_combine","date","decode_packet","delete_module", |
76
|
|
|
"disconnect_node","element","erase","exit","float","float_to_list", |
77
|
|
|
"garbage_collect","get","get_keys","group_leader","halt","hd", |
78
|
|
|
"integer_to_list","internal_bif","iolist_size","iolist_to_binary", |
79
|
|
|
"is_alive","is_atom","is_binary","is_bitstring","is_boolean", |
80
|
|
|
"is_float","is_function","is_integer","is_list","is_number","is_pid", |
81
|
|
|
"is_port","is_process_alive","is_record","is_reference","is_tuple", |
82
|
|
|
"length","link","list_to_atom","list_to_binary","list_to_bitstring", |
83
|
|
|
"list_to_existing_atom","list_to_float","list_to_integer", |
84
|
|
|
"list_to_pid","list_to_tuple","load_module","make_ref","module_loaded", |
85
|
|
|
"monitor_node","node","node_link","node_unlink","nodes","notalive", |
86
|
|
|
"now","open_port","pid_to_list","port_close","port_command", |
87
|
|
|
"port_connect","port_control","pre_loaded","process_flag", |
88
|
|
|
"process_info","processes","purge_module","put","register", |
89
|
|
|
"registered","round","self","setelement","size","spawn","spawn_link", |
90
|
|
|
"spawn_monitor","spawn_opt","split_binary","statistics", |
91
|
|
|
"term_to_binary","time","throw","tl","trunc","tuple_size", |
92
|
|
|
"tuple_to_list","unlink","unregister","whereis"]; |
93
|
|
|
|
94
|
|
|
// upper case: [A-Z] [Ø-Þ] [À-Ö] |
95
|
|
|
// lower case: [a-z] [ß-ö] [ø-ÿ] |
96
|
|
|
var anumRE = /[\w@Ø-ÞÀ-Öß-öø-ÿ]/; |
97
|
|
|
var escapesRE = |
98
|
|
|
/[0-7]{1,3}|[bdefnrstv\\"']|\^[a-zA-Z]|x[0-9a-zA-Z]{2}|x{[0-9a-zA-Z]+}/; |
99
|
|
|
|
100
|
|
|
///////////////////////////////////////////////////////////////////////////// |
101
|
|
|
// tokenizer |
102
|
|
|
|
103
|
|
|
function tokenizer(stream,state) { |
104
|
|
|
// in multi-line string |
105
|
|
|
if (state.in_string) { |
106
|
|
|
state.in_string = (!doubleQuote(stream)); |
107
|
|
|
return rval(state,stream,"string"); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
// in multi-line atom |
111
|
|
|
if (state.in_atom) { |
112
|
|
|
state.in_atom = (!singleQuote(stream)); |
113
|
|
|
return rval(state,stream,"atom"); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
// whitespace |
117
|
|
|
if (stream.eatSpace()) { |
118
|
|
|
return rval(state,stream,"whitespace"); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
// attributes and type specs |
122
|
|
|
if (!peekToken(state) && |
123
|
|
|
stream.match(/-\s*[a-zß-öø-ÿ][\wØ-ÞÀ-Öß-öø-ÿ]*/)) { |
124
|
|
|
if (is_member(stream.current(),typeWords)) { |
125
|
|
|
return rval(state,stream,"type"); |
126
|
|
|
}else{ |
|
|
|
|
127
|
|
|
return rval(state,stream,"attribute"); |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
var ch = stream.next(); |
132
|
|
|
|
133
|
|
|
// comment |
134
|
|
|
if (ch == '%') { |
135
|
|
|
stream.skipToEnd(); |
136
|
|
|
return rval(state,stream,"comment"); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
// colon |
140
|
|
|
if (ch == ":") { |
141
|
|
|
return rval(state,stream,"colon"); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
// macro |
145
|
|
|
if (ch == '?') { |
146
|
|
|
stream.eatSpace(); |
147
|
|
|
stream.eatWhile(anumRE); |
148
|
|
|
return rval(state,stream,"macro"); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
// record |
152
|
|
|
if (ch == "#") { |
153
|
|
|
stream.eatSpace(); |
154
|
|
|
stream.eatWhile(anumRE); |
155
|
|
|
return rval(state,stream,"record"); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
// dollar escape |
159
|
|
|
if (ch == "$") { |
160
|
|
|
if (stream.next() == "\\" && !stream.match(escapesRE)) { |
161
|
|
|
return rval(state,stream,"error"); |
162
|
|
|
} |
163
|
|
|
return rval(state,stream,"number"); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
// dot |
167
|
|
|
if (ch == ".") { |
168
|
|
|
return rval(state,stream,"dot"); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
// quoted atom |
172
|
|
|
if (ch == '\'') { |
173
|
|
|
if (!(state.in_atom = (!singleQuote(stream)))) { |
174
|
|
|
if (stream.match(/\s*\/\s*[0-9]/,false)) { |
175
|
|
|
stream.match(/\s*\/\s*[0-9]/,true); |
176
|
|
|
return rval(state,stream,"fun"); // 'f'/0 style fun |
177
|
|
|
} |
178
|
|
|
if (stream.match(/\s*\(/,false) || stream.match(/\s*:/,false)) { |
179
|
|
|
return rval(state,stream,"function"); |
180
|
|
|
} |
181
|
|
|
} |
182
|
|
|
return rval(state,stream,"atom"); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
// string |
186
|
|
|
if (ch == '"') { |
187
|
|
|
state.in_string = (!doubleQuote(stream)); |
188
|
|
|
return rval(state,stream,"string"); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
// variable |
192
|
|
|
if (/[A-Z_Ø-ÞÀ-Ö]/.test(ch)) { |
193
|
|
|
stream.eatWhile(anumRE); |
194
|
|
|
return rval(state,stream,"variable"); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
// atom/keyword/BIF/function |
198
|
|
|
if (/[a-z_ß-öø-ÿ]/.test(ch)) { |
199
|
|
|
stream.eatWhile(anumRE); |
200
|
|
|
|
201
|
|
|
if (stream.match(/\s*\/\s*[0-9]/,false)) { |
202
|
|
|
stream.match(/\s*\/\s*[0-9]/,true); |
203
|
|
|
return rval(state,stream,"fun"); // f/0 style fun |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
var w = stream.current(); |
207
|
|
|
|
208
|
|
|
if (is_member(w,keywordWords)) { |
209
|
|
|
return rval(state,stream,"keyword"); |
210
|
|
|
}else if (is_member(w,operatorAtomWords)) { |
211
|
|
|
return rval(state,stream,"operator"); |
212
|
|
|
}else if (stream.match(/\s*\(/,false)) { |
213
|
|
|
// 'put' and 'erlang:put' are bifs, 'foo:put' is not |
214
|
|
|
if (is_member(w,bifWords) && |
215
|
|
|
((peekToken(state).token != ":") || |
216
|
|
|
(peekToken(state,2).token == "erlang"))) { |
217
|
|
|
return rval(state,stream,"builtin"); |
218
|
|
|
}else if (is_member(w,guardWords)) { |
219
|
|
|
return rval(state,stream,"guard"); |
220
|
|
|
}else{ |
221
|
|
|
return rval(state,stream,"function"); |
222
|
|
|
} |
223
|
|
|
}else if (is_member(w,operatorAtomWords)) { |
224
|
|
|
return rval(state,stream,"operator"); |
225
|
|
|
}else if (lookahead(stream) == ":") { |
226
|
|
|
if (w == "erlang") { |
227
|
|
|
return rval(state,stream,"builtin"); |
228
|
|
|
} else { |
|
|
|
|
229
|
|
|
return rval(state,stream,"function"); |
230
|
|
|
} |
231
|
|
|
}else if (is_member(w,["true","false"])) { |
232
|
|
|
return rval(state,stream,"boolean"); |
233
|
|
|
}else if (is_member(w,["true","false"])) { |
234
|
|
|
return rval(state,stream,"boolean"); |
235
|
|
|
}else{ |
236
|
|
|
return rval(state,stream,"atom"); |
237
|
|
|
} |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
// number |
241
|
|
|
var digitRE = /[0-9]/; |
242
|
|
|
var radixRE = /[0-9a-zA-Z]/; // 36#zZ style int |
243
|
|
|
if (digitRE.test(ch)) { |
244
|
|
|
stream.eatWhile(digitRE); |
245
|
|
|
if (stream.eat('#')) { // 36#aZ style integer |
246
|
|
|
if (!stream.eatWhile(radixRE)) { |
247
|
|
|
stream.backUp(1); //"36#" - syntax error |
248
|
|
|
} |
249
|
|
|
} else if (stream.eat('.')) { // float |
250
|
|
|
if (!stream.eatWhile(digitRE)) { |
251
|
|
|
stream.backUp(1); // "3." - probably end of function |
252
|
|
|
} else { |
253
|
|
|
if (stream.eat(/[eE]/)) { // float with exponent |
254
|
|
|
if (stream.eat(/[-+]/)) { |
255
|
|
|
if (!stream.eatWhile(digitRE)) { |
256
|
|
|
stream.backUp(2); // "2e-" - syntax error |
257
|
|
|
} |
258
|
|
|
} else { |
259
|
|
|
if (!stream.eatWhile(digitRE)) { |
260
|
|
|
stream.backUp(1); // "2e" - syntax error |
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
} |
264
|
|
|
} |
265
|
|
|
} |
266
|
|
|
return rval(state,stream,"number"); // normal integer |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
// open parens |
270
|
|
|
if (nongreedy(stream,openParenRE,openParenWords)) { |
271
|
|
|
return rval(state,stream,"open_paren"); |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
// close parens |
275
|
|
|
if (nongreedy(stream,closeParenRE,closeParenWords)) { |
276
|
|
|
return rval(state,stream,"close_paren"); |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
// separators |
280
|
|
|
if (greedy(stream,separatorRE,separatorWords)) { |
281
|
|
|
return rval(state,stream,"separator"); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
// operators |
285
|
|
|
if (greedy(stream,operatorSymbolRE,operatorSymbolWords)) { |
286
|
|
|
return rval(state,stream,"operator"); |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
return rval(state,stream,null); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
///////////////////////////////////////////////////////////////////////////// |
293
|
|
|
// utilities |
294
|
|
|
function nongreedy(stream,re,words) { |
295
|
|
|
if (stream.current().length == 1 && re.test(stream.current())) { |
|
|
|
|
296
|
|
|
stream.backUp(1); |
297
|
|
|
while (re.test(stream.peek())) { |
298
|
|
|
stream.next(); |
299
|
|
|
if (is_member(stream.current(),words)) { |
300
|
|
|
return true; |
301
|
|
|
} |
302
|
|
|
} |
303
|
|
|
stream.backUp(stream.current().length-1); |
304
|
|
|
} |
305
|
|
|
return false; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
function greedy(stream,re,words) { |
309
|
|
|
if (stream.current().length == 1 && re.test(stream.current())) { |
|
|
|
|
310
|
|
|
while (re.test(stream.peek())) { |
311
|
|
|
stream.next(); |
312
|
|
|
} |
313
|
|
|
while (0 < stream.current().length) { |
314
|
|
|
if (is_member(stream.current(),words)) { |
315
|
|
|
return true; |
316
|
|
|
}else{ |
|
|
|
|
317
|
|
|
stream.backUp(1); |
318
|
|
|
} |
319
|
|
|
} |
320
|
|
|
stream.next(); |
321
|
|
|
} |
322
|
|
|
return false; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
function doubleQuote(stream) { |
326
|
|
|
return quote(stream, '"', '\\'); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
function singleQuote(stream) { |
330
|
|
|
return quote(stream,'\'','\\'); |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
function quote(stream,quoteChar,escapeChar) { |
334
|
|
|
while (!stream.eol()) { |
335
|
|
|
var ch = stream.next(); |
336
|
|
|
if (ch == quoteChar) { |
337
|
|
|
return true; |
338
|
|
|
}else if (ch == escapeChar) { |
339
|
|
|
stream.next(); |
340
|
|
|
} |
341
|
|
|
} |
342
|
|
|
return false; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
function lookahead(stream) { |
346
|
|
|
var m = stream.match(/([\n\s]+|%[^\n]*\n)*(.)/,false); |
347
|
|
|
return m ? m.pop() : ""; |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
function is_member(element,list) { |
351
|
|
|
return (-1 < list.indexOf(element)); |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
function rval(state,stream,type) { |
355
|
|
|
|
356
|
|
|
// parse stack |
357
|
|
|
pushToken(state,realToken(type,stream)); |
358
|
|
|
|
359
|
|
|
// map erlang token type to CodeMirror style class |
360
|
|
|
// erlang -> CodeMirror tag |
361
|
|
|
switch (type) { |
362
|
|
|
case "atom": return "atom"; |
363
|
|
|
case "attribute": return "attribute"; |
364
|
|
|
case "boolean": return "atom"; |
365
|
|
|
case "builtin": return "builtin"; |
366
|
|
|
case "close_paren": return null; |
367
|
|
|
case "colon": return null; |
368
|
|
|
case "comment": return "comment"; |
369
|
|
|
case "dot": return null; |
370
|
|
|
case "error": return "error"; |
371
|
|
|
case "fun": return "meta"; |
372
|
|
|
case "function": return "tag"; |
373
|
|
|
case "guard": return "property"; |
374
|
|
|
case "keyword": return "keyword"; |
375
|
|
|
case "macro": return "variable-2"; |
376
|
|
|
case "number": return "number"; |
377
|
|
|
case "open_paren": return null; |
378
|
|
|
case "operator": return "operator"; |
379
|
|
|
case "record": return "bracket"; |
380
|
|
|
case "separator": return null; |
381
|
|
|
case "string": return "string"; |
382
|
|
|
case "type": return "def"; |
383
|
|
|
case "variable": return "variable"; |
384
|
|
|
default: return null; |
385
|
|
|
} |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
function aToken(tok,col,ind,typ) { |
389
|
|
|
return {token: tok, |
390
|
|
|
column: col, |
391
|
|
|
indent: ind, |
392
|
|
|
type: typ}; |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
function realToken(type,stream) { |
396
|
|
|
return aToken(stream.current(), |
397
|
|
|
stream.column(), |
398
|
|
|
stream.indentation(), |
399
|
|
|
type); |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
function fakeToken(type) { |
403
|
|
|
return aToken(type,0,0,type); |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
function peekToken(state,depth) { |
407
|
|
|
var len = state.tokenStack.length; |
408
|
|
|
var dep = (depth ? depth : 1); |
409
|
|
|
|
410
|
|
|
if (len < dep) { |
411
|
|
|
return false; |
412
|
|
|
}else{ |
|
|
|
|
413
|
|
|
return state.tokenStack[len-dep]; |
414
|
|
|
} |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
function pushToken(state,token) { |
418
|
|
|
|
419
|
|
|
if (!(token.type == "comment" || token.type == "whitespace")) { |
420
|
|
|
state.tokenStack = maybe_drop_pre(state.tokenStack,token); |
421
|
|
|
state.tokenStack = maybe_drop_post(state.tokenStack); |
422
|
|
|
} |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
function maybe_drop_pre(s,token) { |
426
|
|
|
var last = s.length-1; |
427
|
|
|
|
428
|
|
|
if (0 < last && s[last].type === "record" && token.type === "dot") { |
429
|
|
|
s.pop(); |
430
|
|
|
}else if (0 < last && s[last].type === "group") { |
431
|
|
|
s.pop(); |
432
|
|
|
s.push(token); |
433
|
|
|
}else{ |
434
|
|
|
s.push(token); |
435
|
|
|
} |
436
|
|
|
return s; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
function maybe_drop_post(s) { |
440
|
|
|
var last = s.length-1; |
441
|
|
|
|
442
|
|
|
if (s[last].type === "dot") { |
443
|
|
|
return []; |
444
|
|
|
} |
445
|
|
|
if (s[last].type === "fun" && s[last-1].token === "fun") { |
446
|
|
|
return s.slice(0,last-1); |
447
|
|
|
} |
448
|
|
|
switch (s[s.length-1].token) { |
449
|
|
|
case "}": return d(s,{g:["{"]}); |
450
|
|
|
case "]": return d(s,{i:["["]}); |
451
|
|
|
case ")": return d(s,{i:["("]}); |
452
|
|
|
case ">>": return d(s,{i:["<<"]}); |
453
|
|
|
case "end": return d(s,{i:["begin","case","fun","if","receive","try"]}); |
454
|
|
|
case ",": return d(s,{e:["begin","try","when","->", |
455
|
|
|
",","(","[","{","<<"]}); |
456
|
|
|
case "->": return d(s,{r:["when"], |
457
|
|
|
m:["try","if","case","receive"]}); |
458
|
|
|
case ";": return d(s,{E:["case","fun","if","receive","try","when"]}); |
459
|
|
|
case "catch":return d(s,{e:["try"]}); |
460
|
|
|
case "of": return d(s,{e:["case"]}); |
461
|
|
|
case "after":return d(s,{e:["receive","try"]}); |
462
|
|
|
default: return s; |
463
|
|
|
} |
464
|
|
|
} |
465
|
|
|
|
466
|
|
|
function d(stack,tt) { |
467
|
|
|
// stack is a stack of Token objects. |
468
|
|
|
// tt is an object; {type:tokens} |
469
|
|
|
// type is a char, tokens is a list of token strings. |
470
|
|
|
// The function returns (possibly truncated) stack. |
471
|
|
|
// It will descend the stack, looking for a Token such that Token.token |
472
|
|
|
// is a member of tokens. If it does not find that, it will normally (but |
473
|
|
|
// see "E" below) return stack. If it does find a match, it will remove |
474
|
|
|
// all the Tokens between the top and the matched Token. |
475
|
|
|
// If type is "m", that is all it does. |
476
|
|
|
// If type is "i", it will also remove the matched Token and the top Token. |
477
|
|
|
// If type is "g", like "i", but add a fake "group" token at the top. |
478
|
|
|
// If type is "r", it will remove the matched Token, but not the top Token. |
479
|
|
|
// If type is "e", it will keep the matched Token but not the top Token. |
480
|
|
|
// If type is "E", it behaves as for type "e", except if there is no match, |
481
|
|
|
// in which case it will return an empty stack. |
482
|
|
|
|
483
|
|
|
for (var type in tt) { |
|
|
|
|
484
|
|
|
var len = stack.length-1; |
485
|
|
|
var tokens = tt[type]; |
486
|
|
|
for (var i = len-1; -1 < i ; i--) { |
487
|
|
|
if (is_member(stack[i].token,tokens)) { |
488
|
|
|
var ss = stack.slice(0,i); |
489
|
|
|
switch (type) { |
490
|
|
|
case "m": return ss.concat(stack[i]).concat(stack[len]); |
491
|
|
|
case "r": return ss.concat(stack[len]); |
492
|
|
|
case "i": return ss; |
493
|
|
|
case "g": return ss.concat(fakeToken("group")); |
494
|
|
|
case "E": return ss.concat(stack[i]); |
495
|
|
|
case "e": return ss.concat(stack[i]); |
496
|
|
|
} |
497
|
|
|
} |
498
|
|
|
} |
499
|
|
|
} |
500
|
|
|
return (type == "E" ? [] : stack); |
|
|
|
|
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
///////////////////////////////////////////////////////////////////////////// |
504
|
|
|
// indenter |
505
|
|
|
|
506
|
|
|
function indenter(state,textAfter) { |
507
|
|
|
var t; |
508
|
|
|
var unit = cmCfg.indentUnit; |
509
|
|
|
var wordAfter = wordafter(textAfter); |
510
|
|
|
var currT = peekToken(state,1); |
511
|
|
|
var prevT = peekToken(state,2); |
512
|
|
|
|
513
|
|
|
if (state.in_string || state.in_atom) { |
514
|
|
|
return CodeMirror.Pass; |
515
|
|
|
}else if (!prevT) { |
516
|
|
|
return 0; |
517
|
|
|
}else if (currT.token == "when") { |
518
|
|
|
return currT.column+unit; |
519
|
|
|
}else if (wordAfter === "when" && prevT.type === "function") { |
520
|
|
|
return prevT.indent+unit; |
521
|
|
|
}else if (wordAfter === "(" && currT.token === "fun") { |
522
|
|
|
return currT.column+3; |
523
|
|
|
}else if (wordAfter === "catch" && (t = getToken(state,["try"]))) { |
524
|
|
|
return t.column; |
525
|
|
|
}else if (is_member(wordAfter,["end","after","of"])) { |
526
|
|
|
t = getToken(state,["begin","case","fun","if","receive","try"]); |
527
|
|
|
return t ? t.column : CodeMirror.Pass; |
528
|
|
|
}else if (is_member(wordAfter,closeParenWords)) { |
529
|
|
|
t = getToken(state,openParenWords); |
530
|
|
|
return t ? t.column : CodeMirror.Pass; |
531
|
|
|
}else if (is_member(currT.token,[",","|","||"]) || |
532
|
|
|
is_member(wordAfter,[",","|","||"])) { |
533
|
|
|
t = postcommaToken(state); |
534
|
|
|
return t ? t.column+t.token.length : unit; |
535
|
|
|
}else if (currT.token == "->") { |
536
|
|
|
if (is_member(prevT.token, ["receive","case","if","try"])) { |
537
|
|
|
return prevT.column+unit+unit; |
538
|
|
|
}else{ |
|
|
|
|
539
|
|
|
return prevT.column+unit; |
540
|
|
|
} |
541
|
|
|
}else if (is_member(currT.token,openParenWords)) { |
542
|
|
|
return currT.column+currT.token.length; |
543
|
|
|
}else{ |
544
|
|
|
t = defaultToken(state); |
545
|
|
|
return truthy(t) ? t.column+unit : 0; |
546
|
|
|
} |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
function wordafter(str) { |
550
|
|
|
var m = str.match(/,|[a-z]+|\}|\]|\)|>>|\|+|\(/); |
551
|
|
|
|
552
|
|
|
return truthy(m) && (m.index === 0) ? m[0] : ""; |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
function postcommaToken(state) { |
556
|
|
|
var objs = state.tokenStack.slice(0,-1); |
557
|
|
|
var i = getTokenIndex(objs,"type",["open_paren"]); |
558
|
|
|
|
559
|
|
|
return truthy(objs[i]) ? objs[i] : false; |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
function defaultToken(state) { |
563
|
|
|
var objs = state.tokenStack; |
564
|
|
|
var stop = getTokenIndex(objs,"type",["open_paren","separator","keyword"]); |
565
|
|
|
var oper = getTokenIndex(objs,"type",["operator"]); |
566
|
|
|
|
567
|
|
|
if (truthy(stop) && truthy(oper) && stop < oper) { |
568
|
|
|
return objs[stop+1]; |
569
|
|
|
} else if (truthy(stop)) { |
570
|
|
|
return objs[stop]; |
571
|
|
|
} else { |
572
|
|
|
return false; |
573
|
|
|
} |
574
|
|
|
} |
575
|
|
|
|
576
|
|
|
function getToken(state,tokens) { |
577
|
|
|
var objs = state.tokenStack; |
578
|
|
|
var i = getTokenIndex(objs,"token",tokens); |
579
|
|
|
|
580
|
|
|
return truthy(objs[i]) ? objs[i] : false; |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
function getTokenIndex(objs,propname,propvals) { |
584
|
|
|
|
585
|
|
|
for (var i = objs.length-1; -1 < i ; i--) { |
586
|
|
|
if (is_member(objs[i][propname],propvals)) { |
587
|
|
|
return i; |
588
|
|
|
} |
589
|
|
|
} |
590
|
|
|
return false; |
591
|
|
|
} |
592
|
|
|
|
593
|
|
|
function truthy(x) { |
594
|
|
|
return (x !== false) && (x != null); |
|
|
|
|
595
|
|
|
} |
596
|
|
|
|
597
|
|
|
///////////////////////////////////////////////////////////////////////////// |
598
|
|
|
// this object defines the mode |
599
|
|
|
|
600
|
|
|
return { |
601
|
|
|
startState: |
602
|
|
|
function() { |
603
|
|
|
return {tokenStack: [], |
604
|
|
|
in_string: false, |
605
|
|
|
in_atom: false}; |
606
|
|
|
}, |
607
|
|
|
|
608
|
|
|
token: |
609
|
|
|
function(stream, state) { |
610
|
|
|
return tokenizer(stream, state); |
611
|
|
|
}, |
612
|
|
|
|
613
|
|
|
indent: |
614
|
|
|
function(state, textAfter) { |
615
|
|
|
return indenter(state,textAfter); |
616
|
|
|
}, |
617
|
|
|
|
618
|
|
|
lineComment: "%" |
619
|
|
|
}; |
620
|
|
|
}); |
621
|
|
|
|
622
|
|
|
}); |
623
|
|
|
|
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.
To learn more about declaring variables in Javascript, see the MDN.