Markdown parser, done right. 100% CommonMark support, extensions, syntax plugins & high speed https://markdown-it.github.io/
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.

150 lines
3.8 KiB

// Process *this* and _that_
'use strict';
function isAlphaNum(code) {
return (code >= 0x30 /* 0 */ && code <= 0x39 /* 9 */) ||
(code >= 0x41 /* A */ && code <= 0x5A /* Z */) ||
(code >= 0x61 /* a */ && code <= 0x7A /* z */);
}
// parse sequence of emphasis markers,
// "start" should point at a valid marker
function scanDelims(state, start) {
var pos = start, lastChar, nextChar, count,
can_open = true,
can_close = true,
max = state.posMax,
marker = state.src.charCodeAt(start);
lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1;
while (pos < max && state.src.charCodeAt(pos) === marker) { pos++; }
if (pos >= max) { can_open = false; }
count = pos - start;
if (count >= 4) {
// sequence of four or more unescaped markers can't start/end an emphasis
can_open = can_close = false;
} else {
nextChar = pos < max ? state.src.charCodeAt(pos) : -1;
// check whitespace conditions
if (nextChar === 0x20 || nextChar === 0x0A) { can_open = false; }
if (lastChar === 0x20 || lastChar === 0x0A) { can_close = false; }
if (marker === 0x5F /* _ */) {
// check if we aren't inside the word
if (isAlphaNum(lastChar)) { can_open = false; }
if (isAlphaNum(nextChar)) { can_close = false; }
}
}
return {
can_open: can_open,
can_close: can_close,
delims: count
};
}
module.exports = function emphasis(state, silent) {
var startCount,
count,
found,
oldCount,
newCount,
stack,
res,
max = state.posMax,
start = state.pos,
marker = state.src.charCodeAt(start);
if (marker !== 0x5F/* _ */ && marker !== 0x2A /* * */) { return false; }
if (silent) { return false; } // don't run any pairs in validation mode
res = scanDelims(state, start);
startCount = res.delims;
if (!res.can_open) {
state.pos += startCount;
if (!silent) { state.pending += state.src.slice(start, state.pos); }
return true;
}
if (state.level >= state.options.maxNesting) { return false; }
state.pos = start + startCount;
stack = [ startCount ];
while (state.pos < max) {
if (state.src.charCodeAt(state.pos) === marker) {
res = scanDelims(state, state.pos);
count = res.delims;
if (res.can_close) {
oldCount = stack.pop();
newCount = count;
while (oldCount !== newCount) {
if (newCount < oldCount) {
stack.push(oldCount - newCount);
break;
}
// assert(newCount > oldCount)
newCount -= oldCount;
if (stack.length === 0) { break; }
state.pos += oldCount;
oldCount = stack.pop();
}
if (stack.length === 0) {
startCount = oldCount;
found = true;
break;
}
state.pos += count;
continue;
}
if (res.can_open) { stack.push(count); }
state.pos += count;
continue;
}
state.parser.skipToken(state);
}
if (!found) {
// parser failed to find ending tag, so it's not valid emphasis
state.pos = start;
return false;
}
// found!
state.posMax = state.pos;
state.pos = start + startCount;
if (!silent) {
if (startCount === 2 || startCount === 3) {
state.push({ type: 'strong_open', level: state.level++ });
}
if (startCount === 1 || startCount === 3) {
state.push({ type: 'em_open', level: state.level++ });
}
state.parser.tokenize(state);
if (startCount === 1 || startCount === 3) {
state.push({ type: 'em_close', level: --state.level });
}
if (startCount === 2 || startCount === 3) {
state.push({ type: 'strong_close', level: --state.level });
}
}
state.pos = state.posMax + startCount;
state.posMax = max;
return true;
};