Skip to content

Commit

Permalink
Merge pull request #8778 from dvandersluis/regenerate-todo
Browse files Browse the repository at this point in the history
Add `--regenerate-todo` command line option to rebuild .rubocop_todo.yml using its previous options
  • Loading branch information
bbatsov committed Sep 25, 2020
2 parents fb27511 + f9a30fe commit ebe79a8
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

### New features

* [#8778](https://github.com/rubocop-hq/rubocop/pull/8778): Add command line option `--regenerate-todo`. ([@dvandersluis][])
* [#8790](https://github.com/rubocop-hq/rubocop/pull/8790): Add `AllowedMethods` option to `Style/OptionalBooleanParameter` cop. ([@fatkodima][])
* [#8738](https://github.com/rubocop-hq/rubocop/issues/8738): Add autocorrection to `Style/DateTime`. ([@dvandersluis][])

Expand Down
3 changes: 2 additions & 1 deletion docs/modules/ROOT/pages/configuration.adoc
Expand Up @@ -586,7 +586,8 @@ behavior of a cop instead of disabling it completely.

Then you can start removing the entries in the generated
`.rubocop_todo.yml` file one by one as you work through all the offenses
in the code.
in the code. You can also regenerate your `.rubocop_todo.yml` using
the same options by running `rubocop --regenerate-todo`.

Another way of silencing offense reports, aside from configuration, is
through source code comments. These can be added manually or
Expand Down
14 changes: 10 additions & 4 deletions docs/modules/ROOT/pages/usage/basic_usage.adoc
Expand Up @@ -199,11 +199,14 @@ $ rubocop -h
| `-L/--list-target-files`
| List all files RuboCop will inspect.

| `--no-auto-gen-timestamp`
| Don't include the date and time when --auto-gen-config was run in the config file it generates
| `--[no-]auto-gen-only-exclude`
| Generate only `Exclude` parameters and not `Max` when running `--auto-gen-config`, except if the number of files with offenses is bigger than `exclude-limit`. Default is false

| `--no-offense-counts`
| Don't show offense counts in config file generated by --auto-gen-config
| `--[no-]auto-gen-timestamp`
| Include the date and time when `--auto-gen-config` was run in the config file it generates. Default is true.

| `--[no-]offense-counts`
| Show offense counts in config file generated by `--auto-gen-config`. Default is true.

| `--only`
| Run only the specified cop(s) and/or cops in the specified departments.
Expand All @@ -217,6 +220,9 @@ $ rubocop -h
| `-r/--require`
| Require Ruby file (see xref:extensions.adoc#loading-extensions[Loading Extensions]).

| `--regenerate-todo`
| Regenerate the TODO list using the same options as the last time it was generated with `--auto-gen-config` (generation options can be overridden).

| `--safe`
| Run only safe cops.

Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -629,6 +629,7 @@
require_relative 'rubocop/cli/command/init_dotfile'
require_relative 'rubocop/cli/command/show_cops'
require_relative 'rubocop/cli/command/version'
require_relative 'rubocop/config_regeneration'
require_relative 'rubocop/options'
require_relative 'rubocop/remote_config'
require_relative 'rubocop/target_ruby'
Expand Down
33 changes: 33 additions & 0 deletions lib/rubocop/config_regeneration.rb
@@ -0,0 +1,33 @@
# frozen_string_literal: true

module RuboCop
# This class handles collecting the options for regenerating a TODO file.
# @api private
class ConfigRegeneration
AUTO_GENERATED_FILE = RuboCop::CLI::Command::AutoGenerateConfig::AUTO_GENERATED_FILE
COMMAND_REGEX = /(?<=`rubocop )(.*?)(?=`)/.freeze
DEFAULT_OPTIONS = { auto_gen_config: true }.freeze

# Get options from the comment in the TODO file, and parse them as options
def options
# If there's no existing TODO file, generate one
return DEFAULT_OPTIONS unless todo_exists?

match = generation_command.match(COMMAND_REGEX)
return DEFAULT_OPTIONS unless match

options = match[1].split(' ')
Options.new.parse(options).first
end

private

def todo_exists?
File.exist?(AUTO_GENERATED_FILE) && !File.empty?(AUTO_GENERATED_FILE)
end

def generation_command
File.foreach(AUTO_GENERATED_FILE).take(2).last
end
end
end
17 changes: 12 additions & 5 deletions lib/rubocop/formatter/disabled_config_formatter.rb
Expand Up @@ -32,7 +32,6 @@ def file_started(_file, _file_info)
@exclude_limit_option = @options[:exclude_limit]
@exclude_limit = Integer(@exclude_limit_option ||
RuboCop::Options::DEFAULT_MAXIMUM_EXCLUSION_ITEMS)
@show_offense_counts = !@options[:no_offense_counts]
end

def file_finished(file, offenses)
Expand All @@ -56,6 +55,14 @@ def finished(_inspected_files)

private

def show_timestamp?
@options.fetch(:auto_gen_timestamp, true)
end

def show_offense_counts?
@options.fetch(:offense_counts, true)
end

def command
command = 'rubocop --auto-gen-config'

Expand All @@ -66,15 +73,15 @@ def command
format(' --exclude-limit %<limit>d',
limit: Integer(@exclude_limit_option))
end
command += ' --no-offense-counts' if @options[:no_offense_counts]
command += ' --no-offense-counts' unless show_offense_counts?

command += ' --no-auto-gen-timestamp' if @options[:no_auto_gen_timestamp]
command += ' --no-auto-gen-timestamp' unless show_timestamp?

command
end

def timestamp
@options[:no_auto_gen_timestamp] ? '' : "on #{Time.now.utc} "
show_timestamp? ? "on #{Time.now.utc} " : ''
end

def output_offenses
Expand Down Expand Up @@ -112,7 +119,7 @@ def set_max(cfg, cop_name)
end

def output_cop_comments(output_buffer, cfg, cop_name, offense_count)
output_buffer.puts "# Offense count: #{offense_count}" if @show_offense_counts
output_buffer.puts "# Offense count: #{offense_count}" if show_offense_counts?

cop_class = Cop::Registry.global.find_by_cop_name(cop_name)
output_buffer.puts '# Cop supports --auto-correct.' if cop_class&.support_autocorrect?
Expand Down
34 changes: 18 additions & 16 deletions lib/rubocop/options.rb
Expand Up @@ -114,20 +114,19 @@ def add_configuration_options(opts)
def add_auto_gen_options(opts)
option(opts, '--auto-gen-config')

option(opts, '--regenerate-todo') do
@options.replace(ConfigRegeneration.new.options.merge(@options))
end

option(opts, '--exclude-limit COUNT') do
@validator.validate_exclude_limit_option
end

option(opts, '--disable-uncorrectable')

option(opts, '--no-offense-counts') do
@options[:no_offense_counts] = true
end

option(opts, '--auto-gen-only-exclude')
option(opts, '--no-auto-gen-timestamp') do
@options[:no_auto_gen_timestamp] = true
end
option(opts, '--[no-]offense-counts')
option(opts, '--[no-]auto-gen-only-exclude')
option(opts, '--[no-]auto-gen-timestamp')

option(opts, '--init')
end
Expand Down Expand Up @@ -318,7 +317,7 @@ def validate_auto_gen_config

message = '--%<flag>s can only be used together with --auto-gen-config.'

%i[exclude_limit no_offense_counts no_auto_gen_timestamp
%i[exclude_limit offense_counts auto_gen_timestamp
auto_gen_only_exclude].each do |option|
if @options.key?(option)
raise OptionArgumentError,
Expand Down Expand Up @@ -423,17 +422,20 @@ module OptionsHelp
config: 'Specify configuration file.',
auto_gen_config: ['Generate a configuration file acting as a',
'TODO list.'],
no_offense_counts: ['Do not include offense counts in configuration',
'file generated by --auto-gen-config.'],
no_auto_gen_timestamp:
['Do not include the date and time when',
'the --auto-gen-config was run in the file it',
'generates.'],
regenerate_todo: ['Regenerate the TODO configuration file using',
'the last configuration. If there is no existing',
'TODO file, acts like --auto-gen-config.'],
offense_counts: ['Include offense counts in configuration',
'file generated by --auto-gen-config.',
'Default is true.'],
auto_gen_timestamp:
['Include the date and time when the --auto-gen-config',
'was run in the file it generates. Default is true.'],
auto_gen_only_exclude:
['Generate only Exclude parameters and not Max',
'when running --auto-gen-config, except if the',
'number of files with offenses is bigger than',
'exclude-limit.'],
'exclude-limit. Default is false.'],
exclude_limit: ['Used together with --auto-gen-config to',
'set the limit for how many Exclude',
"properties to generate. Default is #{MAX_EXCL}."],
Expand Down
51 changes: 51 additions & 0 deletions spec/rubocop/config_regeneration_spec.rb
@@ -0,0 +1,51 @@
# frozen_string_literal: true

RSpec.describe RuboCop::ConfigRegeneration, :isolated_environment do
include FileHelper

subject(:config_regeneration) { described_class.new }

describe '#options' do
subject { config_regeneration.options }

context 'when no todo file exists' do
it { is_expected.to eq(auto_gen_config: true) }
end

context 'when there is a blank todo file' do
before { create_file('.rubocop_todo.yml', nil) }

it { is_expected.to eq(auto_gen_config: true) }
end

context 'when the todo file is malformed' do
before { create_file('.rubocop_todo.yml', 'todo') }

it { is_expected.to eq(auto_gen_config: true) }
end

context 'it parses options from the generation comment' do
let(:header) do
<<~HEADER
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 100 --no-offense-counts --no-auto-gen-timestamp`
# on 2020-06-12 17:42:47 UTC using RuboCop version 0.85.1.
HEADER
end

let(:expected_options) do
{
auto_gen_config: true,
auto_gen_only_exclude: true,
exclude_limit: '100',
offense_counts: false,
auto_gen_timestamp: false
}
end

before { create_file('.rubocop_todo.yml', header) }

it { is_expected.to eq(expected_options) }
end
end
end
84 changes: 78 additions & 6 deletions spec/rubocop/options_spec.rb
Expand Up @@ -52,21 +52,24 @@ def abs(path)
files are present in the directory tree.
--auto-gen-config Generate a configuration file acting as a
TODO list.
--regenerate-todo Regenerate the TODO configuration file using
the last configuration. If there is no existing
TODO file, acts like --auto-gen-config.
--exclude-limit COUNT Used together with --auto-gen-config to
set the limit for how many Exclude
properties to generate. Default is 15.
--disable-uncorrectable Used with --auto-correct to annotate any
offenses that do not support autocorrect
with `rubocop:todo` comments.
--no-offense-counts Do not include offense counts in configuration
--[no-]offense-counts Include offense counts in configuration
file generated by --auto-gen-config.
--auto-gen-only-exclude Generate only Exclude parameters and not Max
Default is true.
--[no-]auto-gen-only-exclude Generate only Exclude parameters and not Max
when running --auto-gen-config, except if the
number of files with offenses is bigger than
exclude-limit.
--no-auto-gen-timestamp Do not include the date and time when
the --auto-gen-config was run in the file it
generates.
exclude-limit. Default is false.
--[no-]auto-gen-timestamp Include the date and time when the --auto-gen-config
was run in the file it generates. Default is true.
--init Generate a .rubocop.yml file in the current directory.
-f, --format FORMATTER Choose an output formatter. This option
can be specified multiple times to enable
Expand Down Expand Up @@ -370,6 +373,75 @@ def abs(path)
end
end

describe '--regenerate-todo' do
subject(:parsed_options) { options.parse(command_line_options).first }

let(:config_regeneration) do
instance_double(RuboCop::ConfigRegeneration, options: todo_options)
end
let(:todo_options) { { auto_gen_config: true, exclude_limit: '100', offense_counts: false } }

before do
allow(RuboCop::ConfigRegeneration).to receive(:new).and_return(config_regeneration)
end

context 'when no other options are given' do
let(:command_line_options) { %w[--regenerate-todo] }
let(:expected_options) do
{
auto_gen_config: true,
exclude_limit: '100',
offense_counts: false,
regenerate_todo: true
}
end

it { is_expected.to eq(expected_options) }
end

context 'when todo options are overridden before --regenerate-todo' do
let(:command_line_options) { %w[--exclude-limit 50 --regenerate-todo] }
let(:expected_options) do
{
auto_gen_config: true,
exclude_limit: '50',
offense_counts: false,
regenerate_todo: true
}
end

it { is_expected.to eq(expected_options) }
end

context 'when todo options are overridden after --regenerate-todo' do
let(:command_line_options) { %w[--regenerate-todo --exclude-limit 50] }
let(:expected_options) do
{
auto_gen_config: true,
exclude_limit: '50',
offense_counts: false,
regenerate_todo: true
}
end

it { is_expected.to eq(expected_options) }
end

context 'when disabled options are overridden to be enabled' do
let(:command_line_options) { %w[--regenerate-todo --offense-counts] }
let(:expected_options) do
{
auto_gen_config: true,
exclude_limit: '100',
offense_counts: true,
regenerate_todo: true
}
end

it { is_expected.to eq(expected_options) }
end
end

describe '-s/--stdin' do
before do
$stdin = StringIO.new
Expand Down

0 comments on commit ebe79a8

Please sign in to comment.