A CLI tool that shows the context of grep results
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

grep-around.rb 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #!/usr/bin/env ruby
  2. require 'optparse'
  3. args = Array.new
  4. rg_args = Array.new
  5. add_to_rg_args = false
  6. ARGV.each do |arg|
  7. if arg == "--"
  8. add_to_rg_args = true
  9. else
  10. if add_to_rg_args
  11. rg_args << arg
  12. else
  13. args << arg
  14. end
  15. end
  16. end
  17. options = {}
  18. OptionParser.new do |opts|
  19. opts.banner = "Usage: grep-around.rb [options] -- [rg options]\n" \
  20. "All options after -- will be passed through to ripgrep"
  21. options[:stdin] = false
  22. options[:around] = 1
  23. opts.on("--stdin", "Read input from standard in") do |stdin|
  24. options[:stdin] = stdin
  25. end
  26. opts.on("--around [LINES]", "Lines around") do |around|
  27. options[:around] = around.to_i
  28. end
  29. opts.on("--up [LINES]", "Lines up") do |up|
  30. options[:up] = up.to_i
  31. end
  32. opts.on("--down [LINES]", "Lines down") do |down|
  33. options[:down] = down.to_i
  34. end
  35. end.parse!(args)
  36. %i[up down].each { |n| options[n] = options[:around] } if options[:around]
  37. GREEN = "\u001b[32m"
  38. RED = "\u001b[31m"
  39. RESET = "\u001b[0m"
  40. class GrepAround
  41. def initialize(options, rg_args)
  42. @options = options
  43. @rg_args = rg_args
  44. @hit = nil
  45. @current_line_number = nil
  46. @file_cache = Hash.new
  47. end
  48. def run
  49. results = run_ripgrep
  50. blocks = Array.new
  51. results.split("\n").each do |line|
  52. @hit = if line.match(/\A\d+:/)
  53. {
  54. :file_name => :stdin,
  55. :line_number => line.match(/\A\d+/)[0].to_i
  56. }
  57. else
  58. parse_metadata(line)
  59. end
  60. blocks << get_block
  61. end
  62. puts blocks.join("\n\n")
  63. end
  64. def run_ripgrep
  65. if @options[:stdin]
  66. contents = STDIN.read
  67. @file_cache[:stdin] = contents.split("\n")
  68. `echo '#{contents}' | rg -n #{@rg_args.join(' ')}`
  69. else
  70. `rg -n #{@rg_args.join(' ')}`
  71. end
  72. end
  73. def get_contents(key)
  74. if @file_cache.has_key?(key)
  75. @file_cache[key]
  76. else
  77. contents = File.readlines(@hit[:file_name])
  78. @file_cache[@hit[:file_name]] = contents
  79. contents
  80. end
  81. end
  82. def get_lines(contents, line_number)
  83. starting_line_number = line_number - @options[:up] - 1
  84. ending_line_number = line_number + @options[:down] - 1
  85. contents[starting_line_number..ending_line_number]
  86. end
  87. def parse_metadata(source)
  88. metadata = source.split(': ').first.split(':')
  89. file = metadata[0]
  90. line = metadata[1]
  91. {
  92. :file_name => file,
  93. :line_number => line.to_i,
  94. }
  95. end
  96. def get_block
  97. contents = get_contents(@hit[:file_name])
  98. lines = get_lines(contents, @hit[:line_number])
  99. block = Array.new
  100. block << block_header
  101. @current_line_number = @hit[:line_number] - @options[:up]
  102. lines.each do |line|
  103. block << build_output(line, @current_line_number)
  104. @current_line_number += 1
  105. end
  106. block.join("\n")
  107. end
  108. def block_header
  109. "#{RED}#{@hit[:file_name]}:#{RESET}"
  110. end
  111. def build_output(line, line_number)
  112. output = String.new
  113. output << line_number.to_s
  114. output << ' '
  115. output << GREEN if is_target_line
  116. output << line.gsub("\n", "")
  117. output << RESET if is_target_line
  118. output
  119. end
  120. def is_target_line
  121. @current_line_number == @hit[:line_number]
  122. end
  123. end
  124. GrepAround.new(options, rg_args).run