|
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
/** * Link to the project's GitHub page: * https://github.com/pickhardt/coffeescript-codemirror-mode
*/ (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod); else // Plain browser env
mod(CodeMirror); })(function(CodeMirror) { "use strict";
CodeMirror.defineMode("coffeescript", function(conf, parserConf) { var ERRORCLASS = "error";
function wordRegexp(words) { return new RegExp("^((" + words.join(")|(") + "))\\b"); }
var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/; var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/; var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/; var atProp = /^@[_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", "extends"];
var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
indentKeywords = wordRegexp(indentKeywords);
var stringPrefixes = /^('{3}|\"{3}|['\"])/; var regexPrefixes = /^(\/{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()) { if (state.scope.align === null) state.scope.align = false; var scopeOffset = state.scope.offset; if (stream.eatSpace()) { var lineOffset = stream.indentation(); if (lineOffset > scopeOffset && state.scope.type == "coffee") { 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(), false, "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(), true, "string-2"); return state.tokenize(stream, state); } else { stream.backUp(1); } }
// Handle operators and delimiters
if (stream.match(operators) || stream.match(wordOperators)) { return "operator"; } if (stream.match(delimiters)) { return "punctuation"; }
if (stream.match(constants)) { return "atom"; }
if (stream.match(atProp) || state.prop && stream.match(identifiers)) { return "property"; }
if (stream.match(keywords)) { return "keyword"; }
if (stream.match(identifiers)) { return "variable"; }
// Handle non-detected items
stream.next(); return ERRORCLASS; }
function tokenFactory(delimiter, singleline, outclass) { 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 (parserConf.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 offset = 0, align = false, alignOffset = null; for (var scope = state.scope; scope; scope = scope.prev) { if (scope.type === "coffee" || scope.type == "}") { offset = scope.offset + conf.indentUnit; break; } } if (type !== "coffee") { align = null; alignOffset = stream.column() + stream.current().length; } else if (state.scope.align) { state.scope.align = false; } state.scope = { offset: offset, type: type, prev: state.scope, align: align, alignOffset: alignOffset }; }
function dedent(stream, state) { if (!state.scope.prev) return; if (state.scope.type === "coffee") { var _indent = stream.indentation(); var matched = false; for (var scope = state.scope; scope; scope = scope.prev) { if (_indent === scope.offset) { matched = true; break; } } if (!matched) { return true; } while (state.scope.prev && state.scope.offset !== _indent) { state.scope = state.scope.prev; } return false; } else { state.scope = state.scope.prev; return false; } }
function tokenLexer(stream, state) { var style = state.tokenize(stream, state); var current = stream.current();
// Handle scope changes.
if (current === "return") { state.dedent = true; } if (((current === "->" || current === "=>") && stream.eol()) || 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) { while (state.scope.type == "coffee" && state.scope.prev) state.scope = state.scope.prev; if (state.scope.type == current) state.scope = state.scope.prev; } if (state.dedent && stream.eol()) { if (state.scope.type == "coffee" && state.scope.prev) state.scope = state.scope.prev; state.dedent = false; }
return style; }
var external = { startState: function(basecolumn) { return { tokenize: tokenBase, scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false}, prop: false, dedent: 0 }; },
token: function(stream, state) { var fillAlign = state.scope.align === null && state.scope; if (fillAlign && stream.sol()) fillAlign.align = false;
var style = tokenLexer(stream, state); if (style && style != "comment") { if (fillAlign) fillAlign.align = true; state.prop = style == "punctuation" && stream.current() == "." }
return style; },
indent: function(state, text) { if (state.tokenize != tokenBase) return 0; var scope = state.scope; var closer = text && "])}".indexOf(text.charAt(0)) > -1; if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev; var closes = closer && scope.type === text.charAt(0); if (scope.align) return scope.alignOffset - (closes ? 1 : 0); else return (closes ? scope.prev : scope).offset; },
lineComment: "#", fold: "indent" }; return external; });
// IANA registered media type
// https://www.iana.org/assignments/media-types/
CodeMirror.defineMIME("application/vnd.coffeescript", "coffeescript");
CodeMirror.defineMIME("text/x-coffeescript", "coffeescript"); CodeMirror.defineMIME("text/coffeescript", "coffeescript");
});
|