|
|
CodeMirror.defineMode("gfm", function(config, parserConfig) {
|
|
|
var mdMode = CodeMirror.getMode(config, "markdown");
|
|
|
var aliases = {
|
|
|
html: "htmlmixed",
|
|
|
js: "javascript",
|
|
|
json: "application/json",
|
|
|
c: "text/x-csrc",
|
|
|
"c++": "text/x-c++src",
|
|
|
java: "text/x-java",
|
|
|
csharp: "text/x-csharp",
|
|
|
"c#": "text/x-csharp"
|
|
|
};
|
|
|
|
|
|
// make this lazy so that we don't need to load GFM last
|
|
|
var getMode = (function () {
|
|
|
var i, modes = {}, mimes = {}, mime;
|
|
|
|
|
|
var list = CodeMirror.listModes();
|
|
|
for (i = 0; i < list.length; i++) {
|
|
|
modes[list[i]] = list[i];
|
|
|
}
|
|
|
var mimesList = CodeMirror.listMIMEs();
|
|
|
for (i = 0; i < mimesList.length; i++) {
|
|
|
mime = mimesList[i].mime;
|
|
|
mimes[mime] = mimesList[i].mime;
|
|
|
}
|
|
|
|
|
|
for (var a in aliases) {
|
|
|
if (aliases[a] in modes || aliases[a] in mimes)
|
|
|
modes[a] = aliases[a];
|
|
|
}
|
|
|
|
|
|
return function (lang) {
|
|
|
return modes[lang] ? CodeMirror.getMode(config, modes[lang]) : null;
|
|
|
}
|
|
|
}());
|
|
|
|
|
|
function markdown(stream, state) {
|
|
|
// intercept fenced code blocks
|
|
|
if (stream.sol() && stream.match(/^```([\w+#]*)/)) {
|
|
|
// try switching mode
|
|
|
state.localMode = getMode(RegExp.$1)
|
|
|
if (state.localMode)
|
|
|
state.localState = state.localMode.startState();
|
|
|
|
|
|
state.token = local;
|
|
|
return 'code';
|
|
|
}
|
|
|
|
|
|
return mdMode.token(stream, state.mdState);
|
|
|
}
|
|
|
|
|
|
function local(stream, state) {
|
|
|
if (stream.sol() && stream.match(/^```/)) {
|
|
|
state.localMode = state.localState = null;
|
|
|
state.token = markdown;
|
|
|
return 'code';
|
|
|
}
|
|
|
else if (state.localMode) {
|
|
|
return state.localMode.token(stream, state.localState);
|
|
|
} else {
|
|
|
stream.skipToEnd();
|
|
|
return 'code';
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// custom handleText to prevent emphasis in the middle of a word
|
|
|
// and add autolinking
|
|
|
function handleText(stream, mdState) {
|
|
|
var match;
|
|
|
if (stream.match(/^\w+:\/\/\S+/)) {
|
|
|
return 'link';
|
|
|
}
|
|
|
if (stream.match(/^[^\[*\\<>` _][^\[*\\<>` ]*[^\[*\\<>` _]/)) {
|
|
|
return mdMode.getType(mdState);
|
|
|
}
|
|
|
if (match = stream.match(/^[^\[*\\<>` ]+/)) {
|
|
|
var word = match[0];
|
|
|
if (word[0] === '_' && word[word.length-1] === '_') {
|
|
|
stream.backUp(word.length);
|
|
|
return undefined;
|
|
|
}
|
|
|
return mdMode.getType(mdState);
|
|
|
}
|
|
|
if (stream.eatSpace()) {
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
startState: function() {
|
|
|
var mdState = mdMode.startState();
|
|
|
mdState.text = handleText;
|
|
|
return {token: markdown, mode: "markdown", mdState: mdState,
|
|
|
localMode: null, localState: null};
|
|
|
},
|
|
|
|
|
|
copyState: function(state) {
|
|
|
return {token: state.token, mode: state.mode, mdState: CodeMirror.copyState(mdMode, state.mdState),
|
|
|
localMode: state.localMode,
|
|
|
localState: state.localMode ? CodeMirror.copyState(state.localMode, state.localState) : null};
|
|
|
},
|
|
|
|
|
|
token: function(stream, state) {
|
|
|
/* Parse GFM double bracket links */
|
|
|
if ((ch = stream.peek()) != undefined && ch == '[') {
|
|
|
stream.next(); // Advance the stream
|
|
|
|
|
|
/* Only handle double bracket links */
|
|
|
if ((ch = stream.peek()) == undefined || ch != '[') {
|
|
|
stream.backUp(1);
|
|
|
return state.token(stream, state);
|
|
|
}
|
|
|
|
|
|
while ((ch = stream.next()) != undefined && ch != ']') {}
|
|
|
|
|
|
if (ch == ']' && (ch = stream.next()) != undefined && ch == ']')
|
|
|
return 'link';
|
|
|
|
|
|
/* If we did not find the second ']' */
|
|
|
stream.backUp(1);
|
|
|
}
|
|
|
|
|
|
/* Match GFM latex formulas, as well as latex formulas within '$' */
|
|
|
if (stream.match(/^\$[^\$]+\$/)) {
|
|
|
return "string";
|
|
|
}
|
|
|
|
|
|
if (stream.match(/^\\\((.*?)\\\)/)) {
|
|
|
return "string";
|
|
|
}
|
|
|
|
|
|
if (stream.match(/^\$\$[^\$]+\$\$/)) {
|
|
|
return "string";
|
|
|
}
|
|
|
|
|
|
if (stream.match(/^\\\[(.*?)\\\]/)) {
|
|
|
return "string";
|
|
|
}
|
|
|
|
|
|
return state.token(stream, state);
|
|
|
}
|
|
|
}
|
|
|
}, "markdown");
|
|
|
|