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