Skip to content

Instantly share code, notes, and snippets.

@brandonweiss
Last active September 20, 2018 12:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save brandonweiss/976cee89e9fb8a5b01b4b61aebe8f6fa to your computer and use it in GitHub Desktop.
Save brandonweiss/976cee89e9fb8a5b01b4b61aebe8f6fa to your computer and use it in GitHub Desktop.
RuboCop hack for inline-disabling cops
#!/usr/bin/env ruby
def with_captured_stdout
old_stdout = $stdout
$stdout = StringIO.new
yield
$stdout.string
ensure
$stdout = old_stdout
end
def auto_inline_disable(args)
args = args - ["--auto-inline-disable"]
args << "--format=j"
json = with_captured_stdout do
cli = RuboCop::CLI.new
cli.run(args)
end
results = JSON.parse(json)
files = results["files"].select do |file|
file["offenses"].any?
end
changes = files.each_with_object({}) do |file, hash|
path = file["path"]
hash[path] = file["offenses"].flat_map do |offense|
cop_name = offense["cop_name"]
starting_line_number = offense["location"]["line"]
column = offense["location"]["column"]
length = offense["location"]["length"]
ending_line_number = SaneFile.new(path).range(
line_number: starting_line_number,
column: column,
length: length,
)
[{
line_number: starting_line_number,
instruction: :disable,
cop_name: cop_name,
}, {
line_number: ending_line_number + 1,
instruction: :enable,
cop_name: cop_name,
}]
end
end
changes.each_pair do |path, line_changes|
file = SaneFile.new(path)
line_changes.sort_by { |change| change[:line_number] }.reverse.each do |line_change|
instruction = line_change[:instruction]
line_number = line_change[:line_number]
wrapping_line_number = if instruction == :disable
line_number - 1
elsif instruction == :enable
line_number + 1
end
wrapping_line = file.line(number: wrapping_line_number)
cops = if wrapping_line.include?("# rubocop:")
wrapping_line[/# rubocop:(?:disable|enable) (.*)/, 1].split(", ")
else
[]
end
cops << line_change[:cop_name]
comment = "# rubocop:#{instruction} #{cops.join(', ')}"
if instruction == :disable
file.insert_before_line(number: line_number, content: comment, indent: true)
file.delete_line(number: wrapping_line_number) if wrapping_line.include?("# rubocop:")
elsif instruction == :enable
file.delete_line(number: wrapping_line_number) if wrapping_line.include?("# rubocop:")
file.insert_after_line(number: line_number, content: comment, indent: true)
end
end
file.save
end
exit(0)
end
if ARGV.include?("--auto-inline-disable")
auto_inline_disable(args)
else
RuboCop::CLI.new.run(args)
end
class SaneFile
def initialize(path)
@path = path
end
def range(line_number:, column: nil, length:, buffer: "")
current_line = line(number: line_number)
if buffer.empty?
current_line = current_line[column - 1..-1]
end
if current_line
buffer << current_line
end
if buffer.length >= length
line_number - 1
else
range(
line_number: line_number + 1,
length: length,
buffer: buffer + "\n",
)
end
end
def insert_before_line(number:, content:, indent: false)
indentation = indent ? indentation_of_line(number: number) : 0
insert(index: number - 1, content: content, indentation: indentation)
end
def insert_after_line(number:, content:, indent: false)
indentation = indent ? indentation_of_line(number: number) : 0
insert(index: number, content: content, indentation: indentation)
end
def delete_line(number:)
lines.delete_at(number - 1)
end
def save
File.write(@path, lines.join("\n") + "\n")
end
def line(number:)
lines[number - 1]
end
private def indentation_of_line(number:)
line(number: number - 1)[/\A */].size
end
private def insert(index:, content:, indentation:)
lines.insert(index, (" " * indentation) + content)
end
private def lines
@lines ||= file.split("\n")
end
private def file
@file ||= File.read(@path)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment