Spaces:
Running
Running
| /** | |
| * Link to the project's GitHub page: | |
| * https://github.com/pickhardt/coffeescript-codemirror-mode | |
| */ | |
| CodeMirror.defineMode('coffeescript', function(conf) { | |
| var ERRORCLASS = 'error'; | |
| function wordRegexp(words) { | |
| return new RegExp("^((" + words.join(")|(") + "))\\b"); | |
| } | |
| var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\?]"); | |
| var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\},:`=;\\.]'); | |
| var doubleOperators = new RegExp("^((\->)|(\=>)|(\\+\\+)|(\\+\\=)|(\\-\\-)|(\\-\\=)|(\\*\\*)|(\\*\\=)|(\\/\\/)|(\\/\\=)|(==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//))"); | |
| var doubleDelimiters = new RegExp("^((\\.\\.)|(\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))"); | |
| var tripleDelimiters = new RegExp("^((\\.\\.\\.)|(//=)|(>>=)|(<<=)|(\\*\\*=))"); | |
| var identifiers = new RegExp("^[_A-Za-z$][_A-Za-z$0-9]*"); | |
| var properties = new RegExp("^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*"); | |
| var wordOperators = wordRegexp(['and', 'or', 'not', | |
| 'is', 'isnt', 'in', | |
| 'instanceof', 'typeof']); | |
| var indentKeywords = ['for', 'while', 'loop', 'if', 'unless', 'else', | |
| 'switch', 'try', 'catch', 'finally', 'class']; | |
| var commonKeywords = ['break', 'by', 'continue', 'debugger', 'delete', | |
| 'do', 'in', 'of', 'new', 'return', 'then', | |
| 'this', 'throw', 'when', 'until']; | |
| var keywords = wordRegexp(indentKeywords.concat(commonKeywords)); | |
| indentKeywords = wordRegexp(indentKeywords); | |
| var stringPrefixes = new RegExp("^('{3}|\"{3}|['\"])"); | |
| var regexPrefixes = new RegExp("^(/{3}|/)"); | |
| var commonConstants = ['Infinity', 'NaN', 'undefined', 'null', 'true', 'false', 'on', 'off', 'yes', 'no']; | |
| var constants = wordRegexp(commonConstants); | |
| // Tokenizers | |
| function tokenBase(stream, state) { | |
| // Handle scope changes | |
| if (stream.sol()) { | |
| var scopeOffset = state.scopes[0].offset; | |
| if (stream.eatSpace()) { | |
| var lineOffset = stream.indentation(); | |
| if (lineOffset > scopeOffset) { | |
| return 'indent'; | |
| } else if (lineOffset < scopeOffset) { | |
| return 'dedent'; | |
| } | |
| return null; | |
| } else { | |
| if (scopeOffset > 0) { | |
| dedent(stream, state); | |
| } | |
| } | |
| } | |
| if (stream.eatSpace()) { | |
| return null; | |
| } | |
| var ch = stream.peek(); | |
| // Handle docco title comment (single line) | |
| if (stream.match("####")) { | |
| stream.skipToEnd(); | |
| return 'comment'; | |
| } | |
| // Handle multi line comments | |
| if (stream.match("###")) { | |
| state.tokenize = longComment; | |
| return state.tokenize(stream, state); | |
| } | |
| // Single line comment | |
| if (ch === '#') { | |
| stream.skipToEnd(); | |
| return 'comment'; | |
| } | |
| // Handle number literals | |
| if (stream.match(/^-?[0-9\.]/, false)) { | |
| var floatLiteral = false; | |
| // Floats | |
| if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) { | |
| floatLiteral = true; | |
| } | |
| if (stream.match(/^-?\d+\.\d*/)) { | |
| floatLiteral = true; | |
| } | |
| if (stream.match(/^-?\.\d+/)) { | |
| floatLiteral = true; | |
| } | |
| if (floatLiteral) { | |
| // prevent from getting extra . on 1.. | |
| if (stream.peek() == "."){ | |
| stream.backUp(1); | |
| } | |
| return 'number'; | |
| } | |
| // Integers | |
| var intLiteral = false; | |
| // Hex | |
| if (stream.match(/^-?0x[0-9a-f]+/i)) { | |
| intLiteral = true; | |
| } | |
| // Decimal | |
| if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) { | |
| intLiteral = true; | |
| } | |
| // Zero by itself with no other piece of number. | |
| if (stream.match(/^-?0(?![\dx])/i)) { | |
| intLiteral = true; | |
| } | |
| if (intLiteral) { | |
| return 'number'; | |
| } | |
| } | |
| // Handle strings | |
| if (stream.match(stringPrefixes)) { | |
| state.tokenize = tokenFactory(stream.current(), 'string'); | |
| return state.tokenize(stream, state); | |
| } | |
| // Handle regex literals | |
| if (stream.match(regexPrefixes)) { | |
| if (stream.current() != '/' || stream.match(/^.*\//, false)) { // prevent highlight of division | |
| state.tokenize = tokenFactory(stream.current(), 'string-2'); | |
| return state.tokenize(stream, state); | |
| } else { | |
| stream.backUp(1); | |
| } | |
| } | |
| // Handle operators and delimiters | |
| if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) { | |
| return 'punctuation'; | |
| } | |
| if (stream.match(doubleOperators) | |
| || stream.match(singleOperators) | |
| || stream.match(wordOperators)) { | |
| return 'operator'; | |
| } | |
| if (stream.match(singleDelimiters)) { | |
| return 'punctuation'; | |
| } | |
| if (stream.match(constants)) { | |
| return 'atom'; | |
| } | |
| if (stream.match(keywords)) { | |
| return 'keyword'; | |
| } | |
| if (stream.match(identifiers)) { | |
| return 'variable'; | |
| } | |
| if (stream.match(properties)) { | |
| return 'property'; | |
| } | |
| // Handle non-detected items | |
| stream.next(); | |
| return ERRORCLASS; | |
| } | |
| function tokenFactory(delimiter, outclass) { | |
| var singleline = delimiter.length == 1; | |
| return function(stream, state) { | |
| while (!stream.eol()) { | |
| stream.eatWhile(/[^'"\/\\]/); | |
| if (stream.eat('\\')) { | |
| stream.next(); | |
| if (singleline && stream.eol()) { | |
| return outclass; | |
| } | |
| } else if (stream.match(delimiter)) { | |
| state.tokenize = tokenBase; | |
| return outclass; | |
| } else { | |
| stream.eat(/['"\/]/); | |
| } | |
| } | |
| if (singleline) { | |
| if (conf.mode.singleLineStringErrors) { | |
| outclass = ERRORCLASS; | |
| } else { | |
| state.tokenize = tokenBase; | |
| } | |
| } | |
| return outclass; | |
| }; | |
| } | |
| function longComment(stream, state) { | |
| while (!stream.eol()) { | |
| stream.eatWhile(/[^#]/); | |
| if (stream.match("###")) { | |
| state.tokenize = tokenBase; | |
| break; | |
| } | |
| stream.eatWhile("#"); | |
| } | |
| return "comment"; | |
| } | |
| function indent(stream, state, type) { | |
| type = type || 'coffee'; | |
| var indentUnit = 0; | |
| if (type === 'coffee') { | |
| for (var i = 0; i < state.scopes.length; i++) { | |
| if (state.scopes[i].type === 'coffee') { | |
| indentUnit = state.scopes[i].offset + conf.indentUnit; | |
| break; | |
| } | |
| } | |
| } else { | |
| indentUnit = stream.column() + stream.current().length; | |
| } | |
| state.scopes.unshift({ | |
| offset: indentUnit, | |
| type: type | |
| }); | |
| } | |
| function dedent(stream, state) { | |
| if (state.scopes.length == 1) return; | |
| if (state.scopes[0].type === 'coffee') { | |
| var _indent = stream.indentation(); | |
| var _indent_index = -1; | |
| for (var i = 0; i < state.scopes.length; ++i) { | |
| if (_indent === state.scopes[i].offset) { | |
| _indent_index = i; | |
| break; | |
| } | |
| } | |
| if (_indent_index === -1) { | |
| return true; | |
| } | |
| while (state.scopes[0].offset !== _indent) { | |
| state.scopes.shift(); | |
| } | |
| return false; | |
| } else { | |
| state.scopes.shift(); | |
| return false; | |
| } | |
| } | |
| function tokenLexer(stream, state) { | |
| var style = state.tokenize(stream, state); | |
| var current = stream.current(); | |
| // Handle '.' connected identifiers | |
| if (current === '.') { | |
| style = state.tokenize(stream, state); | |
| current = stream.current(); | |
| if (style === 'variable') { | |
| return 'variable'; | |
| } else { | |
| return ERRORCLASS; | |
| } | |
| } | |
| // Handle scope changes. | |
| if (current === 'return') { | |
| state.dedent += 1; | |
| } | |
| if (((current === '->' || current === '=>') && | |
| !state.lambda && | |
| state.scopes[0].type == 'coffee' && | |
| stream.peek() === '') | |
| || style === 'indent') { | |
| indent(stream, state); | |
| } | |
| var delimiter_index = '[({'.indexOf(current); | |
| if (delimiter_index !== -1) { | |
| indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1)); | |
| } | |
| if (indentKeywords.exec(current)){ | |
| indent(stream, state); | |
| } | |
| if (current == 'then'){ | |
| dedent(stream, state); | |
| } | |
| if (style === 'dedent') { | |
| if (dedent(stream, state)) { | |
| return ERRORCLASS; | |
| } | |
| } | |
| delimiter_index = '])}'.indexOf(current); | |
| if (delimiter_index !== -1) { | |
| if (dedent(stream, state)) { | |
| return ERRORCLASS; | |
| } | |
| } | |
| if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'coffee') { | |
| if (state.scopes.length > 1) state.scopes.shift(); | |
| state.dedent -= 1; | |
| } | |
| return style; | |
| } | |
| var external = { | |
| startState: function(basecolumn) { | |
| return { | |
| tokenize: tokenBase, | |
| scopes: [{offset:basecolumn || 0, type:'coffee'}], | |
| lastToken: null, | |
| lambda: false, | |
| dedent: 0 | |
| }; | |
| }, | |
| token: function(stream, state) { | |
| var style = tokenLexer(stream, state); | |
| state.lastToken = {style:style, content: stream.current()}; | |
| if (stream.eol() && stream.lambda) { | |
| state.lambda = false; | |
| } | |
| return style; | |
| }, | |
| indent: function(state) { | |
| if (state.tokenize != tokenBase) { | |
| return 0; | |
| } | |
| return state.scopes[0].offset; | |
| } | |
| }; | |
| return external; | |
| }); | |
| CodeMirror.defineMIME('text/x-coffeescript', 'coffeescript'); | |