.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / remark-stringify / lib / escape.js
1 'use strict';
2
3 var decimal = require('is-decimal');
4 var alphanumeric = require('is-alphanumeric');
5 var whitespace = require('is-whitespace-character');
6 var escapes = require('markdown-escapes');
7 var prefix = require('./util/entity-prefix-length');
8
9 module.exports = factory;
10
11 var BACKSLASH = '\\';
12 var BULLETS = ['*', '-', '+'];
13 var ALLIGNMENT = [':', '-', ' ', '|'];
14 var entities = {'<': '&lt;', ':': '&#x3A;', '&': '&amp;', '|': '&#x7C;', '~': '&#x7E;'};
15
16 /* Factory to escape characters. */
17 function factory(options) {
18   return escape;
19
20   /* Escape punctuation characters in a node's value. */
21   function escape(value, node, parent) {
22     var self = this;
23     var gfm = options.gfm;
24     var commonmark = options.commonmark;
25     var pedantic = options.pedantic;
26     var markers = commonmark ? ['.', ')'] : ['.'];
27     var siblings = parent && parent.children;
28     var index = siblings && siblings.indexOf(node);
29     var prev = siblings && siblings[index - 1];
30     var next = siblings && siblings[index + 1];
31     var length = value.length;
32     var escapable = escapes(options);
33     var position = -1;
34     var queue = [];
35     var escaped = queue;
36     var afterNewLine;
37     var character;
38     var wordCharBefore;
39     var wordCharAfter;
40     var offset;
41     var replace;
42
43     if (prev) {
44       afterNewLine = text(prev) && /\n\s*$/.test(prev.value);
45     } else {
46       afterNewLine = !parent || parent.type === 'root' || parent.type === 'paragraph';
47     }
48
49     function one(character) {
50       return escapable.indexOf(character) === -1 ?
51         entities[character] : BACKSLASH + character;
52     }
53
54     while (++position < length) {
55       character = value.charAt(position);
56       replace = false;
57
58       if (character === '\n') {
59         afterNewLine = true;
60       } else if (
61         character === BACKSLASH ||
62         character === '`' ||
63         character === '*' ||
64         character === '[' ||
65         character === '<' ||
66         (character === '&' && prefix(value.slice(position)) > 0) ||
67         (character === ']' && self.inLink) ||
68         (gfm && character === '~' && value.charAt(position + 1) === '~') ||
69         (gfm && character === '|' && (self.inTable || alignment(value, position))) ||
70         (
71           character === '_' &&
72           /* Delegate leading/trailing underscores
73            * to the multinode version below. */
74           position > 0 &&
75           position < length - 1 &&
76           (
77               pedantic ||
78               !alphanumeric(value.charAt(position - 1)) ||
79               !alphanumeric(value.charAt(position + 1))
80           )
81         ) ||
82         (gfm && !self.inLink && character === ':' && protocol(queue.join('')))
83       ) {
84         replace = true;
85       } else if (afterNewLine) {
86         if (
87           character === '>' ||
88           character === '#' ||
89           BULLETS.indexOf(character) !== -1
90         ) {
91           replace = true;
92         } else if (decimal(character)) {
93           offset = position + 1;
94
95           while (offset < length) {
96             if (!decimal(value.charAt(offset))) {
97               break;
98             }
99
100             offset++;
101           }
102
103           if (markers.indexOf(value.charAt(offset)) !== -1) {
104             next = value.charAt(offset + 1);
105
106             if (!next || next === ' ' || next === '\t' || next === '\n') {
107               queue.push(value.slice(position, offset));
108               position = offset;
109               character = value.charAt(position);
110               replace = true;
111             }
112           }
113         }
114       }
115
116       if (afterNewLine && !whitespace(character)) {
117         afterNewLine = false;
118       }
119
120       queue.push(replace ? one(character) : character);
121     }
122
123     /* Multi-node versions. */
124     if (siblings && text(node)) {
125       /* Check for an opening parentheses after a
126        * link-reference (which can be joined by
127        * white-space). */
128       if (prev && prev.referenceType === 'shortcut') {
129         position = -1;
130         length = escaped.length;
131
132         while (++position < length) {
133           character = escaped[position];
134
135           if (character === ' ' || character === '\t') {
136             continue;
137           }
138
139           if (character === '(' || character === ':') {
140             escaped[position] = one(character);
141           }
142
143           break;
144         }
145
146         /* If the current node is all spaces / tabs,
147          * preceded by a shortcut, and followed by
148          * a text starting with `(`, escape it. */
149         if (
150           text(next) &&
151           position === length &&
152           next.value.charAt(0) === '('
153         ) {
154           escaped.push(BACKSLASH);
155         }
156       }
157
158       /* Ensure non-auto-links are not seen as links.
159        * This pattern needs to check the preceding
160        * nodes too. */
161       if (
162         gfm &&
163         !self.inLink &&
164         text(prev) &&
165         value.charAt(0) === ':' &&
166         protocol(prev.value.slice(-6))
167       ) {
168         escaped[0] = one(':');
169       }
170
171       /* Escape ampersand if it would otherwise
172        * start an entity. */
173       if (
174         text(next) &&
175         value.charAt(length - 1) === '&' &&
176         prefix('&' + next.value) !== 0
177       ) {
178         escaped[escaped.length - 1] = one('&');
179       }
180
181       /* Escape double tildes in GFM. */
182       if (
183         gfm &&
184         text(next) &&
185         value.charAt(length - 1) === '~' &&
186         next.value.charAt(0) === '~'
187       ) {
188         escaped.splice(escaped.length - 1, 0, BACKSLASH);
189       }
190
191       /* Escape underscores, but not mid-word (unless
192        * in pedantic mode). */
193       wordCharBefore = text(prev) && alphanumeric(prev.value.slice(-1));
194       wordCharAfter = text(next) && alphanumeric(next.value.charAt(0));
195
196       if (length === 1) {
197         if (value === '_' && (pedantic || !wordCharBefore || !wordCharAfter)) {
198           escaped.unshift(BACKSLASH);
199         }
200       } else {
201         if (
202           value.charAt(0) === '_' &&
203           (pedantic || !wordCharBefore || !alphanumeric(value.charAt(1)))
204         ) {
205           escaped.unshift(BACKSLASH);
206         }
207
208         if (
209           value.charAt(length - 1) === '_' &&
210           (pedantic || !wordCharAfter || !alphanumeric(value.charAt(length - 2)))
211         ) {
212           escaped.splice(escaped.length - 1, 0, BACKSLASH);
213         }
214       }
215     }
216
217     return escaped.join('');
218   }
219 }
220
221 /* Check if `index` in `value` is inside an alignment row. */
222 function alignment(value, index) {
223   var start = value.lastIndexOf('\n', index);
224   var end = value.indexOf('\n', index);
225
226   start = start === -1 ? -1 : start;
227   end = end === -1 ? value.length : end;
228
229   while (++start < end) {
230     if (ALLIGNMENT.indexOf(value.charAt(start)) === -1) {
231       return false;
232     }
233   }
234
235   return true;
236 }
237
238 /* Check if `node` is a text node. */
239 function text(node) {
240   return node && node.type === 'text';
241 }
242
243 /* Check if `value` ends in a protocol. */
244 function protocol(value) {
245   var val = value.slice(-6).toLowerCase();
246   return val === 'mailto' || val.slice(-5) === 'https' || val.slice(-4) === 'http';
247 }