shirobai 2026.0713.1900-x86_64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/lib/shirobai/3.1/shirobai.so +0 -0
  4. data/lib/shirobai/3.2/shirobai.so +0 -0
  5. data/lib/shirobai/3.3/shirobai.so +0 -0
  6. data/lib/shirobai/3.4/shirobai.so +0 -0
  7. data/lib/shirobai/4.0/shirobai.so +0 -0
  8. data/lib/shirobai/cop/base.rb +45 -0
  9. data/lib/shirobai/cop/layout/access_modifier_indentation.rb +117 -0
  10. data/lib/shirobai/cop/layout/argument_alignment.rb +95 -0
  11. data/lib/shirobai/cop/layout/array_alignment.rb +109 -0
  12. data/lib/shirobai/cop/layout/assignment_indentation.rb +73 -0
  13. data/lib/shirobai/cop/layout/block_alignment.rb +79 -0
  14. data/lib/shirobai/cop/layout/closing_parenthesis_indentation.rb +51 -0
  15. data/lib/shirobai/cop/layout/def_end_alignment.rb +114 -0
  16. data/lib/shirobai/cop/layout/dot_position.rb +63 -0
  17. data/lib/shirobai/cop/layout/else_alignment.rb +86 -0
  18. data/lib/shirobai/cop/layout/empty_comment.rb +75 -0
  19. data/lib/shirobai/cop/layout/empty_line_after_guard_clause.rb +116 -0
  20. data/lib/shirobai/cop/layout/empty_line_after_magic_comment.rb +82 -0
  21. data/lib/shirobai/cop/layout/empty_line_between_defs.rb +78 -0
  22. data/lib/shirobai/cop/layout/empty_lines.rb +70 -0
  23. data/lib/shirobai/cop/layout/empty_lines_around_arguments.rb +55 -0
  24. data/lib/shirobai/cop/layout/empty_lines_around_begin_body.rb +22 -0
  25. data/lib/shirobai/cop/layout/empty_lines_around_block_body.rb +28 -0
  26. data/lib/shirobai/cop/layout/empty_lines_around_body_shared.rb +58 -0
  27. data/lib/shirobai/cop/layout/empty_lines_around_class_body.rb +27 -0
  28. data/lib/shirobai/cop/layout/empty_lines_around_exception_handling_keywords.rb +24 -0
  29. data/lib/shirobai/cop/layout/empty_lines_around_method_body.rb +22 -0
  30. data/lib/shirobai/cop/layout/empty_lines_around_module_body.rb +26 -0
  31. data/lib/shirobai/cop/layout/end_alignment.rb +101 -0
  32. data/lib/shirobai/cop/layout/end_of_line.rb +90 -0
  33. data/lib/shirobai/cop/layout/extra_spacing.rb +124 -0
  34. data/lib/shirobai/cop/layout/first_argument_indentation.rb +87 -0
  35. data/lib/shirobai/cop/layout/first_array_element_indentation.rb +89 -0
  36. data/lib/shirobai/cop/layout/first_hash_element_indentation.rb +104 -0
  37. data/lib/shirobai/cop/layout/hash_alignment.rb +135 -0
  38. data/lib/shirobai/cop/layout/indentation_consistency.rb +95 -0
  39. data/lib/shirobai/cop/layout/indentation_width.rb +146 -0
  40. data/lib/shirobai/cop/layout/initial_indentation.rb +80 -0
  41. data/lib/shirobai/cop/layout/leading_empty_lines.rb +72 -0
  42. data/lib/shirobai/cop/layout/line_continuation_spacing.rb +157 -0
  43. data/lib/shirobai/cop/layout/line_length.rb +235 -0
  44. data/lib/shirobai/cop/layout/multiline_method_call_brace_layout.rb +126 -0
  45. data/lib/shirobai/cop/layout/multiline_method_call_indentation.rb +87 -0
  46. data/lib/shirobai/cop/layout/multiline_operation_indentation.rb +61 -0
  47. data/lib/shirobai/cop/layout/rescue_ensure_alignment.rb +250 -0
  48. data/lib/shirobai/cop/layout/space_after_colon.rb +58 -0
  49. data/lib/shirobai/cop/layout/space_after_comma.rb +64 -0
  50. data/lib/shirobai/cop/layout/space_after_semicolon.rb +57 -0
  51. data/lib/shirobai/cop/layout/space_around_equals_in_parameter_default.rb +79 -0
  52. data/lib/shirobai/cop/layout/space_around_keyword.rb +69 -0
  53. data/lib/shirobai/cop/layout/space_around_method_call_operator.rb +55 -0
  54. data/lib/shirobai/cop/layout/space_around_operators.rb +121 -0
  55. data/lib/shirobai/cop/layout/space_before_block_braces.rb +150 -0
  56. data/lib/shirobai/cop/layout/space_before_comma.rb +63 -0
  57. data/lib/shirobai/cop/layout/space_before_comment.rb +56 -0
  58. data/lib/shirobai/cop/layout/space_before_first_arg.rb +65 -0
  59. data/lib/shirobai/cop/layout/space_before_semicolon.rb +57 -0
  60. data/lib/shirobai/cop/layout/space_inside_array_literal_brackets.rb +109 -0
  61. data/lib/shirobai/cop/layout/space_inside_block_braces.rb +116 -0
  62. data/lib/shirobai/cop/layout/space_inside_hash_literal_braces.rb +93 -0
  63. data/lib/shirobai/cop/layout/space_inside_parens.rb +67 -0
  64. data/lib/shirobai/cop/layout/space_inside_reference_brackets.rb +104 -0
  65. data/lib/shirobai/cop/layout/space_inside_string_interpolation.rb +81 -0
  66. data/lib/shirobai/cop/layout/trailing_empty_lines.rb +104 -0
  67. data/lib/shirobai/cop/lint/ambiguous_block_association.rb +117 -0
  68. data/lib/shirobai/cop/lint/debugger.rb +44 -0
  69. data/lib/shirobai/cop/lint/duplicate_magic_comment.rb +67 -0
  70. data/lib/shirobai/cop/lint/duplicate_methods.rb +109 -0
  71. data/lib/shirobai/cop/lint/ordered_magic_comments.rb +62 -0
  72. data/lib/shirobai/cop/lint/parentheses_as_grouped_expression.rb +54 -0
  73. data/lib/shirobai/cop/lint/require_parentheses.rb +35 -0
  74. data/lib/shirobai/cop/lint/safe_navigation_chain.rb +52 -0
  75. data/lib/shirobai/cop/lint/self_assignment.rb +105 -0
  76. data/lib/shirobai/cop/lint/unreachable_code.rb +48 -0
  77. data/lib/shirobai/cop/lint/useless_access_modifier.rb +61 -0
  78. data/lib/shirobai/cop/lint/void.rb +60 -0
  79. data/lib/shirobai/cop/metrics/abc_size.rb +66 -0
  80. data/lib/shirobai/cop/metrics/block_length.rb +139 -0
  81. data/lib/shirobai/cop/metrics/block_nesting.rb +62 -0
  82. data/lib/shirobai/cop/metrics/class_length.rb +88 -0
  83. data/lib/shirobai/cop/metrics/complexity_base.rb +35 -0
  84. data/lib/shirobai/cop/metrics/complexity_shared.rb +45 -0
  85. data/lib/shirobai/cop/metrics/cyclomatic_complexity.rb +30 -0
  86. data/lib/shirobai/cop/metrics/method_length.rb +70 -0
  87. data/lib/shirobai/cop/metrics/module_length.rb +83 -0
  88. data/lib/shirobai/cop/metrics/perceived_complexity.rb +30 -0
  89. data/lib/shirobai/cop/naming/ascii_identifiers.rb +63 -0
  90. data/lib/shirobai/cop/naming/method_name.rb +100 -0
  91. data/lib/shirobai/cop/naming/predicate_prefix.rb +107 -0
  92. data/lib/shirobai/cop/naming/variable_number.rb +72 -0
  93. data/lib/shirobai/cop/style/arguments_forwarding.rb +108 -0
  94. data/lib/shirobai/cop/style/block_delimiters.rb +164 -0
  95. data/lib/shirobai/cop/style/colon_method_call.rb +60 -0
  96. data/lib/shirobai/cop/style/empty_literal.rb +184 -0
  97. data/lib/shirobai/cop/style/file_null.rb +55 -0
  98. data/lib/shirobai/cop/style/frozen_string_literal_comment.rb +118 -0
  99. data/lib/shirobai/cop/style/hash_each_methods.rb +50 -0
  100. data/lib/shirobai/cop/style/hash_syntax.rb +147 -0
  101. data/lib/shirobai/cop/style/hash_transform_keys.rb +46 -0
  102. data/lib/shirobai/cop/style/if_unless_modifier.rb +207 -0
  103. data/lib/shirobai/cop/style/line_end_concatenation.rb +42 -0
  104. data/lib/shirobai/cop/style/magic_comment_format.rb +229 -0
  105. data/lib/shirobai/cop/style/mutable_constant.rb +286 -0
  106. data/lib/shirobai/cop/style/nested_parenthesized_calls.rb +88 -0
  107. data/lib/shirobai/cop/style/percent_literal_delimiters.rb +113 -0
  108. data/lib/shirobai/cop/style/redundant_freeze.rb +75 -0
  109. data/lib/shirobai/cop/style/redundant_self.rb +50 -0
  110. data/lib/shirobai/cop/style/redundant_self_assignment.rb +62 -0
  111. data/lib/shirobai/cop/style/semicolon.rb +192 -0
  112. data/lib/shirobai/cop/style/stabby_lambda_parentheses.rb +75 -0
  113. data/lib/shirobai/cop/style/string_literals.rb +135 -0
  114. data/lib/shirobai/cop/style/string_literals_in_interpolation.rb +130 -0
  115. data/lib/shirobai/cop/style/trailing_comma_in_arguments.rb +127 -0
  116. data/lib/shirobai/cop/style/trailing_comma_in_array_literal.rb +127 -0
  117. data/lib/shirobai/cop/style/trailing_comma_in_hash_literal.rb +127 -0
  118. data/lib/shirobai/dispatch.rb +530 -0
  119. data/lib/shirobai/inject.rb +218 -0
  120. data/lib/shirobai/node_locator.rb +124 -0
  121. data/lib/shirobai/source_offsets.rb +43 -0
  122. data/lib/shirobai/version.rb +5 -0
  123. data/lib/shirobai.rb +15 -0
  124. metadata +181 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a1a419f64630d93c94d120072f01047272ec8c3a810ff08ca0cd42005684a702
