3 module.exports = factory;
5 var MERGEABLE_NODES = {
7 blockquote: mergeBlockquote
10 /* Check whether a node is mergeable with adjacent nodes. */
11 function mergeable(node) {
15 if (node.type !== 'text' || !node.position) {
19 start = node.position.start;
20 end = node.position.end;
22 /* Only merge nodes which occupy the same size as their
24 return start.line !== end.line ||
25 end.column - start.column === node.value.length;
28 /* Merge two text nodes: `node` into `prev`. */
29 function mergeText(prev, node) {
30 prev.value += node.value;
35 /* Merge two blockquotes: `node` into `prev`, unless in
37 function mergeBlockquote(prev, node) {
38 if (this.options.commonmark) {
42 prev.children = prev.children.concat(node.children);
47 /* Construct a tokenizer. This creates both
48 * `tokenizeInline` and `tokenizeBlock`. */
49 function factory(type) {
52 /* Tokenizer for a bound `type`. */
53 function tokenize(value, location) {
55 var offset = self.offset;
57 var methods = self[type + 'Methods'];
58 var tokenizers = self[type + 'Tokenizers'];
59 var line = location.line;
60 var column = location.column;
68 /* Trim white space only lines. */
73 /* Expose on `eat`. */
77 /* Sync initial offset. */
80 /* Iterate over `value`, and iterate over all
81 * tokenizers. When one eats something, re-iterate
82 * with the remaining value. If no tokenizer eats,
83 * something failed (should not happen) and an
84 * exception is thrown. */
87 length = methods.length;
90 while (++index < length) {
91 name = methods[index];
92 method = tokenizers[name];
96 /* istanbul ignore next */ (!method.onlyAtStart || self.atStart) &&
97 (!method.notInList || !self.inList) &&
98 (!method.notInBlock || !self.inBlock) &&
99 (!method.notInLink || !self.inLink)
101 valueLength = value.length;
103 method.apply(self, [eat, value]);
105 matched = valueLength !== value.length;
113 /* istanbul ignore if */
115 self.file.fail(new Error('Infinite loop'), eat.now());
123 /* Update line, column, and offset based on
125 function updatePosition(subvalue) {
127 var index = subvalue.indexOf('\n');
129 while (index !== -1) {
132 index = subvalue.indexOf('\n', index + 1);
135 if (lastIndex === -1) {
136 column += subvalue.length;
138 column = subvalue.length - lastIndex;
141 if (line in offset) {
142 if (lastIndex !== -1) {
143 column += offset[line];
144 } else if (column <= offset[line]) {
145 column = offset[line] + 1;
150 /* Get offset. Called before the first character is
151 * eaten to retrieve the range's offsets. */
152 function getOffset() {
153 var indentation = [];
156 /* Done. Called when the last character is
157 * eaten to retrieve the range’s offsets. */
162 indentation.push((offset[pos] || 0) + 1);
171 /* Get the current position. */
173 var pos = {line: line, column: column};
175 pos.offset = self.toOffset(pos);
180 /* Store position information for a node. */
181 function Position(start) {
186 /* Throw when a value is incorrectly eaten.
187 * This shouldn’t happen but will throw on new,
188 * incorrect rules. */
189 function validateEat(subvalue) {
190 /* istanbul ignore if */
191 if (value.substring(0, subvalue.length) !== subvalue) {
192 /* Capture stack-trace. */
195 'Incorrectly eaten value: please report this ' +
196 'warning on http://git.io/vg5Ft'
203 /* Mark position and patch `node.position`. */
204 function position() {
209 /* Add the position to a node. */
210 function update(node, indent) {
211 var prev = node.position;
212 var start = prev ? prev.start : before;
214 var n = prev && prev.end.line;
217 node.position = new Position(start);
219 /* If there was already a `position`, this
220 * node was merged. Fixing `start` wasn’t
221 * hard, but the indent is different.
222 * Especially because some information, the
223 * indent between `n` and `l` wasn’t
224 * tracked. Luckily, that space is
225 * (should be?) empty, so we can safely
226 * check for it now. */
227 if (prev && indent && prev.indent) {
228 combined = prev.indent;
232 combined.push((offset[n] || 0) + 1);
235 combined.push(before.column);
238 indent = combined.concat(indent);
241 node.position.indent = indent || [];
247 /* Add `node` to `parent`s children or to `tokens`.
248 * Performs merges where possible. */
249 function add(node, parent) {
250 var children = parent ? parent.children : tokens;
251 var prev = children[children.length - 1];
255 node.type === prev.type &&
256 node.type in MERGEABLE_NODES &&
260 node = MERGEABLE_NODES[node.type].call(self, prev, node);
267 if (self.atStart && tokens.length !== 0) {
274 /* Remove `subvalue` from `value`.
275 * `subvalue` must be at the start of `value`. */
276 function eat(subvalue) {
277 var indent = getOffset();
278 var pos = position();
281 validateEat(subvalue);
287 value = value.substring(subvalue.length);
289 updatePosition(subvalue);
295 /* Add the given arguments, add `position` to
296 * the returned node, and return the node. */
297 function apply(node, parent) {
298 return pos(add(pos(node), parent), indent);
301 /* Functions just like apply, but resets the
302 * content: the line and column are reversed,
303 * and the eaten value is re-added.
304 * This is useful for nodes with a single
305 * type of content, such as lists and tables.
306 * See `apply` above for what parameters are
309 var node = apply.apply(null, arguments);
312 column = current.column;
313 value = subvalue + value;
318 /* Test the position, after eating, and reverse
319 * to a not-eaten state. */
321 var result = pos({});
324 column = current.column;
325 value = subvalue + value;
327 return result.position;