rubocop 1.30.0 → 1.31.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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/config/default.yml +41 -8
  4. data/config/obsoletion.yml +2 -0
  5. data/exe/rubocop +15 -7
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  7. data/lib/rubocop/cli/command/suggest_extensions.rb +3 -3
  8. data/lib/rubocop/config.rb +4 -0
  9. data/lib/rubocop/config_loader.rb +1 -0
  10. data/lib/rubocop/config_validator.rb +9 -5
  11. data/lib/rubocop/cop/base.rb +4 -0
  12. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
  13. data/lib/rubocop/cop/bundler/gem_filename.rb +4 -4
  14. data/lib/rubocop/cop/bundler/ordered_gems.rb +2 -2
  15. data/lib/rubocop/cop/corrector.rb +2 -2
  16. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +3 -3
  17. data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +1 -1
  18. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +31 -16
  19. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -1
  20. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  21. data/lib/rubocop/cop/gemspec/require_mfa.rb +20 -20
  22. data/lib/rubocop/cop/generator.rb +1 -1
  23. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -5
  24. data/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb +1 -1
  25. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +1 -1
  26. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  27. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  28. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +25 -4
  29. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +4 -4
  30. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +9 -9
  31. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +9 -9
  32. data/lib/rubocop/cop/layout/first_argument_indentation.rb +27 -27
  33. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -11
  34. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +49 -10
  35. data/lib/rubocop/cop/layout/initial_indentation.rb +1 -1
  36. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +68 -0
  37. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +120 -0
  38. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  39. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
  40. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
  41. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  42. data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
  43. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -1
  44. data/lib/rubocop/cop/layout/space_before_first_arg.rb +1 -1
  45. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +10 -10
  46. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +5 -3
  47. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +7 -7
  48. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  49. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +51 -0
  50. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -1
  51. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +121 -0
  52. data/lib/rubocop/cop/lint/number_conversion.rb +3 -3
  53. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  54. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +5 -5
  55. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  56. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -1
  57. data/lib/rubocop/cop/lint/regexp_as_condition.rb +2 -2
  58. data/lib/rubocop/cop/lint/struct_new_override.rb +2 -2
  59. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  60. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +44 -0
  61. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  62. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +36 -12
  63. data/lib/rubocop/cop/mixin/range_help.rb +7 -3
  64. data/lib/rubocop/cop/naming/accessor_method_name.rb +3 -1
  65. data/lib/rubocop/cop/naming/variable_number.rb +17 -17
  66. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  67. data/lib/rubocop/cop/style/and_or.rb +8 -8
  68. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
  69. data/lib/rubocop/cop/style/block_delimiters.rb +4 -2
  70. data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
  71. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -0
  72. data/lib/rubocop/cop/style/empty_else.rb +10 -10
  73. data/lib/rubocop/cop/style/empty_method.rb +16 -1
  74. data/lib/rubocop/cop/style/encoding.rb +1 -1
  75. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  76. data/lib/rubocop/cop/style/fetch_env_var.rb +9 -2
  77. data/lib/rubocop/cop/style/format_string_token.rb +48 -17
  78. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  79. data/lib/rubocop/cop/style/guard_clause.rb +8 -6
  80. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -1
  81. data/lib/rubocop/cop/style/hash_except.rb +88 -8
  82. data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
  83. data/lib/rubocop/cop/style/implicit_runtime_error.rb +2 -2
  84. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
  85. data/lib/rubocop/cop/style/line_end_concatenation.rb +1 -1
  86. data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
  87. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  88. data/lib/rubocop/cop/style/missing_else.rb +24 -24
  89. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -2
  90. data/lib/rubocop/cop/style/multiline_if_then.rb +1 -1
  91. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -3
  92. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -1
  93. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
  94. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -3
  95. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  96. data/lib/rubocop/cop/style/nested_ternary_operator.rb +19 -7
  97. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  98. data/lib/rubocop/cop/style/not.rb +1 -1
  99. data/lib/rubocop/cop/style/redundant_argument.rb +1 -1
  100. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  101. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
  102. data/lib/rubocop/cop/style/rescue_standard_error.rb +10 -10
  103. data/lib/rubocop/cop/style/safe_navigation.rb +3 -0
  104. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -2
  105. data/lib/rubocop/cop/style/string_concatenation.rb +5 -6
  106. data/lib/rubocop/cop/style/struct_inheritance.rb +2 -2
  107. data/lib/rubocop/cop/style/swap_values.rb +1 -1
  108. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  109. data/lib/rubocop/cop/style/unpack_first.rb +1 -1
  110. data/lib/rubocop/cop/util.rb +1 -1
  111. data/lib/rubocop/cops_documentation_generator.rb +18 -1
  112. data/lib/rubocop/formatter/disabled_config_formatter.rb +8 -5
  113. data/lib/rubocop/formatter/formatter_set.rb +20 -19
  114. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +15 -2
  115. data/lib/rubocop/formatter/html_formatter.rb +0 -1
  116. data/lib/rubocop/formatter/offense_count_formatter.rb +2 -0
  117. data/lib/rubocop/formatter/simple_text_formatter.rb +6 -9
  118. data/lib/rubocop/formatter.rb +31 -0
  119. data/lib/rubocop/options.rb +25 -2
  120. data/lib/rubocop/rake_task.rb +30 -9
  121. data/lib/rubocop/runner.rb +2 -2
  122. data/lib/rubocop/server/cache.rb +109 -0
  123. data/lib/rubocop/server/cli.rb +104 -0
  124. data/lib/rubocop/server/client_command/base.rb +44 -0
  125. data/lib/rubocop/server/client_command/exec.rb +59 -0
  126. data/lib/rubocop/server/client_command/restart.rb +25 -0
  127. data/lib/rubocop/server/client_command/start.rb +43 -0
  128. data/lib/rubocop/server/client_command/status.rb +28 -0
  129. data/lib/rubocop/server/client_command/stop.rb +31 -0
  130. data/lib/rubocop/server/client_command.rb +26 -0
  131. data/lib/rubocop/server/core.rb +79 -0
  132. data/lib/rubocop/server/errors.rb +23 -0
  133. data/lib/rubocop/server/helper.rb +34 -0
  134. data/lib/rubocop/server/server_command/base.rb +50 -0
  135. data/lib/rubocop/server/server_command/exec.rb +34 -0
  136. data/lib/rubocop/server/server_command/stop.rb +24 -0
  137. data/lib/rubocop/server/server_command.rb +21 -0
  138. data/lib/rubocop/server/socket_reader.rb +65 -0
  139. data/lib/rubocop/server.rb +53 -0
  140. data/lib/rubocop/version.rb +15 -8
  141. data/lib/rubocop.rb +7 -26
  142. metadata +27 -4
  143. data/lib/rubocop/cop/gemspec/date_assignment.rb +0 -49
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'colorizable'
4
- require_relative 'text_util'
5
-
6
3
  module RuboCop
