class RuboCop::Cop::Layout::SpaceAroundOperators
a ** b
# good
a**b
# bad
@example EnforcedStyleForExponentOperator: space
a**b
# good
a ** b
# bad
@example EnforcedStyleForExponentOperator: no_space (default)
}
11 => 3
1 => 2,
{
# bad
@example AllowForAlignment: false
}
11 => 3
1 => 2,
{
# good
@example AllowForAlignment: true (default)
my_number = 38 / 4
“apple” + “juice”
total = 3 * 4
# good
my_number = 38/4
“apple”+“juice”
total = 3*4
# bad
@example
the previous or next line, not counting empty lines or comment lines.
uses of extra spacing if the intent is to align with an operator on
This cop has ‘AllowForAlignment` option. When `true`, allows most
around operators.
It allows vertical alignment consisting of one or more whitespace
should or shouldn’t have surrounding space depending on configuration.
Checks that operators have space around them, except for ** which
def self.autocorrect_incompatible_with
def self.autocorrect_incompatible_with [Style::SelfAssignment] end
def align_hash_cop_config
def align_hash_cop_config config.for_cop('Layout/HashAlignment') end
def autocorrect(corrector, range)
def autocorrect(corrector, range) if /\*\*/.match?(range.source) && !space_around_exponent_operator? corrector.replace(range, '**') elsif range.source.end_with?("\n") corrector.replace(range, " #{range.source.strip}\n") else enclose_operator_with_space(corrector, range) end end
def check_operator(type, operator, right_operand)
def check_operator(type, operator, right_operand) with_space = range_with_surrounding_space(range: operator) return if with_space.source.start_with?("\n") offense(type, operator, with_space, right_operand) do |msg| add_offense(operator, message: msg) do |corrector| autocorrect(corrector, with_space) end end end
def enclose_operator_with_space(corrector, range)
def enclose_operator_with_space(corrector, range) operator = range.source # If `ForceEqualSignAlignment` is true, `Layout/ExtraSpacing` cop # inserts spaces before operator. If `Layout/SpaceAroundOperators` cop # inserts a space, it collides and raises the infinite loop error. if force_equal_sign_alignment? && !operator.end_with?(' ') corrector.insert_after(range, ' ') else corrector.replace(range, " #{operator.strip} ") end end
def excess_leading_space?(type, operator, with_space)
def excess_leading_space?(type, operator, with_space) return false unless allow_for_alignment? return false unless with_space.source.start_with?(EXCESSIVE_SPACE) return !aligned_with_operator?(operator) unless type == :assignment token = Token.new(operator, nil, operator.source) align_preceding = aligned_with_preceding_assignment(token) return false if align_preceding == :yes || aligned_with_subsequent_assignment(token) == :none aligned_with_subsequent_assignment(token) != :yes end
def excess_trailing_space?(right_operand, with_space)
def excess_trailing_space?(right_operand, with_space) with_space.source.end_with?(EXCESSIVE_SPACE) && (!allow_for_alignment? || !aligned_with_something?(right_operand)) end
def force_equal_sign_alignment?
def force_equal_sign_alignment? config.for_cop('Layout/ExtraSpacing')['ForceEqualSignAlignment'] end
def hash_table_style?
def hash_table_style? align_hash_cop_config && align_hash_cop_config['EnforcedHashRocketStyle'] == 'table' end
def offense(type, operator, with_space, right_operand)
def offense(type, operator, with_space, right_operand) msg = offense_message(type, operator, with_space, right_operand) yield msg if msg end
def offense_message(type, operator, with_space, right_operand)
def offense_message(type, operator, with_space, right_operand) if should_not_have_surrounding_space?(operator) return if with_space.is?(operator.source) "Space around operator `#{operator.source}` detected." elsif !/^\s.*\s$/.match?(with_space.source) "Surrounding space missing for operator `#{operator.source}`." elsif excess_leading_space?(type, operator, with_space) || excess_trailing_space?(right_operand, with_space) "Operator `#{operator.source}` should be surrounded " \ 'by a single space.' end end
def on_assignment(node)
def on_assignment(node) _, rhs, = *node return unless rhs check_operator(:assignment, node.loc.operator, rhs.source_range) end
def on_binary(node)
def on_binary(node) _, rhs, = *node return unless rhs check_operator(:binary, node.loc.operator, rhs.source_range) end
def on_casgn(node)
def on_casgn(node) _, _, right, = *node return unless right check_operator(:assignment, node.loc.operator, right.source_range) end
def on_if(node)
def on_if(node) return unless node.ternary? check_operator(:if, node.loc.question, node.if_branch.source_range) check_operator(:if, node.loc.colon, node.else_branch.source_range) end
def on_match_pattern(node)
def on_match_pattern(node) return if target_ruby_version < 3.0 check_operator(:match_pattern, node.loc.operator, node.source_range) end
def on_pair(node)
def on_pair(node) return unless node.hash_rocket? return if hash_table_style? && !node.parent.pairs_on_same_line? check_operator(:pair, node.loc.operator, node.source_range) end
def on_resbody(node)
def on_resbody(node) return unless node.loc.assoc _, variable, = *node check_operator(:resbody, node.loc.assoc, variable.source_range) end
def on_sclass(node)
def on_sclass(node) check_operator(:sclass, node.loc.operator, node.source_range) end
def on_send(node)
def on_send(node) return if rational_literal?(node) if node.setter_method? on_special_asgn(node) elsif regular_operator?(node) check_operator(:send, node.loc.selector, node.first_argument.source_range) end end
def on_special_asgn(node)
def on_special_asgn(node) _, _, right, = *node return unless right check_operator(:special_asgn, node.loc.operator, right.source_range) end
def operator_with_regular_syntax?(send_node)
def operator_with_regular_syntax?(send_node) send_node.operator_method? && !IRREGULAR_METHODS.include?(send_node.method_name) end
def regular_operator?(send_node)
def regular_operator?(send_node) !send_node.unary_operation? && !send_node.dot? && operator_with_regular_syntax?(send_node) end
def should_not_have_surrounding_space?(operator)
def should_not_have_surrounding_space?(operator) operator.is?('**') ? !space_around_exponent_operator? : false end
def space_around_exponent_operator?
def space_around_exponent_operator? cop_config['EnforcedStyleForExponentOperator'] == 'space' end