.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / utils / whitespaceChecker.js
1 "use strict";
2
3 const configurationError = require("./configurationError");
4 const isSingleLineString = require("./isSingleLineString");
5 const isWhitespace = require("./isWhitespace");
6
7 /**
8  * Create a whitespaceChecker, which exposes the following functions:
9  * - `before()`
10  * - `beforeAllowingIndentation()`
11  * - `after()`
12  * - `afterOneOnly()`
13  *
14  * @param {"space"|"newline"} targetWhitespace - This is a keyword instead
15  *   of the actual character (e.g. " ") in order to accommodate
16  *   different styles of newline ("\n" vs "\r\n")
17  * @param {
18  *     "always"|"never"
19  *     |"always-single-line"|"always-multi-line"
20  *     | "never-single-line"|"never-multi-line"
21  *   } expectation
22  * @param {object} messages - An object of message functions;
23  *   calling `before*()` or `after*()` and the `expectation` that is passed
24  *   determines which message functions are required
25  * @param {function} [messages.exectedBefore]
26  * @param {function} [messages.rejectedBefore]
27  * @param {function} [messages.expectedAfter]
28  * @param {function} [messages.rejectedAfter]
29  * @param {function} [messages.expectedBeforeSingleLine]
30  * @param {function} [messages.rejectedBeforeSingleLine]
31  * @param {function} [messages.expectedBeforeMultiLine]
32  * @param {function} [messages.rejectedBeforeMultiLine]
33  * @return {object} The checker, with its exposed checking functions
34  */
35 module.exports = function(
36   targetWhitespace /*: "space" | "newline"*/,
37   expectation /*: "always" | "never" | "always-single-line"
38     | "always-multi-line" | "never-single-line"|"never-multi-line"*/,
39   messages /*: Object*/
40 ) /*: {
41   before: Function,
42   beforeAllowingIndentation: Function,
43   after: Function,
44   afterOneOnly: Function
45 }*/ {
46   // Keep track of active arguments in order to avoid passing
47   // too much stuff around, making signatures long and confusing.
48   // This variable gets reset anytime a checking function is called.
49   let activeArgs;
50
51   /**
52    * Check for whitespace *before* a character.
53    *
54    * @param {object} args - Named arguments object
55    * @param {string} args.source - The source string
56    * @param {number} args.index - The index of the character to check before
57    * @param {function} args.err - If a violation is found, this callback
58    *   will be invoked with the relevant warning message.
59    *   Typically this callback will report() the violation.
60    * @param {function} args.errTarget - If a violation is found, this string
61    *   will be sent to the relevant warning message.
62    * @param {string} [args.lineCheckStr] - Single- and multi-line checkers
63    *   will use this string to determine whether they should proceed,
64    *   i.e. if this string is one line only, single-line checkers will check,
65    *   multi-line checkers will ignore.
66    *   If none is passed, they will use `source`.
67    * @param {boolean} [args.onlyOneChar=false] - Only check *one* character before.
68    *   By default, "always-*" checks will look for the `targetWhitespace` one
69    *   before and then ensure there is no whitespace two before. This option
70    *   bypasses that second check.
71    * @param {boolean} [args.allowIndentation=false] - Allow arbitrary indentation
72    *   between the `targetWhitespace` (almost definitely a newline) and the `index`.
73    *   With this option, the checker will see if a newline *begins* the whitespace before
74    *   the `index`.
75    */
76   function before(args) {
77     const source = args.source;
78     const index = args.index;
79     const err = args.err;
80     const errTarget = args.errTarget;
81     const lineCheckStr = args.lineCheckStr;
82     const onlyOneChar =
83       args.onlyOneChar === undefined ? false : args.onlyOneChar;
84     const allowIndentation =
85       args.allowIndentation === undefined ? false : args.allowIndentation;
86
87     activeArgs = {
88       source,
89       index,
90       err,
91       errTarget,
92       onlyOneChar,
93       allowIndentation
94     };
95     switch (expectation) {
96       case "always":
97         expectBefore();
98         break;
99       case "never":
100         rejectBefore();
101         break;
102       case "always-single-line":
103         if (!isSingleLineString(lineCheckStr || source)) {
104           return;
105         }
106         expectBefore(messages.expectedBeforeSingleLine);
107         break;
108       case "never-single-line":
109         if (!isSingleLineString(lineCheckStr || source)) {
110           return;
111         }
112         rejectBefore(messages.rejectedBeforeSingleLine);
113         break;
114       case "always-multi-line":
115         if (isSingleLineString(lineCheckStr || source)) {
116           return;
117         }
118         expectBefore(messages.expectedBeforeMultiLine);
119         break;
120       case "never-multi-line":
121         if (isSingleLineString(lineCheckStr || source)) {
122           return;
123         }
124         rejectBefore(messages.rejectedBeforeMultiLine);
125         break;
126       default:
127         throw configurationError(`Unknown expectation "${expectation}"`);
128     }
129   }
130
131   /**
132    * Check for whitespace *after* a character.
133    *
134    * Parameters are pretty much the same as for `before()`, above, just substitute
135    * the word "after" for "before".
136    */
137   function after(args) {
138     const source = args.source;
139     const index = args.index;
140     const err = args.err;
141     const errTarget = args.errTarget;
142     const lineCheckStr = args.lineCheckStr;
143     const onlyOneChar =
144       args.onlyOneChar === undefined ? false : args.onlyOneChar;
145
146     activeArgs = { source, index, err, errTarget, onlyOneChar };
147     switch (expectation) {
148       case "always":
149         expectAfter();
150         break;
151       case "never":
152         rejectAfter();
153         break;
154       case "always-single-line":
155         if (!isSingleLineString(lineCheckStr || source)) {
156           return;
157         }
158         expectAfter(messages.expectedAfterSingleLine);
159         break;
160       case "never-single-line":
161         if (!isSingleLineString(lineCheckStr || source)) {
162           return;
163         }
164         rejectAfter(messages.rejectedAfterSingleLine);
165         break;
166       case "always-multi-line":
167         if (isSingleLineString(lineCheckStr || source)) {
168           return;
169         }
170         expectAfter(messages.expectedAfterMultiLine);
171         break;
172       case "never-multi-line":
173         if (isSingleLineString(lineCheckStr || source)) {
174           return;
175         }
176         rejectAfter(messages.rejectedAfterMultiLine);
177         break;
178       default:
179         throw configurationError(`Unknown expectation "${expectation}"`);
180     }
181   }
182
183   function beforeAllowingIndentation(obj) {
184     before(Object.assign({}, obj, { allowIndentation: true }));
185   }
186
187   function expectBefore() {
188     const messageFunc =
189       arguments.length > 0 && arguments[0] !== undefined
190         ? arguments[0]
191         : messages.expectedBefore;
192
193     if (activeArgs.allowIndentation) {
194       expectBeforeAllowingIndentation(messageFunc);
195       return;
196     }
197
198     const _activeArgs = activeArgs;
199     const source = _activeArgs.source,
200       index = _activeArgs.index;
201
202     const oneCharBefore = source[index - 1];
203     const twoCharsBefore = source[index - 2];
204
205     if (!isValue(oneCharBefore)) {
206       return;
207     }
208
209     if (targetWhitespace === "space" && oneCharBefore === " ") {
210       if (activeArgs.onlyOneChar || !isWhitespace(twoCharsBefore)) {
211         return;
212       }
213     }
214
215     activeArgs.err(
216       messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])
217     );
218   }
219
220   function expectBeforeAllowingIndentation() {
221     const messageFunc =
222       arguments.length > 0 && arguments[0] !== undefined
223         ? arguments[0]
224         : messages.expectedBefore;
225     const _activeArgs2 = activeArgs;
226     const source = _activeArgs2.source,
227       index = _activeArgs2.index,
228       err = _activeArgs2.err;
229
230     const expectedChar = (function() {
231       if (targetWhitespace === "newline") {
232         return "\n";
233       }
234     })();
235     let i = index - 1;
236     while (source[i] !== expectedChar) {
237       if (source[i] === "\t" || source[i] === " ") {
238         i--;
239         continue;
240       }
241       err(
242         messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])
243       );
244       return;
245     }
246   }
247
248   function rejectBefore() {
249     const messageFunc =
250       arguments.length > 0 && arguments[0] !== undefined
251         ? arguments[0]
252         : messages.rejectedBefore;
253     const _activeArgs3 = activeArgs;
254     const source = _activeArgs3.source,
255       index = _activeArgs3.index;
256
257     const oneCharBefore = source[index - 1];
258
259     if (isValue(oneCharBefore) && isWhitespace(oneCharBefore)) {
260       activeArgs.err(
261         messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])
262       );
263     }
264   }
265
266   function afterOneOnly(obj) {
267     after(Object.assign({}, obj, { onlyOneChar: true }));
268   }
269
270   function expectAfter() {
271     const messageFunc =
272       arguments.length > 0 && arguments[0] !== undefined
273         ? arguments[0]
274         : messages.expectedAfter;
275     const _activeArgs4 = activeArgs;
276     const source = _activeArgs4.source,
277       index = _activeArgs4.index;
278
279     const oneCharAfter = source[index + 1];
280     const twoCharsAfter = source[index + 2];
281
282     if (!isValue(oneCharAfter)) {
283       return;
284     }
285
286     if (targetWhitespace === "newline") {
287       // If index is followed by a Windows CR-LF ...
288       if (oneCharAfter === "\r" && twoCharsAfter === "\n") {
289         if (activeArgs.onlyOneChar || !isWhitespace(source[index + 3])) {
290           return;
291         }
292       }
293
294       // If index is followed by a Unix LF ...
295       if (oneCharAfter === "\n") {
296         if (activeArgs.onlyOneChar || !isWhitespace(twoCharsAfter)) {
297           return;
298         }
299       }
300     }
301
302     if (targetWhitespace === "space" && oneCharAfter === " ") {
303       if (activeArgs.onlyOneChar || !isWhitespace(twoCharsAfter)) {
304         return;
305       }
306     }
307
308     activeArgs.err(
309       messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])
310     );
311   }
312
313   function rejectAfter() {
314     const messageFunc =
315       arguments.length > 0 && arguments[0] !== undefined
316         ? arguments[0]
317         : messages.rejectedAfter;
318     const _activeArgs5 = activeArgs;
319     const source = _activeArgs5.source,
320       index = _activeArgs5.index;
321
322     const oneCharAfter = source[index + 1];
323
324     if (isValue(oneCharAfter) && isWhitespace(oneCharAfter)) {
325       activeArgs.err(
326         messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])
327       );
328     }
329   }
330
331   return {
332     before,
333     beforeAllowingIndentation,
334     after,
335     afterOneOnly
336   };
337 };
338
339 function isValue(x) {
340   return x !== undefined && x !== null;
341 }