7
4
  module Formatter
8
5
  # A basic formatter that displays only files with offenses.
@@ -63,7 +60,7 @@ module RuboCop
63
60
  rainbow,
64
61
  # :safe_autocorrect is a derived option based on several command-line
65
62
  # arguments - see Rubocop::Options#add_autocorrection_options
66
- safe_auto_correct: @options[:safe_autocorrect])
63
+ safe_autocorrect: @options[:safe_autocorrect])
67
64
 
68
65
  output.puts
69
66
  output.puts report.summary
@@ -110,22 +107,22 @@ module RuboCop
110
107
  # rubocop:disable Metrics/ParameterLists
111
108
  def initialize(
112
109
  file_count, offense_count, correction_count, correctable_count, rainbow,
113
- safe_auto_correct: false
110
+ safe_autocorrect: false
114
111
  )
115
112
  @file_count = file_count
116
113
  @offense_count = offense_count
117
114
  @correction_count = correction_count
118
115
  @correctable_count = correctable_count
119
116
  @rainbow = rainbow
120
- @safe_auto_correct = safe_auto_correct
117
+ @safe_autocorrect = safe_autocorrect
121
118
  end
122
119
  # rubocop:enable Metrics/ParameterLists
123
120
 
124
121
  def summary
125
122
  if @correction_count.positive?
126
123
  if @correctable_count.positive?
127
- "#{files} inspected, #{offenses} detected, #{corrections} corrected,"\
128
- " #{correctable}"
124
+ "#{files} inspected, #{offenses} detected, #{corrections} corrected, " \
125
+ "#{correctable}"
129
126
  else
130
127
  "#{files} inspected, #{offenses} detected, #{corrections} corrected"
