.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / remark-parse / lib / tokenizer.js
1 'use strict';
2
3 module.exports = factory;
4
5 var MERGEABLE_NODES = {
6   text: mergeText,
7   blockquote: mergeBlockquote
8 };
9
10 /* Check whether a node is mergeable with adjacent nodes. */
11 function mergeable(node) {
12   var start;
13   var end;
14
15   if (node.type !== 'text' || !node.position) {
16     return true;
17   }
18
19   start = node.position.start;
20   end = node.position.end;
21
22   /* Only merge nodes which occupy the same size as their
23    * `value`. */
24   return start.line !== end.line ||
25       end.column - start.column === node.value.length;
26 }
27
28 /* Merge two text nodes: `node` into `prev`. */
29 function mergeText(prev, node) {
30   prev.value += node.value;
31
32   return prev;
33 }
34
35 /* Merge two blockquotes: `node` into `prev`, unless in
36  * CommonMark mode. */
37 function mergeBlockquote(prev, node) {
38   if (this.options.commonmark) {
39     return node;
40   }
41
42   prev.children = prev.children.concat(node.children);
43
44   return prev;
45 }
46
47 /* Construct a tokenizer.  This creates both
48  * `tokenizeInline` and `tokenizeBlock`. */
49 function factory(type) {
50   return tokenize;
51
52   /* Tokenizer for a bound `type`. */
53   function tokenize(value, location) {
54     var self = this;
55     var offset = self.offset;
56     var tokens = [];
57     var methods = self[type + 'Methods'];
58     var tokenizers = self[type + 'Tokenizers'];
59     var line = location.line;
60     var column = location.column;
61     var index;
62     var length;
63     var method;
64     var name;
65     var matched;
66     var valueLength;
67
68     /* Trim white space only lines. */
69     if (!value) {
70       return tokens;
71     }
72
73     /* Expose on `eat`. */
74     eat.now = now;
75     eat.file = self.file;
76
77     /* Sync initial offset. */
78     updatePosition('');
79
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. */
85     while (value) {
86       index = -1;
87       length = methods.length;
88       matched = false;
89
90       while (++index < length) {
91         name = methods[index];
92         method = tokenizers[name];
93
94         if (
95           method &&
96           /* istanbul ignore next */ (!method.onlyAtStart || self.atStart) &&
97           (!method.notInList || !self.inList) &&
98           (!method.notInBlock || !self.inBlock) &&
99           (!method.notInLink || !self.inLink)
100         ) {
101           valueLength = value.length;
102
103           method.apply(self, [eat, value]);
104
105           matched = valueLength !== value.length;
106
107           if (matched) {
108             break;
109           }
110         }
111       }
112
113       /* istanbul ignore if */
114       if (!matched) {
115         self.file.fail(new Error('Infinite loop'), eat.now());
116       }
117     }
118
119     self.eof = now();
120
121     return tokens;
122
123     /* Update line, column, and offset based on
124      * `value`. */
125     function updatePosition(subvalue) {
126       var lastIndex = -1;
127       var index = subvalue.indexOf('\n');
128
129       while (index !== -1) {
130         line++;
131         lastIndex = index;
132         index = subvalue.indexOf('\n', index + 1);
133       }
134
135       if (lastIndex === -1) {
136         column += subvalue.length;
137       } else {
138         column = subvalue.length - lastIndex;
139       }
140
141       if (line in offset) {
142         if (lastIndex !== -1) {
143           column += offset[line];
144         } else if (column <= offset[line]) {
145           column = offset[line] + 1;
146         }
147       }
148     }
149
150     /* Get offset.  Called before the first character is
151      * eaten to retrieve the range's offsets. */
152     function getOffset() {
153       var indentation = [];
154       var pos = line + 1;
155
156       /* Done.  Called when the last character is
157        * eaten to retrieve the range’s offsets. */
158       return function () {
159         var last = line + 1;
160
161         while (pos < last) {
162           indentation.push((offset[pos] || 0) + 1);
163
164           pos++;
165         }
166
167         return indentation;
168       };
169     }
170
171     /* Get the current position. */
172     function now() {
173       var pos = {line: line, column: column};
174
175       pos.offset = self.toOffset(pos);
176
177       return pos;
178     }
179
180     /* Store position information for a node. */
181     function Position(start) {
182       this.start = start;
183       this.end = now();
184     }
185
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. */
193         self.file.fail(
194           new Error(
195             'Incorrectly eaten value: please report this ' +
196             'warning on http://git.io/vg5Ft'
197           ),
198           now()
199         );
200       }
201     }
202
203     /* Mark position and patch `node.position`. */
204     function position() {
205       var before = now();
206
207       return update;
208
209       /* Add the position to a node. */
210       function update(node, indent) {
211         var prev = node.position;
212         var start = prev ? prev.start : before;
213         var combined = [];
214         var n = prev && prev.end.line;
215         var l = before.line;
216
217         node.position = new Position(start);
218
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;
229
230           if (n < l) {
231             while (++n < l) {
232               combined.push((offset[n] || 0) + 1);
233             }
234
235             combined.push(before.column);
236           }
237
238           indent = combined.concat(indent);
239         }
240
241         node.position.indent = indent || [];
242
243         return node;
244       }
245     }
246
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];
252
253       if (
254         prev &&
255         node.type === prev.type &&
256         node.type in MERGEABLE_NODES &&
257         mergeable(prev) &&
258         mergeable(node)
259       ) {
260         node = MERGEABLE_NODES[node.type].call(self, prev, node);
261       }
262
263       if (node !== prev) {
264         children.push(node);
265       }
266
267       if (self.atStart && tokens.length !== 0) {
268         self.exitStart();
269       }
270
271       return node;
272     }
273
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();
279       var current = now();
280
281       validateEat(subvalue);
282
283       apply.reset = reset;
284       reset.test = test;
285       apply.test = test;
286
287       value = value.substring(subvalue.length);
288
289       updatePosition(subvalue);
290
291       indent = indent();
292
293       return apply;
294
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);
299       }
300
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
307        * expected. */
308       function reset() {
309         var node = apply.apply(null, arguments);
310
311         line = current.line;
312         column = current.column;
313         value = subvalue + value;
314
315         return node;
316       }
317
318       /* Test the position, after eating, and reverse
319        * to a not-eaten state. */
320       function test() {
321         var result = pos({});
322
323         line = current.line;
324         column = current.column;
325         value = subvalue + value;
326
327         return result.position;
328       }
329     }
330   }
331 }