helm/bin/wrag

119 lines
2.7 KiB
Plaintext
Raw Normal View History

2014-12-12 12:46:29 -08:00
#!/usr/bin/env ruby
2014-12-12 13:19:47 -08:00
# Wrag stands for writeable-ag; a hacky script that allows you to search with ag
2014-12-12 12:46:29 -08:00
# <https://github.com/ggreer/the_silver_searcher> and edit the results in
# $EDITOR. The changes are propagated to all listed files, if any.
#
# Usage: wrag [pattern]
#
# TODO Also rename changed filenames
# TODO Assemble sed string to make this atomic
require 'optparse'
require 'tempfile'
require 'shellwords'
def usage
puts "Usage: #{File.basename($0)} [-f|--rename-files] [-b|--backup] search_string"
puts "\nAny of ag's flags will work with wrag"
exit
end
usage if ARGV.empty?
2014-12-12 13:19:47 -08:00
## Parsing options ################################
2014-12-12 12:46:29 -08:00
options = {:rename => false,
:backup => false}
opt = OptionParser.new
opt.on('-f', '--rename-files') { options[:rename] = true }
opt.on('-b', '--backup') { options[:backup] = true }
2014-12-12 13:19:47 -08:00
begin
opt.parse
# Get rid of misc args to pass the rest to ag
args = ARGV - ["-f", "--rename-files", "-b", "--backup"]
rescue OptionParser::InvalidOption => e
puts "Invalid option: #{e}"
usage
end
2014-12-12 12:46:29 -08:00
2014-12-12 13:19:47 -08:00
## Perform search #################################
t = Tempfile.new('.ag-results')
out = system("ag #{args.join(' ')} --group --nocolor > #{t.path}")
puts "No matches" if out === false
if out == nil
2014-12-12 12:46:29 -08:00
t.unlink
2014-12-12 13:19:47 -08:00
exit 2
2014-12-12 12:46:29 -08:00
end
old_lines = File.open(t.path).readlines
system("#{ENV['EDITOR']} #{t.path}")
lines = File.open(t.path).readlines
t.unlink
if old_lines.length != lines.length
puts "There was a line-number mismatch. Abort! Abort! Abort!"
2014-12-12 13:19:47 -08:00
exit 3
2014-12-12 12:46:29 -08:00
end
2014-12-12 13:19:47 -08:00
## Parse edited output ############################
2014-12-12 12:46:29 -08:00
i = nil
current_filename = nil
lines.each_with_index do |line, i|
line.strip!
next if line.empty?
if line =~ /^([0-9]+):(.+)/ # make sure it isn't actually a file
filename = current_filename
lineno = $1
content = $2
elsif line =~ /^(.+):([0-9]+):(.+)/
if current_file
puts "Mixing and matching groups and non-groups! Aborting."
exit 3
end
filename = $1
lineno = $2
content = $3
elsif File.exists?(line)
if File.readable?(line)
current_filename = line
else
puts "#{line} isn't readable. Skipping."
end
next
else
puts "Malformed line: #{line}"
2014-12-12 13:19:47 -08:00
exit 3
2014-12-12 12:46:29 -08:00
end
next if line.eql?(old_lines[i].strip)
filename = Shellwords.escape(filename)
lineno = Shellwords.escape(lineno)
content = content.gsub("'", "\\'")
puts "#{filename}:#{lineno}:#{content}"
if system("sed -i .bak '#{lineno}c\\
#{content}
' #{filename}")
puts "Replaced: line #{lineno} in #{filename}"
else
puts "Failed to replace line #{lineno} in #{filename}"
end
unless options[:backup]
system("rm -f #{filename}.bak") if File.exists?("#{filename}.bak")
end
end
if i === nil
puts "Didn't do nuthin'"
else
puts "Done!"
end