| 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 |