-
Notifications
You must be signed in to change notification settings - Fork 0
/
highlight
executable file
·172 lines (142 loc) · 4.19 KB
/
highlight
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#!/usr/bin/env ruby
# Author: Fraser Hanson
# Date: 2010-09
# Purpose: Highlight strings in the input.
# Grep color highlighting without the grepping.
# FIXME:
# -error handling: no such file, no input given, no targets specified,
# only one non-flag arg but no input, not enough colors, etc.
# Options (such as i for case-insensitivity) may be passed to the s///
# operator by specifying them as flags on the command line (eg. "-i" =>
# s///i).
############################################################
require 'pp'
require 'stringio'
usage =<<EOF
usage:
#{File.basename($0)} [options] pattern [ pattern ... ] [filename]
Purpose: highlights in a bright color all occurrences of the string
"pattern" inside "text", which is usually coming from a pipe.
If no input is piped in, then the last argument is assumed to be a
filename.
Options:
-i case insensitive
-s use same colour for all patterns
--colour=n specify starting colour (as an integer)
-w highlight every word
-f <file> take patterns from lines of a file
EOF
# Special escaped strings to surround any text with to highlight it.
$Normal = "\033[0m";
$Colorfmt = "\033[1;%sm"
$color = 31
$colors = Hash.new
$same_color = false
############################################################
# highlight with case sensitive match
def highlight_line(string)
$targets.each do |t|
# match case insensitive if asked nicely
if $options.match(/i/)
re = Regexp.compile(t, true)
else
re = Regexp.compile(t, false)
end
# surround each match with terminal color control chars
string.gsub!(re) do |s|
"%s%s%s" % [get_color(t), $&, $Normal]
end
end
string
end
# given a target string, return the assigned color for that target
def get_color(string)
if $colors[string].nil?
$colors[string] = $Colorfmt % $color
unless $same_color
$color += 1
end
end
$colors[string]
end
############################################################
# Just bail out if output is not to a tty.
exec "cat" unless $stdout.tty?
# Dump the command line flags into a string, targets into an array
$options = String.new
$targets = Array.new
parsing = :options
ARGV.each do |a|
if "-" != a[0].chr
parsing = :targets
end
if :options == parsing
$options << a
else
$targets << a
end
end
## # DEBUG output
## puts "Options: %s" % $options
## puts "Targets: [%s]" % $targets.join("] [")
# Print usage msg and die sometimes
if $options.match(/-h\>/) or 0 == ARGV.size
puts usage
exit
elsif $options.match(/--colou?r=(\d+)/)
$color = $1.to_i
elsif $options.match(/-s\b/)
$same_color = true
elsif $options.match(/-w\b/)
ARGV.delete("-w")
$every_word = true
elsif $options.match(/-f\s+([\S]+)/)
ARGV.delete("-f")
$pattern_file = $1
end
text = nil
if $every_word == true
text = ARGF.read
$targets = text.split.sort.uniq
elsif ! $pattern_file.nil?
$targets = IO.readlines($pattern_file)
end
# Call get_color for each target in order.
# This is needed so that the assigned colour will depend on the order
# the targets are specified in on the cmd line, rather than the order
# they appear in the output. This is convenient because the colours
# previously in use stay the same if you re-run highlight on the same input
# with added arguments.
$targets.each do |target|
get_color(target)
end
# FIXME: targets that are subsets of other targets mess this up.
# Even if the longer target is already highlighted, the smaller target will be
# highlighted too, leading to nested ANSI escape sequences, which does not
# work.
# Examples:
# Compare this:
# echo -e 'dog o\nwood dowry wooden' | highlight.rb dog wood dowry
# With this:
# echo -e 'dog o\nwood dowry wooden' | highlight.rb dog wood dowry o
# Get input lines from $stdin if possible.
# Otherwise, assume the last argument is a filename to process.
lines = Array.new
if $every_word
# Already read STDIN
io = StringIO.new(text)
else
if $stdin.tty?
io = File.open($targets.pop)
else
io = $stdin
end
end
begin
while (line = io.readline)
# Highlight and print each line
line = highlight_line(line)
puts line
end
rescue EOFError, Interrupt, Errno::EPIPE
end