sc-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.
@@ -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