Class: Rubocop::CLI

Inherits:
Object
  • Object
show all
Defined in:
lib/rubocop/cli.rb

Overview

The CLI is a class responsible of handling all the command line interface logic.

Constant Summary collapse

DEFAULT_FORMATTER =
'progress'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCLI

Returns a new instance of CLI.



18
19
20
21
22
23
# File 'lib/rubocop/cli.rb', line 18

def initialize
  @cops = Cop::Cop.all
  @errors = []
  @options = {}
  @config_store = ConfigStore.new
end

Instance Attribute Details

#optionsObject

Returns the value of attribute options.



14
15
16
# File 'lib/rubocop/cli.rb', line 14

def options
  @options
end

#wants_to_quitObject Also known as: wants_to_quit?

If set true while running, RuboCop will abort processing and exit gracefully.



13
14
15
# File 'lib/rubocop/cli.rb', line 13

def wants_to_quit
  @wants_to_quit
end

Instance Method Details

#autocorrect(buffer, cops) ⇒ Object



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/rubocop/cli.rb', line 273

def autocorrect(buffer, cops)
  return unless @options[:autocorrect]

  corrections = cops.reduce([]) do |array, cop|
    array.concat(cop.corrections)
    array
  end

  corrector = Cop::Corrector.new(buffer, corrections)
  new_source = corrector.rewrite

  unless new_source == buffer.source
    filename = buffer.instance_variable_get(:@name)
    File.open(filename, 'w') { |f| f.write(new_source) }
  end
end

#convert_deprecated_options!(args) ⇒ Object

rubocop:enable MethodLength



241
242
243
244
245
246
247
248
249
250
251
# File 'lib/rubocop/cli.rb', line 241

def convert_deprecated_options!(args)
  args.map! do |arg|
    case arg
    when '-e', '--emacs'
      deprecate("#{arg} option", '--format emacs', '1.0.0')
      %w(--format emacs)
    else
      arg
    end
  end.flatten!
end

#display_error_summary(errors) ⇒ Object



262
263
264
265
266
267
268
269
270
271
# File 'lib/rubocop/cli.rb', line 262

def display_error_summary(errors)
  return if errors.empty?
  plural = errors.count > 1 ? 's' : ''
  puts "\n#{errors.count} error#{plural} occurred:".color(:red)
  errors.each { |error| puts error }
  puts 'Errors are usually caused by RuboCop bugs.'
  puts 'Please, report your problems to RuboCop\'s issue tracker.'
  puts 'Mention the following information in the issue report:'
  puts Rubocop::Version.version(true)
end

#handle_error(e, message) ⇒ Object



151
152
153
154
155
156
157
158
159
# File 'lib/rubocop/cli.rb', line 151

def handle_error(e, message)
  @errors << message
  warn message
  if @options[:debug]
    puts e.message, e.backtrace
  else
    warn 'To see the complete backtrace run rubocop -d.'
  end
end

#inspect_file(file) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/rubocop/cli.rb', line 86

def inspect_file(file)
  begin
    processed_source = SourceParser.parse_file(file)
  rescue Encoding::UndefinedConversionError, ArgumentError => e
    handle_error(e, "An error occurred while parsing #{file}.".color(:red))
    return []
  end

  # If we got any syntax errors, return only the syntax offences.
  # Parser may return nil for AST even though there are no syntax errors.
  # e.g. sources which contain only comments
  unless processed_source.diagnostics.empty?
    return processed_source.diagnostics.map do |diagnostic|
             Cop::Offence.from_diagnostic(diagnostic)
           end
  end

  config = @config_store.for(file)
  if @options[:auto_gen_config] && config.contains_auto_generated_config
    fail "Remove #{Config::AUTO_GENERATED_FILE} from the current " +
      'configuration before generating it again.'
  end
  set_config_for_all_cops(config)

  cops = []
  @cops.each do |cop_class|
    cop_name = cop_class.cop_name
    next unless config.cop_enabled?(cop_name)
    next unless !@options[:only] || @options[:only] == cop_name
    cop = setup_cop(cop_class, processed_source.disabled_lines_for_cops)
    cops << cop
  end
  commissioner = Cop::Commissioner.new(cops)
  offences = commissioner.investigate(processed_source)
  process_commissioner_errors(file, commissioner.errors)
  autocorrect(processed_source.buffer, cops)
  offences.sort
end

#parse_options(args) ⇒ Object

rubocop:disable MethodLength



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/rubocop/cli.rb', line 162

