Class: Shirobai::Cop::Layout::TrailingEmptyLines

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

Overview

Drop-in Rust reimplementation of ‘Layout/TrailingEmptyLines`.

The cop touches no AST: stock’s ‘on_new_investigation` looks only at the source buffer’s trailing whitespace. Rust performs the whole byte scan and returns at most one record — the reported caret range, the autocorrect range and its replacement text, and the ‘blank_lines` count that selects the message. Ruby reports the offense and applies the single `corrector.replace`, exactly like stock.

‘EnforcedStyle` (`final_newline` wants 0 trailing blank lines, `final_blank_line` wants 1) is validated through the genuine `ConfigurableEnforcedStyle#style` accessor before the bundle config is derived, so an unrecognized style raises the same error stock would.

Stock reads ‘processed_source.buffer.source`, which the parser normalizes (CRLF `rn` -> `n`, BOM stripping). The bundle, like every cop, runs on `raw_source`; when the parser normalized the source those two differ and the trailing-whitespace byte positions would not line up. So this cop is bundle-eligible only when the buffer source is byte-identical to the raw source (the common case — no CRLF/BOM); otherwise it falls back to a standalone scan of `buffer.source` with offsets converted against the same string. Either way the result is purely config-driven and stateless, so the autocorrect re-passes (fresh `ProcessedSource`) recompute correctly.

Constant Summary collapse

STYLES =
{
  "final_newline" => 0,
  "final_blank_line" => 1
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.badgeObject



40
# File 'lib/shirobai/cop/layout/trailing_empty_lines.rb', line 40

def self.badge = RuboCop::Cop::Badge.parse("Layout/TrailingEmptyLines")

.bundle_args(config) ⇒ Object

Packed config nums: ‘[style]`. An unrecognized `EnforcedStyle` defaults to `final_newline` here; the genuine error is raised by the `style` accessor in `on_new_investigation` before this is consulted.



45
46
47
48
# File 'lib/shirobai/cop/layout/trailing_empty_lines.rb', line 45

def self.bundle_args(config)
  cop_config = config.for_badge(badge)
  [[STYLES.fetch(cop_config["EnforcedStyle"] || "final_newline", 0)]]
end

.cop_nameObject



39
# File 'lib/shirobai/cop/layout/trailing_empty_lines.rb', line 39

def self.cop_name = "Layout/TrailingEmptyLines"

Instance Method Details

#on_new_investigationObject



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/shirobai/cop/layout/trailing_empty_lines.rb', line 50

def on_new_investigation
  # Validate `EnforcedStyle` through the genuine accessor first: stock
  # raises for an unrecognized style, and this must fire before we derive
  # the bundle config (which would otherwise mask it).
  style

  buffer = processed_source.buffer
  # Offsets are byte positions into the string the check scanned. On the
  # bundle path that is `raw_source` (byte-identical to `buffer.source`
  # there); converting against `raw_source` shares the single-slot
  # `SourceOffsets` cache with the other bundled cops. On the CRLF/BOM
  # fallback the check scanned `buffer.source`, so convert against that.
  off = SourceOffsets.for(bundle_eligible? ? processed_source.raw_source : buffer.source)

  resolved_result.each do |report_start, report_end, ac_start, ac_end, replacement, blank_lines|
    report_range = Parser::Source::Range.new(buffer, off[report_start], off[report_end])
    autocorrect_range = Parser::Source::Range.new(buffer, off[ac_start], off[ac_end])

    add_offense(report_range, message: message(blank_lines)) do |corrector|
      corrector.replace(autocorrect_range, replacement)
    end
  end
end