LaravelTest
619 строк · 18.4 Кб
1// CodeMirror, copyright (c) by Marijn Haverbeke and others
2// Distributed under an MIT license: https://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) {19if (typeof exports == "object" && typeof module == "object") // CommonJS20mod(require("../../lib/codemirror"));21else if (typeof define == "function" && define.amd) // AMD22define(["../../lib/codemirror"], mod);23else // Plain browser env24mod(CodeMirror);25})(function(CodeMirror) {26"use strict";27
28CodeMirror.defineMIME("text/x-erlang", "erlang");29
30CodeMirror.defineMode("erlang", function(cmCfg) {31"use strict";32
33/////////////////////////////////////////////////////////////////////////////
34// constants
35
36var typeWords = [37"-type", "-spec", "-export_type", "-opaque"];38
39var keywordWords = [40"after","begin","catch","case","cond","end","fun","if",41"let","of","query","receive","try","when"];42
43var separatorRE = /[\->,;]/;44var separatorWords = [45"->",";",","];46
47var operatorAtomWords = [48"and","andalso","band","bnot","bor","bsl","bsr","bxor",49"div","not","or","orelse","rem","xor"];50
51var operatorSymbolRE = /[\+\-\*\/<>=\|:!]/;52var operatorSymbolWords = [53"=","+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-","!"];54
55var openParenRE = /[<\(\[\{]/;56var openParenWords = [57"<<","(","[","{"];58
59var closeParenRE = /[>\)\]\}]/;60var closeParenWords = [61"}","]",")",">>"];62
63var 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
70var 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] [ß-ö] [ø-ÿ]
96var anumRE = /[\w@Ø-ÞÀ-Öß-öø-ÿ]/;97var 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
103function tokenizer(stream,state) {104// in multi-line string105if (state.in_string) {106state.in_string = (!doubleQuote(stream));107return rval(state,stream,"string");108}109
110// in multi-line atom111if (state.in_atom) {112state.in_atom = (!singleQuote(stream));113return rval(state,stream,"atom");114}115
116// whitespace117if (stream.eatSpace()) {118return rval(state,stream,"whitespace");119}120
121// attributes and type specs122if (!peekToken(state) &&123stream.match(/-\s*[a-zß-öø-ÿ][\wØ-ÞÀ-Öß-öø-ÿ]*/)) {124if (is_member(stream.current(),typeWords)) {125return rval(state,stream,"type");126}else{127return rval(state,stream,"attribute");128}129}130
131var ch = stream.next();132
133// comment134if (ch == '%') {135stream.skipToEnd();136return rval(state,stream,"comment");137}138
139// colon140if (ch == ":") {141return rval(state,stream,"colon");142}143
144// macro145if (ch == '?') {146stream.eatSpace();147stream.eatWhile(anumRE);148return rval(state,stream,"macro");149}150
151// record152if (ch == "#") {153stream.eatSpace();154stream.eatWhile(anumRE);155return rval(state,stream,"record");156}157
158// dollar escape159if (ch == "$") {160if (stream.next() == "\\" && !stream.match(escapesRE)) {161return rval(state,stream,"error");162}163return rval(state,stream,"number");164}165
166// dot167if (ch == ".") {168return rval(state,stream,"dot");169}170
171// quoted atom172if (ch == '\'') {173if (!(state.in_atom = (!singleQuote(stream)))) {174if (stream.match(/\s*\/\s*[0-9]/,false)) {175stream.match(/\s*\/\s*[0-9]/,true);176return rval(state,stream,"fun"); // 'f'/0 style fun177}178if (stream.match(/\s*\(/,false) || stream.match(/\s*:/,false)) {179return rval(state,stream,"function");180}181}182return rval(state,stream,"atom");183}184
185// string186if (ch == '"') {187state.in_string = (!doubleQuote(stream));188return rval(state,stream,"string");189}190
191// variable192if (/[A-Z_Ø-ÞÀ-Ö]/.test(ch)) {193stream.eatWhile(anumRE);194return rval(state,stream,"variable");195}196
197// atom/keyword/BIF/function198if (/[a-z_ß-öø-ÿ]/.test(ch)) {199stream.eatWhile(anumRE);200
201if (stream.match(/\s*\/\s*[0-9]/,false)) {202stream.match(/\s*\/\s*[0-9]/,true);203return rval(state,stream,"fun"); // f/0 style fun204}205
206var w = stream.current();207
208if (is_member(w,keywordWords)) {209return rval(state,stream,"keyword");210}else if (is_member(w,operatorAtomWords)) {211return rval(state,stream,"operator");212}else if (stream.match(/\s*\(/,false)) {213// 'put' and 'erlang:put' are bifs, 'foo:put' is not214if (is_member(w,bifWords) &&215((peekToken(state).token != ":") ||216(peekToken(state,2).token == "erlang"))) {217return rval(state,stream,"builtin");218}else if (is_member(w,guardWords)) {219return rval(state,stream,"guard");220}else{221return rval(state,stream,"function");222}223}else if (lookahead(stream) == ":") {224if (w == "erlang") {225return rval(state,stream,"builtin");226} else {227return rval(state,stream,"function");228}229}else if (is_member(w,["true","false"])) {230return rval(state,stream,"boolean");231}else{232return rval(state,stream,"atom");233}234}235
236// number237var digitRE = /[0-9]/;238var radixRE = /[0-9a-zA-Z]/; // 36#zZ style int239if (digitRE.test(ch)) {240stream.eatWhile(digitRE);241if (stream.eat('#')) { // 36#aZ style integer242if (!stream.eatWhile(radixRE)) {243stream.backUp(1); //"36#" - syntax error244}245} else if (stream.eat('.')) { // float246if (!stream.eatWhile(digitRE)) {247stream.backUp(1); // "3." - probably end of function248} else {249if (stream.eat(/[eE]/)) { // float with exponent250if (stream.eat(/[-+]/)) {251if (!stream.eatWhile(digitRE)) {252stream.backUp(2); // "2e-" - syntax error253}254} else {255if (!stream.eatWhile(digitRE)) {256stream.backUp(1); // "2e" - syntax error257}258}259}260}261}262return rval(state,stream,"number"); // normal integer263}264
265// open parens266if (nongreedy(stream,openParenRE,openParenWords)) {267return rval(state,stream,"open_paren");268}269
270// close parens271if (nongreedy(stream,closeParenRE,closeParenWords)) {272return rval(state,stream,"close_paren");273}274
275// separators276if (greedy(stream,separatorRE,separatorWords)) {277return rval(state,stream,"separator");278}279
280// operators281if (greedy(stream,operatorSymbolRE,operatorSymbolWords)) {282return rval(state,stream,"operator");283}284
285return rval(state,stream,null);286}287
288/////////////////////////////////////////////////////////////////////////////
289// utilities
290function nongreedy(stream,re,words) {291if (stream.current().length == 1 && re.test(stream.current())) {292stream.backUp(1);293while (re.test(stream.peek())) {294stream.next();295if (is_member(stream.current(),words)) {296return true;297}298}299stream.backUp(stream.current().length-1);300}301return false;302}303
304function greedy(stream,re,words) {305if (stream.current().length == 1 && re.test(stream.current())) {306while (re.test(stream.peek())) {307stream.next();308}309while (0 < stream.current().length) {310if (is_member(stream.current(),words)) {311return true;312}else{313stream.backUp(1);314}315}316stream.next();317}318return false;319}320
321function doubleQuote(stream) {322return quote(stream, '"', '\\');323}324
325function singleQuote(stream) {326return quote(stream,'\'','\\');327}328
329function quote(stream,quoteChar,escapeChar) {330while (!stream.eol()) {331var ch = stream.next();332if (ch == quoteChar) {333return true;334}else if (ch == escapeChar) {335stream.next();336}337}338return false;339}340
341function lookahead(stream) {342var m = stream.match(/^\s*([^\s%])/, false)343return m ? m[1] : "";344}345
346function is_member(element,list) {347return (-1 < list.indexOf(element));348}349
350function rval(state,stream,type) {351
352// parse stack353pushToken(state,realToken(type,stream));354
355// map erlang token type to CodeMirror style class356// erlang -> CodeMirror tag357switch (type) {358case "atom": return "atom";359case "attribute": return "attribute";360case "boolean": return "atom";361case "builtin": return "builtin";362case "close_paren": return null;363case "colon": return null;364case "comment": return "comment";365case "dot": return null;366case "error": return "error";367case "fun": return "meta";368case "function": return "tag";369case "guard": return "property";370case "keyword": return "keyword";371case "macro": return "variable-2";372case "number": return "number";373case "open_paren": return null;374case "operator": return "operator";375case "record": return "bracket";376case "separator": return null;377case "string": return "string";378case "type": return "def";379case "variable": return "variable";380default: return null;381}382}383
384function aToken(tok,col,ind,typ) {385return {token: tok,386column: col,387indent: ind,388type: typ};389}390
391function realToken(type,stream) {392return aToken(stream.current(),393stream.column(),394stream.indentation(),395type);396}397
398function fakeToken(type) {399return aToken(type,0,0,type);400}401
402function peekToken(state,depth) {403var len = state.tokenStack.length;404var dep = (depth ? depth : 1);405
406if (len < dep) {407return false;408}else{409return state.tokenStack[len-dep];410}411}412
413function pushToken(state,token) {414
415if (!(token.type == "comment" || token.type == "whitespace")) {416state.tokenStack = maybe_drop_pre(state.tokenStack,token);417state.tokenStack = maybe_drop_post(state.tokenStack);418}419}420
421function maybe_drop_pre(s,token) {422var last = s.length-1;423
424if (0 < last && s[last].type === "record" && token.type === "dot") {425s.pop();426}else if (0 < last && s[last].type === "group") {427s.pop();428s.push(token);429}else{430s.push(token);431}432return s;433}434
435function maybe_drop_post(s) {436if (!s.length) return s437var last = s.length-1;438
439if (s[last].type === "dot") {440return [];441}442if (last > 1 && s[last].type === "fun" && s[last-1].token === "fun") {443return s.slice(0,last-1);444}445switch (s[last].token) {446case "}": return d(s,{g:["{"]});447case "]": return d(s,{i:["["]});448case ")": return d(s,{i:["("]});449case ">>": return d(s,{i:["<<"]});450case "end": return d(s,{i:["begin","case","fun","if","receive","try"]});451case ",": return d(s,{e:["begin","try","when","->",452",","(","[","{","<<"]});453case "->": return d(s,{r:["when"],454m:["try","if","case","receive"]});455case ";": return d(s,{E:["case","fun","if","receive","try","when"]});456case "catch":return d(s,{e:["try"]});457case "of": return d(s,{e:["case"]});458case "after":return d(s,{e:["receive","try"]});459default: return s;460}461}462
463function d(stack,tt) {464// stack is a stack of Token objects.465// tt is an object; {type:tokens}466// type is a char, tokens is a list of token strings.467// The function returns (possibly truncated) stack.468// It will descend the stack, looking for a Token such that Token.token469// is a member of tokens. If it does not find that, it will normally (but470// see "E" below) return stack. If it does find a match, it will remove471// all the Tokens between the top and the matched Token.472// If type is "m", that is all it does.473// If type is "i", it will also remove the matched Token and the top Token.474// If type is "g", like "i", but add a fake "group" token at the top.475// If type is "r", it will remove the matched Token, but not the top Token.476// If type is "e", it will keep the matched Token but not the top Token.477// If type is "E", it behaves as for type "e", except if there is no match,478// in which case it will return an empty stack.479
480for (var type in tt) {481var len = stack.length-1;482var tokens = tt[type];483for (var i = len-1; -1 < i ; i--) {484if (is_member(stack[i].token,tokens)) {485var ss = stack.slice(0,i);486switch (type) {487case "m": return ss.concat(stack[i]).concat(stack[len]);488case "r": return ss.concat(stack[len]);489case "i": return ss;490case "g": return ss.concat(fakeToken("group"));491case "E": return ss.concat(stack[i]);492case "e": return ss.concat(stack[i]);493}494}495}496}497return (type == "E" ? [] : stack);498}499
500/////////////////////////////////////////////////////////////////////////////
501// indenter
502
503function indenter(state,textAfter) {504var t;505var unit = cmCfg.indentUnit;506var wordAfter = wordafter(textAfter);507var currT = peekToken(state,1);508var prevT = peekToken(state,2);509
510if (state.in_string || state.in_atom) {511return CodeMirror.Pass;512}else if (!prevT) {513return 0;514}else if (currT.token == "when") {515return currT.column+unit;516}else if (wordAfter === "when" && prevT.type === "function") {517return prevT.indent+unit;518}else if (wordAfter === "(" && currT.token === "fun") {519return currT.column+3;520}else if (wordAfter === "catch" && (t = getToken(state,["try"]))) {521return t.column;522}else if (is_member(wordAfter,["end","after","of"])) {523t = getToken(state,["begin","case","fun","if","receive","try"]);524return t ? t.column : CodeMirror.Pass;525}else if (is_member(wordAfter,closeParenWords)) {526t = getToken(state,openParenWords);527return t ? t.column : CodeMirror.Pass;528}else if (is_member(currT.token,[",","|","||"]) ||529is_member(wordAfter,[",","|","||"])) {530t = postcommaToken(state);531return t ? t.column+t.token.length : unit;532}else if (currT.token == "->") {533if (is_member(prevT.token, ["receive","case","if","try"])) {534return prevT.column+unit+unit;535}else{536return prevT.column+unit;537}538}else if (is_member(currT.token,openParenWords)) {539return currT.column+currT.token.length;540}else{541t = defaultToken(state);542return truthy(t) ? t.column+unit : 0;543}544}545
546function wordafter(str) {547var m = str.match(/,|[a-z]+|\}|\]|\)|>>|\|+|\(/);548
549return truthy(m) && (m.index === 0) ? m[0] : "";550}551
552function postcommaToken(state) {553var objs = state.tokenStack.slice(0,-1);554var i = getTokenIndex(objs,"type",["open_paren"]);555
556return truthy(objs[i]) ? objs[i] : false;557}558
559function defaultToken(state) {560var objs = state.tokenStack;561var stop = getTokenIndex(objs,"type",["open_paren","separator","keyword"]);562var oper = getTokenIndex(objs,"type",["operator"]);563
564if (truthy(stop) && truthy(oper) && stop < oper) {565return objs[stop+1];566} else if (truthy(stop)) {567return objs[stop];568} else {569return false;570}571}572
573function getToken(state,tokens) {574var objs = state.tokenStack;575var i = getTokenIndex(objs,"token",tokens);576
577return truthy(objs[i]) ? objs[i] : false;578}579
580function getTokenIndex(objs,propname,propvals) {581
582for (var i = objs.length-1; -1 < i ; i--) {583if (is_member(objs[i][propname],propvals)) {584return i;585}586}587return false;588}589
590function truthy(x) {591return (x !== false) && (x != null);592}593
594/////////////////////////////////////////////////////////////////////////////
595// this object defines the mode
596
597return {598startState:599function() {600return {tokenStack: [],601in_string: false,602in_atom: false};603},604
605token:606function(stream, state) {607return tokenizer(stream, state);608},609
610indent:611function(state, textAfter) {612return indenter(state,textAfter);613},614
615lineComment: "%"616};617});618
619});620