.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / fast-glob / node_modules / braces / lib / parsers.js
1 'use strict';
2
3 var Node = require('snapdragon-node');
4 var utils = require('./utils');
5
6 /**
7  * Braces parsers
8  */
9
10 module.exports = function(braces, options) {
11   braces.parser
12     .set('bos', function() {
13       if (!this.parsed) {
14         this.ast = this.nodes[0] = new Node(this.ast);
15       }
16     })
17
18     /**
19      * Character parsers
20      */
21
22     .set('escape', function() {
23       var pos = this.position();
24       var m = this.match(/^(?:\\(.)|\$\{)/);
25       if (!m) return;
26
27       var prev = this.prev();
28       var last = utils.last(prev.nodes);
29
30       var node = pos(new Node({
31         type: 'text',
32         multiplier: 1,
33         val: m[0]
34       }));
35
36       if (node.val === '\\\\') {
37         return node;
38       }
39
40       if (node.val === '${') {
41         var str = this.input;
42         var idx = -1;
43         var ch;
44
45         while ((ch = str[++idx])) {
46           this.consume(1);
47           node.val += ch;
48           if (ch === '\\') {
49             node.val += str[++idx];
50             continue;
51           }
52           if (ch === '}') {
53             break;
54           }
55         }
56       }
57
58       if (this.options.unescape !== false) {
59         node.val = node.val.replace(/\\([{}])/g, '$1');
60       }
61
62       if (last.val === '"' && this.input.charAt(0) === '"') {
63         last.val = node.val;
64         this.consume(1);
65         return;
66       }
67
68       return concatNodes.call(this, pos, node, prev, options);
69     })
70
71     /**
72      * Brackets: "[...]" (basic, this is overridden by
73      * other parsers in more advanced implementations)
74      */
75
76     .set('bracket', function() {
77       var isInside = this.isInside('brace');
78       var pos = this.position();
79       var m = this.match(/^(?:\[([!^]?)([^\]]{2,}|\]-)(\]|[^*+?]+)|\[)/);
80       if (!m) return;
81
82       var prev = this.prev();
83       var val = m[0];
84       var negated = m[1] ? '^' : '';
85       var inner = m[2] || '';
86       var close = m[3] || '';
87
88       if (isInside && prev.type === 'brace') {
89         prev.text = prev.text || '';
90         prev.text += val;
91       }
92
93       var esc = this.input.slice(0, 2);
94       if (inner === '' && esc === '\\]') {
95         inner += esc;
96         this.consume(2);
97
98         var str = this.input;
99         var idx = -1;
100         var ch;
101
102         while ((ch = str[++idx])) {
103           this.consume(1);
104           if (ch === ']') {
105             close = ch;
106             break;
107           }
108           inner += ch;
109         }
110       }
111
112       return pos(new Node({
113         type: 'bracket',
114         val: val,
115         escaped: close !== ']',
116         negated: negated,
117         inner: inner,
118         close: close
119       }));
120     })
121
122     /**
123      * Empty braces (we capture these early to
124      * speed up processing in the compiler)
125      */
126
127     .set('multiplier', function() {
128       var isInside = this.isInside('brace');
129       var pos = this.position();
130       var m = this.match(/^\{((?:,|\{,+\})+)\}/);
131       if (!m) return;
132
133       this.multiplier = true;
134       var prev = this.prev();
135       var val = m[0];
136
137       if (isInside && prev.type === 'brace') {
138         prev.text = prev.text || '';
139         prev.text += val;
140       }
141
142       var node = pos(new Node({
143         type: 'text',
144         multiplier: 1,
145         match: m,
146         val: val
147       }));
148
149       return concatNodes.call(this, pos, node, prev, options);
150     })
151
152     /**
153      * Open
154      */
155
156     .set('brace.open', function() {
157       var pos = this.position();
158       var m = this.match(/^\{(?!(?:[^\\}]?|,+)\})/);
159       if (!m) return;
160
161       var prev = this.prev();
162       var last = utils.last(prev.nodes);
163
164       // if the last parsed character was an extglob character
165       // we need to _not optimize_ the brace pattern because
166       // it might be mistaken for an extglob by a downstream parser
167       if (last && last.val && isExtglobChar(last.val.slice(-1))) {
168         last.optimize = false;
169       }
170
171       var open = pos(new Node({
172         type: 'brace.open',
173         val: m[0]
174       }));
175
176       var node = pos(new Node({
177         type: 'brace',
178         nodes: []
179       }));
180
181       node.push(open);
182       prev.push(node);
183       this.push('brace', node);
184     })
185
186     /**
187      * Close
188      */
189
190     .set('brace.close', function() {
191       var pos = this.position();
192       var m = this.match(/^\}/);
193       if (!m || !m[0]) return;
194
195       var brace = this.pop('brace');
196       var node = pos(new Node({
197         type: 'brace.close',
198         val: m[0]
199       }));
200
201       if (!this.isType(brace, 'brace')) {
202         if (this.options.strict) {
203           throw new Error('missing opening "{"');
204         }
205         node.type = 'text';
206         node.multiplier = 0;
207         node.escaped = true;
208         return node;
209       }
210
211       var prev = this.prev();
212       var last = utils.last(prev.nodes);
213       if (last.text) {
214         var lastNode = utils.last(last.nodes);
215         if (lastNode.val === ')' && /[!@*?+]\(/.test(last.text)) {
216           var open = last.nodes[0];
217           var text = last.nodes[1];
218           if (open.type === 'brace.open' && text && text.type === 'text') {
219             text.optimize = false;
220           }
221         }
222       }
223
224       if (brace.nodes.length > 2) {
225         var first = brace.nodes[1];
226         if (first.type === 'text' && first.val === ',') {
227           brace.nodes.splice(1, 1);
228           brace.nodes.push(first);
229         }
230       }
231
232       brace.push(node);
233     })
234
235     /**
236      * Capture boundary characters
237      */
238
239     .set('boundary', function() {
240       var pos = this.position();
241       var m = this.match(/^[$^](?!\{)/);
242       if (!m) return;
243       return pos(new Node({
244         type: 'text',
245         val: m[0]
246       }));
247     })
248
249     /**
250      * One or zero, non-comma characters wrapped in braces
251      */
252
253     .set('nobrace', function() {
254       var isInside = this.isInside('brace');
255       var pos = this.position();
256       var m = this.match(/^\{[^,]?\}/);
257       if (!m) return;
258
259       var prev = this.prev();
260       var val = m[0];
261
262       if (isInside && prev.type === 'brace') {
263         prev.text = prev.text || '';
264         prev.text += val;
265       }
266
267       return pos(new Node({
268         type: 'text',
269         multiplier: 0,
270         val: val
271       }));
272     })
273
274     /**
275      * Text
276      */
277
278     .set('text', function() {
279       var isInside = this.isInside('brace');
280       var pos = this.position();
281       var m = this.match(/^((?!\\)[^${}[\]])+/);
282       if (!m) return;
283
284       var prev = this.prev();
285       var val = m[0];
286
287       if (isInside && prev.type === 'brace') {
288         prev.text = prev.text || '';
289         prev.text += val;
290       }
291
292       var node = pos(new Node({
293         type: 'text',
294         multiplier: 1,
295         val: val
296       }));
297
298       return concatNodes.call(this, pos, node, prev, options);
299     });
300 };
301
302 /**
303  * Returns true if the character is an extglob character.
304  */
305
306 function isExtglobChar(ch) {
307   return ch === '!' || ch === '@' || ch === '*' || ch === '?' || ch === '+';
308 }
309
310 /**
311  * Combine text nodes, and calculate empty sets (`{,,}`)
312  * @param {Function} `pos` Function to calculate node position
313  * @param {Object} `node` AST node
314  * @return {Object}
315  */
316
317 function concatNodes(pos, node, parent, options) {
318   node.orig = node.val;
319   var prev = this.prev();
320   var last = utils.last(prev.nodes);
321   var isEscaped = false;
322
323   if (node.val.length > 1) {
324     var a = node.val.charAt(0);
325     var b = node.val.slice(-1);
326
327     isEscaped = (a === '"' && b === '"')
328       || (a === "'" && b === "'")
329       || (a === '`' && b === '`');
330   }
331
332   if (isEscaped && options.unescape !== false) {
333     node.val = node.val.slice(1, node.val.length - 1);
334     node.escaped = true;
335   }
336
337   if (node.match) {
338     var match = node.match[1];
339     if (!match || match.indexOf('}') === -1) {
340       match = node.match[0];
341     }
342
343     // replace each set with a single ","
344     var val = match.replace(/\{/g, ',').replace(/\}/g, '');
345     node.multiplier *= val.length;
346     node.val = '';
347   }
348
349   var simpleText = last.type === 'text'
350     && last.multiplier === 1
351     && node.multiplier === 1
352     && node.val;
353
354   if (simpleText) {
355     last.val += node.val;
356     return;
357   }
358
359   prev.push(node);
360 }