def parse_options(args)
  convert_deprecated_options!(args)

  OptionParser.new do |opts|
    opts.banner = 'Usage: rubocop [options] [file1, file2, ...]'

    opts.on('-d', '--debug', 'Display debug info.') do |d|
      @options[:debug] = d
    end
    opts.on('-c', '--config FILE', 'Specify configuration file.') do |f|
      @options[:config] = f
      @config_store.set_options_config(@options[:config])
    end
    opts.on('--only COP', 'Run just one cop.') do |s|
      @options[:only] = s
      validate_only_option
    end
    opts.on('--auto-gen-config',
            'Generate a configuration file acting as a',
            'TODO list.') do
      @options[:auto_gen_config] = true
      @options[:formatters] = [
        [DEFAULT_FORMATTER],
        [Formatter::DisabledConfigFormatter, Config::AUTO_GENERATED_FILE]
      ]
      validate_auto_gen_config_option(args)
    end
    opts.on('-f', '--format FORMATTER',
            'Choose an output formatter. This option',
            'can be specified multiple times to enable',
            'multiple formatters at the same time.',
            '  [p]rogress (default)',
            '  [s]imple',
            '  [c]lang',
            '  [e]macs',
            '  [j]son',
            '  [f]iles',
            '  custom formatter class name') do |key|
      @options[:formatters] ||= []
      @options[:formatters] << [key]
    end
    opts.on('-o', '--out FILE',
            'Write output to a file instead of STDOUT.',
            'This option applies to the previously',
            'specified --format, or the default format',
            'if no format is specified.') do |path|
      @options[:formatters] ||= [[DEFAULT_FORMATTER]]
      @options[:formatters].last << path
    end
    opts.on('-r', '--require FILE', 'Require Ruby file.') do |f|
      require f
    end
    opts.on('-R', '--rails', 'Run extra Rails cops.') do |r|
      @options[:rails] = r
    end
    opts.on('-l', '--lint', 'Run only lint cops.') do |l|
      @options[:lint] = l
    end
    opts.on('-a', '--auto-correct', 'Auto-correct offences.') do |a|
      @options[:autocorrect] = a
    end
    opts.on('-s', '--silent', 'Silence summary.') do |s|
      @options[:silent] = s
    end
    opts.on('-n', '--no-color', 'Disable color output.') do |s|
      Sickill::Rainbow.enabled = false
    end
    opts.on('-v', '--version', 'Display version.') do
      puts Rubocop::Version.version(false)
      exit(0)
    end
    opts.on('-V', '--verbose-version', 'Display verbose version.') do
      puts Rubocop::Version.version(true)
      exit(0)
    end
  end.parse!(args)
end

#process_commissioner_errors(file, file_errors) ⇒ Object



125
126
127
128
129
130
131
132
133
# File 'lib/rubocop/cli.rb', line 125

def process_commissioner_errors(file, file_errors)
  file_errors.each do |cop, errors|
    errors.each do |e|
      handle_error(e,
                   "An error occurred while #{cop.name}".color(:red) +
                   " cop was inspecting #{file}.".color(:red))
    end
  end
end

#run(args = ARGV) ⇒ Fixnum

Entry point for the application logic. Here we do the command line arguments processing and inspect the target files

Returns:

  • (Fixnum)

    UNIX exit code



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/rubocop/cli.rb', line 29

def run(args = ARGV)
  trap_interrupt

  parse_options(args)

  Config.debug = @options[:debug]

  # filter out Rails cops unless requested
  @cops.reject!(&:rails?) unless @options[:rails]

  # filter out style cops when --lint is passed
  @cops.select!(&:lint?) if @options[:lint]

  target_files = target_finder.find(args)
  target_files.each(&:freeze).freeze
  inspected_files = []
  any_failed = false

  formatter_set.started(target_files)

  target_files.each do |file|
    break if wants_to_quit?

    puts "Scanning #{file}" if @options[:debug]
    formatter_set.file_started(file, {})

    offences = inspect_file(file)

    any_failed = true unless offences.empty?
    inspected_files << file
    formatter_set.file_finished(file, offences.freeze)
  end

  formatter_set.finished(inspected_files.freeze)
  formatter_set.close_output_files

  display_error_summary(@errors) unless @options[:silent]

  !any_failed && !wants_to_quit ? 0 : 1
rescue => e
  $stderr.puts e.message
  return 1
end

#set_config_for_all_cops(config) ⇒ Object



135
136
137
138
139
# File 'lib/rubocop/cli.rb', line 135

def set_config_for_all_cops(config)
  @cops.each do |cop_class|
    cop_class.config = config.for_cop(cop_class.cop_name)
  end
end

#setup_cop(cop_class, disabled_lines_for_cops = nil) ⇒ Object



141
142
143
144
145
146
147
148
149
# File 'lib/rubocop/cli.rb', line 141

def setup_cop(cop_class, disabled_lines_for_cops = nil)
  cop = cop_class.new
  cop.debug = @options[:debug]
  cop.autocorrect = @options[:autocorrect]
  if disabled_lines_for_cops
    cop.disabled_lines = disabled_lines_for_cops[cop_class.cop_name]
  end
  cop
end

#trap_interruptObject



253
254
255
256
257
258
259
260
# File 'lib/rubocop/cli.rb', line 253

def trap_interrupt
  Signal.trap('INT') do
    exit!(1) if wants_to_quit?
    self.wants_to_quit = true
    $stderr.puts
    $stderr.puts 'Exiting... Interrupt again to exit immediately.'
  end
end

#validate_auto_gen_config_option(args) ⇒ Object



79
80
81
82
83
84
# File 'lib/rubocop/cli.rb', line 79

def validate_auto_gen_config_option(args)
  if args.any?
    fail ArgumentError,
         '--auto-gen-config can not be combined with any other arguments.'
  end
end

#validate_only_optionObject



73
74
75
76
77
# File 'lib/rubocop/cli.rb', line 73

def validate_only_option
  if @cops.none? { |c| c.cop_name == @options[:only] }
    fail ArgumentError, "Unrecognized cop name: #{@options[:only]}."
  end
end