sc-ansi 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ .idea
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.
@@ -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.
@@ -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
@@ -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!
@@ -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
@@ -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