github.com/jancarloviray/community@v0.41.1-0.20170124221257-33a66c87cf2f/app/public/codemirror/mode/coffeescript/coffeescript.js (about)

     1  // CodeMirror, copyright (c) by Marijn Haverbeke and others
     2  // Distributed under an MIT license: http://codemirror.net/LICENSE
     3  
     4  /**
     5   * Link to the project's GitHub page:
     6   * https://github.com/pickhardt/coffeescript-codemirror-mode
     7   */
     8  (function(mod) {
     9    if (typeof exports == "object" && typeof module == "object") // CommonJS
    10      mod(require("../../lib/codemirror"));
    11    else if (typeof define == "function" && define.amd) // AMD
    12      define(["../../lib/codemirror"], mod);
    13    else // Plain browser env
    14      mod(CodeMirror);
    15  })(function(CodeMirror) {
    16  "use strict";
    17  
    18  CodeMirror.defineMode("coffeescript", function(conf, parserConf) {
    19    var ERRORCLASS = "error";
    20  
    21    function wordRegexp(words) {
    22      return new RegExp("^((" + words.join(")|(") + "))\\b");
    23    }
    24  
    25    var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/;
    26    var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/;
    27    var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;
    28    var atProp = /^@[_A-Za-z$][_A-Za-z$0-9]*/;
    29  
    30    var wordOperators = wordRegexp(["and", "or", "not",
    31                                    "is", "isnt", "in",
    32                                    "instanceof", "typeof"]);
    33    var indentKeywords = ["for", "while", "loop", "if", "unless", "else",
    34                          "switch", "try", "catch", "finally", "class"];
    35    var commonKeywords = ["break", "by", "continue", "debugger", "delete",
    36                          "do", "in", "of", "new", "return", "then",
    37                          "this", "@", "throw", "when", "until", "extends"];
    38  
    39    var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
    40  
    41    indentKeywords = wordRegexp(indentKeywords);
    42  
    43  
    44    var stringPrefixes = /^('{3}|\"{3}|['\"])/;
    45    var regexPrefixes = /^(\/{3}|\/)/;
    46    var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"];
    47    var constants = wordRegexp(commonConstants);
    48  
    49    // Tokenizers
    50    function tokenBase(stream, state) {
    51      // Handle scope changes
    52      if (stream.sol()) {
    53        if (state.scope.align === null) state.scope.align = false;
    54        var scopeOffset = state.scope.offset;
    55        if (stream.eatSpace()) {
    56          var lineOffset = stream.indentation();
    57          if (lineOffset > scopeOffset && state.scope.type == "coffee") {
    58            return "indent";
    59          } else if (lineOffset < scopeOffset) {
    60            return "dedent";
    61          }
    62          return null;
    63        } else {
    64          if (scopeOffset > 0) {
    65            dedent(stream, state);
    66          }
    67        }
    68      }
    69      if (stream.eatSpace()) {
    70        return null;
    71      }
    72  
    73      var ch = stream.peek();
    74  
    75      // Handle docco title comment (single line)
    76      if (stream.match("####")) {
    77        stream.skipToEnd();
    78        return "comment";
    79      }
    80  
    81      // Handle multi line comments
    82      if (stream.match("###")) {
    83        state.tokenize = longComment;
    84        return state.tokenize(stream, state);
    85      }
    86  
    87      // Single line comment
    88      if (ch === "#") {
    89        stream.skipToEnd();
    90        return "comment";
    91      }
    92  
    93      // Handle number literals
    94      if (stream.match(/^-?[0-9\.]/, false)) {
    95        var floatLiteral = false;
    96        // Floats
    97        if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
    98          floatLiteral = true;
    99        }
   100        if (stream.match(/^-?\d+\.\d*/)) {
   101          floatLiteral = true;
   102        }
   103        if (stream.match(/^-?\.\d+/)) {
   104          floatLiteral = true;
   105        }
   106  
   107        if (floatLiteral) {
   108          // prevent from getting extra . on 1..
   109          if (stream.peek() == "."){
   110            stream.backUp(1);
   111          }
   112          return "number";
   113        }
   114        // Integers
   115        var intLiteral = false;
   116        // Hex
   117        if (stream.match(/^-?0x[0-9a-f]+/i)) {
   118          intLiteral = true;
   119        }
   120        // Decimal
   121        if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
   122          intLiteral = true;
   123        }
   124        // Zero by itself with no other piece of number.
   125        if (stream.match(/^-?0(?![\dx])/i)) {
   126          intLiteral = true;
   127        }
   128        if (intLiteral) {
   129          return "number";
   130        }
   131      }
   132  
   133      // Handle strings
   134      if (stream.match(stringPrefixes)) {
   135        state.tokenize = tokenFactory(stream.current(), false, "string");
   136        return state.tokenize(stream, state);
   137      }
   138      // Handle regex literals
   139      if (stream.match(regexPrefixes)) {
   140        if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
   141          state.tokenize = tokenFactory(stream.current(), true, "string-2");
   142          return state.tokenize(stream, state);
   143        } else {
   144          stream.backUp(1);
   145        }
   146      }
   147  
   148  
   149  
   150      // Handle operators and delimiters
   151      if (stream.match(operators) || stream.match(wordOperators)) {
   152        return "operator";
   153      }
   154      if (stream.match(delimiters)) {
   155        return "punctuation";
   156      }
   157  
   158      if (stream.match(constants)) {
   159        return "atom";
   160      }
   161  
   162      if (stream.match(atProp) || state.prop && stream.match(identifiers)) {
   163        return "property";
   164      }
   165  
   166      if (stream.match(keywords)) {
   167        return "keyword";
   168      }
   169  
   170      if (stream.match(identifiers)) {
   171        return "variable";
   172      }
   173  
   174      // Handle non-detected items
   175      stream.next();
   176      return ERRORCLASS;
   177    }
   178  
   179    function tokenFactory(delimiter, singleline, outclass) {
   180      return function(stream, state) {
   181        while (!stream.eol()) {
   182          stream.eatWhile(/[^'"\/\\]/);
   183          if (stream.eat("\\")) {
   184            stream.next();
   185            if (singleline && stream.eol()) {
   186              return outclass;
   187            }
   188          } else if (stream.match(delimiter)) {
   189            state.tokenize = tokenBase;
   190            return outclass;
   191          } else {
   192            stream.eat(/['"\/]/);
   193          }
   194        }
   195        if (singleline) {
   196          if (parserConf.singleLineStringErrors) {
   197            outclass = ERRORCLASS;
   198          } else {
   199            state.tokenize = tokenBase;
   200          }
   201        }
   202        return outclass;
   203      };
   204    }
   205  
   206    function longComment(stream, state) {
   207      while (!stream.eol()) {
   208        stream.eatWhile(/[^#]/);
   209        if (stream.match("###")) {
   210          state.tokenize = tokenBase;
   211          break;
   212        }
   213        stream.eatWhile("#");
   214      }
   215      return "comment";
   216    }
   217  
   218    function indent(stream, state, type) {
   219      type = type || "coffee";
   220      var offset = 0, align = false, alignOffset = null;
   221      for (var scope = state.scope; scope; scope = scope.prev) {
   222        if (scope.type === "coffee" || scope.type == "}") {
   223          offset = scope.offset + conf.indentUnit;
   224          break;
   225        }
   226      }
   227      if (type !== "coffee") {
   228        align = null;
   229        alignOffset = stream.column() + stream.current().length;
   230      } else if (state.scope.align) {
   231        state.scope.align = false;
   232      }
   233      state.scope = {
   234        offset: offset,
   235        type: type,
   236        prev: state.scope,
   237        align: align,
   238        alignOffset: alignOffset
   239      };
   240    }
   241  
   242    function dedent(stream, state) {
   243      if (!state.scope.prev) return;
   244      if (state.scope.type === "coffee") {
   245        var _indent = stream.indentation();
   246        var matched = false;
   247        for (var scope = state.scope; scope; scope = scope.prev) {
   248          if (_indent === scope.offset) {
   249            matched = true;
   250            break;
   251          }
   252        }
   253        if (!matched) {
   254          return true;
   255        }
   256        while (state.scope.prev && state.scope.offset !== _indent) {
   257          state.scope = state.scope.prev;
   258        }
   259        return false;
   260      } else {
   261        state.scope = state.scope.prev;
   262        return false;
   263      }
   264    }
   265  
   266    function tokenLexer(stream, state) {
   267      var style = state.tokenize(stream, state);
   268      var current = stream.current();
   269  
   270      // Handle scope changes.
   271      if (current === "return") {
   272        state.dedent = true;
   273      }
   274      if (((current === "->" || current === "=>") && stream.eol())
   275          || style === "indent") {
   276        indent(stream, state);
   277      }
   278      var delimiter_index = "[({".indexOf(current);
   279      if (delimiter_index !== -1) {
   280        indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
   281      }
   282      if (indentKeywords.exec(current)){
   283        indent(stream, state);
   284      }
   285      if (current == "then"){
   286        dedent(stream, state);
   287      }
   288  
   289  
   290      if (style === "dedent") {
   291        if (dedent(stream, state)) {
   292          return ERRORCLASS;
   293        }
   294      }
   295      delimiter_index = "])}".indexOf(current);
   296      if (delimiter_index !== -1) {
   297        while (state.scope.type == "coffee" && state.scope.prev)
   298          state.scope = state.scope.prev;
   299        if (state.scope.type == current)
   300          state.scope = state.scope.prev;
   301      }
   302      if (state.dedent && stream.eol()) {
   303        if (state.scope.type == "coffee" && state.scope.prev)
   304          state.scope = state.scope.prev;
   305        state.dedent = false;
   306      }
   307  
   308      return style;
   309    }
   310  
   311    var external = {
   312      startState: function(basecolumn) {
   313        return {
   314          tokenize: tokenBase,
   315          scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
   316          prop: false,
   317          dedent: 0
   318        };
   319      },
   320  
   321      token: function(stream, state) {
   322        var fillAlign = state.scope.align === null && state.scope;
   323        if (fillAlign && stream.sol()) fillAlign.align = false;
   324  
   325        var style = tokenLexer(stream, state);
   326        if (style && style != "comment") {
   327          if (fillAlign) fillAlign.align = true;
   328          state.prop = style == "punctuation" && stream.current() == "."
   329        }
   330  
   331        return style;
   332      },
   333  
   334      indent: function(state, text) {
   335        if (state.tokenize != tokenBase) return 0;
   336        var scope = state.scope;
   337        var closer = text && "])}".indexOf(text.charAt(0)) > -1;
   338        if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
   339        var closes = closer && scope.type === text.charAt(0);
   340        if (scope.align)
   341          return scope.alignOffset - (closes ? 1 : 0);
   342        else
   343          return (closes ? scope.prev : scope).offset;
   344      },
   345  
   346      lineComment: "#",
   347      fold: "indent"
   348    };
   349    return external;
   350  });
   351  
   352  CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
   353  CodeMirror.defineMIME("text/coffeescript", "coffeescript");
   354  
   355  });