]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/operator-linebreak.js
3395feae655f64d4348ffcab8fe227e3c22c82c3
2 * @fileoverview Operator linebreak - enforces operator linebreak style of two types: after and before
3 * @author BenoƮt Zugmeyer
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils
= require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
23 description
: "enforce consistent linebreak style for operators",
24 category
: "Stylistic Issues",
26 url
: "https://eslint.org/docs/rules/operator-linebreak"
31 enum: ["after", "before", "none", null]
41 enum: ["after", "before", "none", "ignore"]
46 additionalProperties
: false
53 operatorAtBeginning
: "'{{operator}}' should be placed at the beginning of the line.",
54 operatorAtEnd
: "'{{operator}}' should be placed at the end of the line.",
55 badLinebreak
: "Bad line breaking before and after '{{operator}}'.",
56 noLinebreak
: "There should be no line break before or after '{{operator}}'."
62 const usedDefaultGlobal
= !context
.options
[0];
63 const globalStyle
= context
.options
[0] || "after";
64 const options
= context
.options
[1] || {};
65 const styleOverrides
= options
.overrides
? Object
.assign({}, options
.overrides
) : {};
67 if (usedDefaultGlobal
&& !styleOverrides
["?"]) {
68 styleOverrides
["?"] = "before";
71 if (usedDefaultGlobal
&& !styleOverrides
[":"]) {
72 styleOverrides
[":"] = "before";
75 const sourceCode
= context
.getSourceCode();
77 //--------------------------------------------------------------------------
79 //--------------------------------------------------------------------------
82 * Gets a fixer function to fix rule issues
83 * @param {Token} operatorToken The operator token of an expression
84 * @param {string} desiredStyle The style for the rule. One of 'before', 'after', 'none'
85 * @returns {Function} A fixer function
87 function getFixer(operatorToken
, desiredStyle
) {
89 const tokenBefore
= sourceCode
.getTokenBefore(operatorToken
);
90 const tokenAfter
= sourceCode
.getTokenAfter(operatorToken
);
91 const textBefore
= sourceCode
.text
.slice(tokenBefore
.range
[1], operatorToken
.range
[0]);
92 const textAfter
= sourceCode
.text
.slice(operatorToken
.range
[1], tokenAfter
.range
[0]);
93 const hasLinebreakBefore
= !astUtils
.isTokenOnSameLine(tokenBefore
, operatorToken
);
94 const hasLinebreakAfter
= !astUtils
.isTokenOnSameLine(operatorToken
, tokenAfter
);
95 let newTextBefore
, newTextAfter
;
97 if (hasLinebreakBefore
!== hasLinebreakAfter
&& desiredStyle
!== "none") {
99 // If there is a comment before and after the operator, don't do a fix.
100 if (sourceCode
.getTokenBefore(operatorToken
, { includeComments
: true }) !== tokenBefore
&&
101 sourceCode
.getTokenAfter(operatorToken
, { includeComments
: true }) !== tokenAfter
) {
107 * If there is only one linebreak and it's on the wrong side of the operator, swap the text before and after the operator.
114 newTextBefore
= textAfter
;
115 newTextAfter
= textBefore
;
117 const LINEBREAK_REGEX
= astUtils
.createGlobalLinebreakMatcher();
119 // Otherwise, if no linebreak is desired and no comments interfere, replace the linebreaks with empty strings.
120 newTextBefore
= desiredStyle
=== "before" || textBefore
.trim() ? textBefore
: textBefore
.replace(LINEBREAK_REGEX
, "");
121 newTextAfter
= desiredStyle
=== "after" || textAfter
.trim() ? textAfter
: textAfter
.replace(LINEBREAK_REGEX
, "");
123 // If there was no change (due to interfering comments), don't output a fix.
124 if (newTextBefore
=== textBefore
&& newTextAfter
=== textAfter
) {
129 if (newTextAfter
=== "" && tokenAfter
.type
=== "Punctuator" && "+-".includes(operatorToken
.value
) && tokenAfter
.value
=== operatorToken
.value
) {
131 // To avoid accidentally creating a ++ or -- operator, insert a space if the operator is a +/- and the following token is a unary +/-.
135 return fixer
.replaceTextRange([tokenBefore
.range
[1], tokenAfter
.range
[0]], newTextBefore
+ operatorToken
.value
+ newTextAfter
);
140 * Checks the operator placement
141 * @param {ASTNode} node The node to check
142 * @param {ASTNode} leftSide The node that comes before the operator in `node`
146 function validateNode(node
, leftSide
) {
149 * When the left part of a binary expression is a single expression wrapped in
150 * parentheses (ex: `(a) + b`), leftToken will be the last token of the expression
151 * and operatorToken will be the closing parenthesis.
152 * The leftToken should be the last closing parenthesis, and the operatorToken
153 * should be the token right after that.
155 const operatorToken
= sourceCode
.getTokenAfter(leftSide
, astUtils
.isNotClosingParenToken
);
156 const leftToken
= sourceCode
.getTokenBefore(operatorToken
);
157 const rightToken
= sourceCode
.getTokenAfter(operatorToken
);
158 const operator
= operatorToken
.value
;
159 const operatorStyleOverride
= styleOverrides
[operator
];
160 const style
= operatorStyleOverride
|| globalStyle
;
161 const fix
= getFixer(operatorToken
, style
);
164 if (astUtils
.isTokenOnSameLine(leftToken
, operatorToken
) &&
165 astUtils
.isTokenOnSameLine(operatorToken
, rightToken
)) {
169 } else if (operatorStyleOverride
!== "ignore" && !astUtils
.isTokenOnSameLine(leftToken
, operatorToken
) &&
170 !astUtils
.isTokenOnSameLine(operatorToken
, rightToken
)) {
175 loc
: operatorToken
.loc
,
176 messageId
: "badLinebreak",
183 } else if (style
=== "before" && astUtils
.isTokenOnSameLine(leftToken
, operatorToken
)) {
187 loc
: operatorToken
.loc
,
188 messageId
: "operatorAtBeginning",
195 } else if (style
=== "after" && astUtils
.isTokenOnSameLine(operatorToken
, rightToken
)) {
199 loc
: operatorToken
.loc
,
200 messageId
: "operatorAtEnd",
207 } else if (style
=== "none") {
211 loc
: operatorToken
.loc
,
212 messageId
: "noLinebreak",
223 * Validates a binary expression using `validateNode`
224 * @param {BinaryExpression|LogicalExpression|AssignmentExpression} node node to be validated
227 function validateBinaryExpression(node
) {
228 validateNode(node
, node
.left
);
231 //--------------------------------------------------------------------------
233 //--------------------------------------------------------------------------
236 BinaryExpression
: validateBinaryExpression
,
237 LogicalExpression
: validateBinaryExpression
,
238 AssignmentExpression
: validateBinaryExpression
,
239 VariableDeclarator(node
) {
241 validateNode(node
, node
.id
);
244 ConditionalExpression(node
) {
245 validateNode(node
, node
.test
);
246 validateNode(node
, node
.consequent
);