Class: Shirobai::Cop::Layout::SpaceAroundOperators

Inherits:
RuboCop::Cop::Base
  • Object
show all
Extended by:
RuboCop::Cop::AutoCorrector
Includes:
RuboCop::Cop::RangeHelp
Defined in:
lib/shirobai/cop/layout/space_around_operators.rb

Overview

Drop-in Rust reimplementation of Layout/SpaceAroundOperators.

A hybrid cop: core detection is AST-driven (Rust walks the AST once, reproducing stock's per-node callbacks and range_with_surrounding_space logic), while the AllowForAlignment (default true) check for the excess-space arm consults the parser-gem token stream. Rust does both — the AST walk in the bundle's walk-outer phase, the alignment filter over the lexed token list — and returns one record per offense: [op_start, op_end, ws_start, ws_end, kind, operator, replacement].

[op_start, op_end) is the operator range (the offense highlight). kind selects the message (0 = "Space around operator detected", 1 = "Surrounding space missing", 2 = "should be surrounded by a single space"). The autocorrect replaces the range_with_surrounding_space range [ws_start, ws_end) with replacement (Rust computes the replacement string exactly as stock's autocorrect would, including the ** / rational / / line-continuation / ForceEqualSignAlignment arms).

Config inputs: EnforcedStyleForExponentOperator / EnforcedStyleForRationalLiterals / AllowForAlignment (this cop) plus Layout/HashAlignment's EnforcedHashRocketStyle (the table guard) and Layout/ExtraSpacing's ForceEqualSignAlignment (the autocorrect collision avoidance). All are purely config-driven, so the cop is always bundle eligible — except when raw_source != buffer.source (CRLF/BOM normalization), where the token byte positions would not line up and it falls back to a standalone lex of buffer.source. (CRLF/buffer.source trap, aee0e8e.)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.autocorrect_incompatible_withObject

Stock declares this; preserved so the autocorrect ordering against Style/SelfAssignment matches.



40
41
42
# File 'lib/shirobai/cop/layout/space_around_operators.rb', line 40

def self.autocorrect_incompatible_with
  [RuboCop::Cop::Style::SelfAssignment]
end

.badgeObject



45
# File 'lib/shirobai/cop/layout/space_around_operators.rb', line 45

def self.badge = RuboCop::Cop::Badge.parse(cop_name)

.bundle_args(config) ⇒ Object

Packed config nums: [enabled, exponent_style, rational_style, allow_for_alignment, hash_table_style, force_equal_sign_alignment].

enabled is the token-cop gate the bundle reads to decide whether to collect the parser-gem token stream at all (this is a token cop). It is a superset of "this cop runs": a false positive only costs the token pass on a file the cop never reads, never a wrong offense; a false negative would drop offenses, so we only turn it off when Enabled is literally false.



56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/shirobai/cop/layout/space_around_operators.rb', line 56

def self.bundle_args(config)
  cop_config = config.for_badge(badge)
  enabled = cop_config["Enabled"] == false ? 0 : 1
  exponent = cop_config["EnforcedStyleForExponentOperator"] == "space" ? 1 : 0
  rational = cop_config["EnforcedStyleForRationalLiterals"] == "space" ? 1 : 0
  allow = cop_config.fetch("AllowForAlignment", true) ? 1 : 0

  hash_styles = Array(config.for_cop("Layout/HashAlignment")["EnforcedHashRocketStyle"])
  table = hash_styles.include?("table") ? 1 : 0

  force = config.for_cop("Layout/ExtraSpacing")["ForceEqualSignAlignment"] ? 1 : 0
  [[enabled, exponent, rational, allow, table, force]]
end

.cop_nameObject



44
# File 'lib/shirobai/cop/layout/space_around_operators.rb', line 44

def self.cop_name = "Layout/SpaceAroundOperators"

Instance Method Details

#on_new_investigationObject



70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/shirobai/cop/layout/space_around_operators.rb', line 70

def on_new_investigation
  buffer = processed_source.buffer
  off = SourceOffsets.for(bundle_eligible? ? processed_source.raw_source : buffer.source)

  resolved_result.each do |op_start, op_end, ws_start, ws_end, kind, operator, replacement|
    op_range = Parser::Source::Range.new(buffer, off[op_start], off[op_end])
    ws_range = Parser::Source::Range.new(buffer, off[ws_start], off[ws_end])
    message = message_for(kind, operator)
    add_offense(op_range, message: message) do |corrector|
      corrector.replace(ws_range, replacement)
    end
  end
end