rubocop 1.80.1 → 1.81.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/README.md +1 -1
- data/config/default.yml +10 -0
- data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
- data/lib/rubocop/cli.rb +1 -2
- data/lib/rubocop/config_loader.rb +3 -1
- data/lib/rubocop/config_store.rb +5 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +2 -2
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
- data/lib/rubocop/cop/layout/line_length.rb +9 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +6 -4
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +8 -0
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
- data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
- data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
- data/lib/rubocop/cop/lint/void.rb +7 -0
- data/lib/rubocop/cop/message_annotator.rb +1 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/naming/predicate_method.rb +15 -2
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/it_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
- data/lib/rubocop/cop/style/numbered_parameters.rb +1 -1
- data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +18 -3
- data/lib/rubocop/cop/style/redundant_parentheses.rb +13 -10
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +6 -0
- data/lib/rubocop/cop/style/string_concatenation.rb +16 -12
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
- data/lib/rubocop/cop/style/unless_else.rb +10 -9
- data/lib/rubocop/cop/utils/format_string.rb +10 -0
- data/lib/rubocop/lsp/diagnostic.rb +21 -20
- data/lib/rubocop/lsp/routes.rb +36 -9
- data/lib/rubocop/lsp/runtime.rb +2 -2
- data/lib/rubocop/lsp/server.rb +2 -2
- data/lib/rubocop/lsp/stdin_runner.rb +0 -16
- data/lib/rubocop/target_ruby.rb +10 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +1 -0
- data/lib/ruby_lsp/rubocop/addon.rb +23 -8
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
- metadata +11 -7
@@ -134,6 +134,7 @@ module RuboCop
|
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
137
138
|
def all_fields_literal?(string, arguments)
|
138
139
|
count = 0
|
139
140
|
sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
|
@@ -141,6 +142,7 @@ module RuboCop
|
|
141
142
|
|
142
143
|
sequences.each do |sequence|
|
143
144
|
next if sequence.percent?
|
145
|
+
next if unknown_variable_width?(sequence, arguments)
|
144
146
|
|
145
147
|
hash = arguments.detect(&:hash_type?)
|
146
148
|
next unless (argument = find_argument(sequence, arguments, hash))
|
@@ -151,19 +153,32 @@ module RuboCop
|
|
151
153
|
|
152
154
|
sequences.size == count
|
153
155
|
end
|
156
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
154
157
|
|
158
|
+
# If the sequence has a variable (`*`) width, it cannot be autocorrected
|
159
|
+
# if the width is not given as a numeric literal argument
|
160
|
+
def unknown_variable_width?(sequence, arguments)
|
161
|
+
return false unless sequence.variable_width?
|
162
|
+
|
163
|
+
argument = arguments[sequence.variable_width_argument_number - 1]
|
164
|
+
!numeric?(argument)
|
165
|
+
end
|
166
|
+
|
167
|
+
# rubocop:disable Metrics/AbcSize
|
155
168
|
def find_argument(sequence, arguments, hash)
|
156
169
|
if hash && (sequence.annotated? || sequence.template?)
|
157
170
|
find_hash_value_node(hash, sequence.name.to_sym).first
|
171
|
+
elsif sequence.variable_width?
|
172
|
+
# If the specifier contains `*`, the argument for the width can be ignored.
|
173
|
+
arguments.delete_at(sequence.variable_width_argument_number - 1)
|
174
|
+
arguments.shift
|
158
175
|
elsif sequence.arg_number
|
159
176
|
arguments[sequence.arg_number.to_i - 1]
|
160
177
|
else
|
161
|
-
# If the specifier contains `*`, the following arguments will be used
|
162
|
-
# to specify the width and can be ignored.
|
163
|
-
(sequence.arity - 1).times { arguments.shift }
|
164
178
|
arguments.shift
|
165
179
|
end
|
166
180
|
end
|
181
|
+
# rubocop:enable Metrics/AbcSize
|
167
182
|
|
168
183
|
def matching_argument?(sequence, argument)
|
169
184
|
# Template specifiers don't give a type, any acceptable literal type is ok.
|
@@ -24,9 +24,6 @@ module RuboCop
|
|
24
24
|
(send `{(send _recv _msg) str array hash const #variable?} :[] ...)
|
25
25
|
PATTERN
|
26
26
|
|
27
|
-
# @!method method_node_and_args(node)
|
28
|
-
def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'
|
29
|
-
|
30
27
|
# @!method rescue?(node)
|
31
28
|
def_node_matcher :rescue?, '{^resbody ^^resbody}'
|
32
29
|
|
@@ -205,6 +202,7 @@ module RuboCop
|
|
205
202
|
return false unless node.rescue_type?
|
206
203
|
return false unless (parent = begin_node.parent)
|
207
204
|
return false if parent.if_type? && parent.ternary?
|
205
|
+
return false if parent.conditional? && parent.condition == begin_node
|
208
206
|
|
209
207
|
!parent.type?(:call, :array, :pair)
|
210
208
|
end
|
@@ -228,7 +226,7 @@ module RuboCop
|
|
228
226
|
|
229
227
|
return check_unary(begin_node, node) if node.unary_operation?
|
230
228
|
|
231
|
-
return unless method_call_with_redundant_parentheses?(node)
|
229
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
232
230
|
return if call_chain_starts_with_int?(begin_node, node) ||
|
233
231
|
do_end_block_in_method_chain?(begin_node, node)
|
234
232
|
|
@@ -239,8 +237,7 @@ module RuboCop
|
|
239
237
|
return if begin_node.chained?
|
240
238
|
|
241
239
|
node = node.children.first while suspect_unary?(node)
|
242
|
-
|
243
|
-
return if node.send_type? && !method_call_with_redundant_parentheses?(node)
|
240
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
244
241
|
|
245
242
|
offense(begin_node, 'a unary operation')
|
246
243
|
end
|
@@ -302,13 +299,19 @@ module RuboCop
|
|
302
299
|
end
|
303
300
|
end
|
304
301
|
|
305
|
-
def method_call_with_redundant_parentheses?(node)
|
306
|
-
return false unless node.
|
302
|
+
def method_call_with_redundant_parentheses?(begin_node, node)
|
303
|
+
return false unless node.type?(:call, :super, :yield, :defined?)
|
307
304
|
return false if node.prefix_not?
|
305
|
+
return true if singular_parenthesized_parent?(begin_node)
|
306
|
+
|
307
|
+
node.arguments.empty? || parentheses?(node) || square_brackets?(node)
|
308
|
+
end
|
308
309
|
|
309
|
-
|
310
|
+
def singular_parenthesized_parent?(begin_node)
|
311
|
+
return true unless begin_node.parent
|
312
|
+
return false if begin_node.parent.type?(:splat, :kwsplat)
|
310
313
|
|
311
|
-
|
314
|
+
parentheses?(begin_node) && begin_node.parent.children.one?
|
312
315
|
end
|
313
316
|
|
314
317
|
def only_begin_arg?(args)
|
@@ -66,6 +66,7 @@ module RuboCop
|
|
66
66
|
DETERMINISTIC_REGEX.match?(regexp_node.source)
|
67
67
|
end
|
68
68
|
|
69
|
+
# rubocop:disable Metrics/MethodLength
|
69
70
|
def preferred_argument(regexp_node)
|
70
71
|
new_argument = replacement(regexp_node)
|
71
72
|
|
@@ -73,6 +74,8 @@ module RuboCop
|
|
73
74
|
new_argument.gsub!("'", "\\\\'")
|
74
75
|
new_argument.gsub!('\"', '"')
|
75
76
|
quote = "'"
|
77
|
+
elsif new_argument.include?("\\'")
|
78
|
+
quote = "'"
|
76
79
|
elsif new_argument.include?('\'')
|
77
80
|
new_argument.gsub!("'", "\\\\'")
|
78
81
|
quote = "'"
|
@@ -84,6 +87,7 @@ module RuboCop
|
|
84
87
|
|
85
88
|
"#{quote}#{new_argument}#{quote}"
|
86
89
|
end
|
90
|
+
# rubocop:enable Metrics/MethodLength
|
87
91
|
|
88
92
|
def replacement(regexp_node)
|
89
93
|
regexp_content = regexp_node.content
|
@@ -41,6 +41,7 @@ module RuboCop
|
|
41
41
|
ALLOWED_ALWAYS_ESCAPES = " \n[]^\\#".chars.freeze
|
42
42
|
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES = '-'.chars.freeze
|
43
43
|
ALLOWED_OUTSIDE_CHAR_CLASS_METACHAR_ESCAPES = '.*+?{}()|$'.chars.freeze
|
44
|
+
INTERPOLATION_SIGILS = %w[@ $].freeze
|
44
45
|
|
45
46
|
def on_regexp(node)
|
46
47
|
each_escape(node) do |char, index, within_character_class|
|
@@ -64,6 +65,7 @@ module RuboCop
|
|
64
65
|
# different versions of Ruby so that e.g. /\i/ != /i/
|
65
66
|
return true if /[[:alnum:]]/.match?(char)
|
66
67
|
return true if ALLOWED_ALWAYS_ESCAPES.include?(char) || delimiter?(node, char)
|
68
|
+
return true if requires_escape_to_avoid_interpolation?(node.source[index], char)
|
67
69
|
|
68
70
|
if within_character_class
|
69
71
|
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES.include?(char) &&
|
@@ -95,6 +97,12 @@ module RuboCop
|
|
95
97
|
delimiters.include?(char)
|
96
98
|
end
|
97
99
|
|
100
|
+
def requires_escape_to_avoid_interpolation?(char_before_escape, escaped_char)
|
101
|
+
# Preserve escapes after '#' that would otherwise trigger interpolation:
|
102
|
+
# '#@ivar', '#@@cvar', and '#$gvar'.
|
103
|
+
char_before_escape == '#' && INTERPOLATION_SIGILS.include?(escaped_char)
|
104
|
+
end
|
105
|
+
|
98
106
|
def each_escape(node)
|
99
107
|
node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
|
100
108
|
yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
|
@@ -262,8 +262,14 @@ module RuboCop
|
|
262
262
|
end
|
263
263
|
|
264
264
|
def dotless_operator_call?(method_call)
|
265
|
+
return true if dotless_operator_method?(method_call)
|
266
|
+
|
265
267
|
method_call = method_call.parent while method_call.parent.send_type?
|
266
268
|
|
269
|
+
dotless_operator_method?(method_call)
|
270
|
+
end
|
271
|
+
|
272
|
+
def dotless_operator_method?(method_call)
|
267
273
|
return false if method_call.loc.dot
|
268
274
|
|
269
275
|
method_call.method?(:[]) || method_call.method?(:[]=) || method_call.operator_method?
|
@@ -141,22 +141,26 @@ module RuboCop
|
|
141
141
|
end
|
142
142
|
|
143
143
|
def replacement(parts)
|
144
|
-
interpolated_parts = parts.map
|
145
|
-
case part.type
|
146
|
-
when :str
|
147
|
-
adjust_str(part)
|
148
|
-
when :dstr
|
149
|
-
part.children.all?(&:str_type?) ? adjust_str(part) : part.value
|
150
|
-
else
|
151
|
-
"\#{#{part.source}}"
|
152
|
-
end
|
153
|
-
end
|
144
|
+
interpolated_parts = parts.map { |part| adjust_str(part) }
|
154
145
|
|
155
146
|
"\"#{handle_quotes(interpolated_parts).join}\""
|
156
147
|
end
|
157
148
|
|
158
|
-
def adjust_str(
|
159
|
-
|
149
|
+
def adjust_str(part)
|
150
|
+
case part.type
|
151
|
+
when :str
|
152
|
+
if single_quoted?(part)
|
153
|
+
part.value.gsub(/(\\|"|#\{|#@|#\$)/, '\\\\\&')
|
154
|
+
else
|
155
|
+
part.value.inspect[1..-2]
|
156
|
+
end
|
157
|
+
when :dstr, :begin
|
158
|
+
part.children.map do |child|
|
159
|
+
adjust_str(child)
|
160
|
+
end.join
|
161
|
+
else
|
162
|
+
"\#{#{part.source}}"
|
163
|
+
end
|
160
164
|
end
|
161
165
|
|
162
166
|
def handle_quotes(parts)
|
@@ -10,6 +10,9 @@ module RuboCop
|
|
10
10
|
# for all parenthesized multi-line method calls with arguments.
|
11
11
|
# * `comma`: Requires a comma after the last argument, but only for
|
12
12
|
# parenthesized method calls where each argument is on its own line.
|
13
|
+
# * `diff_comma`: Requires a comma after the last argument, but only
|
14
|
+
# when that argument is followed by an immediate newline, even if
|
15
|
+
# there is an inline comment on the same line.
|
13
16
|
# * `no_comma`: Requires that there is no comma after the last
|
14
17
|
# argument.
|
15
18
|
#
|
@@ -75,6 +78,48 @@ module RuboCop
|
|
75
78
|
# 2,
|
76
79
|
# )
|
77
80
|
#
|
81
|
+
# @example EnforcedStyleForMultiline: diff_comma
|
82
|
+
# # bad
|
83
|
+
# method(1, 2,)
|
84
|
+
#
|
85
|
+
# # good
|
86
|
+
# method(1, 2)
|
87
|
+
#
|
88
|
+
# # good
|
89
|
+
# method(
|
90
|
+
# 1, 2,
|
91
|
+
# 3,
|
92
|
+
# )
|
93
|
+
#
|
94
|
+
# # good
|
95
|
+
# method(
|
96
|
+
# 1, 2, 3,
|
97
|
+
# )
|
98
|
+
#
|
99
|
+
# # good
|
100
|
+
# method(
|
101
|
+
# 1,
|
102
|
+
# 2,
|
103
|
+
# )
|
104
|
+
#
|
105
|
+
# # bad
|
106
|
+
# method(1, [
|
107
|
+
# 2,
|
108
|
+
# ],)
|
109
|
+
#
|
110
|
+
# # good
|
111
|
+
# method(1, [
|
112
|
+
# 2,
|
113
|
+
# ])
|
114
|
+
#
|
115
|
+
# # bad
|
116
|
+
# object[1, 2,
|
117
|
+
# 3, 4,]
|
118
|
+
#
|
119
|
+
# # good
|
120
|
+
# object[1, 2,
|
121
|
+
# 3, 4]
|
122
|
+
#
|
78
123
|
# @example EnforcedStyleForMultiline: no_comma (default)
|
79
124
|
# # bad
|
80
125
|
# method(1, 2,)
|
@@ -20,7 +20,6 @@ module RuboCop
|
|
20
20
|
# # do a different thing...
|
21
21
|
# end
|
22
22
|
class UnlessElse < Base
|
23
|
-
include RangeHelp
|
24
23
|
extend AutoCorrector
|
25
24
|
|
26
25
|
MSG = 'Do not use `unless` with `else`. Rewrite these with the positive case first.'
|
@@ -29,25 +28,27 @@ module RuboCop
|
|
29
28
|
return unless node.unless? && node.else?
|
30
29
|
|
31
30
|
add_offense(node) do |corrector|
|
32
|
-
body_range = range_between_condition_and_else(node, node.condition)
|
33
|
-
else_range = range_between_else_and_end(node)
|
34
|
-
|
35
31
|
next if part_of_ignored_node?(node)
|
36
32
|
|
37
33
|
corrector.replace(node.loc.keyword, 'if')
|
38
|
-
|
39
|
-
|
34
|
+
|
35
|
+
body_range = range_between_condition_and_else(node)
|
36
|
+
else_range = range_between_else_and_end(node)
|
37
|
+
|
38
|
+
corrector.swap(body_range, else_range)
|
40
39
|
end
|
41
40
|
|
42
41
|
ignore_node(node)
|
43
42
|
end
|
44
43
|
|
45
|
-
def range_between_condition_and_else(node
|
46
|
-
|
44
|
+
def range_between_condition_and_else(node)
|
45
|
+
range = node.loc.begin ? node.loc.begin.end : node.condition.source_range
|
46
|
+
|
47
|
+
range.end.join(node.loc.else.begin)
|
47
48
|
end
|
48
49
|
|
49
50
|
def range_between_else_and_end(node)
|
50
|
-
|
51
|
+
node.loc.else.end.join(node.loc.end.begin)
|
51
52
|
end
|
52
53
|
end
|
53
54
|
end
|
@@ -71,6 +71,16 @@ module RuboCop
|
|
71
71
|
name && @source.include?('{')
|
72
72
|
end
|
73
73
|
|
74
|
+
def variable_width?
|
75
|
+
!!width&.start_with?('*')
|
76
|
+
end
|
77
|
+
|
78
|
+
def variable_width_argument_number
|
79
|
+
return unless variable_width?
|
80
|
+
|
81
|
+
width == '*' ? 1 : width.match(DIGIT_DOLLAR)['arg_number'].to_i
|
82
|
+
end
|
83
|
+
|
74
84
|
# Number of arguments required for the format sequence
|
75
85
|
def arity
|
76
86
|
@source.scan('*').count + 1
|
@@ -16,8 +16,8 @@ module RuboCop
|
|
16
16
|
# Diagnostic for Language Server Protocol of RuboCop.
|
17
17
|
# @api private
|
18
18
|
class Diagnostic
|
19
|
-
def initialize(
|
20
|
-
@
|
19
|
+
def initialize(position_encoding, offense, uri, cop_class)
|
20
|
+
@position_encoding = position_encoding
|
21
21
|
@offense = offense
|
22
22
|
@uri = uri
|
23
23
|
@cop_class = cop_class
|
@@ -45,11 +45,11 @@ module RuboCop
|
|
45
45
|
range: LanguageServer::Protocol::Interface::Range.new(
|
46
46
|
start: LanguageServer::Protocol::Interface::Position.new(
|
47
47
|
line: @offense.line - 1,
|
48
|
-
character: highlighted.begin_pos
|
48
|
+
character: to_position_character(highlighted.begin_pos)
|
49
49
|
),
|
50
50
|
end: LanguageServer::Protocol::Interface::Position.new(
|
51
51
|
line: @offense.line - 1,
|
52
|
-
character: highlighted.end_pos
|
52
|
+
character: to_position_character(highlighted.end_pos)
|
53
53
|
)
|
54
54
|
),
|
55
55
|
data: {
|
@@ -107,11 +107,11 @@ module RuboCop
|
|
107
107
|
range: LanguageServer::Protocol::Interface::Range.new(
|
108
108
|
start: LanguageServer::Protocol::Interface::Position.new(
|
109
109
|
line: range.line - 1,
|
110
|
-
character: range.column
|
110
|
+
character: to_position_character(range.column)
|
111
111
|
),
|
112
112
|
end: LanguageServer::Protocol::Interface::Position.new(
|
113
113
|
line: range.last_line - 1,
|
114
|
-
character: range.last_column
|
114
|
+
character: to_position_character(range.last_column)
|
115
115
|
)
|
116
116
|
),
|
117
117
|
new_text: replacement
|
@@ -149,7 +149,7 @@ module RuboCop
|
|
149
149
|
|
150
150
|
eol = LanguageServer::Protocol::Interface::Position.new(
|
151
151
|
line: @offense.line - 1,
|
152
|
-
character:
|
152
|
+
character: to_position_character
|
153
153
|
)
|
154
154
|
|
155
155
|
# TODO: fails for multiline strings - may be preferable to use block
|
@@ -162,19 +162,6 @@ module RuboCop
|
|
162
162
|
[inline_comment]
|
163
163
|
end
|
164
164
|
|
165
|
-
def length_of_line(line)
|
166
|
-
if @document_encoding == Encoding::UTF_16LE
|
167
|
-
line_length = 0
|
168
|
-
line.codepoints.each do |codepoint|
|
169
|
-
line_length += 1
|
170
|
-
line_length += 1 if codepoint > RubyLsp::Document::Scanner::SURROGATE_PAIR_START
|
171
|
-
end
|
172
|
-
line_length
|
173
|
-
else
|
174
|
-
line.length
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
165
|
def correctable?
|
179
166
|
!@offense.corrector.nil?
|
180
167
|
end
|
@@ -184,6 +171,20 @@ module RuboCop
|
|
184
171
|
uri.scheme = 'file' if uri.scheme.nil?
|
185
172
|
uri
|
186
173
|
end
|
174
|
+
|
175
|
+
def to_position_character(utf8_index = nil)
|
176
|
+
str = utf8_index ? @offense.source_line[0, utf8_index] : @offense.source_line
|
177
|
+
case @position_encoding
|
178
|
+
when 'utf-8', Encoding::UTF_8
|
179
|
+
str.bytesize
|
180
|
+
when 'utf-32', Encoding::UTF_32
|
181
|
+
str.size
|
182
|
+
else # 'utf-16'
|
183
|
+
# utf-16 is default position encoding on LSP
|
184
|
+
# https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/
|
185
|
+
str.size + str.count("\u{10000}-\u{10FFFF}")
|
186
|
+
end
|
187
|
+
end
|
187
188
|
end
|
188
189
|
end
|
189
190
|
end
|
data/lib/rubocop/lsp/routes.rb
CHANGED
@@ -15,7 +15,7 @@ module RuboCop
|
|
15
15
|
module LSP
|
16
16
|
# Routes for Language Server Protocol of RuboCop.
|
17
17
|
# @api private
|
18
|
-
class Routes
|
18
|
+
class Routes # rubocop:disable Metrics/ClassLength
|
19
19
|
CONFIGURATION_FILE_PATTERNS = [
|
20
20
|
RuboCop::ConfigFinder::DOTFILE,
|
21
21
|
RuboCop::CLI::Command::AutoGenerateConfig::AUTO_GENERATED_FILE
|
@@ -42,6 +42,7 @@ module RuboCop
|
|
42
42
|
|
43
43
|
handle 'initialize' do |request|
|
44
44
|
initialization_options = extract_initialization_options_from(request)
|
45
|
+
@position_encoding = initialization_options[:position_encoding]
|
45
46
|
|
46
47
|
@server.configure(initialization_options)
|
47
48
|
|
@@ -53,7 +54,8 @@ module RuboCop
|
|
53
54
|
text_document_sync: LanguageServer::Protocol::Interface::TextDocumentSyncOptions.new(
|
54
55
|
change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::INCREMENTAL,
|
55
56
|
open_close: true
|
56
|
-
)
|
57
|
+
),
|
58
|
+
position_encoding: @position_encoding
|
57
59
|
)
|
58
60
|
)
|
59
61
|
)
|
@@ -184,14 +186,26 @@ module RuboCop
|
|
184
186
|
|
185
187
|
def extract_initialization_options_from(request)
|
186
188
|
safe_autocorrect = request.dig(:params, :initializationOptions, :safeAutocorrect)
|
189
|
+
position_encodings = request.dig(:params, :capabilities, :general, :positionEncodings)
|
187
190
|
|
188
191
|
{
|
189
192
|
safe_autocorrect: safe_autocorrect.nil? || safe_autocorrect == true,
|
190
193
|
lint_mode: request.dig(:params, :initializationOptions, :lintMode) == true,
|
191
|
-
layout_mode: request.dig(:params, :initializationOptions, :layoutMode) == true
|
194
|
+
layout_mode: request.dig(:params, :initializationOptions, :layoutMode) == true,
|
195
|
+
position_encoding: position_encoding(position_encodings)
|
192
196
|
}
|
193
197
|
end
|
194
198
|
|
199
|
+
def position_encoding(position_encodings)
|
200
|
+
if position_encodings&.include?('utf-8')
|
201
|
+
'utf-8'
|
202
|
+
elsif position_encodings&.include?('utf-32')
|
203
|
+
'utf-32'
|
204
|
+
else
|
205
|
+
'utf-16'
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
195
209
|
def format_file(file_uri, command: nil)
|
196
210
|
unless (text = @text_cache[file_uri])
|
197
211
|
Logger.log("Format request arrived before text synchronized; skipping: `#{file_uri}'")
|
@@ -219,7 +233,8 @@ module RuboCop
|
|
219
233
|
method: 'textDocument/publishDiagnostics',
|
220
234
|
params: {
|
221
235
|
uri: file_uri,
|
222
|
-
diagnostics: @server.offenses(convert_file_uri_to_path(file_uri),
|
236
|
+
diagnostics: @server.offenses(convert_file_uri_to_path(file_uri),
|
237
|
+
text, @position_encoding)
|
223
238
|
}
|
224
239
|
}
|
225
240
|
end
|
@@ -229,9 +244,8 @@ module RuboCop
|
|
229
244
|
|
230
245
|
start_pos = text_pos(orig_text, range[:start])
|
231
246
|
end_pos = text_pos(orig_text, range[:end])
|
232
|
-
|
233
|
-
|
234
|
-
text_bin.force_encoding(orig_text.encoding)
|
247
|
+
orig_text[start_pos...end_pos] = text
|
248
|
+
orig_text
|
235
249
|
end
|
236
250
|
|
237
251
|
def text_pos(text, range)
|
@@ -240,14 +254,27 @@ module RuboCop
|
|
240
254
|
pos = 0
|
241
255
|
text.each_line.with_index do |l, i|
|
242
256
|
if i == line
|
243
|
-
pos += l
|
257
|
+
pos += line_pos(l, char)
|
244
258
|
return pos
|
245
259
|
end
|
246
|
-
pos += l.
|
260
|
+
pos += l.size
|
247
261
|
end
|
248
262
|
pos
|
249
263
|
end
|
250
264
|
|
265
|
+
def line_pos(line, char)
|
266
|
+
case @position_encoding
|
267
|
+
when 'utf-8'
|
268
|
+
line.byteslice(0, char).size
|
269
|
+
when 'utf-32'
|
270
|
+
char
|
271
|
+
else # 'utf-16'
|
272
|
+
# utf-16 is default position encoding on LSP
|
273
|
+
# https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/
|
274
|
+
line.encode('utf-16be').byteslice(0, char * 2).size
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
251
278
|
def convert_file_uri_to_path(uri)
|
252
279
|
URI.decode_www_form_component(uri.delete_prefix('file://'))
|
253
280
|
end
|
data/lib/rubocop/lsp/runtime.rb
CHANGED
@@ -44,14 +44,14 @@ module RuboCop
|
|
44
44
|
@runner.formatted_source
|
45
45
|
end
|
46
46
|
|
47
|
-
def offenses(path, text,
|
47
|
+
def offenses(path, text, position_encoding, prism_result: nil)
|
48
48
|
diagnostic_options = {}
|
49
49
|
diagnostic_options[:only] = config_only_options if @lint_mode || @layout_mode
|
50
50
|
|
51
51
|
@runner.run(path, text, diagnostic_options, prism_result: prism_result)
|
52
52
|
@runner.offenses.map do |offense|
|
53
53
|
Diagnostic.new(
|
54
|
-
|
54
|
+
position_encoding, offense, path, @cop_registry[offense.cop_name]&.first
|
55
55
|
).to_lsp_diagnostic(@runner.config_for_working_directory)
|
56
56
|
end
|
57
57
|
end
|
data/lib/rubocop/lsp/server.rb
CHANGED
@@ -51,8 +51,8 @@ module RuboCop
|
|
51
51
|
@runtime.format(path, text, command: command)
|
52
52
|
end
|
53
53
|
|
54
|
-
def offenses(path, text)
|
55
|
-
@runtime.offenses(path, text)
|
54
|
+
def offenses(path, text, position_encoding)
|
55
|
+
@runtime.offenses(path, text, position_encoding)
|
56
56
|
end
|
57
57
|
|
58
58
|
def configure(options)
|
@@ -40,7 +40,6 @@ module RuboCop
|
|
40
40
|
super(@options, config_store)
|
41
41
|
end
|
42
42
|
|
43
|
-
# rubocop:disable Metrics/MethodLength
|
44
43
|
def run(path, contents, options, prism_result: nil)
|
45
44
|
@options = options.merge(DEFAULT_RUBOCOP_OPTIONS)
|
46
45
|
@options[:stdin] = contents
|
@@ -54,22 +53,7 @@ module RuboCop
|
|
54
53
|
super([path])
|
55
54
|
|
56
55
|
raise Interrupt if aborting?
|
57
|
-
rescue RuboCop::Runner::InfiniteCorrectionLoop => e
|
58
|
-
if defined?(::RubyLsp::Requests::Formatting::Error)
|
59
|
-
raise ::RubyLsp::Requests::Formatting::Error, e.message
|
60
|
-
end
|
61
|
-
|
62
|
-
raise e
|
63
|
-
rescue RuboCop::ValidationError => e
|
64
|
-
raise ConfigurationError, e.message
|
65
|
-
rescue StandardError => e
|
66
|
-
if defined?(::RubyLsp::Requests::Formatting::Error)
|
67
|
-
raise ::RubyLsp::Requests::Support::InternalRuboCopError, e
|
68
|
-
end
|
69
|
-
|
70
|
-
raise e
|
71
56
|
end
|
72
|
-
# rubocop:enable Metrics/MethodLength
|
73
57
|
|
74
58
|
def formatted_source
|
75
59
|
@options[:stdin]
|
data/lib/rubocop/target_ruby.rb
CHANGED
@@ -110,8 +110,17 @@ module RuboCop
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def version_from_gemspec_file(file)
|
113
|
+
# When using parser_prism, we need to use a Ruby version that Prism supports (3.3+)
|
114
|
+
# for parsing the gemspec file. This doesn't affect the detected Ruby version,
|
115
|
+
# it's just for the parsing step.
|
116
|
+
ruby_version_for_parsing = if @config.parser_engine == :parser_prism
|
117
|
+
3.3
|
118
|
+
else
|
119
|
+
DEFAULT_VERSION
|
120
|
+
end
|
121
|
+
|
113
122
|
processed_source = ProcessedSource.from_file(
|
114
|
-
file,
|
123
|
+
file, ruby_version_for_parsing, parser_engine: @config.parser_engine
|
115
124
|
)
|
116
125
|
return unless processed_source.valid_syntax?
|
117
126
|
|
data/lib/rubocop/version.rb
CHANGED
data/lib/rubocop.rb
CHANGED
@@ -489,6 +489,7 @@ require_relative 'rubocop/cop/style/arguments_forwarding'
|
|
489
489
|
require_relative 'rubocop/cop/style/array_coercion'
|
490
490
|
require_relative 'rubocop/cop/style/array_first_last'
|
491
491
|
require_relative 'rubocop/cop/style/array_intersect'
|
492
|
+
require_relative 'rubocop/cop/style/array_intersect_with_single_element'
|
492
493
|
require_relative 'rubocop/cop/style/array_join'
|
493
494
|
require_relative 'rubocop/cop/style/ascii_comments'
|
494
495
|
require_relative 'rubocop/cop/style/attr'
|