| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- 'use strict';
- /* eslint-disable max-params */
- var trim = require('trim');
- var repeat = require('repeat-string');
- var decimal = require('is-decimal');
- var getIndent = require('../util/get-indentation');
- var removeIndent = require('../util/remove-indentation');
- var interrupt = require('../util/interrupt');
- module.exports = list;
- var C_ASTERISK = '*';
- var C_UNDERSCORE = '_';
- var C_PLUS = '+';
- var C_DASH = '-';
- var C_DOT = '.';
- var C_SPACE = ' ';
- var C_NEWLINE = '\n';
- var C_TAB = '\t';
- var C_PAREN_CLOSE = ')';
- var C_X_LOWER = 'x';
- var TAB_SIZE = 4;
- var EXPRESSION_LOOSE_LIST_ITEM = /\n\n(?!\s*$)/;
- var EXPRESSION_TASK_ITEM = /^\[([ \t]|x|X)][ \t]/;
- var EXPRESSION_BULLET = /^([ \t]*)([*+-]|\d+[.)])( {1,4}(?! )| |\t|$|(?=\n))([^\n]*)/;
- var EXPRESSION_PEDANTIC_BULLET = /^([ \t]*)([*+-]|\d+[.)])([ \t]+)/;
- var EXPRESSION_INITIAL_INDENT = /^( {1,4}|\t)?/gm;
- /* Map of characters which can be used to mark
- * list-items. */
- var LIST_UNORDERED_MARKERS = {};
- LIST_UNORDERED_MARKERS[C_ASTERISK] = true;
- LIST_UNORDERED_MARKERS[C_PLUS] = true;
- LIST_UNORDERED_MARKERS[C_DASH] = true;
- /* Map of characters which can be used to mark
- * list-items after a digit. */
- var LIST_ORDERED_MARKERS = {};
- LIST_ORDERED_MARKERS[C_DOT] = true;
- /* Map of characters which can be used to mark
- * list-items after a digit. */
- var LIST_ORDERED_COMMONMARK_MARKERS = {};
- LIST_ORDERED_COMMONMARK_MARKERS[C_DOT] = true;
- LIST_ORDERED_COMMONMARK_MARKERS[C_PAREN_CLOSE] = true;
- function list(eat, value, silent) {
- var self = this;
- var commonmark = self.options.commonmark;
- var pedantic = self.options.pedantic;
- var tokenizers = self.blockTokenizers;
- var interuptors = self.interruptList;
- var markers;
- var index = 0;
- var length = value.length;
- var start = null;
- var size = 0;
- var queue;
- var ordered;
- var character;
- var marker;
- var nextIndex;
- var startIndex;
- var prefixed;
- var currentMarker;
- var content;
- var line;
- var prevEmpty;
- var empty;
- var items;
- var allLines;
- var emptyLines;
- var item;
- var enterTop;
- var exitBlockquote;
- var isLoose;
- var node;
- var now;
- var end;
- var indented;
- while (index < length) {
- character = value.charAt(index);
- if (character === C_TAB) {
- size += TAB_SIZE - (size % TAB_SIZE);
- } else if (character === C_SPACE) {
- size++;
- } else {
- break;
- }
- index++;
- }
- if (size >= TAB_SIZE) {
- return;
- }
- character = value.charAt(index);
- markers = commonmark ?
- LIST_ORDERED_COMMONMARK_MARKERS :
- LIST_ORDERED_MARKERS;
- if (LIST_UNORDERED_MARKERS[character] === true) {
- marker = character;
- ordered = false;
- } else {
- ordered = true;
- queue = '';
- while (index < length) {
- character = value.charAt(index);
- if (!decimal(character)) {
- break;
- }
- queue += character;
- index++;
- }
- character = value.charAt(index);
- if (!queue || markers[character] !== true) {
- return;
- }
- start = parseInt(queue, 10);
- marker = character;
- }
- character = value.charAt(++index);
- if (character !== C_SPACE && character !== C_TAB) {
- return;
- }
- if (silent) {
- return true;
- }
- index = 0;
- items = [];
- allLines = [];
- emptyLines = [];
- while (index < length) {
- nextIndex = value.indexOf(C_NEWLINE, index);
- startIndex = index;
- prefixed = false;
- indented = false;
- if (nextIndex === -1) {
- nextIndex = length;
- }
- end = index + TAB_SIZE;
- size = 0;
- while (index < length) {
- character = value.charAt(index);
- if (character === C_TAB) {
- size += TAB_SIZE - (size % TAB_SIZE);
- } else if (character === C_SPACE) {
- size++;
- } else {
- break;
- }
- index++;
- }
- if (size >= TAB_SIZE) {
- indented = true;
- }
- if (item && size >= item.indent) {
- indented = true;
- }
- character = value.charAt(index);
- currentMarker = null;
- if (!indented) {
- if (LIST_UNORDERED_MARKERS[character] === true) {
- currentMarker = character;
- index++;
- size++;
- } else {
- queue = '';
- while (index < length) {
- character = value.charAt(index);
- if (!decimal(character)) {
- break;
- }
- queue += character;
- index++;
- }
- character = value.charAt(index);
- index++;
- if (queue && markers[character] === true) {
- currentMarker = character;
- size += queue.length + 1;
- }
- }
- if (currentMarker) {
- character = value.charAt(index);
- if (character === C_TAB) {
- size += TAB_SIZE - (size % TAB_SIZE);
- index++;
- } else if (character === C_SPACE) {
- end = index + TAB_SIZE;
- while (index < end) {
- if (value.charAt(index) !== C_SPACE) {
- break;
- }
- index++;
- size++;
- }
- if (index === end && value.charAt(index) === C_SPACE) {
- index -= TAB_SIZE - 1;
- size -= TAB_SIZE - 1;
- }
- } else if (character !== C_NEWLINE && character !== '') {
- currentMarker = null;
- }
- }
- }
- if (currentMarker) {
- if (!pedantic && marker !== currentMarker) {
- break;
- }
- prefixed = true;
- } else {
- if (!commonmark && !indented && value.charAt(startIndex) === C_SPACE) {
- indented = true;
- } else if (commonmark && item) {
- indented = size >= item.indent || size > TAB_SIZE;
- }
- prefixed = false;
- index = startIndex;
- }
- line = value.slice(startIndex, nextIndex);
- content = startIndex === index ? line : value.slice(index, nextIndex);
- if (
- currentMarker === C_ASTERISK ||
- currentMarker === C_UNDERSCORE ||
- currentMarker === C_DASH
- ) {
- if (tokenizers.thematicBreak.call(self, eat, line, true)) {
- break;
- }
- }
- prevEmpty = empty;
- empty = !trim(content).length;
- if (indented && item) {
- item.value = item.value.concat(emptyLines, line);
- allLines = allLines.concat(emptyLines, line);
- emptyLines = [];
- } else if (prefixed) {
- if (emptyLines.length !== 0) {
- item.value.push('');
- item.trail = emptyLines.concat();
- }
- item = {
- value: [line],
- indent: size,
- trail: []
- };
- items.push(item);
- allLines = allLines.concat(emptyLines, line);
- emptyLines = [];
- } else if (empty) {
- if (prevEmpty) {
- break;
- }
- emptyLines.push(line);
- } else {
- if (prevEmpty) {
- break;
- }
- if (interrupt(interuptors, tokenizers, self, [eat, line, true])) {
- break;
- }
- item.value = item.value.concat(emptyLines, line);
- allLines = allLines.concat(emptyLines, line);
- emptyLines = [];
- }
- index = nextIndex + 1;
- }
- node = eat(allLines.join(C_NEWLINE)).reset({
- type: 'list',
- ordered: ordered,
- start: start,
- loose: null,
- children: []
- });
- enterTop = self.enterList();
- exitBlockquote = self.enterBlock();
- isLoose = false;
- index = -1;
- length = items.length;
- while (++index < length) {
- item = items[index].value.join(C_NEWLINE);
- now = eat.now();
- item = eat(item)(listItem(self, item, now), node);
- if (item.loose) {
- isLoose = true;
- }
- item = items[index].trail.join(C_NEWLINE);
- if (index !== length - 1) {
- item += C_NEWLINE;
- }
- eat(item);
- }
- enterTop();
- exitBlockquote();
- node.loose = isLoose;
- return node;
- }
- function listItem(ctx, value, position) {
- var offsets = ctx.offset;
- var fn = ctx.options.pedantic ? pedanticListItem : normalListItem;
- var checked = null;
- var task;
- var indent;
- value = fn.apply(null, arguments);
- if (ctx.options.gfm) {
- task = value.match(EXPRESSION_TASK_ITEM);
- if (task) {
- indent = task[0].length;
- checked = task[1].toLowerCase() === C_X_LOWER;
- offsets[position.line] += indent;
- value = value.slice(indent);
- }
- }
- return {
- type: 'listItem',
- loose: EXPRESSION_LOOSE_LIST_ITEM.test(value) ||
- value.charAt(value.length - 1) === C_NEWLINE,
- checked: checked,
- children: ctx.tokenizeBlock(value, position)
- };
- }
- /* Create a list-item using overly simple mechanics. */
- function pedanticListItem(ctx, value, position) {
- var offsets = ctx.offset;
- var line = position.line;
- /* Remove the list-item’s bullet. */
- value = value.replace(EXPRESSION_PEDANTIC_BULLET, replacer);
- /* The initial line was also matched by the below, so
- * we reset the `line`. */
- line = position.line;
- return value.replace(EXPRESSION_INITIAL_INDENT, replacer);
- /* A simple replacer which removed all matches,
- * and adds their length to `offset`. */
- function replacer($0) {
- offsets[line] = (offsets[line] || 0) + $0.length;
- line++;
- return '';
- }
- }
- /* Create a list-item using sane mechanics. */
- function normalListItem(ctx, value, position) {
- var offsets = ctx.offset;
- var line = position.line;
- var max;
- var bullet;
- var rest;
- var lines;
- var trimmedLines;
- var index;
- var length;
- /* Remove the list-item’s bullet. */
- value = value.replace(EXPRESSION_BULLET, replacer);
- lines = value.split(C_NEWLINE);
- trimmedLines = removeIndent(value, getIndent(max).indent).split(C_NEWLINE);
- /* We replaced the initial bullet with something
- * else above, which was used to trick
- * `removeIndentation` into removing some more
- * characters when possible. However, that could
- * result in the initial line to be stripped more
- * than it should be. */
- trimmedLines[0] = rest;
- offsets[line] = (offsets[line] || 0) + bullet.length;
- line++;
- index = 0;
- length = lines.length;
- while (++index < length) {
- offsets[line] = (offsets[line] || 0) +
- lines[index].length - trimmedLines[index].length;
- line++;
- }
- return trimmedLines.join(C_NEWLINE);
- function replacer($0, $1, $2, $3, $4) {
- bullet = $1 + $2 + $3;
- rest = $4;
- /* Make sure that the first nine numbered list items
- * can indent with an extra space. That is, when
- * the bullet did not receive an extra final space. */
- if (Number($2) < 10 && bullet.length % 2 === 1) {
- $2 = C_SPACE + $2;
- }
- max = $1 + repeat(C_SPACE, $2.length) + $3;
- return max + rest;
- }
- }
|