.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / operator-linebreak.js
diff --git a/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/rules/operator-linebreak.js b/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/rules/operator-linebreak.js
new file mode 100644 (file)
index 0000000..18da5c5
--- /dev/null
@@ -0,0 +1,247 @@
+/**
+ * @fileoverview Operator linebreak - enforces operator linebreak style of two types: after and before
+ * @author BenoĆ®t Zugmeyer
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("./utils/ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        type: "layout",
+
+        docs: {
+            description: "enforce consistent linebreak style for operators",
+            category: "Stylistic Issues",
+            recommended: false,
+            url: "https://eslint.org/docs/rules/operator-linebreak"
+        },
+
+        schema: [
+            {
+                enum: ["after", "before", "none", null]
+            },
+            {
+                type: "object",
+                properties: {
+                    overrides: {
+                        type: "object",
+                        additionalProperties: {
+                            enum: ["after", "before", "none", "ignore"]
+                        }
+                    }
+                },
+                additionalProperties: false
+            }
+        ],
+
+        fixable: "code",
+
+        messages: {
+            operatorAtBeginning: "'{{operator}}' should be placed at the beginning of the line.",
+            operatorAtEnd: "'{{operator}}' should be placed at the end of the line.",
+            badLinebreak: "Bad line breaking before and after '{{operator}}'.",
+            noLinebreak: "There should be no line break before or after '{{operator}}'."
+        }
+    },
+
+    create(context) {
+
+        const usedDefaultGlobal = !context.options[0];
+        const globalStyle = context.options[0] || "after";
+        const options = context.options[1] || {};
+        const styleOverrides = options.overrides ? Object.assign({}, options.overrides) : {};
+
+        if (usedDefaultGlobal && !styleOverrides["?"]) {
+            styleOverrides["?"] = "before";
+        }
+
+        if (usedDefaultGlobal && !styleOverrides[":"]) {
+            styleOverrides[":"] = "before";
+        }
+
+        const sourceCode = context.getSourceCode();
+
+        //--------------------------------------------------------------------------
+        // Helpers
+        //--------------------------------------------------------------------------
+
+        /**
+         * Gets a fixer function to fix rule issues
+         * @param {Token} operatorToken The operator token of an expression
+         * @param {string} desiredStyle The style for the rule. One of 'before', 'after', 'none'
+         * @returns {Function} A fixer function
+         */
+        function getFixer(operatorToken, desiredStyle) {
+            return fixer => {
+                const tokenBefore = sourceCode.getTokenBefore(operatorToken);
+                const tokenAfter = sourceCode.getTokenAfter(operatorToken);
+                const textBefore = sourceCode.text.slice(tokenBefore.range[1], operatorToken.range[0]);
+                const textAfter = sourceCode.text.slice(operatorToken.range[1], tokenAfter.range[0]);
+                const hasLinebreakBefore = !astUtils.isTokenOnSameLine(tokenBefore, operatorToken);
+                const hasLinebreakAfter = !astUtils.isTokenOnSameLine(operatorToken, tokenAfter);
+                let newTextBefore, newTextAfter;
+
+                if (hasLinebreakBefore !== hasLinebreakAfter && desiredStyle !== "none") {
+
+                    // If there is a comment before and after the operator, don't do a fix.
+                    if (sourceCode.getTokenBefore(operatorToken, { includeComments: true }) !== tokenBefore &&
+                        sourceCode.getTokenAfter(operatorToken, { includeComments: true }) !== tokenAfter) {
+
+                        return null;
+                    }
+
+                    /*
+                     * If there is only one linebreak and it's on the wrong side of the operator, swap the text before and after the operator.
+                     * foo &&
+                     *           bar
+                     * would get fixed to
+                     * foo
+                     *        && bar
+                     */
+                    newTextBefore = textAfter;
+                    newTextAfter = textBefore;
+                } else {
+                    const LINEBREAK_REGEX = astUtils.createGlobalLinebreakMatcher();
+
+                    // Otherwise, if no linebreak is desired and no comments interfere, replace the linebreaks with empty strings.
+                    newTextBefore = desiredStyle === "before" || textBefore.trim() ? textBefore : textBefore.replace(LINEBREAK_REGEX, "");
+                    newTextAfter = desiredStyle === "after" || textAfter.trim() ? textAfter : textAfter.replace(LINEBREAK_REGEX, "");
+
+                    // If there was no change (due to interfering comments), don't output a fix.
+                    if (newTextBefore === textBefore && newTextAfter === textAfter) {
+                        return null;
+                    }
+                }
+
+                if (newTextAfter === "" && tokenAfter.type === "Punctuator" && "+-".includes(operatorToken.value) && tokenAfter.value === operatorToken.value) {
+
+                    // To avoid accidentally creating a ++ or -- operator, insert a space if the operator is a +/- and the following token is a unary +/-.
+                    newTextAfter += " ";
+                }
+
+                return fixer.replaceTextRange([tokenBefore.range[1], tokenAfter.range[0]], newTextBefore + operatorToken.value + newTextAfter);
+            };
+        }
+
+        /**
+         * Checks the operator placement
+         * @param {ASTNode} node The node to check
+         * @param {ASTNode} leftSide The node that comes before the operator in `node`
+         * @private
+         * @returns {void}
+         */
+        function validateNode(node, leftSide) {
+
+            /*
+             * When the left part of a binary expression is a single expression wrapped in
+             * parentheses (ex: `(a) + b`), leftToken will be the last token of the expression
+             * and operatorToken will be the closing parenthesis.
+             * The leftToken should be the last closing parenthesis, and the operatorToken
+             * should be the token right after that.
+             */
+            const operatorToken = sourceCode.getTokenAfter(leftSide, astUtils.isNotClosingParenToken);
+            const leftToken = sourceCode.getTokenBefore(operatorToken);
+            const rightToken = sourceCode.getTokenAfter(operatorToken);
+            const operator = operatorToken.value;
+            const operatorStyleOverride = styleOverrides[operator];
+            const style = operatorStyleOverride || globalStyle;
+            const fix = getFixer(operatorToken, style);
+
+            // if single line
+            if (astUtils.isTokenOnSameLine(leftToken, operatorToken) &&
+                    astUtils.isTokenOnSameLine(operatorToken, rightToken)) {
+
+                // do nothing.
+
+            } else if (operatorStyleOverride !== "ignore" && !astUtils.isTokenOnSameLine(leftToken, operatorToken) &&
+                    !astUtils.isTokenOnSameLine(operatorToken, rightToken)) {
+
+                // lone operator
+                context.report({
+                    node,
+                    loc: operatorToken.loc,
+                    messageId: "badLinebreak",
+                    data: {
+                        operator
+                    },
+                    fix
+                });
+
+            } else if (style === "before" && astUtils.isTokenOnSameLine(leftToken, operatorToken)) {
+
+                context.report({
+                    node,
+                    loc: operatorToken.loc,
+                    messageId: "operatorAtBeginning",
+                    data: {
+                        operator
+                    },
+                    fix
+                });
+
+            } else if (style === "after" && astUtils.isTokenOnSameLine(operatorToken, rightToken)) {
+
+                context.report({
+                    node,
+                    loc: operatorToken.loc,
+                    messageId: "operatorAtEnd",
+                    data: {
+                        operator
+                    },
+                    fix
+                });
+
+            } else if (style === "none") {
+
+                context.report({
+                    node,
+                    loc: operatorToken.loc,
+                    messageId: "noLinebreak",
+                    data: {
+                        operator
+                    },
+                    fix
+                });
+
+            }
+        }
+
+        /**
+         * Validates a binary expression using `validateNode`
+         * @param {BinaryExpression|LogicalExpression|AssignmentExpression} node node to be validated
+         * @returns {void}
+         */
+        function validateBinaryExpression(node) {
+            validateNode(node, node.left);
+        }
+
+        //--------------------------------------------------------------------------
+        // Public
+        //--------------------------------------------------------------------------
+
+        return {
+            BinaryExpression: validateBinaryExpression,
+            LogicalExpression: validateBinaryExpression,
+            AssignmentExpression: validateBinaryExpression,
+            VariableDeclarator(node) {
+                if (node.init) {
+                    validateNode(node, node.id);
+                }
+            },
+            ConditionalExpression(node) {
+                validateNode(node, node.test);
+                validateNode(node, node.consequent);
+            }
+        };
+    }
+};