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.
 
 
 

151 lines
3.9 KiB

'use strict';
var parseLinkDestination = require('../helpers/parse_link_destination');
var parseLinkTitle = require('../helpers/parse_link_title');
var normalizeReference = require('../helpers/normalize_reference');
module.exports = function reference(state, startLine, _endLine, silent) {
var ch,
destEndPos,
destEndLineNo,
endLine,
href,
i,
l,
label,
labelEnd,
res,
start,
str,
terminate,
terminatorRules,
title,
lines = 0,
pos = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine],
nextLine = startLine + 1;
if (pos >= max) { return false; }
if (state.src.charCodeAt(pos) !== 0x5B/* [ */) { return false; }
endLine = state.lineMax;
// jump line-by-line until empty one or EOF
if (nextLine < endLine && !state.isEmpty(nextLine)) {
terminatorRules = state.md.block.ruler.getRules('references');
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
// this would be a code block normally, but after paragraph
// it's considered a lazy continuation regardless of what's there
if (state.tShift[nextLine] - state.blkIndent > 3) { continue; }
// Some tags can terminate paragraph without empty line.
terminate = false;
for (i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true;
break;
}
}
if (terminate) { break; }
}
}
str = state.getLines(startLine, nextLine, state.blkIndent, false).trim();
max = str.length;
for (pos = 1; pos < max; pos++) {
ch = str.charCodeAt(pos);
if (ch === 0x5B /* [ */) {
return false;
} else if (ch === 0x5D /* ] */) {
labelEnd = pos;
break;
} else if (ch === 0x0A /* \n */) {
lines++;
} else if (ch === 0x5C /* \ */) {
pos++;
if (pos < max && str.charCodeAt(pos) === 0x0A) {
lines++;
}
}
}
if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A/* : */) { return false; }
// [label]: destination 'title'
// ^^^ skip optional whitespace here
for (pos = labelEnd + 2; pos < max; pos++) {
ch = str.charCodeAt(pos);
if (ch === 0x0A) {
lines++;
} else if (ch === 0x20) {
/*eslint no-empty:0*/
} else {
break;
}
}
// [label]: destination 'title'
// ^^^^^^^^^^^ parse this
res = parseLinkDestination(str, pos, max);
if (!res.ok) { return false; }
if (!state.md.inline.validateLink(res.str)) { return false; }
href = res.str;
pos = res.pos;
lines += res.lines;
// save cursor state, we could require to rollback later
destEndPos = pos;
destEndLineNo = lines;
// [label]: destination 'title'
// ^^^ skipping those spaces
start = pos;
for (; pos < max; pos++) {
ch = str.charCodeAt(pos);
if (ch === 0x0A) {
lines++;
} else if (ch === 0x20) {
/*eslint no-empty:0*/
} else {
break;
}
}
// [label]: destination 'title'
// ^^^^^^^ parse this
res = parseLinkTitle(str, pos, max);
if (pos < max && start !== pos && res.ok) {
title = res.str;
pos = res.pos;
lines += res.lines;
} else {
title = '';
pos = destEndPos;
lines = destEndLineNo;
}
// skip trailing spaces until the rest of the line
while (pos < max && str.charCodeAt(pos) === 0x20/* space */) { pos++; }
if (pos < max && str.charCodeAt(pos) !== 0x0A) {
// garbage at the end of the line
return false;
}
if (silent) { return true; }
label = normalizeReference(str.slice(1, labelEnd));
if (typeof state.env.references === 'undefined') {
state.env.references = {};
}
if (typeof state.env.references[label] === 'undefined') {
state.env.references[label] = { title: title, href: href };
}
state.line = startLine + lines + 1;
return true;
};