rubocop-vibe 0.2.0 → 0.3.0
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.
- checksums.yaml +4 -4
- data/config/default.yml +39 -4
- data/lib/rubocop/cop/vibe/blank_line_after_assignment.rb +217 -0
- data/lib/rubocop/cop/vibe/blank_line_before_expectation.rb +6 -1
- data/lib/rubocop/cop/vibe/class_organization.rb +20 -1
- data/lib/rubocop/cop/vibe/consecutive_assignment_alignment.rb +5 -102
- data/lib/rubocop/cop/vibe/consecutive_constant_alignment.rb +5 -113
- data/lib/rubocop/cop/vibe/consecutive_indexed_assignment_alignment.rb +145 -0
- data/lib/rubocop/cop/vibe/consecutive_let_alignment.rb +5 -94
- data/lib/rubocop/cop/vibe/describe_block_order.rb +6 -2
- data/lib/rubocop/cop/vibe/explicit_return_conditional.rb +192 -0
- data/lib/rubocop/cop/vibe/is_expected_one_liner.rb +3 -0
- data/lib/rubocop/cop/vibe/mixin/alignment_helpers.rb +92 -0
- data/lib/rubocop/cop/vibe/multiline_hash_argument_style.rb +171 -0
- data/lib/rubocop/cop/vibe/no_compound_conditions.rb +138 -0
- data/lib/rubocop/cop/vibe/no_rubocop_disable.rb +3 -0
- data/lib/rubocop/cop/vibe/no_skipped_tests.rb +1 -0
- data/lib/rubocop/cop/vibe/no_unless_guard_clause.rb +4 -0
- data/lib/rubocop/cop/vibe/prefer_one_liner_expectation.rb +4 -0
- data/lib/rubocop/cop/vibe/raise_unless_block.rb +1 -0
- data/lib/rubocop/cop/vibe/rspec_before_block_style.rb +114 -0
- data/lib/rubocop/cop/vibe/rspec_stub_chain_style.rb +4 -0
- data/lib/rubocop/cop/vibe_cops.rb +7 -0
- data/lib/rubocop/vibe/plugin.rb +4 -4
- data/lib/rubocop/vibe/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e14c979862da1db7feb6dc28697248359d9320450a1bbfca24db50101d9f4c43
|
|
4
|
+
data.tar.gz: e13a6d3fe34d9a24edc775dba8800e901a2a5a59ca5b3f35f516838e4ca79853
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d89ddcdf3d94bb0aeca2ce767157e305a75990b540f2bd91d1e9255e26e29915da0eb7b3188d293a96b0d4bf2ce4ac25c700f4601238705e82f7c62dbdaaeac
|
|
7
|
+
data.tar.gz: 064b6dfe86613f8af93718a277db85d6bd705d7580fa27fe69469c41579aa0e4653c14dd4bcc48b56dd1d808c752921b2f69336816fe0ef3fcbb1098dd0d22f2
|
data/config/default.yml
CHANGED
|
@@ -62,12 +62,24 @@ Style/StringLiterals:
|
|
|
62
62
|
Style/StringLiteralsInInterpolation:
|
|
63
63
|
EnforcedStyle: double_quotes
|
|
64
64
|
|
|
65
|
+
Vibe/BlankLineAfterAssignment:
|
|
66
|
+
Description: "Enforces a blank line after variable assignments when followed by other code."
|
|
67
|
+
Enabled: true
|
|
68
|
+
SafeAutoCorrect: true
|
|
69
|
+
VersionAdded: "0.3.0"
|
|
70
|
+
|
|
65
71
|
Vibe/BlankLineBeforeExpectation:
|
|
66
72
|
Description: "Enforces a blank line before expectation calls when there is setup code above."
|
|
67
73
|
Enabled: true
|
|
68
74
|
SafeAutoCorrect: true
|
|
69
75
|
VersionAdded: "0.1.0"
|
|
70
76
|
|
|
77
|
+
Vibe/ClassOrganization:
|
|
78
|
+
Description: "Enforces consistent organization of class definitions."
|
|
79
|
+
Enabled: true
|
|
80
|
+
SafeAutoCorrect: false
|
|
81
|
+
VersionAdded: "0.1.0"
|
|
82
|
+
|
|
71
83
|
Vibe/ConsecutiveAssignmentAlignment:
|
|
72
84
|
Description: "Enforces alignment of consecutive variable assignments at the = operator."
|
|
73
85
|
Enabled: true
|
|
@@ -80,6 +92,12 @@ Vibe/ConsecutiveConstantAlignment:
|
|
|
80
92
|
SafeAutoCorrect: true
|
|
81
93
|
VersionAdded: "0.2.0"
|
|
82
94
|
|
|
95
|
+
Vibe/ConsecutiveIndexedAssignmentAlignment:
|
|
96
|
+
Description: "Enforces alignment of consecutive indexed assignments at the = operator."
|
|
97
|
+
Enabled: true
|
|
98
|
+
SafeAutoCorrect: true
|
|
99
|
+
VersionAdded: "0.3.0"
|
|
100
|
+
|
|
83
101
|
Vibe/ConsecutiveLetAlignment:
|
|
84
102
|
Description: "Enforces alignment of consecutive let declarations at the { brace."
|
|
85
103
|
Enabled: true
|
|
@@ -92,17 +110,23 @@ Vibe/DescribeBlockOrder:
|
|
|
92
110
|
SafeAutoCorrect: false
|
|
93
111
|
VersionAdded: "0.1.0"
|
|
94
112
|
|
|
113
|
+
Vibe/ExplicitReturnConditional:
|
|
114
|
+
Description: "Enforces explicit if/else/end blocks instead of ternary or trailing conditionals for return values."
|
|
115
|
+
Enabled: true
|
|
116
|
+
SafeAutoCorrect: true
|
|
117
|
+
VersionAdded: "0.3.0"
|
|
118
|
+
|
|
95
119
|
Vibe/IsExpectedOneLiner:
|
|
96
120
|
Description: "Enforces that is_expected is only used in one-liner it { } blocks."
|
|
97
121
|
Enabled: true
|
|
98
122
|
SafeAutoCorrect: true
|
|
99
123
|
VersionAdded: "0.1.0"
|
|
100
124
|
|
|
101
|
-
Vibe/
|
|
102
|
-
Description: "Enforces
|
|
125
|
+
Vibe/MultilineHashArgumentStyle:
|
|
126
|
+
Description: "Enforces one-per-line and alphabetical ordering for hash arguments in multiline method calls."
|
|
103
127
|
Enabled: true
|
|
104
|
-
SafeAutoCorrect:
|
|
105
|
-
VersionAdded: "0.
|
|
128
|
+
SafeAutoCorrect: true
|
|
129
|
+
VersionAdded: "0.3.0"
|
|
106
130
|
|
|
107
131
|
Vibe/NoAssignsAttributeTesting:
|
|
108
132
|
Description: "Enforces that controller specs only test assignment identity, not attributes or associations."
|
|
@@ -110,6 +134,11 @@ Vibe/NoAssignsAttributeTesting:
|
|
|
110
134
|
SafeAutoCorrect: false
|
|
111
135
|
VersionAdded: "0.1.0"
|
|
112
136
|
|
|
137
|
+
Vibe/NoCompoundConditions:
|
|
138
|
+
Description: "Enforces extracting compound boolean conditions into named methods."
|
|
139
|
+
Enabled: true
|
|
140
|
+
VersionAdded: "0.3.0"
|
|
141
|
+
|
|
113
142
|
Vibe/NoRubocopDisable:
|
|
114
143
|
Description: "Enforces that rubocop:disable comments are not used inline."
|
|
115
144
|
Enabled: true
|
|
@@ -139,6 +168,12 @@ Vibe/RaiseUnlessBlock:
|
|
|
139
168
|
SafeAutoCorrect: true
|
|
140
169
|
VersionAdded: "0.2.0"
|
|
141
170
|
|
|
171
|
+
Vibe/RspecBeforeBlockStyle:
|
|
172
|
+
Description: "Enforces using do...end syntax instead of braces for RSpec before/after/around blocks."
|
|
173
|
+
Enabled: true
|
|
174
|
+
SafeAutoCorrect: true
|
|
175
|
+
VersionAdded: "0.3.0"
|
|
176
|
+
|
|
142
177
|
Vibe/RspecStubChainStyle:
|
|
143
178
|
Description: "Enforces that RSpec stub chains are split across lines when too long."
|
|
144
179
|
Enabled: true
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Vibe
|
|
6
|
+
# Enforces a blank line after variable assignments when followed by other code.
|
|
7
|
+
#
|
|
8
|
+
# Variable assignments should be visually separated from the code that uses them.
|
|
9
|
+
# Consecutive assignments are allowed without blank lines between them, but there
|
|
10
|
+
# should be a blank line before non-assignment code.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# # bad
|
|
14
|
+
# deleted_count = delete_batch
|
|
15
|
+
# break if deleted_count < BATCH_SIZE
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# deleted_count = delete_batch
|
|
19
|
+
#
|
|
20
|
+
# break if deleted_count < BATCH_SIZE
|
|
21
|
+
#
|
|
22
|
+
# # good - consecutive assignments don't need blank lines
|
|
23
|
+
# user = find_user
|
|
24
|
+
# account = user.account
|
|
25
|
+
#
|
|
26
|
+
# process(user, account)
|
|
27
|
+
#
|
|
28
|
+
# # good - next line uses the assigned variable
|
|
29
|
+
# forwarded = request.headers["X-Forwarded-For"]
|
|
30
|
+
# forwarded.to_s.split(",").first.to_s.strip.presence
|
|
31
|
+
#
|
|
32
|
+
# # good - consecutive FactoryBot calls
|
|
33
|
+
# website = create(:website)
|
|
34
|
+
# page_view = create(:page_view, website: website)
|
|
35
|
+
# create(:page_view, website: website)
|
|
36
|
+
class BlankLineAfterAssignment < Base
|
|
37
|
+
extend AutoCorrector
|
|
38
|
+
|
|
39
|
+
MSG = "Add a blank line after variable assignment."
|
|
40
|
+
|
|
41
|
+
FACTORY_BOT_METHODS = %i(
|
|
42
|
+
create build build_stubbed attributes_for
|
|
43
|
+
create_list build_list build_stubbed_list attributes_for_list
|
|
44
|
+
).freeze
|
|
45
|
+
|
|
46
|
+
# Check block nodes for assignment statements.
|
|
47
|
+
#
|
|
48
|
+
# @param [RuboCop::AST::Node] node The block node.
|
|
49
|
+
# @return [void]
|
|
50
|
+
def on_block(node)
|
|
51
|
+
if node.body
|
|
52
|
+
check_body(node.body)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
alias on_numblock on_block
|
|
56
|
+
|
|
57
|
+
# Check method definitions for assignment statements.
|
|
58
|
+
#
|
|
59
|
+
# @param [RuboCop::AST::Node] node The def node.
|
|
60
|
+
# @return [void]
|
|
61
|
+
def on_def(node)
|
|
62
|
+
if node.body
|
|
63
|
+
check_body(node.body)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
alias on_defs on_def
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# Check the body for missing blank lines after assignments.
|
|
71
|
+
#
|
|
72
|
+
# @param [RuboCop::AST::Node] body The body node.
|
|
73
|
+
# @return [void]
|
|
74
|
+
def check_body(body)
|
|
75
|
+
statements = extract_statements(body)
|
|
76
|
+
|
|
77
|
+
return if statements.size < 2
|
|
78
|
+
|
|
79
|
+
statements.each_cons(2) do |current, following|
|
|
80
|
+
check_statement_pair(current, following)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Extract statements from a body node.
|
|
85
|
+
#
|
|
86
|
+
# @param [RuboCop::AST::Node] body The body node.
|
|
87
|
+
# @return [Array<RuboCop::AST::Node>]
|
|
88
|
+
def extract_statements(body)
|
|
89
|
+
if body.begin_type?
|
|
90
|
+
body.children
|
|
91
|
+
else
|
|
92
|
+
[body]
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Check a pair of statements for missing blank line after assignment.
|
|
97
|
+
#
|
|
98
|
+
# @param [RuboCop::AST::Node] current The current statement.
|
|
99
|
+
# @param [RuboCop::AST::Node] following The following statement.
|
|
100
|
+
# @return [void]
|
|
101
|
+
def check_statement_pair(current, following)
|
|
102
|
+
return unless assignment?(current)
|
|
103
|
+
|
|
104
|
+
inner_following = inner_statement(following)
|
|
105
|
+
|
|
106
|
+
return if assignment?(inner_following)
|
|
107
|
+
return if blank_line_between?(current, following)
|
|
108
|
+
return if following_uses_assigned_variable?(current, inner_following)
|
|
109
|
+
return if consecutive_factory_bot_calls?(current, following)
|
|
110
|
+
|
|
111
|
+
add_offense(following) do |corrector|
|
|
112
|
+
corrector.insert_after(current, "\n")
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Get the inner statement from a modifier node.
|
|
117
|
+
#
|
|
118
|
+
# @param [RuboCop::AST::Node] node The node to unwrap.
|
|
119
|
+
# @return [RuboCop::AST::Node]
|
|
120
|
+
def inner_statement(node)
|
|
121
|
+
return node.if_branch if node.type?(:if) && node.modifier_form?
|
|
122
|
+
return node.body if node.type?(:while, :until) && node.modifier_form?
|
|
123
|
+
|
|
124
|
+
node
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Check if both statements are FactoryBot calls.
|
|
128
|
+
#
|
|
129
|
+
# @param [RuboCop::AST::Node] assignment The assignment node.
|
|
130
|
+
# @param [RuboCop::AST::Node] following The following statement.
|
|
131
|
+
# @return [Boolean]
|
|
132
|
+
def consecutive_factory_bot_calls?(assignment, following)
|
|
133
|
+
factory_bot_call?(assignment_value(assignment)) && factory_bot_call?(following)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Get the value being assigned.
|
|
137
|
+
#
|
|
138
|
+
# @param [RuboCop::AST::Node] node The assignment node.
|
|
139
|
+
# @return [RuboCop::AST::Node, nil]
|
|
140
|
+
def assignment_value(node)
|
|
141
|
+
node.children.last
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Check if a node is a FactoryBot method call.
|
|
145
|
+
#
|
|
146
|
+
# @param [RuboCop::AST::Node] node The node to check.
|
|
147
|
+
# @return [Boolean]
|
|
148
|
+
def factory_bot_call?(node)
|
|
149
|
+
node.send_type? && FACTORY_BOT_METHODS.include?(node.method_name)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Check if the following statement starts with the assigned variable.
|
|
153
|
+
#
|
|
154
|
+
# @param [RuboCop::AST::Node] assignment The assignment node.
|
|
155
|
+
# @param [RuboCop::AST::Node] following The following statement.
|
|
156
|
+
# @return [Boolean]
|
|
157
|
+
def following_uses_assigned_variable?(assignment, following)
|
|
158
|
+
var_name = assigned_variable_name(assignment)
|
|
159
|
+
|
|
160
|
+
return false unless var_name
|
|
161
|
+
|
|
162
|
+
receiver = leftmost_receiver(following)
|
|
163
|
+
|
|
164
|
+
return false unless receiver
|
|
165
|
+
|
|
166
|
+
receiver.lvar_type? && receiver.children.first == var_name
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Get the variable name from an assignment node.
|
|
170
|
+
#
|
|
171
|
+
# @param [RuboCop::AST::Node] node The assignment node.
|
|
172
|
+
# @return [Symbol, nil]
|
|
173
|
+
def assigned_variable_name(node)
|
|
174
|
+
return node.children.first if node.lvasgn_type?
|
|
175
|
+
|
|
176
|
+
target = node.children.first
|
|
177
|
+
|
|
178
|
+
if target.lvasgn_type?
|
|
179
|
+
target.children.first
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Get the leftmost receiver in a method chain.
|
|
184
|
+
#
|
|
185
|
+
# @param [RuboCop::AST::Node] node The node to check.
|
|
186
|
+
# @return [RuboCop::AST::Node, nil]
|
|
187
|
+
def leftmost_receiver(node)
|
|
188
|
+
return node if node.lvar_type?
|
|
189
|
+
return unless node.send_type?
|
|
190
|
+
|
|
191
|
+
current = node
|
|
192
|
+
|
|
193
|
+
current = current.receiver while current.send_type? && current.receiver
|
|
194
|
+
|
|
195
|
+
current
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Check if a node is a variable assignment.
|
|
199
|
+
#
|
|
200
|
+
# @param [RuboCop::AST::Node] node The node to check.
|
|
201
|
+
# @return [Boolean]
|
|
202
|
+
def assignment?(node)
|
|
203
|
+
node.type?(:lvasgn, :op_asgn, :or_asgn, :and_asgn)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Check if there's a blank line between two statements.
|
|
207
|
+
#
|
|
208
|
+
# @param [RuboCop::AST::Node] previous_node The previous statement.
|
|
209
|
+
# @param [RuboCop::AST::Node] current_node The current statement.
|
|
210
|
+
# @return [Boolean]
|
|
211
|
+
def blank_line_between?(previous_node, current_node)
|
|
212
|
+
current_node.loc.line - previous_node.loc.last_line > 1
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
@@ -67,7 +67,11 @@ module RuboCop
|
|
|
67
67
|
# @param [RuboCop::AST::Node] body The block body.
|
|
68
68
|
# @return [Array<RuboCop::AST::Node>]
|
|
69
69
|
def extract_statements(body)
|
|
70
|
-
body.begin_type?
|
|
70
|
+
if body.begin_type?
|
|
71
|
+
body.children
|
|
72
|
+
else
|
|
73
|
+
[body]
|
|
74
|
+
end
|
|
71
75
|
end
|
|
72
76
|
|
|
73
77
|
# Check statements for missing blank lines before expectations.
|
|
@@ -89,6 +93,7 @@ module RuboCop
|
|
|
89
93
|
# @return [void]
|
|
90
94
|
def check_statement_pair(previous_statement, current_statement)
|
|
91
95
|
expect_node = find_expect_node(current_statement)
|
|
96
|
+
|
|
92
97
|
return unless expect_node
|
|
93
98
|
return if blank_line_between?(previous_statement, current_statement)
|
|
94
99
|
return if find_expect_node(previous_statement)
|
|
@@ -100,6 +100,7 @@ module RuboCop
|
|
|
100
100
|
# @return [void]
|
|
101
101
|
def check_violations(node, elements, is_model)
|
|
102
102
|
violations = find_violations(elements)
|
|
103
|
+
|
|
103
104
|
return if violations.empty?
|
|
104
105
|
|
|
105
106
|
message = is_model ? MODEL_MSG : CLASS_MSG
|
|
@@ -117,9 +118,11 @@ module RuboCop
|
|
|
117
118
|
def on_class(node)
|
|
118
119
|
is_model = rails_model?(node)
|
|
119
120
|
is_controller = rails_controller?(node)
|
|
121
|
+
|
|
120
122
|
return if !is_model && !is_controller && !node.body
|
|
121
123
|
|
|
122
124
|
elements = extract_elements(node, is_model: is_model, is_controller: is_controller)
|
|
125
|
+
|
|
123
126
|
return if elements.size < 2
|
|
124
127
|
|
|
125
128
|
check_violations(node, elements, is_model)
|
|
@@ -135,6 +138,7 @@ module RuboCop
|
|
|
135
138
|
return false unless node.parent_class
|
|
136
139
|
|
|
137
140
|
parent_name = node.parent_class.const_name
|
|
141
|
+
|
|
138
142
|
return false unless parent_name
|
|
139
143
|
|
|
140
144
|
# Check for direct ActiveRecord inheritance.
|
|
@@ -151,6 +155,7 @@ module RuboCop
|
|
|
151
155
|
return false unless node.parent_class
|
|
152
156
|
|
|
153
157
|
parent_name = node.parent_class.const_name
|
|
158
|
+
|
|
154
159
|
return false unless parent_name
|
|
155
160
|
|
|
156
161
|
# Check for direct ActionController inheritance.
|
|
@@ -183,10 +188,12 @@ module RuboCop
|
|
|
183
188
|
visibility = :public
|
|
184
189
|
elements = []
|
|
185
190
|
index = 0
|
|
191
|
+
|
|
186
192
|
process_body_nodes(body).each do |child|
|
|
187
193
|
visibility = child.method_name if visibility_modifier?(child)
|
|
188
194
|
|
|
189
195
|
element = build_element(child, visibility, index, is_model, is_controller)
|
|
196
|
+
|
|
190
197
|
elements << element and index += 1 if element
|
|
191
198
|
end
|
|
192
199
|
|
|
@@ -206,6 +213,7 @@ module RuboCop
|
|
|
206
213
|
return unless categorizable?(child)
|
|
207
214
|
|
|
208
215
|
category = categorize_node(child, visibility, is_model)
|
|
216
|
+
|
|
209
217
|
return unless category
|
|
210
218
|
|
|
211
219
|
# Skip public instance methods in controllers (Rails/ActionOrder handles them).
|
|
@@ -242,6 +250,7 @@ module RuboCop
|
|
|
242
250
|
# @return [Array<Hash>] Array of hashes with :text and :column.
|
|
243
251
|
def extract_source_with_comments(node)
|
|
244
252
|
lines = extract_comment_lines(node)
|
|
253
|
+
|
|
245
254
|
lines.concat(extract_node_lines(node))
|
|
246
255
|
end
|
|
247
256
|
|
|
@@ -264,6 +273,7 @@ module RuboCop
|
|
|
264
273
|
|
|
265
274
|
node.source.lines.map.with_index do |line, idx|
|
|
266
275
|
col = idx.zero? ? node_column : line[/\A\s*/].length
|
|
276
|
+
|
|
267
277
|
{ text: line.chomp, column: col }
|
|
268
278
|
end
|
|
269
279
|
end
|
|
@@ -384,6 +394,7 @@ module RuboCop
|
|
|
384
394
|
# @return [Integer]
|
|
385
395
|
def priority_for(category, _node, is_model)
|
|
386
396
|
priorities = is_model ? MODEL_PRIORITIES : CLASS_PRIORITIES
|
|
397
|
+
|
|
387
398
|
priorities[category] || 999
|
|
388
399
|
end
|
|
389
400
|
|
|
@@ -410,6 +421,7 @@ module RuboCop
|
|
|
410
421
|
# @return [String]
|
|
411
422
|
def scope_sort_key(node)
|
|
412
423
|
first_arg = node.first_argument
|
|
424
|
+
|
|
413
425
|
return "" if first_arg.nil?
|
|
414
426
|
return "" unless first_arg.sym_type?
|
|
415
427
|
|
|
@@ -471,6 +483,7 @@ module RuboCop
|
|
|
471
483
|
base_column = calculate_base_indent(elements)
|
|
472
484
|
replacement = build_replacement(sorted, base_column)
|
|
473
485
|
range = replacement_range(class_node, elements)
|
|
486
|
+
|
|
474
487
|
corrector.replace(range, replacement.chomp)
|
|
475
488
|
end
|
|
476
489
|
|
|
@@ -544,6 +557,7 @@ module RuboCop
|
|
|
544
557
|
# @return [Integer] The base indentation column.
|
|
545
558
|
def calculate_base_indent(elements)
|
|
546
559
|
first_elem = elements.min_by { |e| e[:node].source_range.begin_pos }
|
|
560
|
+
|
|
547
561
|
first_elem[:node].source_range.column
|
|
548
562
|
end
|
|
549
563
|
|
|
@@ -558,6 +572,7 @@ module RuboCop
|
|
|
558
572
|
|
|
559
573
|
source_lines.map do |line|
|
|
560
574
|
relative_indent = " " * [0, line[:column] - min_column].max
|
|
575
|
+
|
|
561
576
|
"#{indent}#{relative_indent}#{line[:text].lstrip}"
|
|
562
577
|
end.join("\n")
|
|
563
578
|
end
|
|
@@ -575,6 +590,7 @@ module RuboCop
|
|
|
575
590
|
state[:category] = element[:category]
|
|
576
591
|
|
|
577
592
|
rendered_source = render_source(element[:source], state[:column])
|
|
593
|
+
|
|
578
594
|
state[:parts] << rendered_source << "\n"
|
|
579
595
|
end
|
|
580
596
|
|
|
@@ -587,6 +603,7 @@ module RuboCop
|
|
|
587
603
|
return if new_visibility == state[:visibility]
|
|
588
604
|
|
|
589
605
|
indent = " " * state[:column]
|
|
606
|
+
|
|
590
607
|
state[:parts] << "\n" if state[:parts].any?
|
|
591
608
|
state[:parts] << "#{indent}#{new_visibility}\n"
|
|
592
609
|
end
|
|
@@ -601,7 +618,9 @@ module RuboCop
|
|
|
601
618
|
# @param [Symbol] last_category The last category.
|
|
602
619
|
# @return [void]
|
|
603
620
|
def add_category_separator(parts, _category, last_category)
|
|
604
|
-
|
|
621
|
+
if last_category
|
|
622
|
+
parts << "\n"
|
|
623
|
+
end
|
|
605
624
|
end
|
|
606
625
|
end
|
|
607
626
|
end
|
|
@@ -26,6 +26,7 @@ module RuboCop
|
|
|
26
26
|
# service = Users::Activate.new # Separate group, not aligned
|
|
27
27
|
class ConsecutiveAssignmentAlignment < Base
|
|
28
28
|
extend AutoCorrector
|
|
29
|
+
include AlignmentHelpers
|
|
29
30
|
|
|
30
31
|
MSG = "Align consecutive assignments at the = operator."
|
|
31
32
|
|
|
@@ -59,98 +60,12 @@ module RuboCop
|
|
|
59
60
|
# @return [void]
|
|
60
61
|
def check_assignments_in_body(body)
|
|
61
62
|
statements = extract_statements(body)
|
|
62
|
-
return if statements.size < 2
|
|
63
|
-
|
|
64
|
-
groups = group_consecutive_assignments(statements)
|
|
65
|
-
groups.each { |group| check_group_alignment(group) }
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Extract statements from a body node.
|
|
69
|
-
#
|
|
70
|
-
# @param [RuboCop::AST::Node] body The body node.
|
|
71
|
-
# @return [Array<RuboCop::AST::Node>]
|
|
72
|
-
def extract_statements(body)
|
|
73
|
-
if body.begin_type?
|
|
74
|
-
body.children
|
|
75
|
-
else
|
|
76
|
-
[body]
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Group consecutive assignments together.
|
|
81
|
-
#
|
|
82
|
-
# @param [Array<RuboCop::AST::Node>] statements The statements.
|
|
83
|
-
# @return [Array<Array<RuboCop::AST::Node>>] Groups of consecutive assignments.
|
|
84
|
-
def group_consecutive_assignments(statements)
|
|
85
|
-
groups = []
|
|
86
|
-
current_group = []
|
|
87
|
-
previous_line = nil
|
|
88
|
-
|
|
89
|
-
statements.each do |statement|
|
|
90
|
-
current_group, previous_line = process_statement(statement, current_group, previous_line, groups)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
finalize_groups(groups, current_group)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Process a single statement for grouping.
|
|
97
|
-
#
|
|
98
|
-
# @param [RuboCop::AST::Node] statement The statement.
|
|
99
|
-
# @param [Array<RuboCop::AST::Node>] current_group The current group.
|
|
100
|
-
# @param [Integer, nil] previous_line The previous line number.
|
|
101
|
-
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
102
|
-
# @return [Array] The updated current_group and previous_line.
|
|
103
|
-
def process_statement(statement, current_group, previous_line, groups)
|
|
104
|
-
if local_variable_assignment?(statement)
|
|
105
|
-
current_group = handle_assignment(statement, current_group, previous_line, groups)
|
|
106
|
-
else
|
|
107
|
-
save_group_if_valid(groups, current_group)
|
|
108
|
-
current_group = []
|
|
109
|
-
end
|
|
110
|
-
[current_group, statement.loc.last_line]
|
|
111
|
-
end
|
|
112
63
|
|
|
113
|
-
|
|
114
|
-
#
|
|
115
|
-
# @param [RuboCop::AST::Node] statement The assignment statement.
|
|
116
|
-
# @param [Array<RuboCop::AST::Node>] current_group The current group.
|
|
117
|
-
# @param [Integer, nil] previous_line The previous line number.
|
|
118
|
-
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
119
|
-
# @return [Array<RuboCop::AST::Node>] The updated current group.
|
|
120
|
-
def handle_assignment(statement, current_group, previous_line, groups)
|
|
121
|
-
if previous_line && statement.loc.line - previous_line > 1
|
|
122
|
-
save_group_if_valid(groups, current_group)
|
|
123
|
-
current_group = []
|
|
124
|
-
end
|
|
125
|
-
current_group << statement
|
|
126
|
-
current_group
|
|
127
|
-
end
|
|
64
|
+
return if statements.size < 2
|
|
128
65
|
|
|
129
|
-
|
|
130
|
-
#
|
|
131
|
-
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
132
|
-
# @param [Array<RuboCop::AST::Node>] group The group to potentially save.
|
|
133
|
-
# @return [void]
|
|
134
|
-
def save_group_if_valid(groups, group)
|
|
135
|
-
groups << group if group.size > 1
|
|
136
|
-
end
|
|
66
|
+
groups = group_consecutive_statements(statements, &:lvasgn_type?)
|
|
137
67
|
|
|
138
|
-
|
|
139
|
-
#
|
|
140
|
-
# @param [Array<Array<RuboCop::AST::Node>>] groups The groups.
|
|
141
|
-
# @param [Array<RuboCop::AST::Node>] current_group The current group.
|
|
142
|
-
# @return [Array<Array<RuboCop::AST::Node>>] The finalized groups.
|
|
143
|
-
def finalize_groups(groups, current_group)
|
|
144
|
-
save_group_if_valid(groups, current_group)
|
|
145
|
-
groups
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# Check if node is a local variable assignment.
|
|
149
|
-
#
|
|
150
|
-
# @param [RuboCop::AST::Node] node The node.
|
|
151
|
-
# @return [Boolean]
|
|
152
|
-
def local_variable_assignment?(node)
|
|
153
|
-
node.lvasgn_type?
|
|
68
|
+
groups.each { |group| check_group_alignment(group) }
|
|
154
69
|
end
|
|
155
70
|
|
|
156
71
|
# Check alignment for a group of assignments.
|
|
@@ -163,6 +78,7 @@ module RuboCop
|
|
|
163
78
|
|
|
164
79
|
group.each do |asgn|
|
|
165
80
|
current_column = asgn.loc.operator.column
|
|
81
|
+
|
|
166
82
|
next if current_column == target_column
|
|
167
83
|
|
|
168
84
|
add_offense(asgn.loc.name) do |corrector|
|
|
@@ -200,19 +116,6 @@ module RuboCop
|
|
|
200
116
|
|
|
201
117
|
[1, current_spaces + spaces_needed].max
|
|
202
118
|
end
|
|
203
|
-
|
|
204
|
-
# Create a source range between two positions.
|
|
205
|
-
#
|
|
206
|
-
# @param [Integer] start_pos The start position.
|
|
207
|
-
# @param [Integer] end_pos The end position.
|
|
208
|
-
# @return [Parser::Source::Range]
|
|
209
|
-
def range_between(start_pos, end_pos)
|
|
210
|
-
Parser::Source::Range.new(
|
|
211
|
-
processed_source.buffer,
|
|
212
|
-
start_pos,
|
|
213
|
-
end_pos
|
|
214
|
-
)
|
|
215
|
-
end
|
|
216
119
|
end
|
|
217
120
|
end
|
|
218
121
|
end
|