Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / spaced-comment.js
1 /**
2  * @fileoverview Source code for spaced-comments rule
3  * @author Gyandeep Singh
4  */
5 "use strict";
6
7 const lodash = require("lodash");
8 const astUtils = require("./utils/ast-utils");
9
10 //------------------------------------------------------------------------------
11 // Helpers
12 //------------------------------------------------------------------------------
13
14 /**
15  * Escapes the control characters of a given string.
16  * @param {string} s A string to escape.
17  * @returns {string} An escaped string.
18  */
19 function escape(s) {
20     return `(?:${lodash.escapeRegExp(s)})`;
21 }
22
23 /**
24  * Escapes the control characters of a given string.
25  * And adds a repeat flag.
26  * @param {string} s A string to escape.
27  * @returns {string} An escaped string.
28  */
29 function escapeAndRepeat(s) {
30     return `${escape(s)}+`;
31 }
32
33 /**
34  * Parses `markers` option.
35  * If markers don't include `"*"`, this adds `"*"` to allow JSDoc comments.
36  * @param {string[]} [markers] A marker list.
37  * @returns {string[]} A marker list.
38  */
39 function parseMarkersOption(markers) {
40
41     // `*` is a marker for JSDoc comments.
42     if (markers.indexOf("*") === -1) {
43         return markers.concat("*");
44     }
45
46     return markers;
47 }
48
49 /**
50  * Creates string pattern for exceptions.
51  * Generated pattern:
52  *
53  * 1. A space or an exception pattern sequence.
54  * @param {string[]} exceptions An exception pattern list.
55  * @returns {string} A regular expression string for exceptions.
56  */
57 function createExceptionsPattern(exceptions) {
58     let pattern = "";
59
60     /*
61      * A space or an exception pattern sequence.
62      * []                 ==> "\s"
63      * ["-"]              ==> "(?:\s|\-+$)"
64      * ["-", "="]         ==> "(?:\s|(?:\-+|=+)$)"
65      * ["-", "=", "--=="] ==> "(?:\s|(?:\-+|=+|(?:\-\-==)+)$)" ==> https://jex.im/regulex/#!embed=false&flags=&re=(%3F%3A%5Cs%7C(%3F%3A%5C-%2B%7C%3D%2B%7C(%3F%3A%5C-%5C-%3D%3D)%2B)%24)
66      */
67     if (exceptions.length === 0) {
68
69         // a space.
70         pattern += "\\s";
71     } else {
72
73         // a space or...
74         pattern += "(?:\\s|";
75
76         if (exceptions.length === 1) {
77
78             // a sequence of the exception pattern.
79             pattern += escapeAndRepeat(exceptions[0]);
80         } else {
81
82             // a sequence of one of the exception patterns.
83             pattern += "(?:";
84             pattern += exceptions.map(escapeAndRepeat).join("|");
85             pattern += ")";
86         }
87         pattern += `(?:$|[${Array.from(astUtils.LINEBREAKS).join("")}]))`;
88     }
89
90     return pattern;
91 }
92
93 /**
94  * Creates RegExp object for `always` mode.
95  * Generated pattern for beginning of comment:
96  *
97  * 1. First, a marker or nothing.
98  * 2. Next, a space or an exception pattern sequence.
99  * @param {string[]} markers A marker list.
100  * @param {string[]} exceptions An exception pattern list.
101  * @returns {RegExp} A RegExp object for the beginning of a comment in `always` mode.
102  */
103 function createAlwaysStylePattern(markers, exceptions) {
104     let pattern = "^";
105
106     /*
107      * A marker or nothing.
108      * ["*"]            ==> "\*?"
109      * ["*", "!"]       ==> "(?:\*|!)?"
110      * ["*", "/", "!<"] ==> "(?:\*|\/|(?:!<))?" ==> https://jex.im/regulex/#!embed=false&flags=&re=(%3F%3A%5C*%7C%5C%2F%7C(%3F%3A!%3C))%3F
111      */
112     if (markers.length === 1) {
113
114         // the marker.
115         pattern += escape(markers[0]);
116     } else {
117
118         // one of markers.
119         pattern += "(?:";
120         pattern += markers.map(escape).join("|");
121         pattern += ")";
122     }
123
124     pattern += "?"; // or nothing.
125     pattern += createExceptionsPattern(exceptions);
126
127     return new RegExp(pattern, "u");
128 }
129
130 /**
131  * Creates RegExp object for `never` mode.
132  * Generated pattern for beginning of comment:
133  *
134  * 1. First, a marker or nothing (captured).
135  * 2. Next, a space or a tab.
136  * @param {string[]} markers A marker list.
137  * @returns {RegExp} A RegExp object for `never` mode.
138  */
139 function createNeverStylePattern(markers) {
140     const pattern = `^(${markers.map(escape).join("|")})?[ \t]+`;
141
142     return new RegExp(pattern, "u");
143 }
144
145 //------------------------------------------------------------------------------
146 // Rule Definition
147 //------------------------------------------------------------------------------
148
149 module.exports = {
150     meta: {
151         type: "suggestion",
152
153         docs: {
154             description: "enforce consistent spacing after the `//` or `/*` in a comment",
155             category: "Stylistic Issues",
156             recommended: false,
157             url: "https://eslint.org/docs/rules/spaced-comment"
158         },
159
160         fixable: "whitespace",
161
162         schema: [
163             {
164                 enum: ["always", "never"]
165             },
166             {
167                 type: "object",
168                 properties: {
169                     exceptions: {
170                         type: "array",
171                         items: {
172                             type: "string"
173                         }
174                     },
175                     markers: {
176                         type: "array",
177                         items: {
178                             type: "string"
179                         }
180                     },
181                     line: {
182                         type: "object",
183                         properties: {
184                             exceptions: {
185                                 type: "array",
186                                 items: {
187                                     type: "string"
188                                 }
189                             },
190                             markers: {
191                                 type: "array",
192                                 items: {
193                                     type: "string"
194                                 }
195                             }
196                         },
197                         additionalProperties: false
198                     },
199                     block: {
200                         type: "object",
201                         properties: {
202                             exceptions: {
203                                 type: "array",
204                                 items: {
205                                     type: "string"
206                                 }
207                             },
208                             markers: {
209                                 type: "array",
210                                 items: {
211                                     type: "string"
212                                 }
213                             },
214                             balanced: {
215                                 type: "boolean",
216                                 default: false
217                             }
218                         },
219                         additionalProperties: false
220                     }
221                 },
222                 additionalProperties: false
223             }
224         ]
225     },
226
227     create(context) {
228
229         const sourceCode = context.getSourceCode();
230
231         // Unless the first option is never, require a space
232         const requireSpace = context.options[0] !== "never";
233
234         /*
235          * Parse the second options.
236          * If markers don't include `"*"`, it's added automatically for JSDoc
237          * comments.
238          */
239         const config = context.options[1] || {};
240         const balanced = config.block && config.block.balanced;
241
242         const styleRules = ["block", "line"].reduce((rule, type) => {
243             const markers = parseMarkersOption(config[type] && config[type].markers || config.markers || []);
244             const exceptions = config[type] && config[type].exceptions || config.exceptions || [];
245             const endNeverPattern = "[ \t]+$";
246
247             // Create RegExp object for valid patterns.
248             rule[type] = {
249                 beginRegex: requireSpace ? createAlwaysStylePattern(markers, exceptions) : createNeverStylePattern(markers),
250                 endRegex: balanced && requireSpace ? new RegExp(`${createExceptionsPattern(exceptions)}$`, "u") : new RegExp(endNeverPattern, "u"),
251                 hasExceptions: exceptions.length > 0,
252                 captureMarker: new RegExp(`^(${markers.map(escape).join("|")})`, "u"),
253                 markers: new Set(markers)
254             };
255
256             return rule;
257         }, {});
258
259         /**
260          * Reports a beginning spacing error with an appropriate message.
261          * @param {ASTNode} node A comment node to check.
262          * @param {string} message An error message to report.
263          * @param {Array} match An array of match results for markers.
264          * @param {string} refChar Character used for reference in the error message.
265          * @returns {void}
266          */
267         function reportBegin(node, message, match, refChar) {
268             const type = node.type.toLowerCase(),
269                 commentIdentifier = type === "block" ? "/*" : "//";
270
271             context.report({
272                 node,
273                 fix(fixer) {
274                     const start = node.range[0];
275                     let end = start + 2;
276
277                     if (requireSpace) {
278                         if (match) {
279                             end += match[0].length;
280                         }
281                         return fixer.insertTextAfterRange([start, end], " ");
282                     }
283                     end += match[0].length;
284                     return fixer.replaceTextRange([start, end], commentIdentifier + (match[1] ? match[1] : ""));
285
286                 },
287                 message,
288                 data: { refChar }
289             });
290         }
291
292         /**
293          * Reports an ending spacing error with an appropriate message.
294          * @param {ASTNode} node A comment node to check.
295          * @param {string} message An error message to report.
296          * @param {string} match An array of the matched whitespace characters.
297          * @returns {void}
298          */
299         function reportEnd(node, message, match) {
300             context.report({
301                 node,
302                 fix(fixer) {
303                     if (requireSpace) {
304                         return fixer.insertTextAfterRange([node.range[0], node.range[1] - 2], " ");
305                     }
306                     const end = node.range[1] - 2,
307                         start = end - match[0].length;
308
309                     return fixer.replaceTextRange([start, end], "");
310
311                 },
312                 message
313             });
314         }
315
316         /**
317          * Reports a given comment if it's invalid.
318          * @param {ASTNode} node a comment node to check.
319          * @returns {void}
320          */
321         function checkCommentForSpace(node) {
322             const type = node.type.toLowerCase(),
323                 rule = styleRules[type],
324                 commentIdentifier = type === "block" ? "/*" : "//";
325
326             // Ignores empty comments and comments that consist only of a marker.
327             if (node.value.length === 0 || rule.markers.has(node.value)) {
328                 return;
329             }
330
331             const beginMatch = rule.beginRegex.exec(node.value);
332             const endMatch = rule.endRegex.exec(node.value);
333
334             // Checks.
335             if (requireSpace) {
336                 if (!beginMatch) {
337                     const hasMarker = rule.captureMarker.exec(node.value);
338                     const marker = hasMarker ? commentIdentifier + hasMarker[0] : commentIdentifier;
339
340                     if (rule.hasExceptions) {
341                         reportBegin(node, "Expected exception block, space or tab after '{{refChar}}' in comment.", hasMarker, marker);
342                     } else {
343                         reportBegin(node, "Expected space or tab after '{{refChar}}' in comment.", hasMarker, marker);
344                     }
345                 }
346
347                 if (balanced && type === "block" && !endMatch) {
348                     reportEnd(node, "Expected space or tab before '*/' in comment.");
349                 }
350             } else {
351                 if (beginMatch) {
352                     if (!beginMatch[1]) {
353                         reportBegin(node, "Unexpected space or tab after '{{refChar}}' in comment.", beginMatch, commentIdentifier);
354                     } else {
355                         reportBegin(node, "Unexpected space or tab after marker ({{refChar}}) in comment.", beginMatch, beginMatch[1]);
356                     }
357                 }
358
359                 if (balanced && type === "block" && endMatch) {
360                     reportEnd(node, "Unexpected space or tab before '*/' in comment.", endMatch);
361                 }
362             }
363         }
364
365         return {
366             Program() {
367                 const comments = sourceCode.getAllComments();
368
369                 comments.filter(token => token.type !== "Shebang").forEach(checkCommentForSpace);
370             }
371         };
372     }
373 };