131
128
  end
@@ -159,7 +156,7 @@ module RuboCop
159
156
  end
160
157
 
161
158
  def correctable
162
- if @safe_auto_correct
159
+ if @safe_autocorrect
163
160
  text = pluralize(@correctable_count, 'more offense')
164
161
  "#{colorize(text, :yellow)} can be corrected with `rubocop -A`"
165
162
  else
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Formatter
5
+ autoload :BaseFormatter, 'rubocop/formatter/base_formatter'
6
+ autoload :SimpleTextFormatter, 'rubocop/formatter/simple_text_formatter'
7
+ # relies on simple text
8
+ autoload :ClangStyleFormatter, 'rubocop/formatter/clang_style_formatter'
9
+ autoload :DisabledConfigFormatter, 'rubocop/formatter/disabled_config_formatter'
10
+ autoload :EmacsStyleFormatter, 'rubocop/formatter/emacs_style_formatter'
11
+ autoload :FileListFormatter, 'rubocop/formatter/file_list_formatter'
12
+ autoload :FuubarStyleFormatter, 'rubocop/formatter/fuubar_style_formatter'
13
+ autoload :GitHubActionsFormatter, 'rubocop/formatter/git_hub_actions_formatter'
14
+ autoload :HTMLFormatter, 'rubocop/formatter/html_formatter'
15
+ autoload :JSONFormatter, 'rubocop/formatter/json_formatter'
16
+ autoload :JUnitFormatter, 'rubocop/formatter/junit_formatter'
17
+ autoload :MarkdownFormatter, 'rubocop/formatter/markdown_formatter'
18
+ autoload :OffenseCountFormatter, 'rubocop/formatter/offense_count_formatter'
19
+ autoload :ProgressFormatter, 'rubocop/formatter/progress_formatter'
20
+ autoload :QuietFormatter, 'rubocop/formatter/quiet_formatter'
21
+ autoload :TapFormatter, 'rubocop/formatter/tap_formatter'
22
+ autoload :WorstOffendersFormatter, 'rubocop/formatter/worst_offenders_formatter'
23
+ autoload :PacmanFormatter, 'rubocop/formatter/pacman_formatter'
24
+ # relies on progress formatter
25
+ autoload :AutoGenConfigFormatter, 'rubocop/formatter/auto_gen_config_formatter'
26
+ autoload :Colorizable, 'rubocop/formatter/colorizable'
27
+
28
+ require_relative 'formatter/formatter_set'
29
+ require_relative 'formatter/text_util'
30
+ end
31
+ end
@@ -63,6 +63,7 @@ module RuboCop
63
63
 
64
64
  add_check_options(opts)
65
65
  add_cache_options(opts)
66
+ add_server_options(opts)
66
67
  add_output_options(opts)
67
68
  add_autocorrection_options(opts)
68
69
  add_config_generation_options(opts)
@@ -129,6 +130,7 @@ module RuboCop
129
130
  end
130
131
  end
131
132
 
133
+ # rubocop:todo Naming/InclusiveLanguage
132
134
  # the autocorrect command-line arguments map to the autocorrect @options values like so:
133
135
  # :fix_layout :autocorrect :safe_autocorrect :autocorrect_all
134
136
  # -x, --fix-layout true true - -
@@ -156,6 +158,7 @@ module RuboCop
156
158
  option(opts, '--disable-uncorrectable')
157
159
  end
158
160
  end
161
+ # rubocop:enable Naming/InclusiveLanguage
159
162
 
160
163
  def add_config_generation_options(opts)
161
164
  section(opts, 'Config Generation') do
@@ -201,6 +204,16 @@ module RuboCop
201
204
  end
202
205
  end
203
206
 
207
+ def add_server_options(opts)
208
+ section(opts, 'Server Options') do
209
+ option(opts, '--[no-]server')
210
+ option(opts, '--restart-server')
211
+ option(opts, '--start-server')
212
+ option(opts, '--stop-server')
213
+ option(opts, '--server-status')
214
+ end
215
+ end
216
+
204
217
  def add_additional_modes(opts)
205
218
  section(opts, 'Additional Modes') do
206
219
  option(opts, '-L', '--list-target-files')
@@ -226,7 +239,7 @@ module RuboCop
226
239
  end
227
240
 
228
241
  def handle_deprecated_option(old_option, new_option)
