123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- // block; "begin", "case", "fun", "if", "receive", "try": closed by "end"
- // block internal; "after", "catch", "of"
- // guard; "when", closed by "->"
- // "->" opens a clause, closed by ";" or "."
- // "<<" opens a binary, closed by ">>"
- // "," appears in arglists, lists, tuples and terminates lines of code
- // "." resets indentation to 0
- // obsolete; "cond", "let", "query"
- CodeMirror.defineMIME("text/x-erlang", "erlang");
- CodeMirror.defineMode("erlang", function(cmCfg) {
- function rval(state,stream,type) {
- // distinguish between "." as terminator and record field operator
- if (type == "record") {
- state.context = "record";
- }else{
- state.context = false;
- }
- // remember last significant bit on last line for indenting
- if (type != "whitespace" && type != "comment") {
- state.lastToken = stream.current();
- }
- // erlang -> CodeMirror tag
- switch (type) {
- case "atom": return "atom";
- case "attribute": return "attribute";
- case "builtin": return "builtin";
- case "comment": return "comment";
- case "fun": return "meta";
- case "function": return "tag";
- case "guard": return "property";
- case "keyword": return "keyword";
- case "macro": return "variable-2";
- case "number": return "number";
- case "operator": return "operator";
- case "record": return "bracket";
- case "string": return "string";
- case "type": return "def";
- case "variable": return "variable";
- case "error": return "error";
- case "separator": return null;
- case "open_paren": return null;
- case "close_paren": return null;
- default: return null;
- }
- }
- var typeWords = [
- "-type", "-spec", "-export_type", "-opaque"];
- var keywordWords = [
- "after","begin","catch","case","cond","end","fun","if",
- "let","of","query","receive","try","when"];
- var separatorWords = [
- "->",";",":",".",","];
- var operatorWords = [
- "and","andalso","band","bnot","bor","bsl","bsr","bxor",
- "div","not","or","orelse","rem","xor"];
- var symbolWords = [
- "+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-"];
- var openParenWords = [
- "<<","(","[","{"];
- var closeParenWords = [
- "}","]",")",">>"];
- var guardWords = [
- "is_atom","is_binary","is_bitstring","is_boolean","is_float",
- "is_function","is_integer","is_list","is_number","is_pid",
- "is_port","is_record","is_reference","is_tuple",
- "atom","binary","bitstring","boolean","function","integer","list",
- "number","pid","port","record","reference","tuple"];
- var bifWords = [
- "abs","adler32","adler32_combine","alive","apply","atom_to_binary",
- "atom_to_list","binary_to_atom","binary_to_existing_atom",
- "binary_to_list","binary_to_term","bit_size","bitstring_to_list",
- "byte_size","check_process_code","contact_binary","crc32",
- "crc32_combine","date","decode_packet","delete_module",
- "disconnect_node","element","erase","exit","float","float_to_list",
- "garbage_collect","get","get_keys","group_leader","halt","hd",
- "integer_to_list","internal_bif","iolist_size","iolist_to_binary",
- "is_alive","is_atom","is_binary","is_bitstring","is_boolean",
- "is_float","is_function","is_integer","is_list","is_number","is_pid",
- "is_port","is_process_alive","is_record","is_reference","is_tuple",
- "length","link","list_to_atom","list_to_binary","list_to_bitstring",
- "list_to_existing_atom","list_to_float","list_to_integer",
- "list_to_pid","list_to_tuple","load_module","make_ref","module_loaded",
- "monitor_node","node","node_link","node_unlink","nodes","notalive",
- "now","open_port","pid_to_list","port_close","port_command",
- "port_connect","port_control","pre_loaded","process_flag",
- "process_info","processes","purge_module","put","register",
- "registered","round","self","setelement","size","spawn","spawn_link",
- "spawn_monitor","spawn_opt","split_binary","statistics",
- "term_to_binary","time","throw","tl","trunc","tuple_size",
- "tuple_to_list","unlink","unregister","whereis"];
- // ignored for indenting purposes
- var ignoreWords = [
- ",", ":", "catch", "after", "of", "cond", "let", "query"];
- var smallRE = /[a-z_]/;
- var largeRE = /[A-Z_]/;
- var digitRE = /[0-9]/;
- var octitRE = /[0-7]/;
- var anumRE = /[a-z_A-Z0-9]/;
- var symbolRE = /[\+\-\*\/<>=\|:]/;
- var openParenRE = /[<\(\[\{]/;
- var closeParenRE = /[>\)\]\}]/;
- var sepRE = /[\->\.,:;]/;
- function isMember(element,list) {
- return (-1 < list.indexOf(element));
- }
- function isPrev(stream,string) {
- var start = stream.start;
- var len = string.length;
- if (len <= start) {
- var word = stream.string.slice(start-len,start);
- return word == string;
- }else{
- return false;
- }
- }
- function tokenize(stream, state) {
- if (stream.eatSpace()) {
- return rval(state,stream,"whitespace");
- }
- // attributes and type specs
- if ((peekToken(state).token == "" || peekToken(state).token == ".") &&
- stream.peek() == '-') {
- stream.next();
- if (stream.eat(smallRE) && stream.eatWhile(anumRE)) {
- if (isMember(stream.current(),typeWords)) {
- return rval(state,stream,"type");
- }else{
- return rval(state,stream,"attribute");
- }
- }
- stream.backUp(1);
- }
- var ch = stream.next();
- // comment
- if (ch == '%') {
- stream.skipToEnd();
- return rval(state,stream,"comment");
- }
- // macro
- if (ch == '?') {
- stream.eatWhile(anumRE);
- return rval(state,stream,"macro");
- }
- // record
- if ( ch == "#") {
- stream.eatWhile(anumRE);
- return rval(state,stream,"record");
- }
- // char
- if ( ch == "$") {
- if (stream.next() == "\\") {
- if (!stream.eatWhile(octitRE)) {
- stream.next();
- }
- }
- return rval(state,stream,"string");
- }
- // quoted atom
- if (ch == '\'') {
- if (singleQuote(stream)) {
- return rval(state,stream,"atom");
- }else{
- return rval(state,stream,"error");
- }
- }
- // string
- if (ch == '"') {
- if (doubleQuote(stream)) {
- return rval(state,stream,"string");
- }else{
- return rval(state,stream,"error");
- }
- }
- // variable
- if (largeRE.test(ch)) {
- stream.eatWhile(anumRE);
- return rval(state,stream,"variable");
- }
- // atom/keyword/BIF/function
- if (smallRE.test(ch)) {
- stream.eatWhile(anumRE);
- if (stream.peek() == "/") {
- stream.next();
- if (stream.eatWhile(digitRE)) {
- return rval(state,stream,"fun"); // f/0 style fun
- }else{
- stream.backUp(1);
- return rval(state,stream,"atom");
- }
- }
- var w = stream.current();
- if (isMember(w,keywordWords)) {
- pushToken(state,stream);
- return rval(state,stream,"keyword");
- }
- if (stream.peek() == "(") {
- // 'put' and 'erlang:put' are bifs, 'foo:put' is not
- if (isMember(w,bifWords) &&
- (!isPrev(stream,":") || isPrev(stream,"erlang:"))) {
- return rval(state,stream,"builtin");
- }else{
- return rval(state,stream,"function");
- }
- }
- if (isMember(w,guardWords)) {
- return rval(state,stream,"guard");
- }
- if (isMember(w,operatorWords)) {
- return rval(state,stream,"operator");
- }
- if (stream.peek() == ":") {
- if (w == "erlang") {
- return rval(state,stream,"builtin");
- } else {
- return rval(state,stream,"function");
- }
- }
- return rval(state,stream,"atom");
- }
- // number
- if (digitRE.test(ch)) {
- stream.eatWhile(digitRE);
- if (stream.eat('#')) {
- stream.eatWhile(digitRE); // 16#10 style integer
- } else {
- if (stream.eat('.')) { // float
- stream.eatWhile(digitRE);
- }
- if (stream.eat(/[eE]/)) {
- stream.eat(/[-+]/); // float with exponent
- stream.eatWhile(digitRE);
- }
- }
- return rval(state,stream,"number"); // normal integer
- }
- // open parens
- if (nongreedy(stream,openParenRE,openParenWords)) {
- pushToken(state,stream);
- return rval(state,stream,"open_paren");
- }
- // close parens
- if (nongreedy(stream,closeParenRE,closeParenWords)) {
- pushToken(state,stream);
- return rval(state,stream,"close_paren");
- }
- // separators
- if (greedy(stream,sepRE,separatorWords)) {
- // distinguish between "." as terminator and record field operator
- if (state.context == false) {
- pushToken(state,stream);
- }
- return rval(state,stream,"separator");
- }
- // operators
- if (greedy(stream,symbolRE,symbolWords)) {
- return rval(state,stream,"operator");
- }
- return rval(state,stream,null);
- }
- function nongreedy(stream,re,words) {
- if (stream.current().length == 1 && re.test(stream.current())) {
- stream.backUp(1);
- while (re.test(stream.peek())) {
- stream.next();
- if (isMember(stream.current(),words)) {
- return true;
- }
- }
- stream.backUp(stream.current().length-1);
- }
- return false;
- }
- function greedy(stream,re,words) {
- if (stream.current().length == 1 && re.test(stream.current())) {
- while (re.test(stream.peek())) {
- stream.next();
- }
- while (0 < stream.current().length) {
- if (isMember(stream.current(),words)) {
- return true;
- }else{
- stream.backUp(1);
- }
- }
- stream.next();
- }
- return false;
- }
- function doubleQuote(stream) {
- return quote(stream, '"', '\\');
- }
- function singleQuote(stream) {
- return quote(stream,'\'','\\');
- }
- function quote(stream,quoteChar,escapeChar) {
- while (!stream.eol()) {
- var ch = stream.next();
- if (ch == quoteChar) {
- return true;
- }else if (ch == escapeChar) {
- stream.next();
- }
- }
- return false;
- }
- function Token(stream) {
- this.token = stream ? stream.current() : "";
- this.column = stream ? stream.column() : 0;
- this.indent = stream ? stream.indentation() : 0;
- }
- function myIndent(state,textAfter) {
- var indent = cmCfg.indentUnit;
- var outdentWords = ["after","catch"];
- var token = (peekToken(state)).token;
- var wordAfter = takewhile(textAfter,/[^a-z]/);
- if (isMember(token,openParenWords)) {
- return (peekToken(state)).column+token.length;
- }else if (token == "." || token == ""){
- return 0;
- }else if (token == "->") {
- if (wordAfter == "end") {
- return peekToken(state,2).column;
- }else if (peekToken(state,2).token == "fun") {
- return peekToken(state,2).column+indent;
- }else{
- return (peekToken(state)).indent+indent;
- }
- }else if (isMember(wordAfter,outdentWords)) {
- return (peekToken(state)).indent;
- }else{
- return (peekToken(state)).column+indent;
- }
- }
- function takewhile(str,re) {
- var m = str.match(re);
- return m ? str.slice(0,m.index) : str;
- }
- function popToken(state) {
- return state.tokenStack.pop();
- }
- function peekToken(state,depth) {
- var len = state.tokenStack.length;
- var dep = (depth ? depth : 1);
- if (len < dep) {
- return new Token;
- }else{
- return state.tokenStack[len-dep];
- }
- }
- function pushToken(state,stream) {
- var token = stream.current();
- var prev_token = peekToken(state).token;
- if (isMember(token,ignoreWords)) {
- return false;
- }else if (drop_both(prev_token,token)) {
- popToken(state);
- return false;
- }else if (drop_first(prev_token,token)) {
- popToken(state);
- return pushToken(state,stream);
- }else{
- state.tokenStack.push(new Token(stream));
- return true;
- }
- }
- function drop_first(open, close) {
- switch (open+" "+close) {
- case "when ->": return true;
- case "-> end": return true;
- case "-> .": return true;
- case ". .": return true;
- default: return false;
- }
- }
- function drop_both(open, close) {
- switch (open+" "+close) {
- case "( )": return true;
- case "[ ]": return true;
- case "{ }": return true;
- case "<< >>": return true;
- case "begin end": return true;
- case "case end": return true;
- case "fun end": return true;
- case "if end": return true;
- case "receive end": return true;
- case "try end": return true;
- case "-> ;": return true;
- default: return false;
- }
- }
- return {
- startState:
- function() {
- return {tokenStack: [],
- context: false,
- lastToken: null};
- },
- token:
- function(stream, state) {
- return tokenize(stream, state);
- },
- indent:
- function(state, textAfter) {
- return myIndent(state,textAfter);
- },
- lineComment: "%"
- };
- });
|