sc-ansi 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.rdoc +54 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/lib/ansi.rb +177 -0
- data/lib/ansi/code.rb +91 -0
- data/lib/ansi/color.rb +127 -0
- data/lib/ansi/cursor.rb +26 -0
- data/lib/ansi/display.rb +54 -0
- data/lib/ansi/match.rb +35 -0
- data/lib/ansi/vt100.rb +177 -0
- data/lib/core_ext/object.rb +15 -0
- data/lib/core_ext/string.rb +34 -0
- data/lib/sc-ansi.rb +9 -0
- data/sc-ansi.gemspec +79 -0
- data/spec/lib/ansi/color_spec.rb +102 -0
- data/spec/lib/ansi/cursor_spec.rb +37 -0
- data/spec/lib/ansi/display_spec.rb +21 -0
- data/spec/lib/ansi/match_spec.rb +53 -0
- data/spec/lib/ansi_spec.rb +44 -0
- data/spec/lib/core_ext/object_spec.rb +19 -0
- data/spec/lib/core_ext/string_spec.rb +68 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- metadata +122 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Colin MacKenzie IV
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
= sc-ansi
|
2
|
+
|
3
|
+
Handles every aspect (that I could think of) of dealing with ANSI escape sequences. You can produce ANSI codes easily
|
4
|
+
enough:
|
5
|
+
|
6
|
+
require 'sc-ansi' # or just require 'ansi' if you prefer.
|
7
|
+
|
8
|
+
"a string!".red #=> a red string!
|
9
|
+
"a string!".blue #=> a blue string!
|
10
|
+
"a string!".red + "another string!".blue #=> a red string and a blue string!
|
11
|
+
|
12
|
+
# or...
|
13
|
+
include ANSI
|
14
|
+
|
15
|
+
red { "a string!" } #=> a red string!
|
16
|
+
blue { "a string!" } #=> a blue string!
|
17
|
+
|
18
|
+
red + "a string!" + blue + "another string!" #=> a red string and a blue string!
|
19
|
+
|
20
|
+
== Parsing ANSI Codes
|
21
|
+
|
22
|
+
The unique thing about sc-ansi is that it can also parse ANSI codes out of an input string. Useful if you're writing
|
23
|
+
your own console application and you want to interpret, say, the UP arrow for history recall.
|
24
|
+
|
25
|
+
include ANSI # for the escape codes
|
26
|
+
|
27
|
+
string = move_up + " hello " + move_down #=> "\e[A hello \e[B"
|
28
|
+
string.ansi_sequences #=> [ ANSI::CURSOR_UP, ANSI::CURSOR_DOWN ]
|
29
|
+
|
30
|
+
string.replace_ansi do |sequence| # sequence is an instance of ANSI::Match
|
31
|
+
case sequence
|
32
|
+
when ANSI::CURSOR_UP # or ANSI::CUU
|
33
|
+
"(up)"
|
34
|
+
when ANSI::CURSOR_DOWN # or ANSI::CUD
|
35
|
+
"(down)"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
#=> "(up) hello (down)"
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
== Note on Patches/Pull Requests
|
43
|
+
|
44
|
+
* Fork the project.
|
45
|
+
* Make your feature addition or bug fix.
|
46
|
+
* Add tests for it. This is important so I don't break it in a
|
47
|
+
future version unintentionally.
|
48
|
+
* Commit, do not mess with rakefile, version, or history.
|
49
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
50
|
+
* Send me a pull request. Bonus points for topic branches.
|
51
|
+
|
52
|
+
== Copyright
|
53
|
+
|
54
|
+
Copyright (c) 2010 Colin MacKenzie IV. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "sc-ansi"
|
8
|
+
gem.summary = %Q{Handles every aspect (that I could think of) of dealing with ANSI escape sequences.}
|
9
|
+
gem.description = %Q{Handles every aspect (that I could think of) of dealing with ANSI escape sequences.}
|
10
|
+
gem.email = "sinisterchipmunk@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/sinisterchipmunk/sc-ansi"
|
12
|
+
gem.authors = ["Colin MacKenzie IV"]
|
13
|
+
gem.add_dependency "sc-core-ext", ">= 1.2.1"
|
14
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec/rake/spectask'
|
23
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
25
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
29
|
+
spec.libs << 'lib' << 'spec'
|
30
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
31
|
+
spec.rcov = true
|
32
|
+
spec.rcov_opts << "-x" << "/Library,spec"
|
33
|
+
end
|
34
|
+
|
35
|
+
task :spec => :check_dependencies
|
36
|
+
|
37
|
+
task :default => :spec
|
38
|
+
|
39
|
+
require 'rake/rdoctask'
|
40
|
+
Rake::RDocTask.new do |rdoc|
|
41
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
42
|
+
|
43
|
+
rdoc.rdoc_dir = 'rdoc'
|
44
|
+
rdoc.title = "sc-ansi #{version}"
|
45
|
+
rdoc.rdoc_files.include('README*')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/lib/ansi.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
# The main module for producing and/or parsing ANSI escape sequences. You can include this module into your class, or
|
2
|
+
# access it directly:
|
3
|
+
#
|
4
|
+
# ANSI.red { "a red string" }
|
5
|
+
#
|
6
|
+
# include ANSI
|
7
|
+
# red { "a red string" }
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# To see which ANSI escape sequences can be generated, take a look at the following files:
|
11
|
+
# lib/ansi/cursor.rb - sequences related to the cursor position
|
12
|
+
# lib/ansi/color.rb - sequences related to text color and decoration
|
13
|
+
# lib/ansi/display.rb - sequences related to the display, such as resolution
|
14
|
+
# lib/ansi/vt100.rb - sequences compatible with VT100 terminals
|
15
|
+
#
|
16
|
+
# You can also define your own ANSI sequences. Obviously, they won't actually work unless the target system supports
|
17
|
+
# them, but this is useful in case you need one that I somehow missed.
|
18
|
+
#
|
19
|
+
# ANSI.define("sequence_name", "sn") { |an_argument| "the sequence with #{an_argument}" }
|
20
|
+
#
|
21
|
+
# Now you can use the new sequence like so:
|
22
|
+
#
|
23
|
+
# ANSI.sequence_name(1) #=> "the sequence with 1"
|
24
|
+
# ANSI.sn(7) #=> "the sequence with 7"
|
25
|
+
#
|
26
|
+
# You can also define sequences that take blocks. In this case, the block argument will be the last argument:
|
27
|
+
#
|
28
|
+
# ANSI.define("block_sequence", "bss") { |arg1, block| "the seq #{arg1} with #{block.call}" }
|
29
|
+
#
|
30
|
+
# ANSI.block_sequence(1) { 55 }
|
31
|
+
# #=> "the seq 1 with 55"
|
32
|
+
#
|
33
|
+
# ANSI.bss(35) { 72 }
|
34
|
+
# #=> "the seq 35 with 72"
|
35
|
+
#
|
36
|
+
# Obviously, the ability to define these sequences (with or without blocks) is probably more useful to me than to you
|
37
|
+
# -- but it's in there if you need it.
|
38
|
+
#
|
39
|
+
module ANSI
|
40
|
+
class << self
|
41
|
+
def codes
|
42
|
+
@codes ||= []
|
43
|
+
end
|
44
|
+
|
45
|
+
# Causes ANSI to unload all generated methods and wipe out the #codes index, and then reload
|
46
|
+
# its internal files. The codes stored in #codes themselves are unaffected, though they will
|
47
|
+
# float into oblivion and be processed by the garbage collector if you haven't captured them
|
48
|
+
# in some way.
|
49
|
+
def reset!
|
50
|
+
codes.clear
|
51
|
+
dynamically_defined_methods.each { |c| undef_method(c) }
|
52
|
+
dynamically_defined_methods.clear
|
53
|
+
arities.clear
|
54
|
+
|
55
|
+
load File.join(File.dirname(__FILE__), "ansi/cursor.rb")
|
56
|
+
load File.join(File.dirname(__FILE__), "ansi/display.rb")
|
57
|
+
load File.join(File.dirname(__FILE__), "ansi/color.rb")
|
58
|
+
load File.join(File.dirname(__FILE__), "ansi/vt100.rb")
|
59
|
+
end
|
60
|
+
|
61
|
+
def dynamically_defined_methods
|
62
|
+
@dynamically_defined_methods ||= []
|
63
|
+
end
|
64
|
+
|
65
|
+
# The arity of the block that was used to define a particular method.
|
66
|
+
def arities
|
67
|
+
@arities ||= {}
|
68
|
+
end
|
69
|
+
|
70
|
+
# Dynamically generates an escape sequence for the specified method name. Arguments are replaced
|
71
|
+
# with "{?}".
|
72
|
+
def generate_sequence(method_name)
|
73
|
+
test_sequence(arities[method_name]) { |*args| send(method_name, *args) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_sequence(arity, &block)
|
77
|
+
args = []
|
78
|
+
arity.times { args << "{?}" } if arity
|
79
|
+
begin
|
80
|
+
return block.call(*args)
|
81
|
+
rescue TypeError => err
|
82
|
+
if err.message =~ /\(expected Proc\)/
|
83
|
+
args.pop
|
84
|
+
args << nil
|
85
|
+
retry
|
86
|
+
else
|
87
|
+
raise err
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Attempts to find an ANSI escape sequence which matches the specified string. The string
|
93
|
+
# is expected to use the notation "{?}" for each parameter instead of a real value. For
|
94
|
+
# instance, the sequence which would return MOVE_UP would look like: "\e[{?}A" instead of
|
95
|
+
# "\e[1A"
|
96
|
+
def recognize(str)
|
97
|
+
match = nil
|
98
|
+
String::ANSI_ESCAPE_SEQUENCE_RX =~ str
|
99
|
+
if $~ && args = $~[2].split(";")
|
100
|
+
codes.uniq.each do |code|
|
101
|
+
_args = args.dup
|
102
|
+
begin
|
103
|
+
result = code.generate(*_args)
|
104
|
+
rescue TypeError => err
|
105
|
+
if err.message =~ /\(expected Proc\)/ && !_args.empty?
|
106
|
+
_args.pop
|
107
|
+
retry
|
108
|
+
end
|
109
|
+
next
|
110
|
+
end
|
111
|
+
|
112
|
+
if result == str
|
113
|
+
match ||= ANSI::Match.new(_args)
|
114
|
+
match << code
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
return match if match
|
119
|
+
raise "ANSI sequence not found: #{str.inspect}"
|
120
|
+
end
|
121
|
+
|
122
|
+
# Aliases a specific code with the given names. This way you don't need to redefine a new constant, so performance
|
123
|
+
# is improved a little bit. Note that the code is expected to be an actual instance of ANSI::Code, and not an
|
124
|
+
# arbitrary string.
|
125
|
+
def alias_code(code, *names, &block)
|
126
|
+
code.add_methods! *names
|
127
|
+
delegate_names_for(code, *names, &block)
|
128
|
+
end
|
129
|
+
|
130
|
+
def define(*names, &block)
|
131
|
+
# this just seems like a Really Bad Idea (TM). Let's just let the user create aliases the hard way.
|
132
|
+
# @cross ||= {}
|
133
|
+
# code = @cross[test_sequence(block.arity) { |*args| instance_exec(*args, &block) }] ||= ANSI::Code.new(&block)
|
134
|
+
code = ANSI::Code.new(&block)
|
135
|
+
code.add_methods!(*names)
|
136
|
+
|
137
|
+
#code = ANSI::Code.new(*names, &block)
|
138
|
+
codes << code
|
139
|
+
delegate_names_for(code, *names, &block)
|
140
|
+
code
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
def delegate_names_for(code, *names, &block) #:nodoc:
|
145
|
+
code_index = codes.index(code)
|
146
|
+
names.flatten.each do |name|
|
147
|
+
const_name = name.to_s.upcase
|
148
|
+
const_set(const_name, code) unless const_defined?(const_name)
|
149
|
+
|
150
|
+
arities[name] = code.arity
|
151
|
+
if method_defined?(name) && $DEBUG
|
152
|
+
warn "Warning: about to overwrite method #{name}..."
|
153
|
+
end
|
154
|
+
|
155
|
+
line = __LINE__ + 1
|
156
|
+
rbcode = <<-end_code
|
157
|
+
def #{name}(*args, &block) # def red(*args, &block)
|
158
|
+
args << block if block_given? # args << block if block_given?
|
159
|
+
ANSI.codes[#{code_index}].send(#{name.inspect}, *args) # ANSI.code[1].send('red', *args)
|
160
|
+
end # end
|
161
|
+
end_code
|
162
|
+
|
163
|
+
class_eval rbcode, __FILE__, line
|
164
|
+
ANSI.instance_eval rbcode
|
165
|
+
@dynamically_defined_methods << name
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
require 'sc-core-ext'
|
172
|
+
require File.join(File.dirname(__FILE__), 'ansi/code')
|
173
|
+
require File.join(File.dirname(__FILE__), "core_ext/object")
|
174
|
+
require File.join(File.dirname(__FILE__), "core_ext/string")
|
175
|
+
require File.join(File.dirname(__FILE__), "ansi/match")
|
176
|
+
|
177
|
+
ANSI.reset!
|
data/lib/ansi/code.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
module ANSI
|
2
|
+
# The object that calling ANSI.define(...) actually creates. This is managed internally and you should rarely
|
3
|
+
# have to interface with it directly.
|
4
|
+
class Code
|
5
|
+
attr_reader :method_name, :arity, :block
|
6
|
+
include ANSI
|
7
|
+
|
8
|
+
# The name of this code. This is assigned to the first non-nil method name that this object uses.
|
9
|
+
def name
|
10
|
+
@name || (@method_name.kind_of?(String) ? @name = @method_name : @method_name.to_s).upcase
|
11
|
+
end
|
12
|
+
|
13
|
+
alias to_s name
|
14
|
+
alias inspect name
|
15
|
+
|
16
|
+
# Returns true if the other object is a kind_of ANSI::Code, *or* if the other object is a kind_of
|
17
|
+
# ANSI::Match and this ANSI::Code is contained in that match. In other words:
|
18
|
+
#
|
19
|
+
# code = ANSI::Code.new("a_code") {}
|
20
|
+
# match = ANSI::Match.new
|
21
|
+
#
|
22
|
+
# code === match
|
23
|
+
# #=> false
|
24
|
+
#
|
25
|
+
# match << code
|
26
|
+
# code === match
|
27
|
+
# #=> true
|
28
|
+
#
|
29
|
+
def ===(other)
|
30
|
+
if other.kind_of?(ANSI::Code)
|
31
|
+
return true
|
32
|
+
elsif other.kind_of?(ANSI::Match) && other == self
|
33
|
+
return true
|
34
|
+
else
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Creates methods with the specified names which are aliases of #generate. These methods are singletons
|
40
|
+
# of this instance of ANSI::Code, so adding method names using this method is only useful within the
|
41
|
+
# context of this instance.
|
42
|
+
#
|
43
|
+
def add_methods!(*names) #:nodoc:
|
44
|
+
names = names.flatten
|
45
|
+
return if names.empty?
|
46
|
+
@method_name ||= names.first.to_s
|
47
|
+
|
48
|
+
names.each do |name|
|
49
|
+
eigen.instance_eval do
|
50
|
+
alias_method name, :generate
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Takes an optional list of method aliases, which are sent to #add_methods!, and a mandatory block argument
|
56
|
+
# which is bound to the #generate method.
|
57
|
+
def initialize(*names, &block)
|
58
|
+
@arity = block.arity || 0
|
59
|
+
@block = block
|
60
|
+
|
61
|
+
eigen.instance_eval do
|
62
|
+
if block.arity > 0
|
63
|
+
# we need a list of arguments to pass to it.
|
64
|
+
define_method "generate" do |*args|
|
65
|
+
# this is to get around the warnings in Ruby 1.8
|
66
|
+
if args.length > block.arity && block.arity == 1
|
67
|
+
raise ArgumentError, "too many arguments (#{args.length} for #{block.arity})", caller
|
68
|
+
end
|
69
|
+
|
70
|
+
# omitted args should be made nil. We do this explicitly to silence warnings in 1.8.
|
71
|
+
if (len = block.arity - args.length)
|
72
|
+
len.times { args << nil }
|
73
|
+
end
|
74
|
+
|
75
|
+
instance_exec(*args, &block)
|
76
|
+
end
|
77
|
+
else
|
78
|
+
define_method("generate", &block)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
add_methods! *names
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
# the singleton class of this instance.
|
87
|
+
def eigen #:nodoc:
|
88
|
+
(class << self; self; end)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/ansi/color.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
module ANSI
|
2
|
+
# Defines various ANSI escape sequences relating to terminal color manipulation.
|
3
|
+
#
|
4
|
+
# A few low-level methods are defined:
|
5
|
+
#
|
6
|
+
# * set_color(*values)
|
7
|
+
# * takes 1 or more numeric color/attribute codes and produces the appropriate escape sequence.
|
8
|
+
# * reset_color
|
9
|
+
# * takes no arguments and resets terminal color and attributes to normal.
|
10
|
+
#
|
11
|
+
# A number of higher-level methods are defined. None of them take any arguments, and they have the effect
|
12
|
+
# that their name implies:
|
13
|
+
#
|
14
|
+
# Attribute methods:
|
15
|
+
# regular (same as reset_color)
|
16
|
+
# bold
|
17
|
+
# underscore
|
18
|
+
# blink
|
19
|
+
# reverse_video
|
20
|
+
# concealed
|
21
|
+
#
|
22
|
+
# Foreground color methods:
|
23
|
+
# black / fg_black
|
24
|
+
# red / fg_red
|
25
|
+
# green / fg_green
|
26
|
+
# yellow / fg_yellow
|
27
|
+
# blue / fg_blue
|
28
|
+
# magenta / fg_magenta
|
29
|
+
# cyan / fg_cyan
|
30
|
+
# white / fg_white
|
31
|
+
#
|
32
|
+
# Background color methods:
|
33
|
+
# bg_black
|
34
|
+
# bg_red
|
35
|
+
# bg_green
|
36
|
+
# bg_yellow
|
37
|
+
# bg_blue
|
38
|
+
# bg_magenta
|
39
|
+
# bg_cyan
|
40
|
+
# bg_white
|
41
|
+
#
|
42
|
+
# Every combination of the above is also generated as a single method call, producing results including (but not
|
43
|
+
# limited to):
|
44
|
+
# regular_red
|
45
|
+
# bold_red
|
46
|
+
# underscore_red
|
47
|
+
# blink_red
|
48
|
+
# reverse_video_red
|
49
|
+
# concealed_red
|
50
|
+
# red_on_white / regular_red_on_white
|
51
|
+
# bold_red_on_white
|
52
|
+
# underscore_red_on_white
|
53
|
+
# blink_red_on_white
|
54
|
+
# . . .
|
55
|
+
#
|
56
|
+
module Color
|
57
|
+
class << ANSI
|
58
|
+
# Defines the escape sequence, and then delegates to it from within String. This makes it possible to do things
|
59
|
+
# like "this is red".red and so forth. Note that to keep a clean namespace, we'll undef and unalias all of this
|
60
|
+
# at the end of the Color module.
|
61
|
+
def define_with_extension(*names, &block) #:nodoc:
|
62
|
+
_define(*names, &block)
|
63
|
+
names.flatten.each do |name|
|
64
|
+
String.class_eval { define_method(name) { ANSI.send(name) { self } } }
|
65
|
+
Symbol.class_eval { define_method(name) { self.to_s.send(name) } }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
alias _define define #:nodoc:
|
70
|
+
alias define define_with_extension #:nodoc:
|
71
|
+
end
|
72
|
+
|
73
|
+
ANSI.define("set_color", "color") do |*values|
|
74
|
+
block = values.last.kind_of?(Proc) ? values.pop : nil
|
75
|
+
colr = "\e[#{values.join(";")}m"
|
76
|
+
if block
|
77
|
+
colr + block.call.to_s + reset_color
|
78
|
+
else
|
79
|
+
colr
|
80
|
+
end
|
81
|
+
end
|
82
|
+
ANSI.define("reset_color", "reset") { color(0) }
|
83
|
+
|
84
|
+
# Various combinations of colors and text attributes:
|
85
|
+
# bold, underscore, blink, reverse_video, concealed
|
86
|
+
# red, fg_red, bg_red, red_on_red, bold_red, bold_red_on_red
|
87
|
+
# etc.
|
88
|
+
colors = %w(black red green yellow blue magenta cyan white)
|
89
|
+
attrs = { "regular" => 0, "bold" => 1, "underscore" => 4, "blink" => 5, "reverse_video" => 7, "concealed" => 8 }
|
90
|
+
|
91
|
+
attrs.each do |attr_name, attr_value|
|
92
|
+
ANSI.define(attr_name) { |block| color(attr_value, &block) } # 0-8
|
93
|
+
end
|
94
|
+
|
95
|
+
colors.each_with_index do |fg_name, fg_value|
|
96
|
+
fg_value += 30
|
97
|
+
ANSI.define(fg_name, "fg_#{fg_name}") { |block| color(fg_value, &block) } # 30-37 (ie red, fg_red)
|
98
|
+
ANSI.define("bg_#{fg_name}") { |block| color(fg_value + 10, &block) } # 40-47 (ie bg_red)
|
99
|
+
|
100
|
+
attrs.each do |attr_name, attr_value|
|
101
|
+
if attr_name.length > 0
|
102
|
+
# (ie bold_red)
|
103
|
+
ANSI.define("#{attr_name}_#{fg_name}") { |block| color(attr_value, fg_value, &block) }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
colors.each_with_index do |bg_name, bg_value|
|
108
|
+
bg_value += 40
|
109
|
+
# (ie red_on_blue)
|
110
|
+
ANSI.define("#{fg_name}_on_#{bg_name}") { |block| color(fg_value, bg_value, &block) }
|
111
|
+
|
112
|
+
attrs.each do |attr_name, attr_value|
|
113
|
+
ANSI.define("#{attr_name}_#{fg_name}_#{bg_name}", "#{attr_name}_#{fg_name}_on_#{bg_name}") do |block|
|
114
|
+
color(attr_value, fg_value, bg_value, &block)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class << ANSI
|
121
|
+
# Cleaning up after ourselves...
|
122
|
+
alias define _define #:nodoc:
|
123
|
+
undef define_with_extension
|
124
|
+
undef _define
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|