229
- warn "#{old_option} is deprecated; use #{new_option}"
242
+ warn rainbow.wrap("#{old_option} is deprecated; use #{new_option} instead.").yellow
230
243
  @options[long_opt_symbol([new_option])] = @options.delete(long_opt_symbol([old_option]))
231
244
  end
232
245
 
@@ -569,7 +582,17 @@ module RuboCop
569
582
  'parallel. Default is true.'],
570
583
  stdin: ['Pipe source from STDIN, using FILE in offense',
571
584
  'reports. This is useful for editor integration.'],
572
- init: 'Generate a .rubocop.yml file in the current directory.'
585
+ init: 'Generate a .rubocop.yml file in the current directory.',
586
+ server: ['If a server process has not been started yet, start',
587
+ 'the server process and execute inspection with server.',
588
+ 'Default is false.',
589
+ 'You can specify the server host and port with the',
590
+ '$RUBOCOP_SERVER_HOST and the $RUBOCOP_SERVER_PORT',
591
+ 'environment variables.'],
592
+ restart_server: 'Restart server process.',
593
+ start_server: 'Start server process.',
594
+ stop_server: 'Stop server process.',
595
+ server_status: 'Show server status.'
573
596
  }.freeze
574
597
  end
575
598
  end
@@ -32,6 +32,15 @@ module RuboCop
32
32
 
33
33
  private
34
34
 
35
+ def perform(option)
36
+ options = full_options.unshift(option)
37
+ # `parallel` will automatically be removed from the options internally.
38
+ # This is a nice to have to suppress the warning message
39
+ # about --parallel and --autocorrect not being compatible.
40
+ options.delete('--parallel')
41
+ run_cli(verbose, options)
42
+ end
43
+
35
44
  def run_cli(verbose, options)
36
45
  # We lazy-load RuboCop so that the task doesn't dramatically impact the
37
46
  # load time of your Rakefile.
@@ -60,19 +69,31 @@ module RuboCop
60
69
  @formatters = []
61
70
  end
62
71
 
