OLD | NEW |
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others | 1 // CodeMirror, copyright (c) by Marijn Haverbeke and others |
2 // Distributed under an MIT license: http://codemirror.net/LICENSE | 2 // Distributed under an MIT license: http://codemirror.net/LICENSE |
3 | 3 |
4 /** | 4 /** |
5 * Link to the project's GitHub page: | 5 * Link to the project's GitHub page: |
6 * https://github.com/pickhardt/coffeescript-codemirror-mode | 6 * https://github.com/pickhardt/coffeescript-codemirror-mode |
7 */ | 7 */ |
8 (function(mod) { | 8 (function(mod) { |
9 if (typeof exports == "object" && typeof module == "object") // CommonJS | 9 if (typeof exports == "object" && typeof module == "object") // CommonJS |
10 mod(require("../../lib/codemirror")); | 10 mod(require("../../lib/codemirror")); |
11 else if (typeof define == "function" && define.amd) // AMD | 11 else if (typeof define == "function" && define.amd) // AMD |
12 define(["../../lib/codemirror"], mod); | 12 define(["../../lib/codemirror"], mod); |
13 else // Plain browser env | 13 else // Plain browser env |
14 mod(CodeMirror); | 14 mod(CodeMirror); |
15 })(function(CodeMirror) { | 15 })(function(CodeMirror) { |
16 "use strict"; | 16 "use strict"; |
17 | 17 |
18 CodeMirror.defineMode("coffeescript", function(conf) { | 18 CodeMirror.defineMode("coffeescript", function(conf, parserConf) { |
19 var ERRORCLASS = "error"; | 19 var ERRORCLASS = "error"; |
20 | 20 |
21 function wordRegexp(words) { | 21 function wordRegexp(words) { |
22 return new RegExp("^((" + words.join(")|(") + "))\\b"); | 22 return new RegExp("^((" + words.join(")|(") + "))\\b"); |
23 } | 23 } |
24 | 24 |
25 var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>
?=?|%=?|&=?|\|=?|\^=?|\~|!|\?)/; | 25 var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>
?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/; |
26 var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/; | 26 var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/; |
27 var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/; | 27 var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/; |
28 var properties = /^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*/; | 28 var atProp = /^@[_A-Za-z$][_A-Za-z$0-9]*/; |
29 | 29 |
30 var wordOperators = wordRegexp(["and", "or", "not", | 30 var wordOperators = wordRegexp(["and", "or", "not", |
31 "is", "isnt", "in", | 31 "is", "isnt", "in", |
32 "instanceof", "typeof"]); | 32 "instanceof", "typeof"]); |
33 var indentKeywords = ["for", "while", "loop", "if", "unless", "else", | 33 var indentKeywords = ["for", "while", "loop", "if", "unless", "else", |
34 "switch", "try", "catch", "finally", "class"]; | 34 "switch", "try", "catch", "finally", "class"]; |
35 var commonKeywords = ["break", "by", "continue", "debugger", "delete", | 35 var commonKeywords = ["break", "by", "continue", "debugger", "delete", |
36 "do", "in", "of", "new", "return", "then", | 36 "do", "in", "of", "new", "return", "then", |
37 "this", "throw", "when", "until"]; | 37 "this", "@", "throw", "when", "until", "extends"]; |
38 | 38 |
39 var keywords = wordRegexp(indentKeywords.concat(commonKeywords)); | 39 var keywords = wordRegexp(indentKeywords.concat(commonKeywords)); |
40 | 40 |
41 indentKeywords = wordRegexp(indentKeywords); | 41 indentKeywords = wordRegexp(indentKeywords); |
42 | 42 |
43 | 43 |
44 var stringPrefixes = /^('{3}|\"{3}|['\"])/; | 44 var stringPrefixes = /^('{3}|\"{3}|['\"])/; |
45 var regexPrefixes = /^(\/{3}|\/)/; | 45 var regexPrefixes = /^(\/{3}|\/)/; |
46 var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false"
, "on", "off", "yes", "no"]; | 46 var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false"
, "on", "off", "yes", "no"]; |
47 var constants = wordRegexp(commonConstants); | 47 var constants = wordRegexp(commonConstants); |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
138 // Handle regex literals | 138 // Handle regex literals |
139 if (stream.match(regexPrefixes)) { | 139 if (stream.match(regexPrefixes)) { |
140 if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent
highlight of division | 140 if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent
highlight of division |
141 state.tokenize = tokenFactory(stream.current(), true, "string-2"); | 141 state.tokenize = tokenFactory(stream.current(), true, "string-2"); |
142 return state.tokenize(stream, state); | 142 return state.tokenize(stream, state); |
143 } else { | 143 } else { |
144 stream.backUp(1); | 144 stream.backUp(1); |
145 } | 145 } |
146 } | 146 } |
147 | 147 |
| 148 |
| 149 |
148 // Handle operators and delimiters | 150 // Handle operators and delimiters |
149 if (stream.match(operators) || stream.match(wordOperators)) { | 151 if (stream.match(operators) || stream.match(wordOperators)) { |
150 return "operator"; | 152 return "operator"; |
151 } | 153 } |
152 if (stream.match(delimiters)) { | 154 if (stream.match(delimiters)) { |
153 return "punctuation"; | 155 return "punctuation"; |
154 } | 156 } |
155 | 157 |
156 if (stream.match(constants)) { | 158 if (stream.match(constants)) { |
157 return "atom"; | 159 return "atom"; |
158 } | 160 } |
159 | 161 |
| 162 if (stream.match(atProp) || state.prop && stream.match(identifiers)) { |
| 163 return "property"; |
| 164 } |
| 165 |
160 if (stream.match(keywords)) { | 166 if (stream.match(keywords)) { |
161 return "keyword"; | 167 return "keyword"; |
162 } | 168 } |
163 | 169 |
164 if (stream.match(identifiers)) { | 170 if (stream.match(identifiers)) { |
165 return "variable"; | 171 return "variable"; |
166 } | 172 } |
167 | 173 |
168 if (stream.match(properties)) { | |
169 return "property"; | |
170 } | |
171 | |
172 // Handle non-detected items | 174 // Handle non-detected items |
173 stream.next(); | 175 stream.next(); |
174 return ERRORCLASS; | 176 return ERRORCLASS; |
175 } | 177 } |
176 | 178 |
177 function tokenFactory(delimiter, singleline, outclass) { | 179 function tokenFactory(delimiter, singleline, outclass) { |
178 return function(stream, state) { | 180 return function(stream, state) { |
179 while (!stream.eol()) { | 181 while (!stream.eol()) { |
180 stream.eatWhile(/[^'"\/\\]/); | 182 stream.eatWhile(/[^'"\/\\]/); |
181 if (stream.eat("\\")) { | 183 if (stream.eat("\\")) { |
182 stream.next(); | 184 stream.next(); |
183 if (singleline && stream.eol()) { | 185 if (singleline && stream.eol()) { |
184 return outclass; | 186 return outclass; |
185 } | 187 } |
186 } else if (stream.match(delimiter)) { | 188 } else if (stream.match(delimiter)) { |
187 state.tokenize = tokenBase; | 189 state.tokenize = tokenBase; |
188 return outclass; | 190 return outclass; |
189 } else { | 191 } else { |
190 stream.eat(/['"\/]/); | 192 stream.eat(/['"\/]/); |
191 } | 193 } |
192 } | 194 } |
193 if (singleline) { | 195 if (singleline) { |
194 if (conf.mode.singleLineStringErrors) { | 196 if (parserConf.singleLineStringErrors) { |
195 outclass = ERRORCLASS; | 197 outclass = ERRORCLASS; |
196 } else { | 198 } else { |
197 state.tokenize = tokenBase; | 199 state.tokenize = tokenBase; |
198 } | 200 } |
199 } | 201 } |
200 return outclass; | 202 return outclass; |
201 }; | 203 }; |
202 } | 204 } |
203 | 205 |
204 function longComment(stream, state) { | 206 function longComment(stream, state) { |
205 while (!stream.eol()) { | 207 while (!stream.eol()) { |
206 stream.eatWhile(/[^#]/); | 208 stream.eatWhile(/[^#]/); |
207 if (stream.match("###")) { | 209 if (stream.match("###")) { |
208 state.tokenize = tokenBase; | 210 state.tokenize = tokenBase; |
209 break; | 211 break; |
210 } | 212 } |
211 stream.eatWhile("#"); | 213 stream.eatWhile("#"); |
212 } | 214 } |
213 return "comment"; | 215 return "comment"; |
214 } | 216 } |
215 | 217 |
216 function indent(stream, state, type) { | 218 function indent(stream, state, type) { |
217 type = type || "coffee"; | 219 type = type || "coffee"; |
218 var offset = 0, align = false, alignOffset = null; | 220 var offset = 0, align = false, alignOffset = null; |
219 for (var scope = state.scope; scope; scope = scope.prev) { | 221 for (var scope = state.scope; scope; scope = scope.prev) { |
220 if (scope.type === "coffee") { | 222 if (scope.type === "coffee" || scope.type == "}") { |
221 offset = scope.offset + conf.indentUnit; | 223 offset = scope.offset + conf.indentUnit; |
222 break; | 224 break; |
223 } | 225 } |
224 } | 226 } |
225 if (type !== "coffee") { | 227 if (type !== "coffee") { |
226 align = null; | 228 align = null; |
227 alignOffset = stream.column() + stream.current().length; | 229 alignOffset = stream.column() + stream.current().length; |
228 } else if (state.scope.align) { | 230 } else if (state.scope.align) { |
229 state.scope.align = false; | 231 state.scope.align = false; |
230 } | 232 } |
(...skipping 27 matching lines...) Expand all Loading... |
258 } else { | 260 } else { |
259 state.scope = state.scope.prev; | 261 state.scope = state.scope.prev; |
260 return false; | 262 return false; |
261 } | 263 } |
262 } | 264 } |
263 | 265 |
264 function tokenLexer(stream, state) { | 266 function tokenLexer(stream, state) { |
265 var style = state.tokenize(stream, state); | 267 var style = state.tokenize(stream, state); |
266 var current = stream.current(); | 268 var current = stream.current(); |
267 | 269 |
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. | 270 // Handle scope changes. |
280 if (current === "return") { | 271 if (current === "return") { |
281 state.dedent += 1; | 272 state.dedent = true; |
282 } | 273 } |
283 if (((current === "->" || current === "=>") && | 274 if (((current === "->" || current === "=>") && stream.eol()) |
284 !state.lambda && | |
285 !stream.peek()) | |
286 || style === "indent") { | 275 || style === "indent") { |
287 indent(stream, state); | 276 indent(stream, state); |
288 } | 277 } |
289 var delimiter_index = "[({".indexOf(current); | 278 var delimiter_index = "[({".indexOf(current); |
290 if (delimiter_index !== -1) { | 279 if (delimiter_index !== -1) { |
291 indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1)); | 280 indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1)); |
292 } | 281 } |
293 if (indentKeywords.exec(current)){ | 282 if (indentKeywords.exec(current)){ |
294 indent(stream, state); | 283 indent(stream, state); |
295 } | 284 } |
296 if (current == "then"){ | 285 if (current == "then"){ |
297 dedent(stream, state); | 286 dedent(stream, state); |
298 } | 287 } |
299 | 288 |
300 | 289 |
301 if (style === "dedent") { | 290 if (style === "dedent") { |
302 if (dedent(stream, state)) { | 291 if (dedent(stream, state)) { |
303 return ERRORCLASS; | 292 return ERRORCLASS; |
304 } | 293 } |
305 } | 294 } |
306 delimiter_index = "])}".indexOf(current); | 295 delimiter_index = "])}".indexOf(current); |
307 if (delimiter_index !== -1) { | 296 if (delimiter_index !== -1) { |
308 while (state.scope.type == "coffee" && state.scope.prev) | 297 while (state.scope.type == "coffee" && state.scope.prev) |
309 state.scope = state.scope.prev; | 298 state.scope = state.scope.prev; |
310 if (state.scope.type == current) | 299 if (state.scope.type == current) |
311 state.scope = state.scope.prev; | 300 state.scope = state.scope.prev; |
312 } | 301 } |
313 if (state.dedent > 0 && stream.eol() && state.scope.type == "coffee") { | 302 if (state.dedent && stream.eol()) { |
314 if (state.scope.prev) state.scope = state.scope.prev; | 303 if (state.scope.type == "coffee" && state.scope.prev) |
315 state.dedent -= 1; | 304 state.scope = state.scope.prev; |
| 305 state.dedent = false; |
316 } | 306 } |
317 | 307 |
318 return style; | 308 return style; |
319 } | 309 } |
320 | 310 |
321 var external = { | 311 var external = { |
322 startState: function(basecolumn) { | 312 startState: function(basecolumn) { |
323 return { | 313 return { |
324 tokenize: tokenBase, | 314 tokenize: tokenBase, |
325 scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false}
, | 315 scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false}
, |
326 lastToken: null, | 316 prop: false, |
327 lambda: false, | |
328 dedent: 0 | 317 dedent: 0 |
329 }; | 318 }; |
330 }, | 319 }, |
331 | 320 |
332 token: function(stream, state) { | 321 token: function(stream, state) { |
333 var fillAlign = state.scope.align === null && state.scope; | 322 var fillAlign = state.scope.align === null && state.scope; |
334 if (fillAlign && stream.sol()) fillAlign.align = false; | 323 if (fillAlign && stream.sol()) fillAlign.align = false; |
335 | 324 |
336 var style = tokenLexer(stream, state); | 325 var style = tokenLexer(stream, state); |
337 if (fillAlign && style && style != "comment") fillAlign.align = true; | 326 if (style && style != "comment") { |
338 | 327 if (fillAlign) fillAlign.align = true; |
339 state.lastToken = {style:style, content: stream.current()}; | 328 state.prop = style == "punctuation" && stream.current() == "." |
340 | |
341 if (stream.eol() && stream.lambda) { | |
342 state.lambda = false; | |
343 } | 329 } |
344 | 330 |
345 return style; | 331 return style; |
346 }, | 332 }, |
347 | 333 |
348 indent: function(state, text) { | 334 indent: function(state, text) { |
349 if (state.tokenize != tokenBase) return 0; | 335 if (state.tokenize != tokenBase) return 0; |
350 var scope = state.scope; | 336 var scope = state.scope; |
351 var closer = text && "])}".indexOf(text.charAt(0)) > -1; | 337 var closer = text && "])}".indexOf(text.charAt(0)) > -1; |
352 if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.pre
v; | 338 if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.pre
v; |
353 var closes = closer && scope.type === text.charAt(0); | 339 var closes = closer && scope.type === text.charAt(0); |
354 if (scope.align) | 340 if (scope.align) |
355 return scope.alignOffset - (closes ? 1 : 0); | 341 return scope.alignOffset - (closes ? 1 : 0); |
356 else | 342 else |
357 return (closes ? scope.prev : scope).offset; | 343 return (closes ? scope.prev : scope).offset; |
358 }, | 344 }, |
359 | 345 |
360 lineComment: "#", | 346 lineComment: "#", |
361 fold: "indent" | 347 fold: "indent" |
362 }; | 348 }; |
363 return external; | 349 return external; |
364 }); | 350 }); |
365 | 351 |
366 CodeMirror.defineMIME("text/x-coffeescript", "coffeescript"); | 352 CodeMirror.defineMIME("text/x-coffeescript", "coffeescript"); |
| 353 CodeMirror.defineMIME("text/coffeescript", "coffeescript"); |
367 | 354 |
368 }); | 355 }); |
OLD | NEW |