LaravelTest
575 строк · 17.6 Кб
1// CodeMirror, copyright (c) by Marijn Haverbeke and others
2// Distributed under an MIT license: https://codemirror.net/LICENSE
3
4// Slim Highlighting for CodeMirror copyright (c) HicknHack Software Gmbh
5
6(function(mod) {7if (typeof exports == "object" && typeof module == "object") // CommonJS8mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby"));9else if (typeof define == "function" && define.amd) // AMD10define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../ruby/ruby"], mod);11else // Plain browser env12mod(CodeMirror);13})(function(CodeMirror) {14"use strict";15
16CodeMirror.defineMode("slim", function(config) {17var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"});18var rubyMode = CodeMirror.getMode(config, "ruby");19var modes = { html: htmlMode, ruby: rubyMode };20var embedded = {21ruby: "ruby",22javascript: "javascript",23css: "text/css",24sass: "text/x-sass",25scss: "text/x-scss",26less: "text/x-less",27styl: "text/x-styl", // no highlighting so far28coffee: "coffeescript",29asciidoc: "text/x-asciidoc",30markdown: "text/x-markdown",31textile: "text/x-textile", // no highlighting so far32creole: "text/x-creole", // no highlighting so far33wiki: "text/x-wiki", // no highlighting so far34mediawiki: "text/x-mediawiki", // no highlighting so far35rdoc: "text/x-rdoc", // no highlighting so far36builder: "text/x-builder", // no highlighting so far37nokogiri: "text/x-nokogiri", // no highlighting so far38erb: "application/x-erb"39};40var embeddedRegexp = function(map){41var arr = [];42for(var key in map) arr.push(key);43return new RegExp("^("+arr.join('|')+"):");44}(embedded);45
46var styleMap = {47"commentLine": "comment",48"slimSwitch": "operator special",49"slimTag": "tag",50"slimId": "attribute def",51"slimClass": "attribute qualifier",52"slimAttribute": "attribute",53"slimSubmode": "keyword special",54"closeAttributeTag": null,55"slimDoctype": null,56"lineContinuation": null57};58var closing = {59"{": "}",60"[": "]",61"(": ")"62};63
64var 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";65var nameChar = nameStartChar + "\\-0-9\xB7\u0300-\u036F\u203F-\u2040";66var nameRegexp = new RegExp("^[:"+nameStartChar+"](?::["+nameChar+"]|["+nameChar+"]*)");67var attributeNameRegexp = new RegExp("^[:"+nameStartChar+"][:\\."+nameChar+"]*(?=\\s*=)");68var wrappedAttributeNameRegexp = new RegExp("^[:"+nameStartChar+"][:\\."+nameChar+"]*");69var classNameRegexp = /^\.-?[_a-zA-Z]+[\w\-]*/;70var classIdRegexp = /^#[_a-zA-Z]+[\w\-]*/;71
72function backup(pos, tokenize, style) {73var restore = function(stream, state) {74state.tokenize = tokenize;75if (stream.pos < pos) {76stream.pos = pos;77return style;78}79return state.tokenize(stream, state);80};81return function(stream, state) {82state.tokenize = restore;83return tokenize(stream, state);84};85}86
87function maybeBackup(stream, state, pat, offset, style) {88var cur = stream.current();89var idx = cur.search(pat);90if (idx > -1) {91state.tokenize = backup(stream.pos, state.tokenize, style);92stream.backUp(cur.length - idx - offset);93}94return style;95}96
97function continueLine(state, column) {98state.stack = {99parent: state.stack,100style: "continuation",101indented: column,102tokenize: state.line103};104state.line = state.tokenize;105}106function finishContinue(state) {107if (state.line == state.tokenize) {108state.line = state.stack.tokenize;109state.stack = state.stack.parent;110}111}112
113function lineContinuable(column, tokenize) {114return function(stream, state) {115finishContinue(state);116if (stream.match(/^\\$/)) {117continueLine(state, column);118return "lineContinuation";119}120var style = tokenize(stream, state);121if (stream.eol() && stream.current().match(/(?:^|[^\\])(?:\\\\)*\\$/)) {122stream.backUp(1);123}124return style;125};126}127function commaContinuable(column, tokenize) {128return function(stream, state) {129finishContinue(state);130var style = tokenize(stream, state);131if (stream.eol() && stream.current().match(/,$/)) {132continueLine(state, column);133}134return style;135};136}137
138function rubyInQuote(endQuote, tokenize) {139// TODO: add multi line support140return function(stream, state) {141var ch = stream.peek();142if (ch == endQuote && state.rubyState.tokenize.length == 1) {143// step out of ruby context as it seems to complete processing all the braces144stream.next();145state.tokenize = tokenize;146return "closeAttributeTag";147} else {148return ruby(stream, state);149}150};151}152function startRubySplat(tokenize) {153var rubyState;154var runSplat = function(stream, state) {155if (state.rubyState.tokenize.length == 1 && !state.rubyState.context.prev) {156stream.backUp(1);157if (stream.eatSpace()) {158state.rubyState = rubyState;159state.tokenize = tokenize;160return tokenize(stream, state);161}162stream.next();163}164return ruby(stream, state);165};166return function(stream, state) {167rubyState = state.rubyState;168state.rubyState = CodeMirror.startState(rubyMode);169state.tokenize = runSplat;170return ruby(stream, state);171};172}173
174function ruby(stream, state) {175return rubyMode.token(stream, state.rubyState);176}177
178function htmlLine(stream, state) {179if (stream.match(/^\\$/)) {180return "lineContinuation";181}182return html(stream, state);183}184function html(stream, state) {185if (stream.match(/^#\{/)) {186state.tokenize = rubyInQuote("}", state.tokenize);187return null;188}189return maybeBackup(stream, state, /[^\\]#\{/, 1, htmlMode.token(stream, state.htmlState));190}191
192function startHtmlLine(lastTokenize) {193return function(stream, state) {194var style = htmlLine(stream, state);195if (stream.eol()) state.tokenize = lastTokenize;196return style;197};198}199
200function startHtmlMode(stream, state, offset) {201state.stack = {202parent: state.stack,203style: "html",204indented: stream.column() + offset, // pipe + space205tokenize: state.line206};207state.line = state.tokenize = html;208return null;209}210
211function comment(stream, state) {212stream.skipToEnd();213return state.stack.style;214}215
216function commentMode(stream, state) {217state.stack = {218parent: state.stack,219style: "comment",220indented: state.indented + 1,221tokenize: state.line222};223state.line = comment;224return comment(stream, state);225}226
227function attributeWrapper(stream, state) {228if (stream.eat(state.stack.endQuote)) {229state.line = state.stack.line;230state.tokenize = state.stack.tokenize;231state.stack = state.stack.parent;232return null;233}234if (stream.match(wrappedAttributeNameRegexp)) {235state.tokenize = attributeWrapperAssign;236return "slimAttribute";237}238stream.next();239return null;240}241function attributeWrapperAssign(stream, state) {242if (stream.match(/^==?/)) {243state.tokenize = attributeWrapperValue;244return null;245}246return attributeWrapper(stream, state);247}248function attributeWrapperValue(stream, state) {249var ch = stream.peek();250if (ch == '"' || ch == "\'") {251state.tokenize = readQuoted(ch, "string", true, false, attributeWrapper);252stream.next();253return state.tokenize(stream, state);254}255if (ch == '[') {256return startRubySplat(attributeWrapper)(stream, state);257}258if (stream.match(/^(true|false|nil)\b/)) {259state.tokenize = attributeWrapper;260return "keyword";261}262return startRubySplat(attributeWrapper)(stream, state);263}264
265function startAttributeWrapperMode(state, endQuote, tokenize) {266state.stack = {267parent: state.stack,268style: "wrapper",269indented: state.indented + 1,270tokenize: tokenize,271line: state.line,272endQuote: endQuote273};274state.line = state.tokenize = attributeWrapper;275return null;276}277
278function sub(stream, state) {279if (stream.match(/^#\{/)) {280state.tokenize = rubyInQuote("}", state.tokenize);281return null;282}283var subStream = new CodeMirror.StringStream(stream.string.slice(state.stack.indented), stream.tabSize);284subStream.pos = stream.pos - state.stack.indented;285subStream.start = stream.start - state.stack.indented;286subStream.lastColumnPos = stream.lastColumnPos - state.stack.indented;287subStream.lastColumnValue = stream.lastColumnValue - state.stack.indented;288var style = state.subMode.token(subStream, state.subState);289stream.pos = subStream.pos + state.stack.indented;290return style;291}292function firstSub(stream, state) {293state.stack.indented = stream.column();294state.line = state.tokenize = sub;295return state.tokenize(stream, state);296}297
298function createMode(mode) {299var query = embedded[mode];300var spec = CodeMirror.mimeModes[query];301if (spec) {302return CodeMirror.getMode(config, spec);303}304var factory = CodeMirror.modes[query];305if (factory) {306return factory(config, {name: query});307}308return CodeMirror.getMode(config, "null");309}310
311function getMode(mode) {312if (!modes.hasOwnProperty(mode)) {313return modes[mode] = createMode(mode);314}315return modes[mode];316}317
318function startSubMode(mode, state) {319var subMode = getMode(mode);320var subState = CodeMirror.startState(subMode);321
322state.subMode = subMode;323state.subState = subState;324
325state.stack = {326parent: state.stack,327style: "sub",328indented: state.indented + 1,329tokenize: state.line330};331state.line = state.tokenize = firstSub;332return "slimSubmode";333}334
335function doctypeLine(stream, _state) {336stream.skipToEnd();337return "slimDoctype";338}339
340function startLine(stream, state) {341var ch = stream.peek();342if (ch == '<') {343return (state.tokenize = startHtmlLine(state.tokenize))(stream, state);344}345if (stream.match(/^[|']/)) {346return startHtmlMode(stream, state, 1);347}348if (stream.match(/^\/(!|\[\w+])?/)) {349return commentMode(stream, state);350}351if (stream.match(/^(-|==?[<>]?)/)) {352state.tokenize = lineContinuable(stream.column(), commaContinuable(stream.column(), ruby));353return "slimSwitch";354}355if (stream.match(/^doctype\b/)) {356state.tokenize = doctypeLine;357return "keyword";358}359
360var m = stream.match(embeddedRegexp);361if (m) {362return startSubMode(m[1], state);363}364
365return slimTag(stream, state);366}367
368function slim(stream, state) {369if (state.startOfLine) {370return startLine(stream, state);371}372return slimTag(stream, state);373}374
375function slimTag(stream, state) {376if (stream.eat('*')) {377state.tokenize = startRubySplat(slimTagExtras);378return null;379}380if (stream.match(nameRegexp)) {381state.tokenize = slimTagExtras;382return "slimTag";383}384return slimClass(stream, state);385}386function slimTagExtras(stream, state) {387if (stream.match(/^(<>?|><?)/)) {388state.tokenize = slimClass;389return null;390}391return slimClass(stream, state);392}393function slimClass(stream, state) {394if (stream.match(classIdRegexp)) {395state.tokenize = slimClass;396return "slimId";397}398if (stream.match(classNameRegexp)) {399state.tokenize = slimClass;400return "slimClass";401}402return slimAttribute(stream, state);403}404function slimAttribute(stream, state) {405if (stream.match(/^([\[\{\(])/)) {406return startAttributeWrapperMode(state, closing[RegExp.$1], slimAttribute);407}408if (stream.match(attributeNameRegexp)) {409state.tokenize = slimAttributeAssign;410return "slimAttribute";411}412if (stream.peek() == '*') {413stream.next();414state.tokenize = startRubySplat(slimContent);415return null;416}417return slimContent(stream, state);418}419function slimAttributeAssign(stream, state) {420if (stream.match(/^==?/)) {421state.tokenize = slimAttributeValue;422return null;423}424// should never happen, because of forward lookup425return slimAttribute(stream, state);426}427
428function slimAttributeValue(stream, state) {429var ch = stream.peek();430if (ch == '"' || ch == "\'") {431state.tokenize = readQuoted(ch, "string", true, false, slimAttribute);432stream.next();433return state.tokenize(stream, state);434}435if (ch == '[') {436return startRubySplat(slimAttribute)(stream, state);437}438if (ch == ':') {439return startRubySplat(slimAttributeSymbols)(stream, state);440}441if (stream.match(/^(true|false|nil)\b/)) {442state.tokenize = slimAttribute;443return "keyword";444}445return startRubySplat(slimAttribute)(stream, state);446}447function slimAttributeSymbols(stream, state) {448stream.backUp(1);449if (stream.match(/^[^\s],(?=:)/)) {450state.tokenize = startRubySplat(slimAttributeSymbols);451return null;452}453stream.next();454return slimAttribute(stream, state);455}456function readQuoted(quote, style, embed, unescaped, nextTokenize) {457return function(stream, state) {458finishContinue(state);459var fresh = stream.current().length == 0;460if (stream.match(/^\\$/, fresh)) {461if (!fresh) return style;462continueLine(state, state.indented);463return "lineContinuation";464}465if (stream.match(/^#\{/, fresh)) {466if (!fresh) return style;467state.tokenize = rubyInQuote("}", state.tokenize);468return null;469}470var escaped = false, ch;471while ((ch = stream.next()) != null) {472if (ch == quote && (unescaped || !escaped)) {473state.tokenize = nextTokenize;474break;475}476if (embed && ch == "#" && !escaped) {477if (stream.eat("{")) {478stream.backUp(2);479break;480}481}482escaped = !escaped && ch == "\\";483}484if (stream.eol() && escaped) {485stream.backUp(1);486}487return style;488};489}490function slimContent(stream, state) {491if (stream.match(/^==?/)) {492state.tokenize = ruby;493return "slimSwitch";494}495if (stream.match(/^\/$/)) { // tag close hint496state.tokenize = slim;497return null;498}499if (stream.match(/^:/)) { // inline tag500state.tokenize = slimTag;501return "slimSwitch";502}503startHtmlMode(stream, state, 0);504return state.tokenize(stream, state);505}506
507var mode = {508// default to html mode509startState: function() {510var htmlState = CodeMirror.startState(htmlMode);511var rubyState = CodeMirror.startState(rubyMode);512return {513htmlState: htmlState,514rubyState: rubyState,515stack: null,516last: null,517tokenize: slim,518line: slim,519indented: 0520};521},522
523copyState: function(state) {524return {525htmlState : CodeMirror.copyState(htmlMode, state.htmlState),526rubyState: CodeMirror.copyState(rubyMode, state.rubyState),527subMode: state.subMode,528subState: state.subMode && CodeMirror.copyState(state.subMode, state.subState),529stack: state.stack,530last: state.last,531tokenize: state.tokenize,532line: state.line533};534},535
536token: function(stream, state) {537if (stream.sol()) {538state.indented = stream.indentation();539state.startOfLine = true;540state.tokenize = state.line;541while (state.stack && state.stack.indented > state.indented && state.last != "slimSubmode") {542state.line = state.tokenize = state.stack.tokenize;543state.stack = state.stack.parent;544state.subMode = null;545state.subState = null;546}547}548if (stream.eatSpace()) return null;549var style = state.tokenize(stream, state);550state.startOfLine = false;551if (style) state.last = style;552return styleMap.hasOwnProperty(style) ? styleMap[style] : style;553},554
555blankLine: function(state) {556if (state.subMode && state.subMode.blankLine) {557return state.subMode.blankLine(state.subState);558}559},560
561innerMode: function(state) {562if (state.subMode) return {state: state.subState, mode: state.subMode};563return {state: state, mode: mode};564}565
566//indent: function(state) {567// return state.indented;568//}569};570return mode;571}, "htmlmixed", "ruby");572
573CodeMirror.defineMIME("text/x-slim", "slim");574CodeMirror.defineMIME("application/x-slim", "slim");575});576