# encoding: utf-8 # rubocop:disable SymbolName module Rubocop module Cop module SurroundingSpace def space_between?(t1, t2) char_preceding_2nd_token = @source[t2.pos.line - 1][t2.pos.column - 1] if char_preceding_2nd_token == '+' && t1.type != :tPLUS # Special case. A unary plus is not present in the tokens. char_preceding_2nd_token = @source[t2.pos.line - 1][t2.pos.column - 2] end t2.pos.line > t1.pos.line || char_preceding_2nd_token == ' ' end def index_of_first_token(node, tokens) @token_table ||= build_token_table(tokens) b = node.loc.expression.begin @token_table[[b.line, b.column]] end def index_of_last_token(node, tokens) @token_table ||= build_token_table(tokens) e = node.loc.expression.end (0...e.column).to_a.reverse.find do |c| ix = @token_table[[e.line, c]] return ix if ix end end def build_token_table(tokens) table = {} tokens.each_with_index do |t, ix| table[[t.pos.line, t.pos.column]] = ix end table end end class SpaceAroundOperators < Cop include SurroundingSpace MSG_MISSING = "Surrounding space missing for operator '%s'." MSG_DETECTED = 'Space around operator ** detected.' BINARY_OPERATORS = [:tEQL, :tAMPER2, :tPIPE, :tCARET, :tPLUS, :tMINUS, :tSTAR2, :tDIVIDE, :tPERCENT, :tEH, :tCOLON, :tANDOP, :tOROP, :tMATCH, :tNMATCH, :tEQ, :tNEQ, :tGT, :tRSHFT, :tGEQ, :tLT, :tLSHFT, :tLEQ, :tASSOC, :tEQQ, :tCMP, :tOP_ASGN] def inspect(source, tokens, sexp, comments) @source = source positions_not_to_check = get_positions_not_to_check(tokens, sexp) tokens.each_cons(3) do |token_before, token, token_after| next if token_before.type == :kDEF # TODO: remove? next if positions_not_to_check.include?(token.pos) case token.type when :tPOW if has_space?(token_before, token, token_after) add_offence(:convention, token.pos.line, MSG_DETECTED) end when *BINARY_OPERATORS check_missing_space(token_before, token, token_after) end end end # Returns an array of positions marking the tokens that this cop # should not check, either because the token is not an operator # or because another cop does the check. def get_positions_not_to_check(tokens, sexp) positions_not_to_check = [] do_not_check_block_arg_pipes(sexp, positions_not_to_check) do_not_check_param_default(tokens, sexp, positions_not_to_check) do_not_check_class_lshift_self(tokens, sexp, positions_not_to_check) do_not_check_def_things(tokens, sexp, positions_not_to_check) do_not_check_singleton_operator_defs(tokens, sexp, positions_not_to_check) positions_not_to_check end def do_not_check_block_arg_pipes(sexp, positions_not_to_check) # each { |a| } # ^ ^ on_node(:block, sexp) do |b| on_node(:args, b) do |a| positions_not_to_check << a.loc.begin << a.loc.end if a.loc.begin end end end def do_not_check_param_default(tokens, sexp, positions_not_to_check) # func(a, b=nil) # ^ on_node(:optarg, sexp) do |optarg| _arg, equals, _value = tokens[index_of_first_token(optarg, tokens), 3] positions_not_to_check << equals.pos end end def do_not_check_class_lshift_self(tokens, sexp, positions_not_to_check) # class <