Fix: merge conflict
[myslice.git] / to-be-integrated / third-party / codemirror-3.15 / mode / erlang / erlang.js
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"
9
10 CodeMirror.defineMIME("text/x-erlang", "erlang");
11
12 CodeMirror.defineMode("erlang", function(cmCfg) {
13
14   function rval(state,stream,type) {
15     // distinguish between "." as terminator and record field operator
16     if (type == "record") {
17       state.context = "record";
18     }else{
19       state.context = false;
20     }
21
22     // remember last significant bit on last line for indenting
23     if (type != "whitespace" && type != "comment") {
24       state.lastToken = stream.current();
25     }
26     //     erlang             -> CodeMirror tag
27     switch (type) {
28       case "atom":        return "atom";
29       case "attribute":   return "attribute";
30       case "builtin":     return "builtin";
31       case "comment":     return "comment";
32       case "fun":         return "meta";
33       case "function":    return "tag";
34       case "guard":       return "property";
35       case "keyword":     return "keyword";
36       case "macro":       return "variable-2";
37       case "number":      return "number";
38       case "operator":    return "operator";
39       case "record":      return "bracket";
40       case "string":      return "string";
41       case "type":        return "def";
42       case "variable":    return "variable";
43       case "error":       return "error";
44       case "separator":   return null;
45       case "open_paren":  return null;
46       case "close_paren": return null;
47       default:            return null;
48     }
49   }
50
51   var typeWords = [
52     "-type", "-spec", "-export_type", "-opaque"];
53
54   var keywordWords = [
55     "after","begin","catch","case","cond","end","fun","if",
56     "let","of","query","receive","try","when"];
57
58   var separatorWords = [
59     "->",";",":",".",","];
60
61   var operatorWords = [
62     "and","andalso","band","bnot","bor","bsl","bsr","bxor",
63     "div","not","or","orelse","rem","xor"];
64
65   var symbolWords = [
66     "+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-"];
67
68   var openParenWords = [
69     "<<","(","[","{"];
70
71   var closeParenWords = [
72     "}","]",")",">>"];
73
74   var guardWords = [
75     "is_atom","is_binary","is_bitstring","is_boolean","is_float",
76     "is_function","is_integer","is_list","is_number","is_pid",
77     "is_port","is_record","is_reference","is_tuple",
78     "atom","binary","bitstring","boolean","function","integer","list",
79     "number","pid","port","record","reference","tuple"];
80
81   var bifWords = [
82     "abs","adler32","adler32_combine","alive","apply","atom_to_binary",
83     "atom_to_list","binary_to_atom","binary_to_existing_atom",
84     "binary_to_list","binary_to_term","bit_size","bitstring_to_list",
85     "byte_size","check_process_code","contact_binary","crc32",
86     "crc32_combine","date","decode_packet","delete_module",
87     "disconnect_node","element","erase","exit","float","float_to_list",
88     "garbage_collect","get","get_keys","group_leader","halt","hd",
89     "integer_to_list","internal_bif","iolist_size","iolist_to_binary",
90     "is_alive","is_atom","is_binary","is_bitstring","is_boolean",
91     "is_float","is_function","is_integer","is_list","is_number","is_pid",
92     "is_port","is_process_alive","is_record","is_reference","is_tuple",
93     "length","link","list_to_atom","list_to_binary","list_to_bitstring",
94     "list_to_existing_atom","list_to_float","list_to_integer",
95     "list_to_pid","list_to_tuple","load_module","make_ref","module_loaded",
96     "monitor_node","node","node_link","node_unlink","nodes","notalive",
97     "now","open_port","pid_to_list","port_close","port_command",
98     "port_connect","port_control","pre_loaded","process_flag",
99     "process_info","processes","purge_module","put","register",
100     "registered","round","self","setelement","size","spawn","spawn_link",
101     "spawn_monitor","spawn_opt","split_binary","statistics",
102     "term_to_binary","time","throw","tl","trunc","tuple_size",
103     "tuple_to_list","unlink","unregister","whereis"];
104
105   // ignored for indenting purposes
106   var ignoreWords = [
107     ",", ":", "catch", "after", "of", "cond", "let", "query"];
108
109
110   var smallRE      = /[a-z_]/;
111   var largeRE      = /[A-Z_]/;
112   var digitRE      = /[0-9]/;
113   var octitRE      = /[0-7]/;
114   var anumRE       = /[a-z_A-Z0-9]/;
115   var symbolRE     = /[\+\-\*\/<>=\|:]/;
116   var openParenRE  = /[<\(\[\{]/;
117   var closeParenRE = /[>\)\]\}]/;
118   var sepRE        = /[\->\.,:;]/;
119
120   function isMember(element,list) {
121     return (-1 < list.indexOf(element));
122   }
123
124   function isPrev(stream,string) {
125     var start = stream.start;
126     var len = string.length;
127     if (len <= start) {
128       var word = stream.string.slice(start-len,start);
129       return word == string;
130     }else{
131       return false;
132     }
133   }
134
135   function tokenize(stream, state) {
136     if (stream.eatSpace()) {
137       return rval(state,stream,"whitespace");
138     }
139
140     // attributes and type specs
141     if ((peekToken(state).token == "" || peekToken(state).token == ".") &&
142         stream.peek() == '-') {
143       stream.next();
144       if (stream.eat(smallRE) && stream.eatWhile(anumRE)) {
145         if (isMember(stream.current(),typeWords)) {
146           return rval(state,stream,"type");
147         }else{
148           return rval(state,stream,"attribute");
149         }
150       }
151       stream.backUp(1);
152     }
153
154     var ch = stream.next();
155
156     // comment
157     if (ch == '%') {
158       stream.skipToEnd();
159       return rval(state,stream,"comment");
160     }
161
162     // macro
163     if (ch == '?') {
164       stream.eatWhile(anumRE);
165       return rval(state,stream,"macro");
166     }
167
168     // record
169     if ( ch == "#") {
170       stream.eatWhile(anumRE);
171       return rval(state,stream,"record");
172     }
173
174     // char
175     if ( ch == "$") {
176       if (stream.next() == "\\") {
177         if (!stream.eatWhile(octitRE)) {
178           stream.next();
179         }
180       }
181       return rval(state,stream,"string");
182     }
183
184     // quoted atom
185     if (ch == '\'') {
186       if (singleQuote(stream)) {
187         return rval(state,stream,"atom");
188       }else{
189         return rval(state,stream,"error");
190       }
191     }
192
193     // string
194     if (ch == '"') {
195       if (doubleQuote(stream)) {
196         return rval(state,stream,"string");
197       }else{
198         return rval(state,stream,"error");
199       }
200     }
201
202     // variable
203     if (largeRE.test(ch)) {
204       stream.eatWhile(anumRE);
205       return rval(state,stream,"variable");
206     }
207
208     // atom/keyword/BIF/function
209     if (smallRE.test(ch)) {
210       stream.eatWhile(anumRE);
211
212       if (stream.peek() == "/") {
213         stream.next();
214         if (stream.eatWhile(digitRE)) {
215           return rval(state,stream,"fun");      // f/0 style fun
216         }else{
217           stream.backUp(1);
218           return rval(state,stream,"atom");
219         }
220       }
221
222       var w = stream.current();
223
224       if (isMember(w,keywordWords)) {
225         pushToken(state,stream);
226         return rval(state,stream,"keyword");
227       }
228       if (stream.peek() == "(") {
229         // 'put' and 'erlang:put' are bifs, 'foo:put' is not
230         if (isMember(w,bifWords) &&
231             (!isPrev(stream,":") || isPrev(stream,"erlang:"))) {
232           return rval(state,stream,"builtin");
233         }else{
234           return rval(state,stream,"function");
235         }
236       }
237       if (isMember(w,guardWords)) {
238         return rval(state,stream,"guard");
239       }
240       if (isMember(w,operatorWords)) {
241         return rval(state,stream,"operator");
242       }
243       if (stream.peek() == ":") {
244         if (w == "erlang") {
245           return rval(state,stream,"builtin");
246         } else {
247           return rval(state,stream,"function");
248         }
249       }
250       return rval(state,stream,"atom");
251     }
252
253     // number
254     if (digitRE.test(ch)) {
255       stream.eatWhile(digitRE);
256       if (stream.eat('#')) {
257         stream.eatWhile(digitRE);    // 16#10  style integer
258       } else {
259         if (stream.eat('.')) {       // float
260           stream.eatWhile(digitRE);
261         }
262         if (stream.eat(/[eE]/)) {
263           stream.eat(/[-+]/);        // float with exponent
264           stream.eatWhile(digitRE);
265         }
266       }
267       return rval(state,stream,"number");   // normal integer
268     }
269
270     // open parens
271     if (nongreedy(stream,openParenRE,openParenWords)) {
272       pushToken(state,stream);
273       return rval(state,stream,"open_paren");
274     }
275
276     // close parens
277     if (nongreedy(stream,closeParenRE,closeParenWords)) {
278       pushToken(state,stream);
279       return rval(state,stream,"close_paren");
280     }
281
282     // separators
283     if (greedy(stream,sepRE,separatorWords)) {
284       // distinguish between "." as terminator and record field operator
285       if (state.context == false) {
286         pushToken(state,stream);
287       }
288       return rval(state,stream,"separator");
289     }
290
291     // operators
292     if (greedy(stream,symbolRE,symbolWords)) {
293       return rval(state,stream,"operator");
294     }
295
296     return rval(state,stream,null);
297   }
298
299   function nongreedy(stream,re,words) {
300     if (stream.current().length == 1 && re.test(stream.current())) {
301       stream.backUp(1);
302       while (re.test(stream.peek())) {
303         stream.next();
304         if (isMember(stream.current(),words)) {
305           return true;
306         }
307       }
308       stream.backUp(stream.current().length-1);
309     }
310     return false;
311   }
312
313   function greedy(stream,re,words) {
314     if (stream.current().length == 1 && re.test(stream.current())) {
315       while (re.test(stream.peek())) {
316         stream.next();
317       }
318       while (0 < stream.current().length) {
319         if (isMember(stream.current(),words)) {
320           return true;
321         }else{
322           stream.backUp(1);
323         }
324       }
325       stream.next();
326     }
327     return false;
328   }
329
330   function doubleQuote(stream) {
331     return quote(stream, '"', '\\');
332   }
333
334   function singleQuote(stream) {
335     return quote(stream,'\'','\\');
336   }
337
338   function quote(stream,quoteChar,escapeChar) {
339     while (!stream.eol()) {
340       var ch = stream.next();
341       if (ch == quoteChar) {
342         return true;
343       }else if (ch == escapeChar) {
344         stream.next();
345       }
346     }
347     return false;
348   }
349
350   function Token(stream) {
351     this.token  = stream ? stream.current() : "";
352     this.column = stream ? stream.column() : 0;
353     this.indent = stream ? stream.indentation() : 0;
354   }
355
356   function myIndent(state,textAfter) {
357     var indent = cmCfg.indentUnit;
358     var outdentWords = ["after","catch"];
359     var token = (peekToken(state)).token;
360     var wordAfter = takewhile(textAfter,/[^a-z]/);
361
362     if (isMember(token,openParenWords)) {
363       return (peekToken(state)).column+token.length;
364     }else if (token == "." || token == ""){
365       return 0;
366     }else if (token == "->") {
367       if (wordAfter == "end") {
368         return peekToken(state,2).column;
369       }else if (peekToken(state,2).token == "fun") {
370         return peekToken(state,2).column+indent;
371       }else{
372         return (peekToken(state)).indent+indent;
373       }
374     }else if (isMember(wordAfter,outdentWords)) {
375       return (peekToken(state)).indent;
376     }else{
377       return (peekToken(state)).column+indent;
378     }
379   }
380
381   function takewhile(str,re) {
382     var m = str.match(re);
383     return m ? str.slice(0,m.index) : str;
384   }
385
386   function popToken(state) {
387     return state.tokenStack.pop();
388   }
389
390   function peekToken(state,depth) {
391     var len = state.tokenStack.length;
392     var dep = (depth ? depth : 1);
393     if (len < dep) {
394       return new Token;
395     }else{
396       return state.tokenStack[len-dep];
397     }
398   }
399
400   function pushToken(state,stream) {
401     var token = stream.current();
402     var prev_token = peekToken(state).token;
403     if (isMember(token,ignoreWords)) {
404       return false;
405     }else if (drop_both(prev_token,token)) {
406       popToken(state);
407       return false;
408     }else if (drop_first(prev_token,token)) {
409       popToken(state);
410       return pushToken(state,stream);
411     }else{
412       state.tokenStack.push(new Token(stream));
413       return true;
414     }
415   }
416
417   function drop_first(open, close) {
418     switch (open+" "+close) {
419       case "when ->":       return true;
420       case "-> end":        return true;
421       case "-> .":          return true;
422       case ". .":           return true;
423       default:              return false;
424     }
425   }
426
427   function drop_both(open, close) {
428     switch (open+" "+close) {
429       case "( )":         return true;
430       case "[ ]":         return true;
431       case "{ }":         return true;
432       case "<< >>":       return true;
433       case "begin end":   return true;
434       case "case end":    return true;
435       case "fun end":     return true;
436       case "if end":      return true;
437       case "receive end": return true;
438       case "try end":     return true;
439       case "-> ;":        return true;
440       default:            return false;
441     }
442   }
443
444   return {
445     startState:
446       function() {
447         return {tokenStack: [],
448                 context: false,
449                 lastToken: null};
450       },
451
452     token:
453       function(stream, state) {
454         return tokenize(stream, state);
455       },
456
457     indent:
458       function(state, textAfter) {
459         return myIndent(state,textAfter);
460       },
461
462     lineComment: "%"
463   };
464 });