63
- def setup_subtasks(name, *args, &task_block)
72
+ def setup_subtasks(name, *args, &task_block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
64
73
  namespace(name) do
65
- desc 'Autocorrect RuboCop offenses'
74
+ # rubocop:todo Naming/InclusiveLanguage
75
+ task(:auto_correct, *args) do
76
+ warn Rainbow(
77
+ 'rubocop:auto_correct task is deprecated; ' \
78
+ 'use rubocop:autocorrect task or rubocop:autocorrect_all task instead.'
79
+ ).yellow
80
+ ::Rake::Task['rubocop:autocorrect'].invoke
81
+ end
82
+ # rubocop:enable Naming/InclusiveLanguage
83
+
84
+ desc "Autocorrect RuboCop offenses (only when it's safe)."
85
+ task(:autocorrect, *args) do |_, task_args|
86
+ RakeFileUtils.verbose(verbose) do
87
+ yield(*[self, task_args].slice(0, task_block.arity)) if task_block
88
+ perform('--autocorrect')
89
+ end
90
+ end
66
91
 
67
- task(:auto_correct, *args) do |_, task_args|
92
+ desc 'Autocorrect RuboCop offenses (safe and unsafe).'
93
+ task(:autocorrect_all, *args) do |_, task_args|
68
94
  RakeFileUtils.verbose(verbose) do
69
95
  yield(*[self, task_args].slice(0, task_block.arity)) if task_block
70
- options = full_options.unshift('--autocorrect-all')
71
- # `parallel` will automatically be removed from the options internally.
72
- # This is a nice to have to suppress the warning message
73
- # about --parallel and --autocorrect not being compatible.
74
- options.delete('--parallel')
75
- run_cli(verbose, options)
96
+ perform('--autocorrect-all')
76
97
  end
77
98
  end
78
99
  end
@@ -382,7 +382,7 @@ module RuboCop
382
382
  if @options[:display_only_fail_level_offenses]
383
383
  offenses.select { |o| considered_failure?(o) }
384
384
  elsif @options[:display_only_safe_correctable]
385
- offenses.select { |o| supports_safe_auto_correct?(o) }
385
+ offenses.select { |o| supports_safe_autocorrect?(o) }
386
386
  elsif @options[:display_only_correctable]
387
387
  offenses.select(&:correctable?)
388
388
  else
@@ -390,7 +390,7 @@ module RuboCop
390
390
  end
391
391
  end
392
392
 
393
- def supports_safe_auto_correct?(offense)
393
+ def supports_safe_autocorrect?(offense)
394
394
  cop_class = Cop::Registry.global.find_by_cop_name(offense.cop_name)
395
395
  default_cfg = default_config(offense.cop_name)
396
396
 
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ #
6
+ # This code is based on https://github.com/fohte/rubocop-daemon.
7
+ #
8
+ # Copyright (c) 2018 Hayato Kawai
9
+ #
10
+ # The MIT License (MIT)
11
+ #
12
+ # https://github.com/fohte/rubocop-daemon/blob/master/LICENSE.txt
13
+ #
14
+ module RuboCop
15
+ module Server
16
+ # Caches the states of server process.
17
+ # @api private
18
+ class Cache
19
+ GEMFILE_NAMES = %w[Gemfile gems.rb].freeze
20
+
21
+ class << self
22
+ # Searches for Gemfile or gems.rb in the current dir or any parent dirs
23
+ def project_dir
24
+ current_dir = Dir.pwd
25
+ while current_dir != '/'
26
+ return current_dir if GEMFILE_NAMES.any? do |gemfile|
27
+ File.exist?(File.join(current_dir, gemfile))
28
+ end
29
+
30
+ current_dir = File.expand_path('..', current_dir)
31
+ end
32
+ # If we can't find a Gemfile, just use the current directory
33
+ Dir.pwd
34
+ end
35
+
36
+ def project_dir_cache_key
37
+ @project_dir_cache_key ||= project_dir[1..].tr('/', '+')
38
+ end
39
+
40
+ def dir
41
+ cache_path = File.expand_path('~/.cache/rubocop_cache/server')
42
+ Pathname.new(File.join(cache_path, project_dir_cache_key)).tap do |d|
43
+ d.mkpath unless d.exist?
44
+ end
45
+ end
46
+
47
+ def port_path
48
+ dir.join('port')
49
+ end
50
+
51
+ def token_path
52
+ dir.join('token')
53
+ end
54
+
55
+ def pid_path
56
+ dir.join('pid')
57
+ end
58
+
59
+ def lock_path
60
+ dir.join('lock')
61
+ end
62
+
63
+ def status_path
64
+ dir.join('status')
65
+ end
66
+
67
+ def version_path
68
+ dir.join('version')
69
+ end
70
+
71
+ def pid_running?
72
+ Process.kill(0, pid_path.read.to_i) == 1
73
+ rescue Errno::ESRCH
74
+ false
75
+ end
76
+
77
+ def acquire_lock
78
+ lock_file = File.open(lock_path, File::CREAT)
79
+ # flock returns 0 if successful, and false if not.
80
+ flock_result = lock_file.flock(File::LOCK_EX | File::LOCK_NB)
81
+ yield flock_result != false
82
+ ensure
83
+ lock_file.flock(File::LOCK_UN)
84
+ lock_file.close
85
+ end
86
+
87
+ def write_port_and_token_files(port:, token:)
88
+ port_path.write(port)
89
+ token_path.write(token)
90
+ end
91
+
92
+ def write_pid_file
93
+ pid_path.write(Process.pid)
94
+ yield
95
+ ensure
96
+ dir.rmtree
97
+ end
98
+
99
+ def write_status_file(status)
100
+ status_path.write(status)
101
+ end
102
+
103
+ def write_version_file(version)
104
+ version_path.write(version)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require 'rainbow'
5
+
6
+ #
7
+ # This code is based on https://github.com/fohte/rubocop-daemon.
8
+ #
9
+ # Copyright (c) 2018 Hayato Kawai
10
+ #
11
+ # The MIT License (MIT)
12
+ #
13
+ # https://github.com/fohte/rubocop-daemon/blob/master/LICENSE.txt
14
+ #
15
+ module RuboCop
16
+ module Server
17
+ # The CLI is a class responsible of handling server command line interface logic.
18
+ # @api private
19
+ class CLI
20
+ # Same exit status value as `RuboCop::CLI`.
21
+ STATUS_SUCCESS = 0
22
+ STATUS_ERROR = 2
23
+
24
+ SERVER_OPTIONS = %w[
25
+ --server --no-server --server-status --restart-server --start-server --stop-server
26
+ ].freeze
27
+ EXCLUSIVE_OPTIONS = (SERVER_OPTIONS - %w[--server --no-server]).freeze
28
+
29
+ def initialize
30
+ @exit = false
31
+ end
32
+
33
+ def run(argv = ARGV)
34
+ unless Server.support_server?
35
+ return error('RuboCop server is not supported by this Ruby.') if use_server_option?(argv)
36
+
37
+ return STATUS_SUCCESS
38
+ end
39
+
40
+ deleted_server_arguments = delete_server_argument_from(argv)
41
+
42
+ if deleted_server_arguments.size >= 2
43
+ return error("#{deleted_server_arguments.join(', ')} cannot be specified together.")
44
+ end
45
+
46
+ server_command = deleted_server_arguments.first
47
+
48
+ if EXCLUSIVE_OPTIONS.include?(server_command) && argv.count >= 2
49
+ return error("#{server_command} cannot be combined with other options.")
50
+ end
51
+
52
+ run_command(server_command)
53
+
54
+ STATUS_SUCCESS
55
+ end
56
+
57
+ def exit?
58
+ @exit
59
+ end
60
+
61
+ private
62
+
63
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength:
64
+ def run_command(server_command)
65
+ case server_command
66
+ when '--server'
67
+ Server::ClientCommand::Start.new.run unless Server.running?
68
+ when '--no-server'
69
+ Server::ClientCommand::Stop.new.run if Server.running?
70
+ when '--restart-server'
71
+ @exit = true
72
+ Server::ClientCommand::Restart.new.run
73
+ when '--start-server'
74
+ @exit = true
75
+ Server::ClientCommand::Start.new.run
76
+ when '--stop-server'
77
+ @exit = true
78
+ Server::ClientCommand::Stop.new.run
79
+ when '--server-status'
80
+ @exit = true
81
+ Server::ClientCommand::Status.new.run
82
+ end
83
+ end
84
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength:
85
+
86
+ def delete_server_argument_from(all_arguments)
87
+ SERVER_OPTIONS.each_with_object([]) do |server_option, server_arguments|
88
+ server_arguments << all_arguments.delete(server_option)
89
+ end.compact
90
+ end
91
+
92
+ def use_server_option?(argv)
93
+ (argv & SERVER_OPTIONS).any?
94
+ end
95
+
96
+ def error(message)
97
+ @exit = true
98
+ warn Rainbow(message).red
99
+
100
+ STATUS_ERROR
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+ require 'socket'
5
+
6
+ #
7
+ # This code is based on https://github.com/fohte/rubocop-daemon.
8
+ #
9
+ # Copyright (c) 2018 Hayato Kawai
10
+ #
11
+ # The MIT License (MIT)
12
+ #
13
+ # https://github.com/fohte/rubocop-daemon/blob/master/LICENSE.txt
14
+ #
15
+ module RuboCop
16
+ module Server
17
+ module ClientCommand
18
+ # Abstract base class for server client command.
19
+ # @api private
20
+ class Base
21
+ def run
22
+ raise NotImplementedError
23
+ end
24
+
25
+ private
26
+
27
+ def send_request(command:, args: [], body: '')
28
+ TCPSocket.open('127.0.0.1', Cache.port_path.read) do |socket|
29
+ socket.puts [Cache.token_path.read, Dir.pwd, command, *args].shelljoin
30
+ socket.write body
31
+ socket.close_write
32
+ $stdout.write socket.read(4096) until socket.eof?
33
+ end
34
+ end
35
+
36
+ def check_running_server
37
+ Server.running?.tap do |running|
38
+ warn 'RuboCop server is not running.' unless running
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # This code is based on https://github.com/fohte/rubocop-daemon.
5
+ #
6
+ # Copyright (c) 2018 Hayato Kawai
7
+ #
8
+ # The MIT License (MIT)
9
+ #
10
+ # https://github.com/fohte/rubocop-daemon/blob/master/LICENSE.txt
11
+ #
12
+ module RuboCop
13
+ module Server
14
+ module ClientCommand
15
+ # This class is a client command to execute server process.
16
+ # @api private
17
+ class Exec < Base
18
+ def run
19
+ ensure_server!
20
+ Cache.status_path.delete if Cache.status_path.file?
21
+ send_request(
22
+ command: 'exec',
23
+ args: ARGV.dup,
24
+ body: $stdin.tty? ? '' : $stdin.read
25
+ )
26
+ status
27
+ end
28
+
29
+ private
30
+
31
+ def ensure_server!
32
+ if incompatible_version?
33
+ puts 'RuboCop version incompatibility found, RuboCop server restarting...'
34
+ ClientCommand::Stop.new.run
35
+ elsif check_running_server
36
+ return
37
+ end
38
+
39
+ ClientCommand::Start.new.run
40
+ end
41
+
42
+ def incompatible_version?
43
+ RuboCop::Version::STRING != Cache.version_path.read
44
+ end
45
+
46
+ def status
47
+ unless Cache.status_path.file?
48
+ raise "RuboCop server: Could not find status file at: #{Cache.status_path}"
49
+ end
50
+
51
+ status = Cache.status_path.read
52
+ raise "RuboCop server: '#{status}' is not a valid status!" if (status =~ /^\d+$/).nil?
53
+
54
+ status.to_i
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # This code is based on https://github.com/fohte/rubocop-daemon.
5
+ #
6
+ # Copyright (c) 2018 Hayato Kawai
7
+ #
8
+ # The MIT License (MIT)
9
+ #
10
+ # https://github.com/fohte/rubocop-daemon/blob/master/LICENSE.txt
11
+ #
12
+ module RuboCop
13
+ module Server
14
+ module ClientCommand
15
+ # This class is a client command to restart server process.
16
+ # @api private
17
+ class Restart < Base
18
+ def run
19
+ ClientCommand::Stop.new.run
20
+ ClientCommand::Start.new.run
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # This code is based on https://github.com/fohte/rubocop-daemon.
5
+ #
6
+ # Copyright (c) 2018 Hayato Kawai
7
+ #
8
+ # The MIT License (MIT)
9
+ #
10
+ # https://github.com/fohte/rubocop-daemon/blob/master/LICENSE.txt
11
+ #
12
+ module RuboCop
13
+ module Server
14
+ module ClientCommand
15
+ # This class is a client command to start server process.
16
+ # @api private
17
+ class Start < Base
18
+ def run
19
+ if Server.running?
20
+ warn "RuboCop server (#{Cache.pid_path.read}) is already running."
21
+ return
22
+ end
23
+
24
+ Cache.acquire_lock do |locked|
25
+ unless locked
26
+ # Another process is already starting server,
27
+ # so wait for it to be ready.
28
+ Server.wait_for_running_status!(true)
29
+ exit 0
30
+ end
31
+
32
+ Cache.write_version_file(RuboCop::Version::STRING)
33
+
34
+ host = ENV.fetch('RUBOCOP_SERVER_HOST', '127.0.0.1')
35
+ port = ENV.fetch('RUBOCOP_SERVER_PORT', 0)
36
+
37
+ Server::Core.new.start(host, port)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # This code is based on https://github.com/fohte/rubocop-daemon.
5
+ #
6
+ # Copyright (c) 2018 Hayato Kawai
7
+ #
8
+ # The MIT License (MIT)
9
+ #
10
+ # https://github.com/fohte/rubocop-daemon/blob/master/LICENSE.txt
11
+ #
12
+ module RuboCop
13
+ module Server
14
+ module ClientCommand
15
+ # This class is a client command to show server process status.
16
+ # @api private
17
+ class Status < Base
18
+ def run
19
+ if Server.running?
20
+ puts "RuboCop server (#{Cache.pid_path.read}) is running."
21
+ else
22
+ puts 'RuboCop server is not running.'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # This code is based on https://github.com/fohte/rubocop-daemon.
5
+ #
6
+ # Copyright (c) 2018 Hayato Kawai
7
+ #
8
+ # The MIT License (MIT)
9
+ #
10
+ # https://github.com/fohte/rubocop-daemon/blob/master/LICENSE.txt
11
+ #
12
+ module RuboCop
13
+ module Server
14
+ module ClientCommand
15
+ # This class is a client command to stop server process.
16
+ # @api private
17
+ class Stop < Base
18
+ def run
19
+ return unless check_running_server
20
+
21
+ pid = fork do
22
+ send_request(command: 'stop')
23
+ Server.wait_for_running_status!(false)
24
+ end
25
+
26
+ Process.waitpid(pid)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end