mirror of
https://github.com/go-gitea/gitea
synced 2024-11-13 21:54:24 +00:00
313 lines
8.3 KiB
JavaScript
313 lines
8.3 KiB
JavaScript
|
// 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"));
|
||
|
else if (typeof define == "function" && define.amd) // AMD
|
||
|
define(["../../lib/codemirror"], mod);
|
||
|
else // Plain browser env
|
||
|
mod(CodeMirror);
|
||
|
})(function(CodeMirror) {
|
||
|
"use strict";
|
||
|
|
||
|
CodeMirror.defineMode('tiki', function(config) {
|
||
|
function inBlock(style, terminator, returnTokenizer) {
|
||
|
return function(stream, state) {
|
||
|
while (!stream.eol()) {
|
||
|
if (stream.match(terminator)) {
|
||
|
state.tokenize = inText;
|
||
|
break;
|
||
|
}
|
||
|
stream.next();
|
||
|
}
|
||
|
|
||
|
if (returnTokenizer) state.tokenize = returnTokenizer;
|
||
|
|
||
|
return style;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function inLine(style) {
|
||
|
return function(stream, state) {
|
||
|
while(!stream.eol()) {
|
||
|
stream.next();
|
||
|
}
|
||
|
state.tokenize = inText;
|
||
|
return style;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function inText(stream, state) {
|
||
|
function chain(parser) {
|
||
|
state.tokenize = parser;
|
||
|
return parser(stream, state);
|
||
|
}
|
||
|
|
||
|
var sol = stream.sol();
|
||
|
var ch = stream.next();
|
||
|
|
||
|
//non start of line
|
||
|
switch (ch) { //switch is generally much faster than if, so it is used here
|
||
|
case "{": //plugin
|
||
|
stream.eat("/");
|
||
|
stream.eatSpace();
|
||
|
stream.eatWhile(/[^\s\u00a0=\"\'\/?(}]/);
|
||
|
state.tokenize = inPlugin;
|
||
|
return "tag";
|
||
|
case "_": //bold
|
||
|
if (stream.eat("_"))
|
||
|
return chain(inBlock("strong", "__", inText));
|
||
|
break;
|
||
|
case "'": //italics
|
||
|
if (stream.eat("'"))
|
||
|
return chain(inBlock("em", "''", inText));
|
||
|
break;
|
||
|
case "(":// Wiki Link
|
||
|
if (stream.eat("("))
|
||
|
return chain(inBlock("variable-2", "))", inText));
|
||
|
break;
|
||
|
case "[":// Weblink
|
||
|
return chain(inBlock("variable-3", "]", inText));
|
||
|
break;
|
||
|
case "|": //table
|
||
|
if (stream.eat("|"))
|
||
|
return chain(inBlock("comment", "||"));
|
||
|
break;
|
||
|
case "-":
|
||
|
if (stream.eat("=")) {//titleBar
|
||
|
return chain(inBlock("header string", "=-", inText));
|
||
|
} else if (stream.eat("-")) {//deleted
|
||
|
return chain(inBlock("error tw-deleted", "--", inText));
|
||
|
}
|
||
|
break;
|
||
|
case "=": //underline
|
||
|
if (stream.match("=="))
|
||
|
return chain(inBlock("tw-underline", "===", inText));
|
||
|
break;
|
||
|
case ":":
|
||
|
if (stream.eat(":"))
|
||
|
return chain(inBlock("comment", "::"));
|
||
|
break;
|
||
|
case "^": //box
|
||
|
return chain(inBlock("tw-box", "^"));
|
||
|
break;
|
||
|
case "~": //np
|
||
|
if (stream.match("np~"))
|
||
|
return chain(inBlock("meta", "~/np~"));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//start of line types
|
||
|
if (sol) {
|
||
|
switch (ch) {
|
||
|
case "!": //header at start of line
|
||
|
if (stream.match('!!!!!')) {
|
||
|
return chain(inLine("header string"));
|
||
|
} else if (stream.match('!!!!')) {
|
||
|
return chain(inLine("header string"));
|
||
|
} else if (stream.match('!!!')) {
|
||
|
return chain(inLine("header string"));
|
||
|
} else if (stream.match('!!')) {
|
||
|
return chain(inLine("header string"));
|
||
|
} else {
|
||
|
return chain(inLine("header string"));
|
||
|
}
|
||
|
break;
|
||
|
case "*": //unordered list line item, or <li /> at start of line
|
||
|
case "#": //ordered list line item, or <li /> at start of line
|
||
|
case "+": //ordered list line item, or <li /> at start of line
|
||
|
return chain(inLine("tw-listitem bracket"));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//stream.eatWhile(/[&{]/); was eating up plugins, turned off to act less like html and more like tiki
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
var indentUnit = config.indentUnit;
|
||
|
|
||
|
// Return variables for tokenizers
|
||
|
var pluginName, type;
|
||
|
function inPlugin(stream, state) {
|
||
|
var ch = stream.next();
|
||
|
var peek = stream.peek();
|
||
|
|
||
|
if (ch == "}") {
|
||
|
state.tokenize = inText;
|
||
|
//type = ch == ")" ? "endPlugin" : "selfclosePlugin"; inPlugin
|
||
|
return "tag";
|
||
|
} else if (ch == "(" || ch == ")") {
|
||
|
return "bracket";
|
||
|
} else if (ch == "=") {
|
||
|
type = "equals";
|
||
|
|
||
|
if (peek == ">") {
|
||
|
ch = stream.next();
|
||
|
peek = stream.peek();
|
||
|
}
|
||
|
|
||
|
//here we detect values directly after equal character with no quotes
|
||
|
if (!/[\'\"]/.test(peek)) {
|
||
|
state.tokenize = inAttributeNoQuote();
|
||
|
}
|
||
|
//end detect values
|
||
|
|
||
|
return "operator";
|
||
|
} else if (/[\'\"]/.test(ch)) {
|
||
|
state.tokenize = inAttribute(ch);
|
||
|
return state.tokenize(stream, state);
|
||
|
} else {
|
||
|
stream.eatWhile(/[^\s\u00a0=\"\'\/?]/);
|
||
|
return "keyword";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function inAttribute(quote) {
|
||
|
return function(stream, state) {
|
||
|
while (!stream.eol()) {
|
||
|
if (stream.next() == quote) {
|
||
|
state.tokenize = inPlugin;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return "string";
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function inAttributeNoQuote() {
|
||
|
return function(stream, state) {
|
||
|
while (!stream.eol()) {
|
||
|
var ch = stream.next();
|
||
|
var peek = stream.peek();
|
||
|
if (ch == " " || ch == "," || /[ )}]/.test(peek)) {
|
||
|
state.tokenize = inPlugin;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return "string";
|
||
|
};
|
||
|
}
|
||
|
|
||
|
var curState, setStyle;
|
||
|
function pass() {
|
||
|
for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
|
||
|
}
|
||
|
|
||
|
function cont() {
|
||
|
pass.apply(null, arguments);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
function pushContext(pluginName, startOfLine) {
|
||
|
var noIndent = curState.context && curState.context.noIndent;
|
||
|
curState.context = {
|
||
|
prev: curState.context,
|
||
|
pluginName: pluginName,
|
||
|
indent: curState.indented,
|
||
|
startOfLine: startOfLine,
|
||
|
noIndent: noIndent
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function popContext() {
|
||
|
if (curState.context) curState.context = curState.context.prev;
|
||
|
}
|
||
|
|
||
|
function element(type) {
|
||
|
if (type == "openPlugin") {curState.pluginName = pluginName; return cont(attributes, endplugin(curState.startOfLine));}
|
||
|
else if (type == "closePlugin") {
|
||
|
var err = false;
|
||
|
if (curState.context) {
|
||
|
err = curState.context.pluginName != pluginName;
|
||
|
popContext();
|
||
|
} else {
|
||
|
err = true;
|
||
|
}
|
||
|
if (err) setStyle = "error";
|
||
|
return cont(endcloseplugin(err));
|
||
|
}
|
||
|
else if (type == "string") {
|
||
|
if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata");
|
||
|
if (curState.tokenize == inText) popContext();
|
||
|
return cont();
|
||
|
}
|
||
|
else return cont();
|
||
|
}
|
||
|
|
||
|
function endplugin(startOfLine) {
|
||
|
return function(type) {
|
||
|
if (
|
||
|
type == "selfclosePlugin" ||
|
||
|
type == "endPlugin"
|
||
|
)
|
||
|
return cont();
|
||
|
if (type == "endPlugin") {pushContext(curState.pluginName, startOfLine); return cont();}
|
||
|
return cont();
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function endcloseplugin(err) {
|
||
|
return function(type) {
|
||
|
if (err) setStyle = "error";
|
||
|
if (type == "endPlugin") return cont();
|
||
|
return pass();
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function attributes(type) {
|
||
|
if (type == "keyword") {setStyle = "attribute"; return cont(attributes);}
|
||
|
if (type == "equals") return cont(attvalue, attributes);
|
||
|
return pass();
|
||
|
}
|
||
|
function attvalue(type) {
|
||
|
if (type == "keyword") {setStyle = "string"; return cont();}
|
||
|
if (type == "string") return cont(attvaluemaybe);
|
||
|
return pass();
|
||
|
}
|
||
|
function attvaluemaybe(type) {
|
||
|
if (type == "string") return cont(attvaluemaybe);
|
||
|
else return pass();
|
||
|
}
|
||
|
return {
|
||
|
startState: function() {
|
||
|
return {tokenize: inText, cc: [], indented: 0, startOfLine: true, pluginName: null, context: null};
|
||
|
},
|
||
|
token: function(stream, state) {
|
||
|
if (stream.sol()) {
|
||
|
state.startOfLine = true;
|
||
|
state.indented = stream.indentation();
|
||
|
}
|
||
|
if (stream.eatSpace()) return null;
|
||
|
|
||
|
setStyle = type = pluginName = null;
|
||
|
var style = state.tokenize(stream, state);
|
||
|
if ((style || type) && style != "comment") {
|
||
|
curState = state;
|
||
|
while (true) {
|
||
|
var comb = state.cc.pop() || element;
|
||
|
if (comb(type || style)) break;
|
||
|
}
|
||
|
}
|
||
|
state.startOfLine = false;
|
||
|
return setStyle || style;
|
||
|
},
|
||
|
indent: function(state, textAfter) {
|
||
|
var context = state.context;
|
||
|
if (context && context.noIndent) return 0;
|
||
|
if (context && /^{\//.test(textAfter))
|
||
|
context = context.prev;
|
||
|
while (context && !context.startOfLine)
|
||
|
context = context.prev;
|
||
|
if (context) return context.indent + indentUnit;
|
||
|
else return 0;
|
||
|
},
|
||
|
electricChars: "/"
|
||
|
};
|
||
|
});
|
||
|
|
||
|
CodeMirror.defineMIME("text/tiki", "tiki");
|
||
|
|
||
|
});
|