+++ /dev/null
-'use strict';
-
-module.exports = factory;
-
-var MERGEABLE_NODES = {
- text: mergeText,
- blockquote: mergeBlockquote
-};
-
-/* Check whether a node is mergeable with adjacent nodes. */
-function mergeable(node) {
- var start;
- var end;
-
- if (node.type !== 'text' || !node.position) {
- return true;
- }
-
- start = node.position.start;
- end = node.position.end;
-
- /* Only merge nodes which occupy the same size as their
- * `value`. */
- return start.line !== end.line ||
- end.column - start.column === node.value.length;
-}
-
-/* Merge two text nodes: `node` into `prev`. */
-function mergeText(prev, node) {
- prev.value += node.value;
-
- return prev;
-}
-
-/* Merge two blockquotes: `node` into `prev`, unless in
- * CommonMark mode. */
-function mergeBlockquote(prev, node) {
- if (this.options.commonmark) {
- return node;
- }
-
- prev.children = prev.children.concat(node.children);
-
- return prev;
-}
-
-/* Construct a tokenizer. This creates both
- * `tokenizeInline` and `tokenizeBlock`. */
-function factory(type) {
- return tokenize;
-
- /* Tokenizer for a bound `type`. */
- function tokenize(value, location) {
- var self = this;
- var offset = self.offset;
- var tokens = [];
- var methods = self[type + 'Methods'];
- var tokenizers = self[type + 'Tokenizers'];
- var line = location.line;
- var column = location.column;
- var index;
- var length;
- var method;
- var name;
- var matched;
- var valueLength;
-
- /* Trim white space only lines. */
- if (!value) {
- return tokens;
- }
-
- /* Expose on `eat`. */
- eat.now = now;
- eat.file = self.file;
-
- /* Sync initial offset. */
- updatePosition('');
-
- /* Iterate over `value`, and iterate over all
- * tokenizers. When one eats something, re-iterate
- * with the remaining value. If no tokenizer eats,
- * something failed (should not happen) and an
- * exception is thrown. */
- while (value) {
- index = -1;
- length = methods.length;
- matched = false;
-
- while (++index < length) {
- name = methods[index];
- method = tokenizers[name];
-
- if (
- method &&
- /* istanbul ignore next */ (!method.onlyAtStart || self.atStart) &&
- (!method.notInList || !self.inList) &&
- (!method.notInBlock || !self.inBlock) &&
- (!method.notInLink || !self.inLink)
- ) {
- valueLength = value.length;
-
- method.apply(self, [eat, value]);
-
- matched = valueLength !== value.length;
-
- if (matched) {
- break;
- }
- }
- }
-
- /* istanbul ignore if */
- if (!matched) {
- self.file.fail(new Error('Infinite loop'), eat.now());
- }
- }
-
- self.eof = now();
-
- return tokens;
-
- /* Update line, column, and offset based on
- * `value`. */
- function updatePosition(subvalue) {
- var lastIndex = -1;
- var index = subvalue.indexOf('\n');
-
- while (index !== -1) {
- line++;
- lastIndex = index;
- index = subvalue.indexOf('\n', index + 1);
- }
-
- if (lastIndex === -1) {
- column += subvalue.length;
- } else {
- column = subvalue.length - lastIndex;
- }
-
- if (line in offset) {
- if (lastIndex !== -1) {
- column += offset[line];
- } else if (column <= offset[line]) {
- column = offset[line] + 1;
- }
- }
- }
-
- /* Get offset. Called before the first character is
- * eaten to retrieve the range's offsets. */
- function getOffset() {
- var indentation = [];
- var pos = line + 1;
-
- /* Done. Called when the last character is
- * eaten to retrieve the range’s offsets. */
- return function () {
- var last = line + 1;
-
- while (pos < last) {
- indentation.push((offset[pos] || 0) + 1);
-
- pos++;
- }
-
- return indentation;
- };
- }
-
- /* Get the current position. */
- function now() {
- var pos = {line: line, column: column};
-
- pos.offset = self.toOffset(pos);
-
- return pos;
- }
-
- /* Store position information for a node. */
- function Position(start) {
- this.start = start;
- this.end = now();
- }
-
- /* Throw when a value is incorrectly eaten.
- * This shouldn’t happen but will throw on new,
- * incorrect rules. */
- function validateEat(subvalue) {
- /* istanbul ignore if */
- if (value.substring(0, subvalue.length) !== subvalue) {
- /* Capture stack-trace. */
- self.file.fail(
- new Error(
- 'Incorrectly eaten value: please report this ' +
- 'warning on http://git.io/vg5Ft'
- ),
- now()
- );
- }
- }
-
- /* Mark position and patch `node.position`. */
- function position() {
- var before = now();
-
- return update;
-
- /* Add the position to a node. */
- function update(node, indent) {
- var prev = node.position;
- var start = prev ? prev.start : before;
- var combined = [];
- var n = prev && prev.end.line;
- var l = before.line;
-
- node.position = new Position(start);
-
- /* If there was already a `position`, this
- * node was merged. Fixing `start` wasn’t
- * hard, but the indent is different.
- * Especially because some information, the
- * indent between `n` and `l` wasn’t
- * tracked. Luckily, that space is
- * (should be?) empty, so we can safely
- * check for it now. */
- if (prev && indent && prev.indent) {
- combined = prev.indent;
-
- if (n < l) {
- while (++n < l) {
- combined.push((offset[n] || 0) + 1);
- }
-
- combined.push(before.column);
- }
-
- indent = combined.concat(indent);
- }
-
- node.position.indent = indent || [];
-
- return node;
- }
- }
-
- /* Add `node` to `parent`s children or to `tokens`.
- * Performs merges where possible. */
- function add(node, parent) {
- var children = parent ? parent.children : tokens;
- var prev = children[children.length - 1];
-
- if (
- prev &&
- node.type === prev.type &&
- node.type in MERGEABLE_NODES &&
- mergeable(prev) &&
- mergeable(node)
- ) {
- node = MERGEABLE_NODES[node.type].call(self, prev, node);
- }
-
- if (node !== prev) {
- children.push(node);
- }
-
- if (self.atStart && tokens.length !== 0) {
- self.exitStart();
- }
-
- return node;
- }
-
- /* Remove `subvalue` from `value`.
- * `subvalue` must be at the start of `value`. */
- function eat(subvalue) {
- var indent = getOffset();
- var pos = position();
- var current = now();
-
- validateEat(subvalue);
-
- apply.reset = reset;
- reset.test = test;
- apply.test = test;
-
- value = value.substring(subvalue.length);
-
- updatePosition(subvalue);
-
- indent = indent();
-
- return apply;
-
- /* Add the given arguments, add `position` to
- * the returned node, and return the node. */
- function apply(node, parent) {
- return pos(add(pos(node), parent), indent);
- }
-
- /* Functions just like apply, but resets the
- * content: the line and column are reversed,
- * and the eaten value is re-added.
- * This is useful for nodes with a single
- * type of content, such as lists and tables.
- * See `apply` above for what parameters are
- * expected. */
- function reset() {
- var node = apply.apply(null, arguments);
-
- line = current.line;
- column = current.column;
- value = subvalue + value;
-
- return node;
- }
-
- /* Test the position, after eating, and reverse
- * to a not-eaten state. */
- function test() {
- var result = pos({});
-
- line = current.line;
- column = current.column;
- value = subvalue + value;
-
- return result.position;
- }
- }
- }
-}