4
+ data.tar.gz: c96975359454665c58c5ae06825502d540895ddd4bf17971ce10f1870bdc097a
5
+ SHA512:
6
+ metadata.gz: 88c81dbf1bfc415fdc4d09b8b477c9a6b0151d843644796bf299faffe84b2500cd5382402581dbc48602ab5bf461adf6448b2537018470aabed461b11ea74262
7
+ data.tar.gz: 1c395e268195a7fdcadf2f6ec8951d1d35244c6a6335fa53764536f0854dc550d0c0f83f190edd72e096f9b87d6a2fffc314d35ec5c5634cdfa15275ad1a2197
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 fusagiko / takayamaki
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop"
4
+
5
+ module Shirobai
6
+ module Cop
7
+ # Class-level replica of `RuboCop::Cop::AllowedMethods#allowed_methods`,
8
+ # for `bundle_args` class methods that must derive a cop's config without
9
+ # a cop instance (`cop_config` is `config.for_badge(badge)`).
10
+ def self.allowed_methods_config(cop_config)
11
+ deprecated = Array(cop_config.fetch("IgnoredMethods", [])) +
12
+ Array(cop_config.fetch("ExcludedMethods", []))
13
+ allowed = Array(cop_config.fetch("AllowedMethods", []))
14
+ allowed += deprecated unless deprecated.any?(Regexp)
15
+ allowed
16
+ end
17
+
18
+ # Shared `bundle_eligible?` for wrapper cops.
19
+ #
20
+ # The bundled (shared-walk) path scans `raw_source`; the standalone path
21
+ # scans the parser-normalized `buffer.source`. The two agree only when they
22
+ # are byte-identical (CRLF / BOM / `__END__` truncation break that), so a cop
23
+ # may take the bundle path exactly then; otherwise it falls back so every
24
+ # offset lines up with parser-gem's index.
25
+ #
26
+ # The verdict is memoized, but the memo is guarded on the `processed_source`
27
+ # identity. RuboCop's real CLI builds a fresh cop per file, yet a reused
28
+ # instance (vendor specs, and any future RuboCop change to instance reuse)
29
+ # investigates several sources in turn. A plain `@bundle_eligible` /
30
+ # `.nil?` / `defined?` memo would freeze the first file's verdict and leak it
31
+ # onto later files with a different eligibility; keying on the source
32
+ # identity recomputes it on each new investigation.
33
+ module BundleEligible
34
+ private
35
+
36
+ def bundle_eligible?
37
+ src = processed_source
38
+ return @bundle_eligible if defined?(@bundle_eligible_for) && @bundle_eligible_for.equal?(src)
39
+
40
+ @bundle_eligible_for = src
41
+ @bundle_eligible = src.buffer.source == src.raw_source
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shirobai
4
+ module Cop
5
+ module Layout
6
+ # Drop-in Rust reimplementation of `Layout/AccessModifierIndentation`.
7
+ #
8
+ # Rust walks the AST once, reproducing stock's `on_class` / `on_sclass` /
9
+ # `on_module` / `on_block` alias chain: for every class-like / block
10
+ # node, look at the body and — when it is a parser-`(begin ...)` (>= 2
11
+ # statements; prism's single-statement `StatementsNode` mirrors stock's
12
+ # bare body so we skip it) — inspect each direct child `send` that is a
13
+ # `bare_access_modifier?` (no receiver / no arguments / no block, name
14
+ # in `{public, protected, private, module_function}`). Same-line
15
+ # modifiers (`same_line?(node, modifier)`) are skipped, exactly like
16
+ # stock. For each kept modifier Rust returns `column_delta = expected -
17
+ # actual` (matching stock's `@column_delta` in
18
+ # `column_offset_between(modifier.source_range, node.loc.end)`); the
19
+ # wrapper either fires `correct_style_detected` (delta zero) or registers
20
+ # the offense and shifts the modifier's line by `column_delta` (mirroring
21
+ # stock's `AlignmentCorrector.correct(corrector, processed_source, node,
22
+ # column_delta)` for a one-line node, which simply inserts or removes
23
+ # leading whitespace).
24
+ #
25
+ # Stock's `AlignmentCorrector.correct` reads `processed_source.config`
26
+ # (for `using_tabs?`); a faithful drop-in does the same — autocorrect is
27
+ # skipped entirely under `Layout/IndentationStyle: tabs`, just like
28
+ # stock.
29
+ #
30
+ # Offenses come from the per-file bundled run (`Shirobai::Dispatch`); the
31
+ # behaviour is purely config-driven (`EnforcedStyle` + a per-cop or
32
+ # global `IndentationWidth`), so this cop is always bundle eligible.
33
+ class AccessModifierIndentation < RuboCop::Cop::Base
34
+ include RuboCop::Cop::ConfigurableEnforcedStyle
35
+ extend RuboCop::Cop::AutoCorrector
36
+
37
+ STYLE_TO_U8 = { indent: 0, outdent: 1 }.freeze
38
+
39
+ def self.cop_name = "Layout/AccessModifierIndentation"
40
+ def self.badge = RuboCop::Cop::Badge.parse(cop_name)
41
+
42
+ # Packed args for the bundled run: `[style, indentation_width]`.
43
+ # `indentation_width` mirrors `Alignment#configured_indentation_width`
44
+ # — the cop's own `IndentationWidth` if set, else
45
+ # `Layout/IndentationWidth`'s `Width`, else 2.
46
+ def self.bundle_args(config)
47
+ own = config.for_badge(badge)
48
+ style = STYLE_TO_U8.fetch((own["EnforcedStyle"] || "indent").to_sym, 0)
49
+ width = own["IndentationWidth"] ||
50
+ config.for_cop("Layout/IndentationWidth")["Width"] ||
51
+ 2
52
+ [style, width]
53
+ end
54
+
55
+ def on_new_investigation
56
+ buffer = processed_source.buffer
57
+ off = SourceOffsets.for(processed_source.raw_source)
58
+
59
+ records_for_source.each do |start, fin, message, column_delta|
60
+ send_range = Parser::Source::Range.new(buffer, off[start], off[fin])
61
+ if column_delta.zero?
62
+ correct_style_detected
63
+ next
64
+ end
65
+ add_offense(send_range, message: message) do |corrector|
66
+ # Delegate to stock's `AlignmentCorrector.correct`. It reads
67
+ # `processed_source.config` for tab-style detection and string /
68
+ # block-comment range avoidance; for a single-line modifier
69
+ # those checks fall through to a single `each_line` iteration
70
+ # that just inserts or removes leading whitespace. Passing the
71
+ # `Parser::Source::Range` directly avoids needing the actual
72
+ # AST node — stock's implementation accepts either (`node
73
+ # .respond_to?(:loc) ? node.source_range : node`).
74
+ RuboCop::Cop::AlignmentCorrector.correct(
75
+ corrector, processed_source, send_range, column_delta
76
+ )
77
+ opposite_or_unrecognized_style(column_delta)
78
+ end
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def records_for_source
85
+ Dispatch.offenses_for(processed_source, config, :access_modifier_indentation)
86
+ end
87
+
88
+ # Mirror stock's `check_modifier` after `add_offense`: if the actual
89
+ # offset equals `unexpected_indent_offset` (`indentation_width -
90
+ # expected`), call `opposite_style_detected`; otherwise
91
+ # `unrecognized_style_detected`. Recover the actual offset from
92
+ # `column_delta` since `column_delta = expected - actual`.
93
+ def opposite_or_unrecognized_style(column_delta)
94
+ expected = expected_indent_offset
95
+ actual = expected - column_delta
96
+ unexpected = configured_indentation_width - expected
97
+ if actual == unexpected
98
+ opposite_style_detected
99
+ else
100
+ unrecognized_style_detected
101
+ end
102
+ end
103
+
104
+ def expected_indent_offset
105
+ style == :outdent ? 0 : configured_indentation_width
106
+ end
107
+
108
+ # Mirror `Alignment#configured_indentation_width` exactly.
109
+ def configured_indentation_width
110
+ cop_config["IndentationWidth"] ||
111
+ config.for_cop("Layout/IndentationWidth")["Width"] ||
112
+ 2
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shirobai
4
+ module Cop
5
+ module Layout
6
+ # Drop-in Rust reimplementation of `Layout/ArgumentAlignment`.
7
+ #
8
+ # Rust parses the source, walks every multi-argument method call, picks the
9
+ # alignment base for the configured `EnforcedStyle`
10
+ # (`with_first_argument` / `with_fixed_indentation`) and returns each
11
+ # misaligned argument as an offense range plus its `column_delta`. Ruby
12
+ # supplies the flattened config and applies the realignment via
13
+ # `AlignmentCorrector` (the same division of labour as the multiline
14
+ # indentation cops). Offenses come from the per-file bundled run
15
+ # (`Shirobai::Dispatch`); the config derivation is purely config-driven,
16
+ # so this cop is always bundle-eligible.
17
+ class ArgumentAlignment < RuboCop::Cop::Base
18
+ include RuboCop::Cop::Alignment
19
+ extend RuboCop::Cop::AutoCorrector
20
+
21
+ ALIGN_PARAMS_MSG = "Align the arguments of a method call if they span more than one line."
22
+
23
+ FIXED_INDENT_MSG = "Use one level of indentation for arguments " \
24
+ "following the first line of a multi-line method call."
25
+
26
+ def self.cop_name = "Layout/ArgumentAlignment"
27
+ def self.badge = RuboCop::Cop::Badge.parse("Layout/ArgumentAlignment")
28
+
29
+ # Packed args for the bundled run: `[style, indentation_width,
30
+ # incompatible]`. `incompatible` replicates the instance derivation
31
+ # exactly: it is only true for the explicit `with_first_argument` style
32
+ # combined with a separator-aligned `Layout/HashAlignment`.
33
+ def self.bundle_args(config)
34
+ cop_config = config.for_badge(badge)
35
+ enforced_style = cop_config["EnforcedStyle"]
36
+ incompatible = enforced_style == "with_first_argument" &&
37
+ RuboCop::Cop::Layout::HashAlignment::SEPARATOR_ALIGNMENT_STYLES.any? do |sep_style|
38
+ config.for_enabled_cop("Layout/HashAlignment")[sep_style]&.include?("separator")
39
+ end
40
+ [
41
+ enforced_style == "with_fixed_indentation" ? 1 : 0,
42
+ cop_config["IndentationWidth"] || config.for_cop("Layout/IndentationWidth")["Width"] || 2,
43
+ incompatible
44
+ ]
45
+ end
46
+
47
+ def on_new_investigation
48
+ buffer = processed_source.buffer
49
+ message = fixed_indentation? ? FIXED_INDENT_MSG : ALIGN_PARAMS_MSG
50
+
51
+ offenses = Dispatch.offenses_for(processed_source, config, :argument_alignment)
52
+ off = SourceOffsets.for(processed_source.raw_source)
53
+ # Stock hands `AlignmentCorrector` the argument NODE; its
54
+ # `inside_string_ranges` taboo then protects multi-line string
55
+ # interiors from the shift. Locate the parser node for each
56
+ # correctable range so the bare range (no taboo) is only the
57
+ # fallback (fluentd test_config_parser `%()` bodies).
58
+ located = NodeLocator.locate(
59
+ processed_source,
60
+ offenses.filter_map { |s, f, _d, ac| [off[s], off[f]] if ac }
61
+ )
62
+ offenses.each do |start, fin, column_delta, autocorrect|
63
+ range = Parser::Source::Range.new(buffer, off[start], off[fin])
64
+ # Always pass a block so the offense is correctable, matching stock:
65
+ # the Alignment mixin's `register_offense` always hands `add_offense`
66
+ # a block, even for the `within?` case where it passes a nil node and
67
+ # the corrector ends up empty. A blockless `add_offense` would instead
68
+ # mark the offense uncorrectable. `autocorrect` is false when the
69
+ # offense is nested in an already-corrected range; then the block
70
+ # returns early, leaving an empty (no-op) corrector, so the offense is
71
+ # reported and stays correctable but is not rewritten this pass.
72
+ add_offense(range, message: message) do |corrector|
73
+ next unless autocorrect
74
+
75
+ target = located[[off[start], off[fin]]] || range
76
+ RuboCop::Cop::AlignmentCorrector.correct(corrector, processed_source, target, column_delta)
77
+ end
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ # Config-derived and stable for the life of the instance; shares the
84
+ # derivation with the bundled run (single source of truth).
85
+ def bundle_args
86
+ @bundle_args ||= self.class.bundle_args(config)
87
+ end
88
+
89
+ def fixed_indentation?
90
+ bundle_args[0] == 1
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shirobai
4
+ module Cop
5
+ module Layout
6
+ # Drop-in Rust reimplementation of `Layout/ArrayAlignment`.
7
+ #
8
+ # Rust parses the source, walks every 2+-element array literal (plus the
9
+ # bracket-less arrays parser-gem synthesizes: single-assignment RHS lists
10
+ # and `rescue` exception lists; masgn RHS lists are skipped like stock),
11
+ # picks the alignment base for the configured `EnforcedStyle`
12
+ # (`with_first_element` / `with_fixed_indentation`) and returns each
13
+ # misaligned element as an offense range plus its `column_delta`. Ruby
14
+ # supplies the flattened config and applies the realignment via
15
+ # `AlignmentCorrector` (the same division of labour as
16
+ # `Layout/ArgumentAlignment`). The corrector receives the parser NODE for
17
+ # the offense range (resolved like `Layout/IndentationConsistency`), so
18
+ # heredoc bodies and multi-line string interiors inside a shifted element
19
+ # stay untouched, matching stock's taboo-range protection. Offenses come
20
+ # from the per-file bundled run (`Shirobai::Dispatch`); the config
21
+ # derivation is purely config-driven, so this cop is always
22
+ # bundle-eligible.
23
+ class ArrayAlignment < RuboCop::Cop::Base
24
+ include RuboCop::Cop::Alignment
25
+ extend RuboCop::Cop::AutoCorrector
26
+
27
+ ALIGN_ELEMENTS_MSG = "Align the elements of an array literal " \
28
+ "if they span more than one line."
29
+
30
+ FIXED_INDENT_MSG = "Use one level of indentation for elements " \
31
+ "following the first line of a multi-line array."
32
+
33
+ def self.cop_name = "Layout/ArrayAlignment"
34
+ def self.badge = RuboCop::Cop::Badge.parse("Layout/ArrayAlignment")
35
+
36
+ # Packed args for the bundled run: `[style, indentation_width]`.
37
+ def self.bundle_args(config)
38
+ cop_config = config.for_badge(badge)
39
+ [
40
+ cop_config["EnforcedStyle"] == "with_fixed_indentation" ? 1 : 0,
41
+ cop_config["IndentationWidth"] || config.for_cop("Layout/IndentationWidth")["Width"] || 2
42
+ ]
43
+ end
44
+
45
+ def on_new_investigation
46
+ buffer = processed_source.buffer
47
+ message = fixed_indentation? ? FIXED_INDENT_MSG : ALIGN_ELEMENTS_MSG
48
+
49
+ offenses = Dispatch.offenses_for(processed_source, config, :array_alignment)
50
+ off = SourceOffsets.for(processed_source.raw_source)
51
+ offenses.each do |start, fin, column_delta, autocorrect|
52
+ range = Parser::Source::Range.new(buffer, off[start], off[fin])
53
+ # Always pass a block so the offense is correctable, matching stock:
54
+ # the Alignment mixin's `register_offense` always hands `add_offense`
55
+ # a block, even for the `within?` case (`autocorrect: false`) where it
56
+ # passes a nil node and the corrector ends up empty. A blockless
57
+ # `add_offense` would instead mark the offense uncorrectable. When
58
+ # `autocorrect` is false the block returns early, leaving an empty
59
+ # (no-op) corrector, so the offense stays correctable but is not
60
+ # rewritten this pass.
61
+ add_offense(range, message: message) do |corrector|
62
+ next unless autocorrect
63
+
64
+ # Stock passes the element NODE, whose string/heredoc interiors
65
+ # `AlignmentCorrector` marks taboo. A bare range would realign
66
+ # heredoc bodies inside the element.
67
+ target = node_at(off[start], off[fin]) || range
68
+ RuboCop::Cop::AlignmentCorrector.correct(
69
+ corrector, processed_source, target, column_delta
70
+ )
71
+ end
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ # Config-derived and stable for the life of the instance; shares the
78
+ # derivation with the bundled run (single source of truth).
79
+ def bundle_args
80
+ @bundle_args ||= self.class.bundle_args(config)
81
+ end
82
+
83
+ def fixed_indentation?
84
+ bundle_args[0] == 1
85
+ end
86
+
87
+ # The parser node whose `source_range` matches `[cs, ce)` (CHARACTER
88
+ # offsets), so `AlignmentCorrector` can protect heredocs / string
89
+ # interiors that a bare range would not. Falls back to `nil` (bare
90
+ # range) when not found.
91
+ def node_at(cs, ce)
92
+ ast = processed_source.ast
93
+ return nil unless ast
94
+
95
+ found = nil
96
+ ast.each_node do |n|
97
+ r = n.source_range
98
+ next unless r
99
+ next unless r.begin_pos == cs && r.end_pos == ce
100
+
101
+ found = n
102
+ break
103
+ end
104
+ found
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shirobai
4
+ module Cop
5
+ module Layout
6
+ # Drop-in Rust reimplementation of `Layout/AssignmentIndentation`.
7
+ #
8
+ # Rust walks the AST once and, for each assignment / setter call whose
9
+ # operator is on a different line from the RHS, computes the RHS's
10
+ # expected display column (`leftmost_multiple_assignment.display_column +
11
+ # IndentationWidth`). Misaligned RHSes that begin their own line emit a
12
+ # `[rhs_start, rhs_end, column_delta]` record, which the wrapper turns
13
+ # into an offense at the RHS range. Autocorrect re-locates the matching
14
+ # `Parser::AST::Node` by `rhs_start` and hands it to stock's
15
+ # `AlignmentCorrector#correct` with the same `column_delta` (this keeps
16
+ # the heredoc / string-literal taboo handling identical to stock).
17
+ class AssignmentIndentation < RuboCop::Cop::Base
18
+ extend RuboCop::Cop::AutoCorrector
19
+
20
+ MSG = "Indent the first line of the right-hand-side of a multi-line assignment."
21
+
22
+ def self.cop_name = "Layout/AssignmentIndentation"
23
+ def self.badge = RuboCop::Cop::Badge.parse(cop_name)
24
+
25
+ # `IndentationWidth` falls back to `Layout/IndentationWidth.Width`,
26
+ # then to 2 — exactly stock's `configured_indentation_width`.
27
+ def self.bundle_args(config)
28
+ own = config.for_badge(badge)["IndentationWidth"]
29
+ width = own || config.for_cop("Layout/IndentationWidth")["Width"] || 2
30
+ [width]
31
+ end
32
+
33
+ def on_new_investigation
34
+ buffer = processed_source.buffer
35
+ off = SourceOffsets.for(processed_source.raw_source)
36
+
37
+ records_for_source.each do |rhs_start, rhs_end, column_delta|
38
+ range = Parser::Source::Range.new(buffer, off[rhs_start], off[rhs_end])
39
+ node = locate_rhs_node(off[rhs_start])
40
+ add_offense(range, message: MSG) do |corrector|
41
+ # The corrector target is the Parser::AST::Node so stock's
42
+ # `inside_string_ranges` / `block_comment_within?` checks work;
43
+ # if relocation fails (defensive), fall back to the range.
44
+ ::RuboCop::Cop::AlignmentCorrector.correct(
45
+ corrector, processed_source, node || range, column_delta
46
+ )
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def records_for_source
54
+ Dispatch.offenses_for(processed_source, config, :assignment_indentation)
55
+ end
56
+
57
+ # Locate the `Parser::AST::Node` whose source range begins at
58
+ # `rhs_begin_pos`. Stock hands the RHS node to `AlignmentCorrector` so
59
+ # that `inside_string_ranges` (heredoc / string literal taboos) and
60
+ # `block_comment_within?` can inspect descendants. Offenses are rare
61
+ # in practice, so the DFS cost is negligible per investigation.
62
+ def locate_rhs_node(rhs_begin_pos)
63
+ processed_source.ast&.each_node do |node|
64
+ return node if node.respond_to?(:loc) &&
65
+ node.source_range &&
66
+ node.source_range.begin_pos == rhs_begin_pos
67
+ end
68
+ nil
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shirobai
4
+ module Cop
5
+ module Layout
6
+ # Drop-in Rust reimplementation of `Layout/BlockAlignment`.
7
+ #
8
+ # Rust walks the AST once, reproducing stock's `on_block` (and
9
+ # `on_numblock` / `on_itblock`): it picks the block's alignment target by
10
+ # walking the lineage (`block_end_align_target`), unwraps op_asgn / masgn
11
+ # LHS (`find_lhs_node`), and decides whether the closing token (`end` /
12
+ # `}`) is misaligned under the configured `EnforcedStyleAlignWith`. Each
13
+ # misaligned block returns its closing-token range, the formatted message
14
+ # (including the `either`-only " or ..." alternative), and the autocorrect
15
+ # target column (`compute_start_col`).
16
+ #
17
+ # The autocorrect mirrors `BlockAlignment#autocorrect`: the delta between
18
+ # the target column and the closing token's column is applied as inserted
19
+ # spaces before the token (positive delta) or removed leading whitespace
20
+ # (negative delta).
21
+ #
22
+ # Offenses come from the per-file bundled run (`Shirobai::Dispatch`); the
23
+ # style is purely config-driven, so this cop is always bundle eligible.
24
+ class BlockAlignment < RuboCop::Cop::Base
25
+ include RuboCop::Cop::ConfigurableEnforcedStyle
26
+ include RuboCop::Cop::RangeHelp
27
+ extend RuboCop::Cop::AutoCorrector
28
+
29
+ STYLE_TO_U8 = { either: 0, start_of_block: 1, start_of_line: 2 }.freeze
30
+
31
+ def self.cop_name = "Layout/BlockAlignment"
32
+ def self.badge = RuboCop::Cop::Badge.parse(cop_name)
33
+
34
+ # Packed args for the bundled run: `[style]`. `EnforcedStyleAlignWith`
35
+ # defaults to `either` (0) when the config does not mention this cop.
36
+ def self.bundle_args(config)
37
+ align = config.for_badge(badge)["EnforcedStyleAlignWith"]
38
+ [STYLE_TO_U8.fetch((align || "either").to_sym, 0)]
39
+ end
40
+
41
+ def on_new_investigation
42
+ buffer = processed_source.buffer
43
+ off = SourceOffsets.for(processed_source.raw_source)
44
+
45
+ records_for_source.each do |end_start, end_end, message, align_column|
46
+ end_range = Parser::Source::Range.new(buffer, off[end_start], off[end_end])
47
+ add_offense(end_range, message: message) do |corrector|
48
+ autocorrect(corrector, buffer, end_range, align_column)
49
+ end
50
+ end
51
+ end
52
+
53
+ # `EnforcedStyleAlignWith` keys the style.
54
+ def style_parameter_name
55
+ "EnforcedStyleAlignWith"
56
+ end
57
+
58
+ private
59
+
60
+ def records_for_source
61
+ Dispatch.offenses_for(processed_source, config, :block_alignment)
62
+ end
63
+
64
+ # `BlockAlignment#autocorrect`: `delta = start_col - loc_end.column`.
65
+ # Positive inserts `delta` spaces before the closing token; negative
66
+ # removes `-delta` characters of leading whitespace.
67
+ def autocorrect(corrector, buffer, end_range, start_col)
68
+ delta = start_col - end_range.column
69
+ if delta.positive?
70
+ corrector.insert_before(end_range, " " * delta)
71
+ elsif delta.negative?
72
+ range = Parser::Source::Range.new(buffer, end_range.begin_pos + delta, end_range.begin_pos)
73
+ corrector.remove(range)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shirobai
4
+ module Cop
5
+ module Layout
6
+ # Drop-in Rust reimplementation of `Layout/ClosingParenthesisIndentation`.
7
+ #
8
+ # Rust parses the source, walks every parenthesized method call, method
9
+ # definition and grouped expression, and returns each hanging `)` that is
10
+ # misindented as an offense range plus its `column_delta` and message.
11
+ # Ruby supplies the flattened config and applies the realignment via
12
+ # `AlignmentCorrector` over the same `)` range, exactly like stock (whose
13
+ # `autocorrect` passes `right_paren` itself). Offenses come from the
14
+ # per-file bundled run (`Shirobai::Dispatch`); the config is a single
15
+ # number, so this cop is always bundle-eligible.
16
+ class ClosingParenthesisIndentation < RuboCop::Cop::Base
17
+ include RuboCop::Cop::Alignment
18
+ extend RuboCop::Cop::AutoCorrector
19
+
20
+ def self.cop_name = "Layout/ClosingParenthesisIndentation"
21
+ def self.badge = RuboCop::Cop::Badge.parse("Layout/ClosingParenthesisIndentation")
22
+
23
+ # Packed args for the bundled run: `[indentation_width]`
24
+ # (`configured_indentation_width` from the `Alignment` mixin).
25
+ def self.bundle_args(config)
26
+ cop_config = config.for_badge(badge)
27
+ [cop_config["IndentationWidth"] || config.for_cop("Layout/IndentationWidth")["Width"] || 2]
28
+ end
29
+
30
+ def on_new_investigation
31
+ buffer = processed_source.buffer
32
+
33
+ offenses = Dispatch.offenses_for(processed_source, config, :closing_parenthesis_indentation)
34
+ off = SourceOffsets.for(processed_source.raw_source)
35
+ offenses.each do |start, fin, column_delta, message|
36
+ range = Parser::Source::Range.new(buffer, off[start], off[fin])
37
+ # Stock yields the corrector block for every offense (no per-offense
38
+ # gating); `AlignmentCorrector` itself decides whether the corrector
39
+ # stays empty (tabs / block comments), which keeps the lint-mode
40
+ # `correctable?` status identical to stock.
41
+ add_offense(range, message: message) do |corrector|
42
+ RuboCop::Cop::AlignmentCorrector.correct(
43
+ corrector, processed_source, range, column_delta
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end