all repos — NoPaste @ 3e3c433f026c49e04d0efa0f0f39fc688ed32bb1

Resurrected - The PussTheCat.org fork of NoPaste

scripts/CodeMirror/mode/coffeescript/coffeescript.js (view raw)

  1// CodeMirror, copyright (c) by Marijn Haverbeke and others
  2// Distributed under an MIT license: https://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
 18CodeMirror.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// IANA registered media type
353// https://www.iana.org/assignments/media-types/
354CodeMirror.defineMIME("application/vnd.coffeescript", "coffeescript");
355
356CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
357CodeMirror.defineMIME("text/coffeescript", "coffeescript");
358
359});