markdown.js
230 lines
| 5.2 KiB
| application/javascript
|
JavascriptLexer
Fernando Perez
|
r4933 | CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { | ||
var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true }); | ||||
var header = 'header' | ||||
, code = 'code' | ||||
, quote = 'quote' | ||||
, list = 'list' | ||||
, hr = 'hr' | ||||
, linktext = 'linktext' | ||||
, linkhref = 'linkhref' | ||||
, em = 'em' | ||||
, strong = 'strong' | ||||
, emstrong = 'emstrong'; | ||||
var hrRE = /^[*-=_]/ | ||||
, ulRE = /^[*-+]\s+/ | ||||
, olRE = /^[0-9]\.\s+/ | ||||
, headerRE = /^(?:\={3,}|-{3,})$/ | ||||
, codeRE = /^(k:\t|\s{4,})/ | ||||
, textRE = /^[^\[*_\\<>`]+/; | ||||
function switchInline(stream, state, f) { | ||||
state.f = state.inline = f; | ||||
return f(stream, state); | ||||
} | ||||
function switchBlock(stream, state, f) { | ||||
state.f = state.block = f; | ||||
return f(stream, state); | ||||
} | ||||
// Blocks | ||||
function blockNormal(stream, state) { | ||||
if (stream.match(codeRE)) { | ||||
stream.skipToEnd(); | ||||
return code; | ||||
} | ||||
if (stream.eatSpace()) { | ||||
return null; | ||||
} | ||||
if (stream.peek() === '#' || stream.match(headerRE)) { | ||||
stream.skipToEnd(); | ||||
return header; | ||||
} | ||||
if (stream.eat('>')) { | ||||
state.indentation++; | ||||
return quote; | ||||
} | ||||
if (stream.peek() === '<') { | ||||
return switchBlock(stream, state, htmlBlock); | ||||
} | ||||
if (stream.peek() === '[') { | ||||
return switchInline(stream, state, footnoteLink); | ||||
} | ||||
if (hrRE.test(stream.peek())) { | ||||
var re = new RegExp('(?:\s*['+stream.peek()+']){3,}$'); | ||||
if (stream.match(re, true)) { | ||||
return hr; | ||||
} | ||||
} | ||||
var match; | ||||
if (match = stream.match(ulRE, true) || stream.match(olRE, true)) { | ||||
state.indentation += match[0].length; | ||||
return list; | ||||
} | ||||
return switchInline(stream, state, state.inline); | ||||
} | ||||
function htmlBlock(stream, state) { | ||||
var type = htmlMode.token(stream, state.htmlState); | ||||
if (stream.eol() && !state.htmlState.context) { | ||||
state.block = blockNormal; | ||||
} | ||||
return type; | ||||
} | ||||
// Inline | ||||
function inlineNormal(stream, state) { | ||||
function getType() { | ||||
return state.strong ? (state.em ? emstrong : strong) | ||||
: (state.em ? em : null); | ||||
} | ||||
if (stream.match(textRE, true)) { | ||||
return getType(); | ||||
} | ||||
var ch = stream.next(); | ||||
if (ch === '\\') { | ||||
stream.next(); | ||||
return getType(); | ||||
} | ||||
if (ch === '`') { | ||||
return switchInline(stream, state, inlineElement(code, '`')); | ||||
} | ||||
if (ch === '<') { | ||||
return switchInline(stream, state, inlineElement(linktext, '>')); | ||||
} | ||||
if (ch === '[') { | ||||
return switchInline(stream, state, linkText); | ||||
} | ||||
var t = getType(); | ||||
if (ch === '*' || ch === '_') { | ||||
if (stream.eat(ch)) { | ||||
return (state.strong = !state.strong) ? getType() : t; | ||||
} | ||||
return (state.em = !state.em) ? getType() : t; | ||||
} | ||||
return getType(); | ||||
} | ||||
function linkText(stream, state) { | ||||
while (!stream.eol()) { | ||||
var ch = stream.next(); | ||||
if (ch === '\\') stream.next(); | ||||
if (ch === ']') { | ||||
state.inline = state.f = linkHref; | ||||
return linktext; | ||||
} | ||||
} | ||||
return linktext; | ||||
} | ||||
function linkHref(stream, state) { | ||||
stream.eatSpace(); | ||||
var ch = stream.next(); | ||||
if (ch === '(' || ch === '[') { | ||||
return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']')); | ||||
} | ||||
return 'error'; | ||||
} | ||||
function footnoteLink(stream, state) { | ||||
if (stream.match(/^[^\]]*\]:/, true)) { | ||||
state.f = footnoteUrl; | ||||
return linktext; | ||||
} | ||||
return switchInline(stream, state, inlineNormal); | ||||
} | ||||
function footnoteUrl(stream, state) { | ||||
stream.eatSpace(); | ||||
stream.match(/^[^\s]+/, true); | ||||
state.f = state.inline = inlineNormal; | ||||
return linkhref; | ||||
} | ||||
function inlineElement(type, endChar, next) { | ||||
next = next || inlineNormal; | ||||
return function(stream, state) { | ||||
while (!stream.eol()) { | ||||
var ch = stream.next(); | ||||
if (ch === '\\') stream.next(); | ||||
if (ch === endChar) { | ||||
state.inline = state.f = next; | ||||
return type; | ||||
} | ||||
} | ||||
return type; | ||||
}; | ||||
} | ||||
return { | ||||
startState: function() { | ||||
return { | ||||
f: blockNormal, | ||||
block: blockNormal, | ||||
htmlState: htmlMode.startState(), | ||||
indentation: 0, | ||||
inline: inlineNormal, | ||||
em: false, | ||||
strong: false | ||||
}; | ||||
}, | ||||
copyState: function(s) { | ||||
return { | ||||
f: s.f, | ||||
block: s.block, | ||||
htmlState: CodeMirror.copyState(htmlMode, s.htmlState), | ||||
indentation: s.indentation, | ||||
inline: s.inline, | ||||
em: s.em, | ||||
strong: s.strong | ||||
}; | ||||
}, | ||||
token: function(stream, state) { | ||||
if (stream.sol()) { | ||||
state.f = state.block; | ||||
var previousIndentation = state.indentation | ||||
, currentIndentation = 0; | ||||
while (previousIndentation > 0) { | ||||
if (stream.eat(' ')) { | ||||
previousIndentation--; | ||||
currentIndentation++; | ||||
} else if (previousIndentation >= 4 && stream.eat('\t')) { | ||||
previousIndentation -= 4; | ||||
currentIndentation += 4; | ||||
} else { | ||||
break; | ||||
} | ||||
} | ||||
state.indentation = currentIndentation; | ||||
if (currentIndentation > 0) return null; | ||||
} | ||||
return state.f(stream, state); | ||||
} | ||||
}; | ||||
}); | ||||
CodeMirror.defineMIME("text/x-markdown", "markdown"); | ||||