rubocop 1.80.2 → 1.86.1
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/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/config/default.yml +170 -19
- data/config/obsoletion.yml +9 -0
- data/lib/rubocop/cache_config.rb +29 -0
- data/lib/rubocop/cli/command/auto_generate_config.rb +3 -3
- data/lib/rubocop/cli/command/lsp.rb +1 -1
- data/lib/rubocop/cli/command/mcp.rb +19 -0
- data/lib/rubocop/cli/command/show_cops.rb +2 -2
- data/lib/rubocop/cli/command/show_docs_url.rb +1 -1
- data/lib/rubocop/cli.rb +28 -6
- data/lib/rubocop/comment_config.rb +62 -17
- data/lib/rubocop/config.rb +14 -10
- data/lib/rubocop/config_finder.rb +1 -1
- data/lib/rubocop/config_loader.rb +20 -21
- data/lib/rubocop/config_loader_resolver.rb +9 -7
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
- data/lib/rubocop/config_store.rb +6 -1
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +8 -4
- data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
- data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +22 -6
- data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
- data/lib/rubocop/cop/documentation.rb +2 -3
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +10 -5
- data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
- data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
- data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +9 -9
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.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/internal_affairs/useless_message_assertion.rb +4 -4
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/case_indentation.rb +3 -1
- data/lib/rubocop/cop/layout/class_structure.rb +13 -6
- data/lib/rubocop/cop/layout/dot_position.rb +2 -2
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +12 -2
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +31 -13
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
- data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
- data/lib/rubocop/cop/layout/end_alignment.rb +8 -1
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +34 -1
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +26 -0
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
- data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
- data/lib/rubocop/cop/layout/hash_alignment.rb +3 -6
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +33 -3
- data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +111 -7
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +26 -9
- data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +57 -57
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +56 -56
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +204 -39
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +6 -4
- data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
- data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
- data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +4 -2
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +9 -8
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
- data/lib/rubocop/cop/lint/constant_reassignment.rb +59 -9
- data/lib/rubocop/cop/lint/constant_resolution.rb +1 -1
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
- data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
- data/lib/rubocop/cop/lint/debugger.rb +0 -2
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
- data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
- data/lib/rubocop/cop/lint/duplicate_methods.rb +111 -12
- data/lib/rubocop/cop/lint/else_layout.rb +19 -0
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
- data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
- data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
- data/lib/rubocop/cop/lint/empty_when.rb +8 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
- data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +16 -6
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -0
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -9
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +0 -9
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +23 -6
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +8 -2
- data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -0
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
- data/lib/rubocop/cop/lint/self_assignment.rb +10 -2
- data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
- data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
- data/lib/rubocop/cop/lint/syntax.rb +25 -1
- data/lib/rubocop/cop/lint/to_json.rb +12 -16
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
- data/lib/rubocop/cop/lint/unreachable_code.rb +5 -3
- data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +45 -17
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
- data/lib/rubocop/cop/lint/useless_or.rb +15 -2
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +24 -9
- data/lib/rubocop/cop/lint/void.rb +39 -12
- data/lib/rubocop/cop/message_annotator.rb +1 -1
- data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
- data/lib/rubocop/cop/migration/department_name.rb +12 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +4 -6
- data/lib/rubocop/cop/mixin/code_length.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +5 -5
- data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
- data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
- data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
- data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
- data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -5
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/method_name.rb +4 -2
- data/lib/rubocop/cop/naming/predicate_method.rb +27 -4
- data/lib/rubocop/cop/naming/predicate_prefix.rb +11 -11
- data/lib/rubocop/cop/offense.rb +9 -1
- data/lib/rubocop/cop/registry.rb +20 -13
- data/lib/rubocop/cop/security/eval.rb +15 -2
- data/lib/rubocop/cop/security/json_load.rb +33 -11
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +15 -4
- data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
- data/lib/rubocop/cop/style/alias.rb +4 -1
- data/lib/rubocop/cop/style/and_or.rb +1 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
- data/lib/rubocop/cop/style/array_intersect.rb +2 -2
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
- data/lib/rubocop/cop/style/array_join.rb +4 -2
- data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
- data/lib/rubocop/cop/style/attr.rb +5 -2
- data/lib/rubocop/cop/style/bare_percent_literals.rb +4 -3
- data/lib/rubocop/cop/style/begin_block.rb +3 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +27 -34
- data/lib/rubocop/cop/style/case_equality.rb +15 -13
- data/lib/rubocop/cop/style/class_and_module_children.rb +11 -2
- data/lib/rubocop/cop/style/collection_compact.rb +36 -16
- data/lib/rubocop/cop/style/colon_method_call.rb +3 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +8 -18
- data/lib/rubocop/cop/style/constant_visibility.rb +17 -12
- data/lib/rubocop/cop/style/copyright.rb +1 -1
- data/lib/rubocop/cop/style/documentation.rb +6 -6
- data/lib/rubocop/cop/style/documentation_method.rb +8 -8
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/each_with_object.rb +2 -0
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_class_definition.rb +119 -0
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +0 -6
- data/lib/rubocop/cop/style/encoding.rb +7 -1
- data/lib/rubocop/cop/style/end_block.rb +3 -1
- data/lib/rubocop/cop/style/endless_method.rb +23 -5
- data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
- data/lib/rubocop/cop/style/file_open.rb +84 -0
- data/lib/rubocop/cop/style/float_division.rb +15 -1
- data/lib/rubocop/cop/style/for.rb +3 -0
- data/lib/rubocop/cop/style/format_string_token.rb +49 -5
- data/lib/rubocop/cop/style/global_vars.rb +5 -2
- data/lib/rubocop/cop/style/guard_clause.rb +27 -22
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +27 -9
- data/lib/rubocop/cop/style/hash_lookup_method.rb +101 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
- data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -5
- data/lib/rubocop/cop/style/if_unless_modifier.rb +57 -17
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -12
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +4 -1
- data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
- data/lib/rubocop/cop/style/inline_comment.rb +4 -1
- data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
- data/lib/rubocop/cop/style/lambda_call.rb +8 -8
- data/lib/rubocop/cop/style/magic_comment_format.rb +2 -2
- data/lib/rubocop/cop/style/map_join.rb +123 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +15 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
- data/lib/rubocop/cop/style/method_def_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/module_member_existence_check.rb +107 -0
- data/lib/rubocop/cop/style/multiline_if_then.rb +4 -4
- data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
- data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
- data/lib/rubocop/cop/style/negative_array_index.rb +220 -0
- data/lib/rubocop/cop/style/nil_comparison.rb +11 -10
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
- data/lib/rubocop/cop/style/not.rb +2 -0
- data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
- data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
- data/lib/rubocop/cop/style/one_line_conditional.rb +21 -12
- data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +6 -2
- data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
- data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
- data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
- data/lib/rubocop/cop/style/proc.rb +3 -2
- data/lib/rubocop/cop/style/raise_args.rb +1 -1
- data/lib/rubocop/cop/style/reduce_to_hash.rb +184 -0
- data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
- data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
- data/lib/rubocop/cop/style/redundant_each.rb +3 -3
- data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +26 -5
- data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
- data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +26 -22
- data/lib/rubocop/cop/style/redundant_percent_q.rb +5 -3
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
- data/lib/rubocop/cop/style/redundant_return.rb +3 -1
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
- data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
- data/lib/rubocop/cop/style/reverse_find.rb +51 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
- data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
- data/lib/rubocop/cop/style/select_by_range.rb +197 -0
- data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
- data/lib/rubocop/cop/style/semicolon.rb +25 -7
- data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +8 -1
- data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
- data/lib/rubocop/cop/style/super_arguments.rb +2 -2
- data/lib/rubocop/cop/style/symbol_proc.rb +4 -3
- data/lib/rubocop/cop/style/tally_method.rb +181 -0
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
- data/lib/rubocop/cop/style/unless_else.rb +10 -9
- data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
- data/lib/rubocop/cop/team.rb +4 -4
- data/lib/rubocop/cop/util.rb +2 -3
- data/lib/rubocop/cop/utils/format_string.rb +10 -0
- data/lib/rubocop/cop/variable_force/branch.rb +30 -6
- data/lib/rubocop/cops_documentation_generator.rb +4 -4
- data/lib/rubocop/directive_comment.rb +48 -4
- data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
- data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
- data/lib/rubocop/formatter/formatter_set.rb +2 -2
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
- data/lib/rubocop/formatter/tap_formatter.rb +5 -2
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/formatter.rb +22 -21
- data/lib/rubocop/lsp/diagnostic.rb +18 -33
- data/lib/rubocop/lsp/disable_comment_edits.rb +135 -0
- data/lib/rubocop/lsp/routes.rb +12 -5
- data/lib/rubocop/lsp/runtime.rb +13 -3
- data/lib/rubocop/lsp/stdin_runner.rb +8 -17
- data/lib/rubocop/magic_comment.rb +20 -0
- data/lib/rubocop/mcp/server.rb +200 -0
- data/lib/rubocop/options.rb +10 -1
- data/lib/rubocop/path_util.rb +14 -2
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +10 -8
- data/lib/rubocop/result_cache.rb +60 -37
- data/lib/rubocop/rspec/cop_helper.rb +8 -0
- data/lib/rubocop/rspec/shared_contexts.rb +18 -5
- data/lib/rubocop/rspec/support.rb +2 -1
- data/lib/rubocop/runner.rb +12 -3
- data/lib/rubocop/server/cache.rb +6 -29
- data/lib/rubocop/server/core.rb +2 -0
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +31 -14
- data/lib/rubocop/version.rb +2 -2
- data/lib/rubocop.rb +20 -0
- data/lib/ruby_lsp/rubocop/addon.rb +23 -8
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
- metadata +33 -9
|
@@ -24,7 +24,7 @@ module RuboCop
|
|
|
24
24
|
def file_finished(file, offenses)
|
|
25
25
|
return if offenses.empty?
|
|
26
26
|
|
|
27
|
-
path = Pathname.new(file).relative_path_from(Pathname.new(
|
|
27
|
+
path = Pathname.new(file).relative_path_from(Pathname.new(PathUtil.pwd))
|
|
28
28
|
@offense_counts[path] = offenses.size
|
|
29
29
|
end
|
|
30
30
|
|
data/lib/rubocop/formatter.rb
CHANGED
|
@@ -3,32 +3,33 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
# The bootstrap module for formatter.
|
|
5
5
|
module Formatter
|
|
6
|
-
|
|
6
|
+
autoload :Colorizable, 'rubocop/formatter/colorizable'
|
|
7
|
+
autoload :TextUtil, 'rubocop/formatter/text_util'
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
autoload :BaseFormatter, 'rubocop/formatter/base_formatter'
|
|
10
|
+
autoload :SimpleTextFormatter, 'rubocop/formatter/simple_text_formatter'
|
|
10
11
|
|
|
11
12
|
# relies on simple text
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
autoload :ClangStyleFormatter, 'rubocop/formatter/clang_style_formatter'
|
|
14
|
+
autoload :DisabledConfigFormatter, 'rubocop/formatter/disabled_config_formatter'
|
|
15
|
+
autoload :EmacsStyleFormatter, 'rubocop/formatter/emacs_style_formatter'
|
|
16
|
+
autoload :FileListFormatter, 'rubocop/formatter/file_list_formatter'
|
|
17
|
+
autoload :FuubarStyleFormatter, 'rubocop/formatter/fuubar_style_formatter'
|
|
18
|
+
autoload :GitHubActionsFormatter, 'rubocop/formatter/github_actions_formatter'
|
|
19
|
+
autoload :HTMLFormatter, 'rubocop/formatter/html_formatter'
|
|
20
|
+
autoload :JSONFormatter, 'rubocop/formatter/json_formatter'
|
|
21
|
+
autoload :JUnitFormatter, 'rubocop/formatter/junit_formatter'
|
|
22
|
+
autoload :MarkdownFormatter, 'rubocop/formatter/markdown_formatter'
|
|
23
|
+
autoload :OffenseCountFormatter, 'rubocop/formatter/offense_count_formatter'
|
|
24
|
+
autoload :PacmanFormatter, 'rubocop/formatter/pacman_formatter'
|
|
25
|
+
autoload :ProgressFormatter, 'rubocop/formatter/progress_formatter'
|
|
26
|
+
autoload :QuietFormatter, 'rubocop/formatter/quiet_formatter'
|
|
27
|
+
autoload :TapFormatter, 'rubocop/formatter/tap_formatter'
|
|
28
|
+
autoload :WorstOffendersFormatter, 'rubocop/formatter/worst_offenders_formatter'
|
|
28
29
|
|
|
29
30
|
# relies on progress formatter
|
|
30
|
-
|
|
31
|
+
autoload :AutoGenConfigFormatter, 'rubocop/formatter/auto_gen_config_formatter'
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
autoload :FormatterSet, 'rubocop/formatter/formatter_set'
|
|
33
34
|
end
|
|
34
35
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'language_server-protocol'
|
|
4
|
+
require_relative 'disable_comment_edits'
|
|
3
5
|
require_relative 'severity'
|
|
4
6
|
|
|
5
7
|
#
|
|
@@ -16,11 +18,12 @@ module RuboCop
|
|
|
16
18
|
# Diagnostic for Language Server Protocol of RuboCop.
|
|
17
19
|
# @api private
|
|
18
20
|
class Diagnostic
|
|
19
|
-
def initialize(document_encoding, offense, uri, cop_class)
|
|
21
|
+
def initialize(document_encoding, offense, uri, cop_class, processed_source = nil)
|
|
20
22
|
@document_encoding = document_encoding
|
|
21
23
|
@offense = offense
|
|
22
24
|
@uri = uri
|
|
23
25
|
@cop_class = cop_class
|
|
26
|
+
@processed_source = processed_source
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
def to_lsp_code_actions
|
|
@@ -45,11 +48,11 @@ module RuboCop
|
|
|
45
48
|
range: LanguageServer::Protocol::Interface::Range.new(
|
|
46
49
|
start: LanguageServer::Protocol::Interface::Position.new(
|
|
47
50
|
line: @offense.line - 1,
|
|
48
|
-
character: highlighted.begin_pos
|
|
51
|
+
character: to_position_character(highlighted.begin_pos)
|
|
49
52
|
),
|
|
50
53
|
end: LanguageServer::Protocol::Interface::Position.new(
|
|
51
54
|
line: @offense.line - 1,
|
|
52
|
-
character: highlighted.end_pos
|
|
55
|
+
character: to_position_character(highlighted.end_pos)
|
|
53
56
|
)
|
|
54
57
|
),
|
|
55
58
|
data: {
|
|
@@ -107,11 +110,11 @@ module RuboCop
|
|
|
107
110
|
range: LanguageServer::Protocol::Interface::Range.new(
|
|
108
111
|
start: LanguageServer::Protocol::Interface::Position.new(
|
|
109
112
|
line: range.line - 1,
|
|
110
|
-
character: range.column
|
|
113
|
+
character: to_position_character(range.column)
|
|
111
114
|
),
|
|
112
115
|
end: LanguageServer::Protocol::Interface::Position.new(
|
|
113
116
|
line: range.last_line - 1,
|
|
114
|
-
character: range.last_column
|
|
117
|
+
character: to_position_character(range.last_column)
|
|
115
118
|
)
|
|
116
119
|
),
|
|
117
120
|
new_text: replacement
|
|
@@ -141,37 +144,19 @@ module RuboCop
|
|
|
141
144
|
# rubocop:enable Metrics/MethodLength
|
|
142
145
|
|
|
143
146
|
def line_disable_comment
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
eol = LanguageServer::Protocol::Interface::Position.new(
|
|
151
|
-
line: @offense.line - 1,
|
|
152
|
-
character: length_of_line(@offense.source_line)
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
# TODO: fails for multiline strings - may be preferable to use block
|
|
156
|
-
# comments to disable some offenses
|
|
157
|
-
inline_comment = LanguageServer::Protocol::Interface::TextEdit.new(
|
|
158
|
-
range: LanguageServer::Protocol::Interface::Range.new(start: eol, end: eol),
|
|
159
|
-
new_text: new_text
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
[inline_comment]
|
|
147
|
+
DisableCommentEdits.new(
|
|
148
|
+
offense: @offense,
|
|
149
|
+
document_encoding: @document_encoding,
|
|
150
|
+
processed_source: @processed_source
|
|
151
|
+
).edits
|
|
163
152
|
end
|
|
164
153
|
|
|
165
|
-
def
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
line_length += 1
|
|
170
|
-
line_length += 1 if codepoint > RubyLsp::Document::Scanner::SURROGATE_PAIR_START
|
|
171
|
-
end
|
|
172
|
-
line_length
|
|
154
|
+
def to_position_character(utf8_index)
|
|
155
|
+
str = @offense.source_line[0, utf8_index]
|
|
156
|
+
if @document_encoding == Encoding::UTF_16LE || @document_encoding.nil?
|
|
157
|
+
str.length + str.b.count("\xf0-\xff".b)
|
|
173
158
|
else
|
|
174
|
-
|
|
159
|
+
str.length
|
|
175
160
|
end
|
|
176
161
|
end
|
|
177
162
|
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module LSP
|
|
5
|
+
# Builds LSP text edits for rubocop:disable comments.
|
|
6
|
+
# @api private
|
|
7
|
+
class DisableCommentEdits
|
|
8
|
+
def initialize(offense:, document_encoding:, processed_source:)
|
|
9
|
+
@offense = offense
|
|
10
|
+
@document_encoding = document_encoding
|
|
11
|
+
@processed_source = processed_source
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def edits
|
|
15
|
+
literal_range = multiline_literal_range
|
|
16
|
+
return block_disable_comments(literal_range) if literal_range
|
|
17
|
+
|
|
18
|
+
inline_disable_comment
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def inline_disable_comment
|
|
24
|
+
eol = position(@offense.line - 1, @offense.source_line.length, @offense.source_line)
|
|
25
|
+
[text_edit(eol, inline_comment_text)]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def inline_comment_text
|
|
29
|
+
if @offense.source_line.include?(' # rubocop:disable ')
|
|
30
|
+
",#{@offense.cop_name}"
|
|
31
|
+
else
|
|
32
|
+
" # rubocop:disable #{@offense.cop_name}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def block_disable_comments(range)
|
|
37
|
+
full_line_range = range_by_lines(range)
|
|
38
|
+
leading_whitespace = full_line_range.source_line[/^\s*/].to_s
|
|
39
|
+
[
|
|
40
|
+
disable_edit(full_line_range.first_line, leading_whitespace),
|
|
41
|
+
enable_edit(full_line_range, leading_whitespace)
|
|
42
|
+
]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def disable_edit(first_line, leading_whitespace)
|
|
46
|
+
position = position(first_line - 1, 0, '')
|
|
47
|
+
text_edit(position, "#{leading_whitespace}# rubocop:disable #{@offense.cop_name}\n")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def enable_edit(full_line_range, leading_whitespace)
|
|
51
|
+
last_line = full_line_range.last_line
|
|
52
|
+
last_line_text = full_line_range.source_buffer.source_line(last_line)
|
|
53
|
+
position = position(last_line - 1, last_line_text.length, last_line_text)
|
|
54
|
+
text_edit(position, "\n#{leading_whitespace}# rubocop:enable #{@offense.cop_name}")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def text_edit(position, new_text)
|
|
58
|
+
range = LanguageServer::Protocol::Interface::Range.new(start: position, end: position)
|
|
59
|
+
LanguageServer::Protocol::Interface::TextEdit.new(range: range, new_text: new_text)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def position(line, utf8_index, line_text)
|
|
63
|
+
LanguageServer::Protocol::Interface::Position.new(
|
|
64
|
+
line: line,
|
|
65
|
+
character: position_character(utf8_index, line_text)
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def position_character(utf8_index, line_text)
|
|
70
|
+
str = line_text[0, utf8_index]
|
|
71
|
+
if @document_encoding == Encoding::UTF_16LE || @document_encoding.nil?
|
|
72
|
+
str.length + str.b.count("\xf0-\xff".b)
|
|
73
|
+
else
|
|
74
|
+
str.length
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def multiline_literal_range
|
|
79
|
+
return unless @processed_source&.ast
|
|
80
|
+
|
|
81
|
+
offense_range = @offense.location
|
|
82
|
+
multiline_ranges&.find do |range|
|
|
83
|
+
eol_comment_would_be_inside_literal?(offense_range, range)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def multiline_ranges
|
|
88
|
+
@processed_source.ast.each_node.filter_map do |node|
|
|
89
|
+
if surrounding_heredoc?(node)
|
|
90
|
+
heredoc_range(node)
|
|
91
|
+
elsif string_continuation?(node)
|
|
92
|
+
range_by_lines(node.source_range)
|
|
93
|
+
elsif surrounding_percent_array?(node) || multiline_string?(node)
|
|
94
|
+
node.source_range
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def eol_comment_would_be_inside_literal?(offense_range, literal_range)
|
|
100
|
+
offense_line = offense_range.line
|
|
101
|
+
offense_line >= literal_range.first_line && offense_line < literal_range.last_line
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def surrounding_heredoc?(node)
|
|
105
|
+
node.any_str_type? && node.heredoc?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def heredoc_range(node)
|
|
109
|
+
node.source_range.join(node.loc.heredoc_end)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def surrounding_percent_array?(node)
|
|
113
|
+
node.array_type? && node.percent_literal?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def string_continuation?(node)
|
|
117
|
+
node.any_str_type? && node.source.match?(/\\\s*$/)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def multiline_string?(node)
|
|
121
|
+
node.dstr_type? && node.multiline?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def range_by_lines(range)
|
|
125
|
+
begin_of_first_line = range.begin_pos - range.column
|
|
126
|
+
|
|
127
|
+
last_line = range.source_buffer.source_line(range.last_line)
|
|
128
|
+
last_line_offset = last_line.length - range.last_column
|
|
129
|
+
end_of_last_line = range.end_pos + last_line_offset
|
|
130
|
+
|
|
131
|
+
Parser::Source::Range.new(range.source_buffer, begin_of_first_line, end_of_last_line)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
data/lib/rubocop/lsp/routes.rb
CHANGED
|
@@ -61,9 +61,16 @@ module RuboCop
|
|
|
61
61
|
|
|
62
62
|
handle 'initialized' do |_request|
|
|
63
63
|
version = RuboCop::Version::STRING
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
# Only one JIT can be enabled at the same time, since YJIT and ZJIT are mutually exclusive.
|
|
65
|
+
jit = if Object.const_defined?('RubyVM::YJIT') && RubyVM::YJIT.enabled?
|
|
66
|
+
'+YJIT'
|
|
67
|
+
elsif Object.const_defined?('RubyVM::ZJIT') && RubyVM::ZJIT.enabled?
|
|
68
|
+
'+ZJIT'
|
|
69
|
+
else
|
|
70
|
+
''
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
Logger.log("RuboCop #{version} language server#{jit} initialized, PID #{Process.pid}")
|
|
67
74
|
end
|
|
68
75
|
|
|
69
76
|
handle 'shutdown' do |request|
|
|
@@ -203,13 +210,13 @@ module RuboCop
|
|
|
203
210
|
|
|
204
211
|
return [] if new_text == text
|
|
205
212
|
|
|
206
|
-
[
|
|
213
|
+
[{
|
|
207
214
|
newText: new_text,
|
|
208
215
|
range: {
|
|
209
216
|
start: { line: 0, character: 0 },
|
|
210
217
|
end: { line: text.count("\n") + 1, character: 0 }
|
|
211
218
|
}
|
|
212
|
-
]
|
|
219
|
+
}]
|
|
213
220
|
end
|
|
214
221
|
|
|
215
222
|
def diagnostic(file_uri, text)
|
data/lib/rubocop/lsp/runtime.rb
CHANGED
|
@@ -49,15 +49,25 @@ module RuboCop
|
|
|
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
|
+
processed_source = @runner.processed_source
|
|
53
|
+
config = @runner.config_for_working_directory
|
|
52
54
|
@runner.offenses.map do |offense|
|
|
53
|
-
|
|
54
|
-
document_encoding, offense, path, @cop_registry[offense.cop_name]&.first
|
|
55
|
-
).to_lsp_diagnostic(@runner.config_for_working_directory)
|
|
55
|
+
build_diagnostic(offense, path, document_encoding, processed_source, config)
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
private
|
|
60
60
|
|
|
61
|
+
def build_diagnostic(offense, path, document_encoding, processed_source, config)
|
|
62
|
+
Diagnostic.new(
|
|
63
|
+
document_encoding,
|
|
64
|
+
offense,
|
|
65
|
+
path,
|
|
66
|
+
@cop_registry[offense.cop_name]&.first,
|
|
67
|
+
processed_source
|
|
68
|
+
).to_lsp_diagnostic(config)
|
|
69
|
+
end
|
|
70
|
+
|
|
61
71
|
def config_only_options
|
|
62
72
|
only_options = []
|
|
63
73
|
only_options << 'Lint' if @lint_mode
|
|
@@ -17,7 +17,7 @@ module RuboCop
|
|
|
17
17
|
class StdinRunner < RuboCop::Runner
|
|
18
18
|
class ConfigurationError < StandardError; end
|
|
19
19
|
|
|
20
|
-
attr_reader :offenses, :config_for_working_directory
|
|
20
|
+
attr_reader :offenses, :config_for_working_directory, :processed_source
|
|
21
21
|
|
|
22
22
|
DEFAULT_RUBOCOP_OPTIONS = {
|
|
23
23
|
stderr: true,
|
|
@@ -40,12 +40,12 @@ 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
|
|
47
46
|
|
|
48
47
|
@prism_result = prism_result
|
|
48
|
+
@processed_source = nil
|
|
49
49
|
|
|
50
50
|
@offenses = []
|
|
51
51
|
@warnings = []
|
|
@@ -54,22 +54,7 @@ module RuboCop
|
|
|
54
54
|
super([path])
|
|
55
55
|
|
|
56
56
|
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
57
|
end
|
|
72
|
-
# rubocop:enable Metrics/MethodLength
|
|
73
58
|
|
|
74
59
|
def formatted_source
|
|
75
60
|
@options[:stdin]
|
|
@@ -80,6 +65,12 @@ module RuboCop
|
|
|
80
65
|
def file_finished(_file, offenses)
|
|
81
66
|
@offenses = offenses
|
|
82
67
|
end
|
|
68
|
+
|
|
69
|
+
def do_inspection_loop(file)
|
|
70
|
+
source, offenses = super
|
|
71
|
+
@processed_source = source
|
|
72
|
+
[source, offenses]
|
|
73
|
+
end
|
|
83
74
|
end
|
|
84
75
|
end
|
|
85
76
|
end
|
|
@@ -11,6 +11,7 @@ module RuboCop
|
|
|
11
11
|
KEYWORDS = {
|
|
12
12
|
encoding: '(?:en)?coding',
|
|
13
13
|
frozen_string_literal: 'frozen[_-]string[_-]literal',
|
|
14
|
+
rbs_inline: 'rbs_inline',
|
|
14
15
|
shareable_constant_value: 'shareable[_-]constant[_-]value',
|
|
15
16
|
typed: 'typed'
|
|
16
17
|
}.freeze
|
|
@@ -36,6 +37,7 @@ module RuboCop
|
|
|
36
37
|
def any?
|
|
37
38
|
frozen_string_literal_specified? ||
|
|
38
39
|
encoding_specified? ||
|
|
40
|
+
rbs_inline_specified? ||
|
|
39
41
|
shareable_constant_value_specified? ||
|
|
40
42
|
typed_specified?
|
|
41
43
|
end
|
|
@@ -60,6 +62,10 @@ module RuboCop
|
|
|
60
62
|
[true, false].include?(frozen_string_literal)
|
|
61
63
|
end
|
|
62
64
|
|
|
65
|
+
def valid_rbs_inline_value?
|
|
66
|
+
%w[enabled disabled].include?(extract_rbs_inline_value)
|
|
67
|
+
end
|
|
68
|
+
|
|
63
69
|
def valid_shareable_constant_value?
|
|
64
70
|
%w[none literal experimental_everything experimental_copy].include?(shareable_constant_value)
|
|
65
71
|
end
|
|
@@ -105,6 +111,10 @@ module RuboCop
|
|
|
105
111
|
specified?(encoding)
|
|
106
112
|
end
|
|
107
113
|
|
|
114
|
+
def rbs_inline_specified?
|
|
115
|
+
valid_rbs_inline_value?
|
|
116
|
+
end
|
|
117
|
+
|
|
108
118
|
# Was the Sorbet `typed` sigil specified?
|
|
109
119
|
#
|
|
110
120
|
# @return [Boolean]
|
|
@@ -203,6 +213,9 @@ module RuboCop
|
|
|
203
213
|
match(KEYWORDS[:frozen_string_literal])
|
|
204
214
|
end
|
|
205
215
|
|
|
216
|
+
# Emacs comments cannot specify RBS::inline behavior.
|
|
217
|
+
def extract_rbs_inline_value; end
|
|
218
|
+
|
|
206
219
|
def extract_shareable_constant_value
|
|
207
220
|
match(KEYWORDS[:shareable_constant_value])
|
|
208
221
|
end
|
|
@@ -242,6 +255,9 @@ module RuboCop
|
|
|
242
255
|
# Vim comments cannot specify frozen string literal behavior.
|
|
243
256
|
def frozen_string_literal; end
|
|
244
257
|
|
|
258
|
+
# Vim comments cannot specify RBS::inline behavior.
|
|
259
|
+
def extract_rbs_inline_value; end
|
|
260
|
+
|
|
245
261
|
# Vim comments cannot specify shareable constant values behavior.
|
|
246
262
|
def shareable_constant_value; end
|
|
247
263
|
|
|
@@ -296,6 +312,10 @@ module RuboCop
|
|
|
296
312
|
extract(/\A\s*#\s*#{KEYWORDS[:frozen_string_literal]}:\s*#{TOKEN}\s*\z/io)
|
|
297
313
|
end
|
|
298
314
|
|
|
315
|
+
def extract_rbs_inline_value
|
|
316
|
+
extract(/\A\s*#\s*#{KEYWORDS[:rbs_inline]}:\s*#{TOKEN}\s*\z/io)
|
|
317
|
+
end
|
|
318
|
+
|
|
299
319
|
def extract_shareable_constant_value
|
|
300
320
|
extract(/\A\s*#\s*#{KEYWORDS[:shareable_constant_value]}:\s*#{TOKEN}\s*\z/io)
|
|
301
321
|
end
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'mcp'
|
|
5
|
+
|
|
6
|
+
required_mcp_version = '0.6.0'
|
|
7
|
+
|
|
8
|
+
if Gem::Version.new(required_mcp_version) > Gem::Version.new(MCP::VERSION)
|
|
9
|
+
# While `mcp` is not a runtime dependency, users may have an outdated version installed.
|
|
10
|
+
warn <<~MESSAGE
|
|
11
|
+
Error: `mcp` gem version #{MCP::VERSION} was loaded, but `rubocop --mcp` requires #{required_mcp_version}.
|
|
12
|
+
- If you're using Bundler and don't yet have `gem 'mcp'` as a dependency, add it now.
|
|
13
|
+
- If you're using Bundler and already have `gem 'mcp'` as a dependency, update it to the most recent version.
|
|
14
|
+
- If you don't use Bundler, run `gem update mcp`.
|
|
15
|
+
MESSAGE
|
|
16
|
+
exit!
|
|
17
|
+
end
|
|
18
|
+
rescue LoadError => e
|
|
19
|
+
raise unless e.path == 'mcp'
|
|
20
|
+
|
|
21
|
+
warn <<~MESSAGE
|
|
22
|
+
Error: Unable to load `mcp` gem. Add `gem 'mcp', '~> 0.6'` to your Gemfile, or run `gem install mcp`.
|
|
23
|
+
MESSAGE
|
|
24
|
+
|
|
25
|
+
exit!
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
require_relative '../lsp'
|
|
29
|
+
require_relative '../lsp/runtime'
|
|
30
|
+
|
|
31
|
+
module RuboCop
|
|
32
|
+
module MCP
|
|
33
|
+
# RuboCop MCP Server.
|
|
34
|
+
# @api private
|
|
35
|
+
class Server
|
|
36
|
+
def initialize(config_store)
|
|
37
|
+
@config_store = config_store
|
|
38
|
+
@runtime = RuboCop::LSP::Runtime.new(@config_store)
|
|
39
|
+
@options = {}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def start
|
|
43
|
+
# No `protocol_version` is specified because draft feature by default can be used.
|
|
44
|
+
server = ::MCP::Server.new(
|
|
45
|
+
name: 'rubocop_mcp_server',
|
|
46
|
+
version: RuboCop::Version::STRING,
|
|
47
|
+
tools: [inspection_tool, autocorrection_tool]
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
::MCP::Server::Transports::StdioTransport.new(server).open
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def inspection_tool
|
|
56
|
+
build_tool(
|
|
57
|
+
name: 'rubocop_inspection',
|
|
58
|
+
description: 'Inspect Ruby code for offenses. ' \
|
|
59
|
+
'Provide `source_code` to check inline code or `path` to check files.',
|
|
60
|
+
title: "RuboCop's inspection",
|
|
61
|
+
destructive_hint: false,
|
|
62
|
+
idempotent_hint: true,
|
|
63
|
+
read_only_hint: true,
|
|
64
|
+
safety_required: false
|
|
65
|
+
) do |path, source_code|
|
|
66
|
+
run_inspection(path, source_code)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def autocorrection_tool
|
|
71
|
+
build_tool(
|
|
72
|
+
name: 'rubocop_autocorrection',
|
|
73
|
+
description: 'Autocorrect RuboCop offenses in Ruby code. ' \
|
|
74
|
+
'Provide `source_code` to correct inline code or `path` to correct files. ' \
|
|
75
|
+
'Set `safety` to false to include unsafe corrections.',
|
|
76
|
+
title: "RuboCop's autocorrection",
|
|
77
|
+
destructive_hint: true,
|
|
78
|
+
idempotent_hint: false,
|
|
79
|
+
read_only_hint: false,
|
|
80
|
+
safety_required: true
|
|
81
|
+
) do |path, source_code, safety|
|
|
82
|
+
run_autocorrection(path, source_code, safety)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def run_inspection(path, source_code)
|
|
87
|
+
if source_code
|
|
88
|
+
offenses = @runtime.offenses(path || 'example.rb', source_code, source_code.encoding)
|
|
89
|
+
offenses.to_json
|
|
90
|
+
else
|
|
91
|
+
process_files(path, filter_empty: true) do |file, source|
|
|
92
|
+
offenses = @runtime.offenses(file, source, source.encoding)
|
|
93
|
+
|
|
94
|
+
{ path: PathUtil.relative_path(file), offenses: offenses }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def run_autocorrection(path, source_code, safety)
|
|
100
|
+
command = safety ? 'rubocop.formatAutocorrects' : 'rubocop.formatAutocorrectsAll'
|
|
101
|
+
|
|
102
|
+
if source_code
|
|
103
|
+
@runtime.format(path || 'example.rb', source_code, command: command).tap do |corrected|
|
|
104
|
+
write_file(path, corrected) if path
|
|
105
|
+
end
|
|
106
|
+
else
|
|
107
|
+
process_files(path) do |file, source|
|
|
108
|
+
@runtime.format(file, source, command: command).then do |corrected|
|
|
109
|
+
write_file(file, corrected)
|
|
110
|
+
|
|
111
|
+
{ path: PathUtil.relative_path(file), corrected: source != corrected }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def process_files(path, filter_empty: false)
|
|
118
|
+
target_finder = RuboCop::TargetFinder.new(@config_store, @options)
|
|
119
|
+
target_files = target_finder.find(path ? [path] : [], :only_recognized_file_types)
|
|
120
|
+
all_files = target_files.map { |file| yield(file, read_file(file)) }
|
|
121
|
+
files = filter_empty ? all_files.reject { |f| f[:offenses]&.empty? } : all_files
|
|
122
|
+
|
|
123
|
+
{ files: files, summary: build_summary(target_files, all_files) }.to_json
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def read_file(file)
|
|
127
|
+
config = @config_store.for_file(file)
|
|
128
|
+
RuboCop::ProcessedSource.from_file(
|
|
129
|
+
file, config.target_ruby_version, parser_engine: config.parser_engine
|
|
130
|
+
).raw_source
|
|
131
|
+
rescue Errno::ENOENT
|
|
132
|
+
raise RuboCop::Error, "No such file or directory: #{file}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def write_file(file, content)
|
|
136
|
+
File.write(file, content)
|
|
137
|
+
rescue Errno::EACCES
|
|
138
|
+
raise RuboCop::Error, "Permission denied: #{file}"
|
|
139
|
+
rescue Errno::ENOSPC
|
|
140
|
+
raise RuboCop::Error, "No space left on device: #{file}"
|
|
141
|
+
rescue Errno::EROFS
|
|
142
|
+
raise RuboCop::Error, "Read-only file system: #{file}"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# NOTE: It is useful for RuboCop's result summary to be shown in the LLM's responses
|
|
146
|
+
# during interactions, so the summary is returned in a form that is easy for the LLM
|
|
147
|
+
# to reason about. Since LLM execution is non-deterministic, it is also sensible to
|
|
148
|
+
# compute the summary deterministically at this stage.
|
|
149
|
+
def build_summary(target_files, files)
|
|
150
|
+
summary = { target_file_count: target_files.count }
|
|
151
|
+
if files.first&.key?(:offenses)
|
|
152
|
+
summary[:offense_count] = files.sum { |f| f[:offenses].size }
|
|
153
|
+
else
|
|
154
|
+
summary[:corrected_file_count] = files.count { |f| f[:corrected] }
|
|
155
|
+
end
|
|
156
|
+
summary
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
|
|
160
|
+
def build_tool(
|
|
161
|
+
name:, description:,
|
|
162
|
+
title:, destructive_hint:, idempotent_hint:, read_only_hint:, safety_required:
|
|
163
|
+
)
|
|
164
|
+
if safety_required
|
|
165
|
+
safety_property = { safety: { type: 'boolean' } }
|
|
166
|
+
required = ['safety']
|
|
167
|
+
else
|
|
168
|
+
safety_property = {}
|
|
169
|
+
required = nil
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
::MCP::Tool.define(
|
|
173
|
+
name: name,
|
|
174
|
+
description: description,
|
|
175
|
+
input_schema: {
|
|
176
|
+
properties: {
|
|
177
|
+
path: { type: 'string' },
|
|
178
|
+
source_code: { type: 'string' }
|
|
179
|
+
}.merge(safety_property),
|
|
180
|
+
required: required
|
|
181
|
+
}.compact,
|
|
182
|
+
annotations: {
|
|
183
|
+
title: title,
|
|
184
|
+
destructive_hint: destructive_hint,
|
|
185
|
+
idempotent_hint: idempotent_hint,
|
|
186
|
+
open_world_hint: false,
|
|
187
|
+
read_only_hint: read_only_hint
|
|
188
|
+
}
|
|
189
|
+
) do |path: nil, source_code: nil, safety: true|
|
|
190
|
+
result = yield(path, source_code, safety)
|
|
191
|
+
|
|
192
|
+
::MCP::Tool::Response.new([{ type: 'text', text: result }])
|
|
193
|
+
rescue RuboCop::Error => e
|
|
194
|
+
::MCP::Tool::Response.new([{ type: 'text', text: e.message }], error: true)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
# rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|