mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
590 lines
16 KiB
590 lines
16 KiB
// CodeMirror, copyright (c) by Marijn Haverbeke and others |
|
// Distributed under an MIT license: http://codemirror.net/LICENSE |
|
|
|
(function(mod) { |
|
if (typeof exports == "object" && typeof module == "object") // CommonJS |
|
mod(require("../../lib/codemirror"), require("../javascript/javascript"), require("../css/css"), require("../htmlmixed/htmlmixed")); |
|
else if (typeof define == "function" && define.amd) // AMD |
|
define(["../../lib/codemirror", "../javascript/javascript", "../css/css", "../htmlmixed/htmlmixed"], mod); |
|
else // Plain browser env |
|
mod(CodeMirror); |
|
})(function(CodeMirror) { |
|
"use strict"; |
|
|
|
CodeMirror.defineMode('jade', function (config) { |
|
// token types |
|
var KEYWORD = 'keyword'; |
|
var DOCTYPE = 'meta'; |
|
var ID = 'builtin'; |
|
var CLASS = 'qualifier'; |
|
|
|
var ATTRS_NEST = { |
|
'{': '}', |
|
'(': ')', |
|
'[': ']' |
|
}; |
|
|
|
var jsMode = CodeMirror.getMode(config, 'javascript'); |
|
|
|
function State() { |
|
this.javaScriptLine = false; |
|
this.javaScriptLineExcludesColon = false; |
|
|
|
this.javaScriptArguments = false; |
|
this.javaScriptArgumentsDepth = 0; |
|
|
|
this.isInterpolating = false; |
|
this.interpolationNesting = 0; |
|
|
|
this.jsState = CodeMirror.startState(jsMode); |
|
|
|
this.restOfLine = ''; |
|
|
|
this.isIncludeFiltered = false; |
|
this.isEach = false; |
|
|
|
this.lastTag = ''; |
|
this.scriptType = ''; |
|
|
|
// Attributes Mode |
|
this.isAttrs = false; |
|
this.attrsNest = []; |
|
this.inAttributeName = true; |
|
this.attributeIsType = false; |
|
this.attrValue = ''; |
|
|
|
// Indented Mode |
|
this.indentOf = Infinity; |
|
this.indentToken = ''; |
|
|
|
this.innerMode = null; |
|
this.innerState = null; |
|
|
|
this.innerModeForLine = false; |
|
} |
|
/** |
|
* Safely copy a state |
|
* |
|
* @return {State} |
|
*/ |
|
State.prototype.copy = function () { |
|
var res = new State(); |
|
res.javaScriptLine = this.javaScriptLine; |
|
res.javaScriptLineExcludesColon = this.javaScriptLineExcludesColon; |
|
res.javaScriptArguments = this.javaScriptArguments; |
|
res.javaScriptArgumentsDepth = this.javaScriptArgumentsDepth; |
|
res.isInterpolating = this.isInterpolating; |
|
res.interpolationNesting = this.interpolationNesting; |
|
|
|
res.jsState = CodeMirror.copyState(jsMode, this.jsState); |
|
|
|
res.innerMode = this.innerMode; |
|
if (this.innerMode && this.innerState) { |
|
res.innerState = CodeMirror.copyState(this.innerMode, this.innerState); |
|
} |
|
|
|
res.restOfLine = this.restOfLine; |
|
|
|
res.isIncludeFiltered = this.isIncludeFiltered; |
|
res.isEach = this.isEach; |
|
res.lastTag = this.lastTag; |
|
res.scriptType = this.scriptType; |
|
res.isAttrs = this.isAttrs; |
|
res.attrsNest = this.attrsNest.slice(); |
|
res.inAttributeName = this.inAttributeName; |
|
res.attributeIsType = this.attributeIsType; |
|
res.attrValue = this.attrValue; |
|
res.indentOf = this.indentOf; |
|
res.indentToken = this.indentToken; |
|
|
|
res.innerModeForLine = this.innerModeForLine; |
|
|
|
return res; |
|
}; |
|
|
|
function javaScript(stream, state) { |
|
if (stream.sol()) { |
|
// if javaScriptLine was set at end of line, ignore it |
|
state.javaScriptLine = false; |
|
state.javaScriptLineExcludesColon = false; |
|
} |
|
if (state.javaScriptLine) { |
|
if (state.javaScriptLineExcludesColon && stream.peek() === ':') { |
|
state.javaScriptLine = false; |
|
state.javaScriptLineExcludesColon = false; |
|
return; |
|
} |
|
var tok = jsMode.token(stream, state.jsState); |
|
if (stream.eol()) state.javaScriptLine = false; |
|
return tok || true; |
|
} |
|
} |
|
function javaScriptArguments(stream, state) { |
|
if (state.javaScriptArguments) { |
|
if (state.javaScriptArgumentsDepth === 0 && stream.peek() !== '(') { |
|
state.javaScriptArguments = false; |
|
return; |
|
} |
|
if (stream.peek() === '(') { |
|
state.javaScriptArgumentsDepth++; |
|
} else if (stream.peek() === ')') { |
|
state.javaScriptArgumentsDepth--; |
|
} |
|
if (state.javaScriptArgumentsDepth === 0) { |
|
state.javaScriptArguments = false; |
|
return; |
|
} |
|
|
|
var tok = jsMode.token(stream, state.jsState); |
|
return tok || true; |
|
} |
|
} |
|
|
|
function yieldStatement(stream) { |
|
if (stream.match(/^yield\b/)) { |
|
return 'keyword'; |
|
} |
|
} |
|
|
|
function doctype(stream) { |
|
if (stream.match(/^(?:doctype) *([^\n]+)?/)) { |
|
return DOCTYPE; |
|
} |
|
} |
|
|
|
function interpolation(stream, state) { |
|
if (stream.match('#{')) { |
|
state.isInterpolating = true; |
|
state.interpolationNesting = 0; |
|
return 'punctuation'; |
|
} |
|
} |
|
|
|
function interpolationContinued(stream, state) { |
|
if (state.isInterpolating) { |
|
if (stream.peek() === '}') { |
|
state.interpolationNesting--; |
|
if (state.interpolationNesting < 0) { |
|
stream.next(); |
|
state.isInterpolating = false; |
|
return 'punctuation'; |
|
} |
|
} else if (stream.peek() === '{') { |
|
state.interpolationNesting++; |
|
} |
|
return jsMode.token(stream, state.jsState) || true; |
|
} |
|
} |
|
|
|
function caseStatement(stream, state) { |
|
if (stream.match(/^case\b/)) { |
|
state.javaScriptLine = true; |
|
return KEYWORD; |
|
} |
|
} |
|
|
|
function when(stream, state) { |
|
if (stream.match(/^when\b/)) { |
|
state.javaScriptLine = true; |
|
state.javaScriptLineExcludesColon = true; |
|
return KEYWORD; |
|
} |
|
} |
|
|
|
function defaultStatement(stream) { |
|
if (stream.match(/^default\b/)) { |
|
return KEYWORD; |
|
} |
|
} |
|
|
|
function extendsStatement(stream, state) { |
|
if (stream.match(/^extends?\b/)) { |
|
state.restOfLine = 'string'; |
|
return KEYWORD; |
|
} |
|
} |
|
|
|
function append(stream, state) { |
|
if (stream.match(/^append\b/)) { |
|
state.restOfLine = 'variable'; |
|
return KEYWORD; |
|
} |
|
} |
|
function prepend(stream, state) { |
|
if (stream.match(/^prepend\b/)) { |
|
state.restOfLine = 'variable'; |
|
return KEYWORD; |
|
} |
|
} |
|
function block(stream, state) { |
|
if (stream.match(/^block\b *(?:(prepend|append)\b)?/)) { |
|
state.restOfLine = 'variable'; |
|
return KEYWORD; |
|
} |
|
} |
|
|
|
function include(stream, state) { |
|
if (stream.match(/^include\b/)) { |
|
state.restOfLine = 'string'; |
|
return KEYWORD; |
|
} |
|
} |
|
|
|
function includeFiltered(stream, state) { |
|
if (stream.match(/^include:([a-zA-Z0-9\-]+)/, false) && stream.match('include')) { |
|
state.isIncludeFiltered = true; |
|
return KEYWORD; |
|
} |
|
} |
|
|
|
function includeFilteredContinued(stream, state) { |
|
if (state.isIncludeFiltered) { |
|
var tok = filter(stream, state); |
|
state.isIncludeFiltered = false; |
|
state.restOfLine = 'string'; |
|
return tok; |
|
} |
|
} |
|
|
|
function mixin(stream, state) { |
|
if (stream.match(/^mixin\b/)) { |
|
state.javaScriptLine = true; |
|
return KEYWORD; |
|
} |
|
} |
|
|
|
function call(stream, state) { |
|
if (stream.match(/^\+([-\w]+)/)) { |
|
if (!stream.match(/^\( *[-\w]+ *=/, false)) { |
|
state.javaScriptArguments = true; |
|
state.javaScriptArgumentsDepth = 0; |
|
} |
|
return 'variable'; |
|
} |
|
if (stream.match(/^\+#{/, false)) { |
|
stream.next(); |
|
state.mixinCallAfter = true; |
|
return interpolation(stream, state); |
|
} |
|
} |
|
function callArguments(stream, state) { |
|
if (state.mixinCallAfter) { |
|
state.mixinCallAfter = false; |
|
if (!stream.match(/^\( *[-\w]+ *=/, false)) { |
|
state.javaScriptArguments = true; |
|
state.javaScriptArgumentsDepth = 0; |
|
} |
|
return true; |
|
} |
|
} |
|
|
|
function conditional(stream, state) { |
|
if (stream.match(/^(if|unless|else if|else)\b/)) { |
|
state.javaScriptLine = true; |
|
return KEYWORD; |
|
} |
|
} |
|
|
|
function each(stream, state) { |
|
if (stream.match(/^(- *)?(each|for)\b/)) { |
|
state.isEach = true; |
|
return KEYWORD; |
|
} |
|
} |
|
function eachContinued(stream, state) { |
|
if (state.isEach) { |
|
if (stream.match(/^ in\b/)) { |
|
state.javaScriptLine = true; |
|
state.isEach = false; |
|
return KEYWORD; |
|
} else if (stream.sol() || stream.eol()) { |
|
state.isEach = false; |
|
} else if (stream.next()) { |
|
while (!stream.match(/^ in\b/, false) && stream.next()); |
|
return 'variable'; |
|
} |
|
} |
|
} |
|
|
|
function whileStatement(stream, state) { |
|
if (stream.match(/^while\b/)) { |
|
state.javaScriptLine = true; |
|
return KEYWORD; |
|
} |
|
} |
|
|
|
function tag(stream, state) { |
|
var captures; |
|
if (captures = stream.match(/^(\w(?:[-:\w]*\w)?)\/?/)) { |
|
state.lastTag = captures[1].toLowerCase(); |
|
if (state.lastTag === 'script') { |
|
state.scriptType = 'application/javascript'; |
|
} |
|
return 'tag'; |
|
} |
|
} |
|
|
|
function filter(stream, state) { |
|
if (stream.match(/^:([\w\-]+)/)) { |
|
var innerMode; |
|
if (config && config.innerModes) { |
|
innerMode = config.innerModes(stream.current().substring(1)); |
|
} |
|
if (!innerMode) { |
|
innerMode = stream.current().substring(1); |
|
} |
|
if (typeof innerMode === 'string') { |
|
innerMode = CodeMirror.getMode(config, innerMode); |
|
} |
|
setInnerMode(stream, state, innerMode); |
|
return 'atom'; |
|
} |
|
} |
|
|
|
function code(stream, state) { |
|
if (stream.match(/^(!?=|-)/)) { |
|
state.javaScriptLine = true; |
|
return 'punctuation'; |
|
} |
|
} |
|
|
|
function id(stream) { |
|
if (stream.match(/^#([\w-]+)/)) { |
|
return ID; |
|
} |
|
} |
|
|
|
function className(stream) { |
|
if (stream.match(/^\.([\w-]+)/)) { |
|
return CLASS; |
|
} |
|
} |
|
|
|
function attrs(stream, state) { |
|
if (stream.peek() == '(') { |
|
stream.next(); |
|
state.isAttrs = true; |
|
state.attrsNest = []; |
|
state.inAttributeName = true; |
|
state.attrValue = ''; |
|
state.attributeIsType = false; |
|
return 'punctuation'; |
|
} |
|
} |
|
|
|
function attrsContinued(stream, state) { |
|
if (state.isAttrs) { |
|
if (ATTRS_NEST[stream.peek()]) { |
|
state.attrsNest.push(ATTRS_NEST[stream.peek()]); |
|
} |
|
if (state.attrsNest[state.attrsNest.length - 1] === stream.peek()) { |
|
state.attrsNest.pop(); |
|
} else if (stream.eat(')')) { |
|
state.isAttrs = false; |
|
return 'punctuation'; |
|
} |
|
if (state.inAttributeName && stream.match(/^[^=,\)!]+/)) { |
|
if (stream.peek() === '=' || stream.peek() === '!') { |
|
state.inAttributeName = false; |
|
state.jsState = CodeMirror.startState(jsMode); |
|
if (state.lastTag === 'script' && stream.current().trim().toLowerCase() === 'type') { |
|
state.attributeIsType = true; |
|
} else { |
|
state.attributeIsType = false; |
|
} |
|
} |
|
return 'attribute'; |
|
} |
|
|
|
var tok = jsMode.token(stream, state.jsState); |
|
if (state.attributeIsType && tok === 'string') { |
|
state.scriptType = stream.current().toString(); |
|
} |
|
if (state.attrsNest.length === 0 && (tok === 'string' || tok === 'variable' || tok === 'keyword')) { |
|
try { |
|
Function('', 'var x ' + state.attrValue.replace(/,\s*$/, '').replace(/^!/, '')); |
|
state.inAttributeName = true; |
|
state.attrValue = ''; |
|
stream.backUp(stream.current().length); |
|
return attrsContinued(stream, state); |
|
} catch (ex) { |
|
//not the end of an attribute |
|
} |
|
} |
|
state.attrValue += stream.current(); |
|
return tok || true; |
|
} |
|
} |
|
|
|
function attributesBlock(stream, state) { |
|
if (stream.match(/^&attributes\b/)) { |
|
state.javaScriptArguments = true; |
|
state.javaScriptArgumentsDepth = 0; |
|
return 'keyword'; |
|
} |
|
} |
|
|
|
function indent(stream) { |
|
if (stream.sol() && stream.eatSpace()) { |
|
return 'indent'; |
|
} |
|
} |
|
|
|
function comment(stream, state) { |
|
if (stream.match(/^ *\/\/(-)?([^\n]*)/)) { |
|
state.indentOf = stream.indentation(); |
|
state.indentToken = 'comment'; |
|
return 'comment'; |
|
} |
|
} |
|
|
|
function colon(stream) { |
|
if (stream.match(/^: */)) { |
|
return 'colon'; |
|
} |
|
} |
|
|
|
function text(stream, state) { |
|
if (stream.match(/^(?:\| ?| )([^\n]+)/)) { |
|
return 'string'; |
|
} |
|
if (stream.match(/^(<[^\n]*)/, false)) { |
|
// html string |
|
setInnerMode(stream, state, 'htmlmixed'); |
|
state.innerModeForLine = true; |
|
return innerMode(stream, state, true); |
|
} |
|
} |
|
|
|
function dot(stream, state) { |
|
if (stream.eat('.')) { |
|
var innerMode = null; |
|
if (state.lastTag === 'script' && state.scriptType.toLowerCase().indexOf('javascript') != -1) { |
|
innerMode = state.scriptType.toLowerCase().replace(/"|'/g, ''); |
|
} else if (state.lastTag === 'style') { |
|
innerMode = 'css'; |
|
} |
|
setInnerMode(stream, state, innerMode); |
|
return 'dot'; |
|
} |
|
} |
|
|
|
function fail(stream) { |
|
stream.next(); |
|
return null; |
|
} |
|
|
|
|
|
function setInnerMode(stream, state, mode) { |
|
mode = CodeMirror.mimeModes[mode] || mode; |
|
mode = config.innerModes ? config.innerModes(mode) || mode : mode; |
|
mode = CodeMirror.mimeModes[mode] || mode; |
|
mode = CodeMirror.getMode(config, mode); |
|
state.indentOf = stream.indentation(); |
|
|
|
if (mode && mode.name !== 'null') { |
|
state.innerMode = mode; |
|
} else { |
|
state.indentToken = 'string'; |
|
} |
|
} |
|
function innerMode(stream, state, force) { |
|
if (stream.indentation() > state.indentOf || (state.innerModeForLine && !stream.sol()) || force) { |
|
if (state.innerMode) { |
|
if (!state.innerState) { |
|
state.innerState = state.innerMode.startState ? CodeMirror.startState(state.innerMode, stream.indentation()) : {}; |
|
} |
|
return stream.hideFirstChars(state.indentOf + 2, function () { |
|
return state.innerMode.token(stream, state.innerState) || true; |
|
}); |
|
} else { |
|
stream.skipToEnd(); |
|
return state.indentToken; |
|
} |
|
} else if (stream.sol()) { |
|
state.indentOf = Infinity; |
|
state.indentToken = null; |
|
state.innerMode = null; |
|
state.innerState = null; |
|
} |
|
} |
|
function restOfLine(stream, state) { |
|
if (stream.sol()) { |
|
// if restOfLine was set at end of line, ignore it |
|
state.restOfLine = ''; |
|
} |
|
if (state.restOfLine) { |
|
stream.skipToEnd(); |
|
var tok = state.restOfLine; |
|
state.restOfLine = ''; |
|
return tok; |
|
} |
|
} |
|
|
|
|
|
function startState() { |
|
return new State(); |
|
} |
|
function copyState(state) { |
|
return state.copy(); |
|
} |
|
/** |
|
* Get the next token in the stream |
|
* |
|
* @param {Stream} stream |
|
* @param {State} state |
|
*/ |
|
function nextToken(stream, state) { |
|
var tok = innerMode(stream, state) |
|
|| restOfLine(stream, state) |
|
|| interpolationContinued(stream, state) |
|
|| includeFilteredContinued(stream, state) |
|
|| eachContinued(stream, state) |
|
|| attrsContinued(stream, state) |
|
|| javaScript(stream, state) |
|
|| javaScriptArguments(stream, state) |
|
|| callArguments(stream, state) |
|
|
|
|| yieldStatement(stream, state) |
|
|| doctype(stream, state) |
|
|| interpolation(stream, state) |
|
|| caseStatement(stream, state) |
|
|| when(stream, state) |
|
|| defaultStatement(stream, state) |
|
|| extendsStatement(stream, state) |
|
|| append(stream, state) |
|
|| prepend(stream, state) |
|
|| block(stream, state) |
|
|| include(stream, state) |
|
|| includeFiltered(stream, state) |
|
|| mixin(stream, state) |
|
|| call(stream, state) |
|
|| conditional(stream, state) |
|
|| each(stream, state) |
|
|| whileStatement(stream, state) |
|
|| tag(stream, state) |
|
|| filter(stream, state) |
|
|| code(stream, state) |
|
|| id(stream, state) |
|
|| className(stream, state) |
|
|| attrs(stream, state) |
|
|| attributesBlock(stream, state) |
|
|| indent(stream, state) |
|
|| text(stream, state) |
|
|| comment(stream, state) |
|
|| colon(stream, state) |
|
|| dot(stream, state) |
|
|| fail(stream, state); |
|
|
|
return tok === true ? null : tok; |
|
} |
|
return { |
|
startState: startState, |
|
copyState: copyState, |
|
token: nextToken |
|
}; |
|
}, 'javascript', 'css', 'htmlmixed'); |
|
|
|
CodeMirror.defineMIME('text/x-jade', 'jade'); |
|
|
|
});
|
|
|