summaryrefslogtreecommitdiff
path: root/tools/eslint/lib/rules/operator-linebreak.js
blob: 020c61544c6a8959685a9bd3e5b051804d2629da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/**
 * @fileoverview Operator linebreak - enforces operator linebreak style of two types: after and before
 * @author Benoît Zugmeyer
 * @copyright 2015 Benoît Zugmeyer. All rights reserved.
 */

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

    var style = context.options[0] || "after";

    //--------------------------------------------------------------------------
    // Helpers
    //--------------------------------------------------------------------------

    /**
     * Checks whether two tokens are on the same line.
     * @param {ASTNode} left The leftmost token.
     * @param {ASTNode} right The rightmost token.
     * @returns {boolean} True if the tokens are on the same line, false if not.
     * @private
     */
    function isSameLine(left, right) {
        return left.loc.end.line === right.loc.start.line;
    }

    /**
     * Checks the operator placement
     * @param {ASTNode} node The binary operator node to check
     * @private
     * @returns {void}
     */
    function validateBinaryExpression(node) {
        var leftToken = context.getLastToken(node.left || node.id);
        var operatorToken = context.getTokenAfter(leftToken);

        // 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.
        while (operatorToken.value === ")") {
            leftToken = operatorToken;
            operatorToken = context.getTokenAfter(operatorToken);
        }

        var rightToken = context.getTokenAfter(operatorToken);
        var operator = operatorToken.value;

        // if single line
        if (isSameLine(leftToken, operatorToken) &&
                isSameLine(operatorToken, rightToken)) {

            return;

        } else if (!isSameLine(leftToken, operatorToken) &&
                !isSameLine(operatorToken, rightToken)) {

            // lone operator
            context.report(node, {
                line: operatorToken.loc.end.line,
                column: operatorToken.loc.end.column
            }, "Bad line breaking before and after '" + operator + "'.");

        } else if (style === "before" && isSameLine(leftToken, operatorToken)) {

            context.report(node, {
                line: operatorToken.loc.end.line,
                column: operatorToken.loc.end.column
            }, "'" + operator + "' should be placed at the beginning of the line.");

        } else if (style === "after" && isSameLine(operatorToken, rightToken)) {

            context.report(node, {
                line: operatorToken.loc.end.line,
                column: operatorToken.loc.end.column
            }, "'" + operator + "' should be placed at the end of the line.");
        }
    }

    //--------------------------------------------------------------------------
    // Public
    //--------------------------------------------------------------------------

    return {
        "BinaryExpression": validateBinaryExpression,
        "LogicalExpression": validateBinaryExpression,
        "AssignmentExpression": validateBinaryExpression,
        "VariableDeclarator": function (node) {
            if (node.init) {
                validateBinaryExpression(node);
            }
        }
    };
};