Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
776 lars 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 properties = /^(@|this\.)[_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
    // Handle operators and delimiters
149
    if (stream.match(operators) || stream.match(wordOperators)) {
150
      return "operator";
151
    }
152
    if (stream.match(delimiters)) {
153
      return "punctuation";
154
    }
155
 
156
    if (stream.match(constants)) {
157
      return "atom";
158
    }
159
 
160
    if (stream.match(keywords)) {
161
      return "keyword";
162
    }
163
 
164
    if (stream.match(identifiers)) {
165
      return "variable";
166
    }
167
 
168
    if (stream.match(properties)) {
169
      return "property";
170
    }
171
 
172
    // Handle non-detected items
173
    stream.next();
174
    return ERRORCLASS;
175
  }
176
 
177
  function tokenFactory(delimiter, singleline, outclass) {
178
    return function(stream, state) {
179
      while (!stream.eol()) {
180
        stream.eatWhile(/[^'"\/\\]/);
181
        if (stream.eat("\\")) {
182
          stream.next();
183
          if (singleline && stream.eol()) {
184
            return outclass;
185
          }
186
        } else if (stream.match(delimiter)) {
187
          state.tokenize = tokenBase;
188
          return outclass;
189
        } else {
190
          stream.eat(/['"\/]/);
191
        }
192
      }
193
      if (singleline) {
194
        if (parserConf.singleLineStringErrors) {
195
          outclass = ERRORCLASS;
196
        } else {
197
          state.tokenize = tokenBase;
198
        }
199
      }
200
      return outclass;
201
    };
202
  }
203
 
204
  function longComment(stream, state) {
205
    while (!stream.eol()) {
206
      stream.eatWhile(/[^#]/);
207
      if (stream.match("###")) {
208
        state.tokenize = tokenBase;
209
        break;
210
      }
211
      stream.eatWhile("#");
212
    }
213
    return "comment";
214
  }
215
 
216
  function indent(stream, state, type) {
217
    type = type || "coffee";
218
    var offset = 0, align = false, alignOffset = null;
219
    for (var scope = state.scope; scope; scope = scope.prev) {
220
      if (scope.type === "coffee" || scope.type == "}") {
221
        offset = scope.offset + conf.indentUnit;
222
        break;
223
      }
224
    }
225
    if (type !== "coffee") {
226
      align = null;
227
      alignOffset = stream.column() + stream.current().length;
228
    } else if (state.scope.align) {
229
      state.scope.align = false;
230
    }
231
    state.scope = {
232
      offset: offset,
233
      type: type,
234
      prev: state.scope,
235
      align: align,
236
      alignOffset: alignOffset
237
    };
238
  }
239
 
240
  function dedent(stream, state) {
241
    if (!state.scope.prev) return;
242
    if (state.scope.type === "coffee") {
243
      var _indent = stream.indentation();
244
      var matched = false;
245
      for (var scope = state.scope; scope; scope = scope.prev) {
246
        if (_indent === scope.offset) {
247
          matched = true;
248
          break;
249
        }
250
      }
251
      if (!matched) {
252
        return true;
253
      }
254
      while (state.scope.prev && state.scope.offset !== _indent) {
255
        state.scope = state.scope.prev;
256
      }
257
      return false;
258
    } else {
259
      state.scope = state.scope.prev;
260
      return false;
261
    }
262
  }
263
 
264
  function tokenLexer(stream, state) {
265
    var style = state.tokenize(stream, state);
266
    var current = stream.current();
267
 
268
    // Handle "." connected identifiers
269
    if (current === ".") {
270
      style = state.tokenize(stream, state);
271
      current = stream.current();
272
      if (/^\.[\w$]+$/.test(current)) {
273
        return "variable";
274
      } else {
275
        return ERRORCLASS;
276
      }
277
    }
278
 
279
    // Handle scope changes.
280
    if (current === "return") {
281
      state.dedent = true;
282
    }
283
    if (((current === "->" || current === "=>") &&
284
         !state.lambda &&
285
         !stream.peek())
286
        || style === "indent") {
287
      indent(stream, state);
288
    }
289
    var delimiter_index = "[({".indexOf(current);
290
    if (delimiter_index !== -1) {
291
      indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
292
    }
293
    if (indentKeywords.exec(current)){
294
      indent(stream, state);
295
    }
296
    if (current == "then"){
297
      dedent(stream, state);
298
    }
299
 
300
 
301
    if (style === "dedent") {
302
      if (dedent(stream, state)) {
303
        return ERRORCLASS;
304
      }
305
    }
306
    delimiter_index = "])}".indexOf(current);
307
    if (delimiter_index !== -1) {
308
      while (state.scope.type == "coffee" && state.scope.prev)
309
        state.scope = state.scope.prev;
310
      if (state.scope.type == current)
311
        state.scope = state.scope.prev;
312
    }
313
    if (state.dedent && stream.eol()) {
314
      if (state.scope.type == "coffee" && state.scope.prev)
315
        state.scope = state.scope.prev;
316
      state.dedent = false;
317
    }
318
 
319
    return style;
320
  }
321
 
322
  var external = {
323
    startState: function(basecolumn) {
324
      return {
325
        tokenize: tokenBase,
326
        scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
327
        lastToken: null,
328
        lambda: false,
329
        dedent: 0
330
      };
331
    },
332
 
333
    token: function(stream, state) {
334
      var fillAlign = state.scope.align === null && state.scope;
335
      if (fillAlign && stream.sol()) fillAlign.align = false;
336
 
337
      var style = tokenLexer(stream, state);
338
      if (fillAlign && style && style != "comment") fillAlign.align = true;
339
 
340
      state.lastToken = {style:style, content: stream.current()};
341
 
342
      if (stream.eol() && stream.lambda) {
343
        state.lambda = false;
344
      }
345
 
346
      return style;
347
    },
348
 
349
    indent: function(state, text) {
350
      if (state.tokenize != tokenBase) return 0;
351
      var scope = state.scope;
352
      var closer = text && "])}".indexOf(text.charAt(0)) > -1;
353
      if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
354
      var closes = closer && scope.type === text.charAt(0);
355
      if (scope.align)
356
        return scope.alignOffset - (closes ? 1 : 0);
357
      else
358
        return (closes ? scope.prev : scope).offset;
359
    },
360
 
361
    lineComment: "#",
362
    fold: "indent"
363
  };
364
  return external;
365
});
366
 
367
CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
368
 
369
});