e2edd4282da578468571a15bcd4dfc3174c1d61b
[dotfiles/.git] / func-call-spacing.js
1 /**
2  * @fileoverview Rule to control spacing within function calls
3  * @author Matt DuVall <http://www.mattduvall.com>
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils");
13
14 //------------------------------------------------------------------------------
15 // Rule Definition
16 //------------------------------------------------------------------------------
17
18 module.exports = {
19     meta: {
20         type: "layout",
21
22         docs: {
23             description: "require or disallow spacing between function identifiers and their invocations",
24             category: "Stylistic Issues",
25             recommended: false,
26             url: "https://eslint.org/docs/rules/func-call-spacing"
27         },
28
29         fixable: "whitespace",
30
31         schema: {
32             anyOf: [
33                 {
34                     type: "array",
35                     items: [
36                         {
37                             enum: ["never"]
38                         }
39                     ],
40                     minItems: 0,
41                     maxItems: 1
42                 },
43                 {
44                     type: "array",
45                     items: [
46                         {
47                             enum: ["always"]
48                         },
49                         {
50                             type: "object",
51                             properties: {
52                                 allowNewlines: {
53                                     type: "boolean"
54                                 }
55                             },
56                             additionalProperties: false
57                         }
58                     ],
59                     minItems: 0,
60                     maxItems: 2
61                 }
62             ]
63         },
64
65         messages: {
66             unexpected: "Unexpected newline between function name and paren.",
67             missing: "Missing space between function name and paren."
68         }
69     },
70
71     create(context) {
72
73         const never = context.options[0] !== "always";
74         const allowNewlines = !never && context.options[1] && context.options[1].allowNewlines;
75         const sourceCode = context.getSourceCode();
76         const text = sourceCode.getText();
77
78         /**
79          * Check if open space is present in a function name
80          * @param {ASTNode} node node to evaluate
81          * @param {Token} leftToken The last token of the callee. This may be the closing parenthesis that encloses the callee.
82          * @param {Token} rightToken Tha first token of the arguments. this is the opening parenthesis that encloses the arguments.
83          * @returns {void}
84          * @private
85          */
86         function checkSpacing(node, leftToken, rightToken) {
87             const textBetweenTokens = text.slice(leftToken.range[1], rightToken.range[0]).replace(/\/\*.*?\*\//gu, "");
88             const hasWhitespace = /\s/u.test(textBetweenTokens);
89             const hasNewline = hasWhitespace && astUtils.LINEBREAK_MATCHER.test(textBetweenTokens);
90
91             /*
92              * never allowNewlines hasWhitespace hasNewline message
93              * F     F             F             F          Missing space between function name and paren.
94              * F     F             F             T          (Invalid `!hasWhitespace && hasNewline`)
95              * F     F             T             T          Unexpected newline between function name and paren.
96              * F     F             T             F          (OK)
97              * F     T             T             F          (OK)
98              * F     T             T             T          (OK)
99              * F     T             F             T          (Invalid `!hasWhitespace && hasNewline`)
100              * F     T             F             F          Missing space between function name and paren.
101              * T     T             F             F          (Invalid `never && allowNewlines`)
102              * T     T             F             T          (Invalid `!hasWhitespace && hasNewline`)
103              * T     T             T             T          (Invalid `never && allowNewlines`)
104              * T     T             T             F          (Invalid `never && allowNewlines`)
105              * T     F             T             F          Unexpected space between function name and paren.
106              * T     F             T             T          Unexpected space between function name and paren.
107              * T     F             F             T          (Invalid `!hasWhitespace && hasNewline`)
108              * T     F             F             F          (OK)
109              *
110              * T                   T                        Unexpected space between function name and paren.
111              * F                   F                        Missing space between function name and paren.
112              * F     F                           T          Unexpected newline between function name and paren.
113              */
114
115             if (never && hasWhitespace) {
116                 context.report({
117                     node,
118                     loc: leftToken.loc.start,
119                     messageId: "unexpected",
120                     fix(fixer) {
121
122                         /*
123                          * Only autofix if there is no newline
124                          * https://github.com/eslint/eslint/issues/7787
125                          */
126                         if (!hasNewline) {
127                             return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
128                         }
129
130                         return null;
131                     }
132                 });
133             } else if (!never && !hasWhitespace) {
134                 context.report({
135                     node,
136                     loc: leftToken.loc.start,
137                     messageId: "missing",
138                     fix(fixer) {
139                         return fixer.insertTextBefore(rightToken, " ");
140                     }
141                 });
142             } else if (!never && !allowNewlines && hasNewline) {
143                 context.report({
144                     node,
145                     loc: leftToken.loc.start,
146                     messageId: "unexpected",
147                     fix(fixer) {
148                         return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ");
149                     }
150                 });
151             }
152         }
153
154         return {
155             "CallExpression, NewExpression"(node) {
156                 const lastToken = sourceCode.getLastToken(node);
157                 const lastCalleeToken = sourceCode.getLastToken(node.callee);
158                 const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
159                 const prevToken = parenToken && sourceCode.getTokenBefore(parenToken);
160
161                 // Parens in NewExpression are optional
162                 if (!(parenToken && parenToken.range[1] < node.range[1])) {
163                     return;
164                 }
165
166                 checkSpacing(node, prevToken, parenToken);
167             },
168
169             ImportExpression(node) {
170                 const leftToken = sourceCode.getFirstToken(node);
171                 const rightToken = sourceCode.getTokenAfter(leftToken);
172
173                 checkSpacing(node, leftToken, rightToken);
174             }
175         };
176
177     }
178 };