123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- #!/usr/bin/env ruby
- require 'optparse'
-
- args = Array.new
- rg_args = Array.new
- add_to_rg_args = false
-
- ARGV.each do |arg|
- if arg == "--"
- add_to_rg_args = true
- else
- if add_to_rg_args
- rg_args << arg
- else
- args << arg
- end
- end
- end
-
- options = {}
- OptionParser.new do |opts|
- opts.banner = "Usage: grep-around.rb [options] -- [rg options]\n" \
- "All options after -- will be passed through to ripgrep"
- options[:stdin] = false
- options[:around] = 1
-
- opts.on("--stdin", "Read input from standard in") do |stdin|
- options[:stdin] = stdin
- end
-
- opts.on("--around [LINES]", "Lines around") do |around|
- options[:around] = around.to_i
- end
-
- opts.on("--up [LINES]", "Lines up") do |up|
- options[:up] = up.to_i
- end
-
- opts.on("--down [LINES]", "Lines down") do |down|
- options[:down] = down.to_i
- end
- end.parse!(args)
-
- %i[up down].each { |n| options[n] = options[:around] } if options[:around]
-
- GREEN = "\u001b[32m"
- RED = "\u001b[31m"
- RESET = "\u001b[0m"
-
- class GrepAround
- def initialize(options, rg_args)
- @options = options
- @rg_args = rg_args
- @hit = nil
- @current_line_number = nil
- @file_cache = Hash.new
- end
-
- def run
- results = run_ripgrep
- blocks = Array.new
- results.split("\n").each do |line|
- @hit = if line.match(/\A\d+:/)
- {
- :file_name => :stdin,
- :line_number => line.match(/\A\d+/)[0].to_i
- }
- else
- parse_metadata(line)
- end
- blocks << get_block
- end
- puts blocks.join("\n\n")
- end
-
- def run_ripgrep
- if @options[:stdin]
- contents = STDIN.read
- @file_cache[:stdin] = contents.split("\n")
- `echo '#{contents}' | rg -n #{@rg_args.join(' ')}`
- else
- `rg -n #{@rg_args.join(' ')}`
- end
- end
-
- def get_contents(key)
- if @file_cache.has_key?(key)
- @file_cache[key]
- else
- contents = File.readlines(@hit[:file_name])
- @file_cache[@hit[:file_name]] = contents
- contents
- end
- end
-
- def get_lines(contents, line_number)
- starting_line_number = line_number - @options[:up] - 1
- ending_line_number = line_number + @options[:down] - 1
- contents[starting_line_number..ending_line_number]
- end
-
- def parse_metadata(source)
- metadata = source.split(': ').first.split(':')
- file = metadata[0]
- line = metadata[1]
- {
- :file_name => file,
- :line_number => line.to_i,
- }
- end
-
- def get_block
- contents = get_contents(@hit[:file_name])
- lines = get_lines(contents, @hit[:line_number])
-
- block = Array.new
- block << block_header
- @current_line_number = @hit[:line_number] - @options[:up]
- lines.each do |line|
- block << build_output(line, @current_line_number)
- @current_line_number += 1
- end
-
- block.join("\n")
- end
-
- def block_header
- "#{RED}#{@hit[:file_name]}:#{RESET}"
- end
-
- def build_output(line, line_number)
- output = String.new
- output << line_number.to_s
- output << ' '
- output << GREEN if is_target_line
- output << line.gsub("\n", "")
- output << RESET if is_target_line
-
- output
- end
-
- def is_target_line
- @current_line_number == @hit[:line_number]
- end
- end
-
- GrepAround.new(options, rg_args).run
|