ansi 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY +12 -0
- data/LICENSE +789 -0
- data/MANIFEST +36 -0
- data/README +72 -0
- data/demo/logger.rd +31 -0
- data/demo/progressbar.rd +63 -0
- data/lib/ansi.rb +11 -0
- data/lib/ansi/code.rb +229 -0
- data/lib/ansi/logger.rb +209 -0
- data/lib/ansi/progressbar.rb +268 -0
- data/lib/ansi/string.rb +249 -0
- data/lib/ansi/terminal.rb +43 -0
- data/lib/ansi/terminal/curses.rb +27 -0
- data/lib/ansi/terminal/stty.rb +55 -0
- data/lib/ansi/terminal/termios.rb +63 -0
- data/lib/ansi/terminal/win32.rb +107 -0
- data/meta/abstract +3 -0
- data/meta/authors +3 -0
- data/meta/created +1 -0
- data/meta/homepage +1 -0
- data/meta/license +1 -0
- data/meta/package +1 -0
- data/meta/project +1 -0
- data/meta/released +1 -0
- data/meta/repository +1 -0
- data/meta/summary +1 -0
- data/meta/title +1 -0
- data/meta/version +1 -0
- data/test/test_ansicode.rb +20 -0
- data/test/test_progressbar.rb +18 -0
- metadata +93 -0
data/lib/ansi/logger.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
# Ansi::Logger
|
2
|
+
# Copyright (c) 2009 Thomas Sawyer
|
3
|
+
# Copyright (c) 2005 George Moschovitis
|
4
|
+
|
5
|
+
require "logger"
|
6
|
+
require "time"
|
7
|
+
require "ansi/code"
|
8
|
+
|
9
|
+
# = ANSI::Logger
|
10
|
+
#
|
11
|
+
# Extended variation of Ruby's standard Logger library.
|
12
|
+
# Mainly for compatibility purposes (with what?)
|
13
|
+
#
|
14
|
+
# log = ANSI::Logger.new
|
15
|
+
#
|
16
|
+
# log.formatter do |severity, timestamp, progname, msg|
|
17
|
+
# ANSI::Logger::SIMPLE_FORMAT % [severity, msg]
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
#--
|
21
|
+
# TODO: What's all this about then?
|
22
|
+
#
|
23
|
+
# When using debug level logger messages always append 'if $DBG'
|
24
|
+
# at the end. This hack is needed because Ruby does not support
|
25
|
+
# lazy evaluation (lisp macros).
|
26
|
+
#++
|
27
|
+
class ANSI::Logger < Logger
|
28
|
+
|
29
|
+
# Some available logging formats.
|
30
|
+
SIMPLE_FORMAT = "%5s: %s\n"
|
31
|
+
DETAILED_FORMAT = "%s %5s: %s\n"
|
32
|
+
|
33
|
+
#
|
34
|
+
class ::Logger::LogDevice
|
35
|
+
attr_writer :ansicolor
|
36
|
+
|
37
|
+
def ansicolor?
|
38
|
+
@ansicolor.nil? ? true : @ansicolor
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
def ansicolor?
|
44
|
+
@logdev.ansicolor?
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
def ansicolor=(on)
|
49
|
+
@logdev.ansicolor = on
|
50
|
+
end
|
51
|
+
|
52
|
+
# Dictate the way in which this logger should format the
|
53
|
+
# messages it displays. This method requires a block. The
|
54
|
+
# block should return formatted strings given severity,
|
55
|
+
# timestamp, progname and msg.
|
56
|
+
#
|
57
|
+
# === Example
|
58
|
+
#
|
59
|
+
# logger = ANSI::Logger.new
|
60
|
+
#
|
61
|
+
# logger.formatter do |severity, timestamp, progname, msg|
|
62
|
+
# "#{progname}@#{timestamp} - #{severity}::#{msg}"
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
def formatter(&block)
|
66
|
+
self.formatter = block if block
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
def styles(options=nil)
|
71
|
+
@styles ||= {
|
72
|
+
:info => [],
|
73
|
+
:warn => [:yellow],
|
74
|
+
:debug => [:cyan],
|
75
|
+
:error => [:red],
|
76
|
+
:fatal => [:bold, :red]
|
77
|
+
}
|
78
|
+
@styles.merge!(options) if options
|
79
|
+
@styles
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
def info(progname=nil, &block)
|
84
|
+
return unless info?
|
85
|
+
@logdev.ansicolor? ? info_with_color{ super } : super
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
def warn(progname=nil, &block)
|
90
|
+
return unless warn?
|
91
|
+
@logdev.ansicolor? ? warn_with_color{ super } : super
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
def debug(progname=nil, &block)
|
96
|
+
return unless debug?
|
97
|
+
@logdev.ansicolor? ? debug_with_color{ super } : super
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
def error(progname=nil, &block)
|
102
|
+
return unless error?
|
103
|
+
@logdev.ansicolor? ? error_with_color{ super } : super
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
def fatal(progname=nil, &block)
|
108
|
+
return unless error?
|
109
|
+
@logdev.ansicolor? ? fatal_with_color{ super } : super
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def info_with_color #:yield:
|
115
|
+
styles[:info].each{ |s| self << ANSI::Code.send(s) }
|
116
|
+
yield
|
117
|
+
self << ANSI::Code.clear
|
118
|
+
end
|
119
|
+
|
120
|
+
def warn_with_color #:yield:
|
121
|
+
styles[:warn].each{ |s| self << ANSI::Code.send(s) }
|
122
|
+
yield
|
123
|
+
self << ANSI::Code.clear
|
124
|
+
end
|
125
|
+
|
126
|
+
def error_with_color #:yield:
|
127
|
+
styles[:error].each{ |s| self << ANSI::Code.send(s) }
|
128
|
+
yield
|
129
|
+
self << ANSI::Code.clear
|
130
|
+
end
|
131
|
+
|
132
|
+
def debug_with_color #:yield:
|
133
|
+
styles[:debug].each{ |s| self << ANSI::Code.send(s) }
|
134
|
+
yield
|
135
|
+
self << ANSI::Code.clear
|
136
|
+
end
|
137
|
+
|
138
|
+
def fatal_with_color #:yield:
|
139
|
+
styles[:fatal].each{ |s| self << ANSI::Code.send(s) }
|
140
|
+
yield
|
141
|
+
self << ANSI::Code.clear
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
# NOTE: trace is deprecated b/c binding of caller is no longer possible.
|
148
|
+
=begin
|
149
|
+
# Prints a trace message to DEBUGLOG (at debug level).
|
150
|
+
# Useful for emitting the value of variables, etc. Use
|
151
|
+
# like this:
|
152
|
+
#
|
153
|
+
# x = y = 5
|
154
|
+
# trace 'x' # -> 'x = 5'
|
155
|
+
# trace 'x ** y' # -> 'x ** y = 3125'
|
156
|
+
#
|
157
|
+
# If you have a more complicated value, like an array of
|
158
|
+
# hashes, then you'll probably want to use an alternative
|
159
|
+
# output format. For instance:
|
160
|
+
#
|
161
|
+
# trace 'value', :yaml
|
162
|
+
#
|
163
|
+
# Valid output format values (the _style_ parameter) are:
|
164
|
+
#
|
165
|
+
# :p :inspect
|
166
|
+
# :pp (pretty-print, using 'pp' library)
|
167
|
+
# :s :to_s
|
168
|
+
# :y :yaml :to_yaml (using the 'yaml' library')
|
169
|
+
#
|
170
|
+
# The default is <tt>:p</tt>.
|
171
|
+
#
|
172
|
+
# CREDITS:
|
173
|
+
#
|
174
|
+
# This code comes straight from the dev-utils Gem.
|
175
|
+
# Author: Gavin Sinclair <gsinclair@soyabean.com.au>
|
176
|
+
|
177
|
+
def trace(expr, style=:p)
|
178
|
+
unless expr.respond_to? :to_str
|
179
|
+
warn "trace: Can't evaluate the given value: #{caller.first}"
|
180
|
+
else
|
181
|
+
raise "FACETS: binding/or_caller is no longer possible"
|
182
|
+
require "facets/core/binding/self/of_caller"
|
183
|
+
|
184
|
+
Binding.of_caller do |b|
|
185
|
+
value = b.eval(expr.to_str)
|
186
|
+
formatter = TRACE_STYLES[style] || :inspect
|
187
|
+
case formatter
|
188
|
+
when :pp then require 'pp'
|
189
|
+
when :y, :yaml, :to_yaml then require 'yaml'
|
190
|
+
end
|
191
|
+
value_s = value.send(formatter)
|
192
|
+
message = "#{expr} = #{value_s}"
|
193
|
+
lines = message.split(/\n/)
|
194
|
+
indent = " "
|
195
|
+
debug(lines.shift)
|
196
|
+
lines.each do |line|
|
197
|
+
debug(indent + line)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
TRACE_STYLES = {} # :nodoc:
|
204
|
+
TRACE_STYLES.update(
|
205
|
+
:pp => :pp_s, :s => :to_s, :p => :inspect,
|
206
|
+
:y => :to_yaml, :yaml => :to_yaml,
|
207
|
+
:inspect => :inspect, :to_yaml => :to_yaml
|
208
|
+
)
|
209
|
+
=end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
# Copyright (C) 2009 Thomas Sawyer
|
2
|
+
#
|
3
|
+
# This library is based on the original ProgressBar
|
4
|
+
# by Satoru Takabayashi.
|
5
|
+
#
|
6
|
+
# ProgressBar Copyright (C) 2001 Satoru Takabayashi
|
7
|
+
|
8
|
+
require 'ansi/code'
|
9
|
+
|
10
|
+
# ProgressBar is a text-based progressbar library.
|
11
|
+
#
|
12
|
+
# pbar = Progressbar.new( "Demo", 100 )
|
13
|
+
# 100.times { pbar.inc }
|
14
|
+
# pbar.finish
|
15
|
+
#
|
16
|
+
class ANSI::Progressbar
|
17
|
+
#
|
18
|
+
def initialize(title, total, out=STDERR)
|
19
|
+
@title = title
|
20
|
+
@total = total
|
21
|
+
@out = out
|
22
|
+
|
23
|
+
@bar_length = 80
|
24
|
+
@bar_mark = "o"
|
25
|
+
@total_overflow = true
|
26
|
+
@current = 0
|
27
|
+
@previous = 0
|
28
|
+
@is_finished = false
|
29
|
+
@start_time = Time.now
|
30
|
+
@format = "%-14s %3d%% %s %s"
|
31
|
+
@format_arguments = [:title, :percentage, :bar, :stat]
|
32
|
+
@styles = {}
|
33
|
+
#
|
34
|
+
yield self if block_given?
|
35
|
+
#
|
36
|
+
show_progress
|
37
|
+
end
|
38
|
+
|
39
|
+
public
|
40
|
+
|
41
|
+
attr_accessor :format
|
42
|
+
attr_accessor :format_arguments
|
43
|
+
attr_accessor :styles
|
44
|
+
|
45
|
+
#
|
46
|
+
def title=(str)
|
47
|
+
@title = str
|
48
|
+
end
|
49
|
+
#
|
50
|
+
def bar_mark=(mark)
|
51
|
+
@bar_mark = String(mark)[0..0]
|
52
|
+
end
|
53
|
+
|
54
|
+
def total_overflow=(boolv)
|
55
|
+
@total_overflow = boolv ? true : false
|
56
|
+
end
|
57
|
+
|
58
|
+
# Set format and format arguments.
|
59
|
+
def format(format, *arguments)
|
60
|
+
@format = format
|
61
|
+
@format_arguments = *arguments unless arguments.empty?
|
62
|
+
end
|
63
|
+
|
64
|
+
# Set ANSI styling options.
|
65
|
+
def style(options)
|
66
|
+
@styles = options
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
def standard_mode
|
71
|
+
@format = "%-14s %3d%% %s %s"
|
72
|
+
@format_arguments = [:title, :percentage, :bar, :stat]
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
def transfer_mode
|
77
|
+
@format = "%-14s %3d%% %s %s"
|
78
|
+
@format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
|
79
|
+
end
|
80
|
+
|
81
|
+
# For backward compatability
|
82
|
+
alias_method :file_transfer_mode, :transfer_mode
|
83
|
+
|
84
|
+
def finish
|
85
|
+
@current = @total
|
86
|
+
@is_finished = true
|
87
|
+
show_progress
|
88
|
+
end
|
89
|
+
|
90
|
+
def flush
|
91
|
+
@out.flush
|
92
|
+
end
|
93
|
+
|
94
|
+
def halt
|
95
|
+
@is_finished = true
|
96
|
+
show_progress
|
97
|
+
end
|
98
|
+
|
99
|
+
def set(count)
|
100
|
+
if count < 0
|
101
|
+
raise "invalid count less than zero: #{count}"
|
102
|
+
elsif count > @total
|
103
|
+
if @total_overflow
|
104
|
+
@total = count + 1
|
105
|
+
else
|
106
|
+
raise "invalid count greater than total: #{count}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
@current = count
|
110
|
+
show_progress
|
111
|
+
@previous = @current
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
def reset
|
116
|
+
@current = 0
|
117
|
+
@is_finished = false
|
118
|
+
end
|
119
|
+
|
120
|
+
def inc(step = 1)
|
121
|
+
@current += step
|
122
|
+
@current = @total if @current > @total
|
123
|
+
show_progress
|
124
|
+
@previous = @current
|
125
|
+
end
|
126
|
+
|
127
|
+
def inspect
|
128
|
+
"(ProgressBar: #{@current}/#{@total})"
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
#
|
134
|
+
def convert_bytes(bytes)
|
135
|
+
if bytes < 1024
|
136
|
+
sprintf("%6dB", bytes)
|
137
|
+
elsif bytes < 1024 * 1000 # 1000kb
|
138
|
+
sprintf("%5.1fKB", bytes.to_f / 1024)
|
139
|
+
elsif bytes < 1024 * 1024 * 1000 # 1000mb
|
140
|
+
sprintf("%5.1fMB", bytes.to_f / 1024 / 1024)
|
141
|
+
else
|
142
|
+
sprintf("%5.1fGB", bytes.to_f / 1024 / 1024 / 1024)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
#
|
146
|
+
def transfer_rate
|
147
|
+
bytes_per_second = @current.to_f / (Time.now - @start_time)
|
148
|
+
sprintf("%s/s", convert_bytes(bytes_per_second))
|
149
|
+
end
|
150
|
+
#
|
151
|
+
def bytes
|
152
|
+
convert_bytes(@current)
|
153
|
+
end
|
154
|
+
#
|
155
|
+
def format_time(t)
|
156
|
+
t = t.to_i
|
157
|
+
sec = t % 60
|
158
|
+
min = (t / 60) % 60
|
159
|
+
hour = t / 3600
|
160
|
+
sprintf("%02d:%02d:%02d", hour, min, sec);
|
161
|
+
end
|
162
|
+
#
|
163
|
+
# ETA stands for Estimated Time of Arrival.
|
164
|
+
def eta
|
165
|
+
if @current == 0
|
166
|
+
"ETA: --:--:--"
|
167
|
+
else
|
168
|
+
elapsed = Time.now - @start_time
|
169
|
+
eta = elapsed * @total / @current - elapsed;
|
170
|
+
sprintf("ETA: %s", format_time(eta))
|
171
|
+
end
|
172
|
+
end
|
173
|
+
#
|
174
|
+
def elapsed
|
175
|
+
elapsed = Time.now - @start_time
|
176
|
+
sprintf("Time: %s", format_time(elapsed))
|
177
|
+
end
|
178
|
+
#
|
179
|
+
def stat
|
180
|
+
if @is_finished then elapsed else eta end
|
181
|
+
end
|
182
|
+
#
|
183
|
+
def stat_for_file_transfer
|
184
|
+
if @is_finished then
|
185
|
+
sprintf("%s %s %s", bytes, transfer_rate, elapsed)
|
186
|
+
else
|
187
|
+
sprintf("%s %s %s", bytes, transfer_rate, eta)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
#
|
191
|
+
def eol
|
192
|
+
if @is_finished then "\n" else "\r" end
|
193
|
+
end
|
194
|
+
#
|
195
|
+
def bar
|
196
|
+
len = percentage * @bar_length / 100
|
197
|
+
sprintf("|%s%s|", @bar_mark * len, " " * (@bar_length - len))
|
198
|
+
end
|
199
|
+
#
|
200
|
+
def percentage
|
201
|
+
if @total.zero?
|
202
|
+
100
|
203
|
+
else
|
204
|
+
@current * 100 / @total
|
205
|
+
end
|
206
|
+
end
|
207
|
+
#
|
208
|
+
def title
|
209
|
+
@title[0,13] + ":"
|
210
|
+
end
|
211
|
+
#
|
212
|
+
def get_width
|
213
|
+
# FIXME: I don't know how portable it is.
|
214
|
+
default_width = 80
|
215
|
+
begin
|
216
|
+
tiocgwinsz = 0x5413
|
217
|
+
data = [0, 0, 0, 0].pack("SSSS")
|
218
|
+
if @out.ioctl(tiocgwinsz, data) >= 0 then
|
219
|
+
rows, cols, xpixels, ypixels = data.unpack("SSSS")
|
220
|
+
if cols >= 0 then cols else default_width end
|
221
|
+
else
|
222
|
+
default_width
|
223
|
+
end
|
224
|
+
rescue Exception
|
225
|
+
default_width
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
#
|
230
|
+
def show
|
231
|
+
arguments = @format_arguments.map do |method|
|
232
|
+
colorize(send(method), styles[method])
|
233
|
+
end
|
234
|
+
line = sprintf(@format, *arguments)
|
235
|
+
width = get_width
|
236
|
+
length = ANSI::Code.uncolored(line).length
|
237
|
+
if length == width - 1
|
238
|
+
@out.print(line + eol)
|
239
|
+
elsif length >= width
|
240
|
+
@bar_length = [@bar_length - (length - width + 1), 0].max
|
241
|
+
@bar_length == 0 ? @out.print(line + eol) : show
|
242
|
+
else #line.length < width - 1
|
243
|
+
@bar_length += width - length + 1
|
244
|
+
show
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
#
|
249
|
+
def show_progress
|
250
|
+
if @total.zero?
|
251
|
+
cur_percentage = 100
|
252
|
+
prev_percentage = 0
|
253
|
+
else
|
254
|
+
cur_percentage = (@current * 100 / @total).to_i
|
255
|
+
prev_percentage = (@previous * 100 / @total).to_i
|
256
|
+
end
|
257
|
+
if cur_percentage > prev_percentage || @is_finished
|
258
|
+
show
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
#
|
263
|
+
def colorize(part, style)
|
264
|
+
return part unless style
|
265
|
+
[style].flatten.inject(part){ |pt, st| ANSI::Code.send(st){ pt } }
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|