Blame | Letzte Änderung | Log anzeigen | RSS feed
// CodeMirror, copyright (c) by Marijn Haverbeke and others// Distributed under an MIT license: http://codemirror.net/LICENSE// Slim Highlighting for CodeMirror copyright (c) HicknHack Software Gmbh(function(mod) {if (typeof exports == "object" && typeof module == "object") // CommonJSmod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby"));else if (typeof define == "function" && define.amd) // AMDdefine(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../ruby/ruby"], mod);else // Plain browser envmod(CodeMirror);})(function(CodeMirror) {"use strict";CodeMirror.defineMode("slim", function(config) {var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"});var rubyMode = CodeMirror.getMode(config, "ruby");var modes = { html: htmlMode, ruby: rubyMode };var embedded = {ruby: "ruby",javascript: "javascript",css: "text/css",sass: "text/x-sass",scss: "text/x-scss",less: "text/x-less",styl: "text/x-styl", // no highlighting so farcoffee: "coffeescript",asciidoc: "text/x-asciidoc",markdown: "text/x-markdown",textile: "text/x-textile", // no highlighting so farcreole: "text/x-creole", // no highlighting so farwiki: "text/x-wiki", // no highlighting so farmediawiki: "text/x-mediawiki", // no highlighting so farrdoc: "text/x-rdoc", // no highlighting so farbuilder: "text/x-builder", // no highlighting so farnokogiri: "text/x-nokogiri", // no highlighting so farerb: "application/x-erb"};var embeddedRegexp = function(map){var arr = [];for(var key in map) arr.push(key);return new RegExp("^("+arr.join('|')+"):");}(embedded);var styleMap = {"commentLine": "comment","slimSwitch": "operator special","slimTag": "tag","slimId": "attribute def","slimClass": "attribute qualifier","slimAttribute": "attribute","slimSubmode": "keyword special","closeAttributeTag": null,"slimDoctype": null,"lineContinuation": null};var closing = {"{": "}","[": "]","(": ")"};var nameStartChar = "_a-zA-Z\xC0-\xD6\xD8-\xF6\xF8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";var nameChar = nameStartChar + "\\-0-9\xB7\u0300-\u036F\u203F-\u2040";var nameRegexp = new RegExp("^[:"+nameStartChar+"](?::["+nameChar+"]|["+nameChar+"]*)");var attributeNameRegexp = new RegExp("^[:"+nameStartChar+"][:\\."+nameChar+"]*(?=\\s*=)");var wrappedAttributeNameRegexp = new RegExp("^[:"+nameStartChar+"][:\\."+nameChar+"]*");var classNameRegexp = /^\.-?[_a-zA-Z]+[\w\-]*/;var classIdRegexp = /^#[_a-zA-Z]+[\w\-]*/;function backup(pos, tokenize, style) {var restore = function(stream, state) {state.tokenize = tokenize;if (stream.pos < pos) {stream.pos = pos;return style;}return state.tokenize(stream, state);};return function(stream, state) {state.tokenize = restore;return tokenize(stream, state);};}function maybeBackup(stream, state, pat, offset, style) {var cur = stream.current();var idx = cur.search(pat);if (idx > -1) {state.tokenize = backup(stream.pos, state.tokenize, style);stream.backUp(cur.length - idx - offset);}return style;}function continueLine(state, column) {state.stack = {parent: state.stack,style: "continuation",indented: column,tokenize: state.line};state.line = state.tokenize;}function finishContinue(state) {if (state.line == state.tokenize) {state.line = state.stack.tokenize;state.stack = state.stack.parent;}}function lineContinuable(column, tokenize) {return function(stream, state) {finishContinue(state);if (stream.match(/^\\$/)) {continueLine(state, column);return "lineContinuation";}var style = tokenize(stream, state);if (stream.eol() && stream.current().match(/(?:^|[^\\])(?:\\\\)*\\$/)) {stream.backUp(1);}return style;};}function commaContinuable(column, tokenize) {return function(stream, state) {finishContinue(state);var style = tokenize(stream, state);if (stream.eol() && stream.current().match(/,$/)) {continueLine(state, column);}return style;};}function rubyInQuote(endQuote, tokenize) {// TODO: add multi line supportreturn function(stream, state) {var ch = stream.peek();if (ch == endQuote && state.rubyState.tokenize.length == 1) {// step out of ruby context as it seems to complete processing all the bracesstream.next();state.tokenize = tokenize;return "closeAttributeTag";} else {return ruby(stream, state);}};}function startRubySplat(tokenize) {var rubyState;var runSplat = function(stream, state) {if (state.rubyState.tokenize.length == 1 && !state.rubyState.context.prev) {stream.backUp(1);if (stream.eatSpace()) {state.rubyState = rubyState;state.tokenize = tokenize;return tokenize(stream, state);}stream.next();}return ruby(stream, state);};return function(stream, state) {rubyState = state.rubyState;state.rubyState = rubyMode.startState();state.tokenize = runSplat;return ruby(stream, state);};}function ruby(stream, state) {return rubyMode.token(stream, state.rubyState);}function htmlLine(stream, state) {if (stream.match(/^\\$/)) {return "lineContinuation";}return html(stream, state);}function html(stream, state) {if (stream.match(/^#\{/)) {state.tokenize = rubyInQuote("}", state.tokenize);return null;}return maybeBackup(stream, state, /[^\\]#\{/, 1, htmlMode.token(stream, state.htmlState));}function startHtmlLine(lastTokenize) {return function(stream, state) {var style = htmlLine(stream, state);if (stream.eol()) state.tokenize = lastTokenize;return style;};}function startHtmlMode(stream, state, offset) {state.stack = {parent: state.stack,style: "html",indented: stream.column() + offset, // pipe + spacetokenize: state.line};state.line = state.tokenize = html;return null;}function comment(stream, state) {stream.skipToEnd();return state.stack.style;}function commentMode(stream, state) {state.stack = {parent: state.stack,style: "comment",indented: state.indented + 1,tokenize: state.line};state.line = comment;return comment(stream, state);}function attributeWrapper(stream, state) {if (stream.eat(state.stack.endQuote)) {state.line = state.stack.line;state.tokenize = state.stack.tokenize;state.stack = state.stack.parent;return null;}if (stream.match(wrappedAttributeNameRegexp)) {state.tokenize = attributeWrapperAssign;return "slimAttribute";}stream.next();return null;}function attributeWrapperAssign(stream, state) {if (stream.match(/^==?/)) {state.tokenize = attributeWrapperValue;return null;}return attributeWrapper(stream, state);}function attributeWrapperValue(stream, state) {var ch = stream.peek();if (ch == '"' || ch == "\'") {state.tokenize = readQuoted(ch, "string", true, false, attributeWrapper);stream.next();return state.tokenize(stream, state);}if (ch == '[') {return startRubySplat(attributeWrapper)(stream, state);}if (stream.match(/^(true|false|nil)\b/)) {state.tokenize = attributeWrapper;return "keyword";}return startRubySplat(attributeWrapper)(stream, state);}function startAttributeWrapperMode(state, endQuote, tokenize) {state.stack = {parent: state.stack,style: "wrapper",indented: state.indented + 1,tokenize: tokenize,line: state.line,endQuote: endQuote};state.line = state.tokenize = attributeWrapper;return null;}function sub(stream, state) {if (stream.match(/^#\{/)) {state.tokenize = rubyInQuote("}", state.tokenize);return null;}var subStream = new CodeMirror.StringStream(stream.string.slice(state.stack.indented), stream.tabSize);subStream.pos = stream.pos - state.stack.indented;subStream.start = stream.start - state.stack.indented;subStream.lastColumnPos = stream.lastColumnPos - state.stack.indented;subStream.lastColumnValue = stream.lastColumnValue - state.stack.indented;var style = state.subMode.token(subStream, state.subState);stream.pos = subStream.pos + state.stack.indented;return style;}function firstSub(stream, state) {state.stack.indented = stream.column();state.line = state.tokenize = sub;return state.tokenize(stream, state);}function createMode(mode) {var query = embedded[mode];var spec = CodeMirror.mimeModes[query];if (spec) {return CodeMirror.getMode(config, spec);}var factory = CodeMirror.modes[query];if (factory) {return factory(config, {name: query});}return CodeMirror.getMode(config, "null");}function getMode(mode) {if (!modes.hasOwnProperty(mode)) {return modes[mode] = createMode(mode);}return modes[mode];}function startSubMode(mode, state) {var subMode = getMode(mode);var subState = subMode.startState && subMode.startState();state.subMode = subMode;state.subState = subState;state.stack = {parent: state.stack,style: "sub",indented: state.indented + 1,tokenize: state.line};state.line = state.tokenize = firstSub;return "slimSubmode";}function doctypeLine(stream, _state) {stream.skipToEnd();return "slimDoctype";}function startLine(stream, state) {var ch = stream.peek();if (ch == '<') {return (state.tokenize = startHtmlLine(state.tokenize))(stream, state);}if (stream.match(/^[|']/)) {return startHtmlMode(stream, state, 1);}if (stream.match(/^\/(!|\[\w+])?/)) {return commentMode(stream, state);}if (stream.match(/^(-|==?[<>]?)/)) {state.tokenize = lineContinuable(stream.column(), commaContinuable(stream.column(), ruby));return "slimSwitch";}if (stream.match(/^doctype\b/)) {state.tokenize = doctypeLine;return "keyword";}var m = stream.match(embeddedRegexp);if (m) {return startSubMode(m[1], state);}return slimTag(stream, state);}function slim(stream, state) {if (state.startOfLine) {return startLine(stream, state);}return slimTag(stream, state);}function slimTag(stream, state) {if (stream.eat('*')) {state.tokenize = startRubySplat(slimTagExtras);return null;}if (stream.match(nameRegexp)) {state.tokenize = slimTagExtras;return "slimTag";}return slimClass(stream, state);}function slimTagExtras(stream, state) {if (stream.match(/^(<>?|><?)/)) {state.tokenize = slimClass;return null;}return slimClass(stream, state);}function slimClass(stream, state) {if (stream.match(classIdRegexp)) {state.tokenize = slimClass;return "slimId";}if (stream.match(classNameRegexp)) {state.tokenize = slimClass;return "slimClass";}return slimAttribute(stream, state);}function slimAttribute(stream, state) {if (stream.match(/^([\[\{\(])/)) {return startAttributeWrapperMode(state, closing[RegExp.$1], slimAttribute);}if (stream.match(attributeNameRegexp)) {state.tokenize = slimAttributeAssign;return "slimAttribute";}if (stream.peek() == '*') {stream.next();state.tokenize = startRubySplat(slimContent);return null;}return slimContent(stream, state);}function slimAttributeAssign(stream, state) {if (stream.match(/^==?/)) {state.tokenize = slimAttributeValue;return null;}// should never happen, because of forward lookupreturn slimAttribute(stream, state);}function slimAttributeValue(stream, state) {var ch = stream.peek();if (ch == '"' || ch == "\'") {state.tokenize = readQuoted(ch, "string", true, false, slimAttribute);stream.next();return state.tokenize(stream, state);}if (ch == '[') {return startRubySplat(slimAttribute)(stream, state);}if (ch == ':') {return startRubySplat(slimAttributeSymbols)(stream, state);}if (stream.match(/^(true|false|nil)\b/)) {state.tokenize = slimAttribute;return "keyword";}return startRubySplat(slimAttribute)(stream, state);}function slimAttributeSymbols(stream, state) {stream.backUp(1);if (stream.match(/^[^\s],(?=:)/)) {state.tokenize = startRubySplat(slimAttributeSymbols);return null;}stream.next();return slimAttribute(stream, state);}function readQuoted(quote, style, embed, unescaped, nextTokenize) {return function(stream, state) {finishContinue(state);var fresh = stream.current().length == 0;if (stream.match(/^\\$/, fresh)) {if (!fresh) return style;continueLine(state, state.indented);return "lineContinuation";}if (stream.match(/^#\{/, fresh)) {if (!fresh) return style;state.tokenize = rubyInQuote("}", state.tokenize);return null;}var escaped = false, ch;while ((ch = stream.next()) != null) {if (ch == quote && (unescaped || !escaped)) {state.tokenize = nextTokenize;break;}if (embed && ch == "#" && !escaped) {if (stream.eat("{")) {stream.backUp(2);break;}}escaped = !escaped && ch == "\\";}if (stream.eol() && escaped) {stream.backUp(1);}return style;};}function slimContent(stream, state) {if (stream.match(/^==?/)) {state.tokenize = ruby;return "slimSwitch";}if (stream.match(/^\/$/)) { // tag close hintstate.tokenize = slim;return null;}if (stream.match(/^:/)) { // inline tagstate.tokenize = slimTag;return "slimSwitch";}startHtmlMode(stream, state, 0);return state.tokenize(stream, state);}var mode = {// default to html modestartState: function() {var htmlState = htmlMode.startState();var rubyState = rubyMode.startState();return {htmlState: htmlState,rubyState: rubyState,stack: null,last: null,tokenize: slim,line: slim,indented: 0};},copyState: function(state) {return {htmlState : CodeMirror.copyState(htmlMode, state.htmlState),rubyState: CodeMirror.copyState(rubyMode, state.rubyState),subMode: state.subMode,subState: state.subMode && CodeMirror.copyState(state.subMode, state.subState),stack: state.stack,last: state.last,tokenize: state.tokenize,line: state.line};},token: function(stream, state) {if (stream.sol()) {state.indented = stream.indentation();state.startOfLine = true;state.tokenize = state.line;while (state.stack && state.stack.indented > state.indented && state.last != "slimSubmode") {state.line = state.tokenize = state.stack.tokenize;state.stack = state.stack.parent;state.subMode = null;state.subState = null;}}if (stream.eatSpace()) return null;var style = state.tokenize(stream, state);state.startOfLine = false;if (style) state.last = style;return styleMap.hasOwnProperty(style) ? styleMap[style] : style;},blankLine: function(state) {if (state.subMode && state.subMode.blankLine) {return state.subMode.blankLine(state.subState);}},innerMode: function(state) {if (state.subMode) return {state: state.subState, mode: state.subMode};return {state: state, mode: mode};}//indent: function(state) {// return state.indented;//}};return mode;}, "htmlmixed", "ruby");CodeMirror.defineMIME("text/x-slim", "slim");CodeMirror.defineMIME("application/x-slim", "slim");});