123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- // Process definition lists
- //
- 'use strict';
- module.exports = function deflist_plugin(md) {
- var isSpace = md.utils.isSpace;
- // Search `[:~][\n ]`, returns next pos after marker on success
- // or -1 on fail.
- function skipMarker(state, line) {
- var pos, marker,
- start = state.bMarks[line] + state.tShift[line],
- max = state.eMarks[line];
- if (start >= max) { return -1; }
- // Check bullet
- marker = state.src.charCodeAt(start++);
- if (marker !== 0x7E/* ~ */ && marker !== 0x3A/* : */) { return -1; }
- pos = state.skipSpaces(start);
- // require space after ":"
- if (start === pos) { return -1; }
- // no empty definitions, e.g. " : "
- if (pos >= max) { return -1; }
- return start;
- }
- function markTightParagraphs(state, idx) {
- var i, l,
- level = state.level + 2;
- for (i = idx + 2, l = state.tokens.length - 2; i < l; i++) {
- if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') {
- state.tokens[i + 2].hidden = true;
- state.tokens[i].hidden = true;
- i += 2;
- }
- }
- }
- function deflist(state, startLine, endLine, silent) {
- var ch,
- contentStart,
- ddLine,
- dtLine,
- itemLines,
- listLines,
- listTokIdx,
- max,
- nextLine,
- offset,
- oldDDIndent,
- oldIndent,
- oldParentType,
- oldSCount,
- oldTShift,
- oldTight,
- pos,
- prevEmptyEnd,
- tight,
- token;
- if (silent) {
- // quirk: validation mode validates a dd block only, not a whole deflist
- if (state.ddIndent < 0) { return false; }
- return skipMarker(state, startLine) >= 0;
- }
- nextLine = startLine + 1;
- if (nextLine >= endLine) { return false; }
- if (state.isEmpty(nextLine)) {
- nextLine++;
- if (nextLine >= endLine) { return false; }
- }
- if (state.sCount[nextLine] < state.blkIndent) { return false; }
- contentStart = skipMarker(state, nextLine);
- if (contentStart < 0) { return false; }
- // Start list
- listTokIdx = state.tokens.length;
- tight = true;
- token = state.push('dl_open', 'dl', 1);
- token.map = listLines = [ startLine, 0 ];
- //
- // Iterate list items
- //
- dtLine = startLine;
- ddLine = nextLine;
- // One definition list can contain multiple DTs,
- // and one DT can be followed by multiple DDs.
- //
- // Thus, there is two loops here, and label is
- // needed to break out of the second one
- //
- /*eslint no-labels:0,block-scoped-var:0*/
- OUTER:
- for (;;) {
- prevEmptyEnd = false;
- token = state.push('dt_open', 'dt', 1);
- token.map = [ dtLine, dtLine ];
- token = state.push('inline', '', 0);
- token.map = [ dtLine, dtLine ];
- token.content = state.getLines(dtLine, dtLine + 1, state.blkIndent, false).trim();
- token.children = [];
- token = state.push('dt_close', 'dt', -1);
- for (;;) {
- token = state.push('dd_open', 'dd', 1);
- token.map = itemLines = [ nextLine, 0 ];
- pos = contentStart;
- max = state.eMarks[ddLine];
- offset = state.sCount[ddLine] + contentStart - (state.bMarks[ddLine] + state.tShift[ddLine]);
- while (pos < max) {
- ch = state.src.charCodeAt(pos);
- if (isSpace(ch)) {
- if (ch === 0x09) {
- offset += 4 - offset % 4;
- } else {
- offset++;
- }
- } else {
- break;
- }
- pos++;
- }
- contentStart = pos;
- oldTight = state.tight;
- oldDDIndent = state.ddIndent;
- oldIndent = state.blkIndent;
- oldTShift = state.tShift[ddLine];
- oldSCount = state.sCount[ddLine];
- oldParentType = state.parentType;
- state.blkIndent = state.ddIndent = state.sCount[ddLine] + 2;
- state.tShift[ddLine] = contentStart - state.bMarks[ddLine];
- state.sCount[ddLine] = offset;
- state.tight = true;
- state.parentType = 'deflist';
- state.md.block.tokenize(state, ddLine, endLine, true);
- // If any of list item is tight, mark list as tight
- if (!state.tight || prevEmptyEnd) {
- tight = false;
- }
- // Item become loose if finish with empty line,
- // but we should filter last element, because it means list finish
- prevEmptyEnd = (state.line - ddLine) > 1 && state.isEmpty(state.line - 1);
- state.tShift[ddLine] = oldTShift;
- state.sCount[ddLine] = oldSCount;
- state.tight = oldTight;
- state.parentType = oldParentType;
- state.blkIndent = oldIndent;
- state.ddIndent = oldDDIndent;
- token = state.push('dd_close', 'dd', -1);
- itemLines[1] = nextLine = state.line;
- if (nextLine >= endLine) { break OUTER; }
- if (state.sCount[nextLine] < state.blkIndent) { break OUTER; }
- contentStart = skipMarker(state, nextLine);
- if (contentStart < 0) { break; }
- ddLine = nextLine;
- // go to the next loop iteration:
- // insert DD tag and repeat checking
- }
- if (nextLine >= endLine) { break; }
- dtLine = nextLine;
- if (state.isEmpty(dtLine)) { break; }
- if (state.sCount[dtLine] < state.blkIndent) { break; }
- ddLine = dtLine + 1;
- if (ddLine >= endLine) { break; }
- if (state.isEmpty(ddLine)) { ddLine++; }
- if (ddLine >= endLine) { break; }
- if (state.sCount[ddLine] < state.blkIndent) { break; }
- contentStart = skipMarker(state, ddLine);
- if (contentStart < 0) { break; }
- // go to the next loop iteration:
- // insert DT and DD tags and repeat checking
- }
- // Finilize list
- token = state.push('dl_close', 'dl', -1);
- listLines[1] = nextLine;
- state.line = nextLine;
- // mark paragraphs tight if needed
- if (tight) {
- markTightParagraphs(state, listTokIdx);
- }
- return true;
- }
- md.block.ruler.before('paragraph', 'deflist', deflist, { alt: [ 'paragraph', 'reference', 'blockquote' ] });
- };
|