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,249 @@
1
+ require 'ansi/code'
2
+ #require 'ansi/layout/split'
3
+ #require 'clio/facets/string'
4
+
5
+ # Create a new Ansi::String object.
6
+ def ANSI.string(str)
7
+ ANSI::String.new(str)
8
+ end
9
+
10
+ # Clio Strings stores a regular string (@text) and
11
+ # a Hash mapping character index to ansicodes (@marks).
12
+ # For example is we have the string:
13
+ #
14
+ # "Big Apple"
15
+ #
16
+ # And applied the color red to it, the marks hash would be:
17
+ #
18
+ # { 0=>[:red] , 9=>[:clear] }
19
+ #
20
+ # TODO: In the future we may be able to subclass String,
21
+ # instead of delegating via @text, but not until it is more compatible.
22
+ #
23
+ class ANSI::String
24
+
25
+ CLR = ANSI::Code.clear
26
+
27
+ attr :text
28
+ attr :marks
29
+
30
+ # New Ansi::String
31
+ def initialize(text=nil, marks=nil)
32
+ @text = (text || '').to_s
33
+ @marks = marks || []
34
+ yield(self) if block_given?
35
+ end
36
+
37
+ # Convert Ansi::String object to normal String.
38
+ # This converts the intental markup codes to ANSI codes.
39
+ def to_s
40
+ s = text.dup
41
+ m = marks.sort do |(a,b)|
42
+ v = b[0] <=> a[0]
43
+ if v == 0
44
+ (b[1] == :clear or b[1] == :reset) ? -1 : 1
45
+ else
46
+ v
47
+ end
48
+ end
49
+ m.each do |(index, code)|
50
+ s.insert(index, ANSICode.__send__(code))
51
+ end
52
+ #s << CLR unless s =~ /#{Regexp.escape(CLR)}$/ # always end with a clear
53
+ s
54
+ end
55
+
56
+ # Ansi::String is a type of String.
57
+ alias_method :to_str, :to_s
58
+
59
+ # The size of the base text.
60
+ def size ; text.size ; end
61
+
62
+ # Upcase the string.
63
+ def upcase ; self.class.new(text.upcase, marks) ; end
64
+ def upcase! ; text.upcase! ; end
65
+
66
+ # Downcase the string.
67
+ def downcase ; self.class.new(text.upcase, marks) ; end
68
+ def downcase! ; text.upcase! ; end
69
+
70
+ # Add one String to another, or to a regular String.
71
+ def +(other)
72
+ case other
73
+ when String
74
+ ntext = text + other.text
75
+ nmarks = marks.dup
76
+ omarks = shift_marks(0, text.size, other.marks)
77
+ omarks.each{ |(i, c)| nmarks << [i,c] }
78
+ else
79
+ ntext = text + other.to_s
80
+ nmarks = marks.dup
81
+ end
82
+ self.class.new(ntext, nmarks)
83
+ end
84
+
85
+ #
86
+ #def |(other)
87
+ # Split.new(self, other)
88
+ #end
89
+
90
+ #
91
+ #def lr(other, options={})
92
+ # Split.new(self, other, options)
93
+ #end
94
+
95
+ # slice
96
+ def slice(*args)
97
+ if args.size == 2
98
+ index, len = *args
99
+ endex = index+len
100
+ new_text = text[index, len]
101
+ new_marks = []
102
+ marks.each do |(i, v)|
103
+ new_marks << [i, v] if i >= index && i < endex
104
+ end
105
+ self.class.new(new_text, new_marks)
106
+ elsif args.size == 1
107
+ rng = args.first
108
+ case rng
109
+ when Range
110
+ index, endex = rng.begin, rng.end
111
+ new_text = text[rng]
112
+ new_marks = []
113
+ marks.each do |(i, v)|
114
+ new_marks << [i, v] if i >= index && i < endex
115
+ end
116
+ self.class.new(new_text, new_marks)
117
+ else
118
+ nm = marks.select do |(i,c)|
119
+ marks[0] == rng or ( marks[0] == rng + 1 && [:clear, :reset].include?(marks[1]) )
120
+ end
121
+ self.class.new(text[rng,1], nm)
122
+ end
123
+ else
124
+ raise ArgumentError
125
+ end
126
+ end
127
+
128
+ #
129
+ alias_method :[], :slice
130
+
131
+ # This is more limited than the normal String method.
132
+ # It does not yet support a block, and +replacement+
133
+ # won't substitue for \1, \2, etc.
134
+ #
135
+ # TODO: block support.
136
+ def sub!(pattern, replacement=nil, &block)
137
+ mark_changes = []
138
+ text = @text.sub(pattern) do |s|
139
+ index = $~.begin(0)
140
+ replacement = block.call(s) if block_given?
141
+ delta = (replacement.size - s.size)
142
+ mark_changes << [index, delta]
143
+ replacement
144
+ end
145
+ marks = @marks
146
+ mark_changes.each do |index, delta|
147
+ marks = shift_marks(index, delta, marks)
148
+ end
149
+ @text = text
150
+ @marks = marks
151
+ self
152
+ end
153
+
154
+ # See #sub!.
155
+ def sub(pattern,replacement=nil, &block)
156
+ dup.sub!(pattern, replacement, &block)
157
+ end
158
+
159
+ #
160
+ def gsub!(pattern, replacement=nil, &block)
161
+ mark_changes = []
162
+ mark_additions = []
163
+ text = @text.gsub(pattern) do |s|
164
+ index = $~.begin(0)
165
+ replacement = block.call(self.class.new(s)) if block_given?
166
+ if self.class===replacement
167
+ adj_marks = replacement.marks.map{ |(i,c)| [i+index,c] }
168
+ mark_additions.concat(adj_marks)
169
+ replacement = replacement.text
170
+ end
171
+ delta = (replacement.size - s.size)
172
+ mark_changes << [index, delta]
173
+ replacement
174
+ end
175
+ marks = @marks
176
+ mark_changes.each do |(index, delta)|
177
+ marks = shift_marks(index, delta, marks)
178
+ end
179
+ marks.concat(mark_additions)
180
+ @text = text
181
+ @marks = marks
182
+ self
183
+ end
184
+
185
+ # See #gsub!.
186
+ def gsub(pattern, replacement=nil, &block)
187
+ dup.gsub!(pattern, replacement, &block)
188
+ end
189
+
190
+ #
191
+ def ansi(code)
192
+ m = marks.dup
193
+ m.unshift([0, code])
194
+ m.push([size, :clear])
195
+ self.class.new(text, m)
196
+ end
197
+ alias_method :color, :ansi
198
+
199
+ #
200
+ def ansi!(code)
201
+ marks.unshift([0, ansicolor])
202
+ marks.push([size, :clear])
203
+ end
204
+ alias_method :color!, :ansi!
205
+
206
+ def red ; color(:red) ; end
207
+ def green ; color(:green) ; end
208
+ def blue ; color(:blue) ; end
209
+ def black ; color(:black) ; end
210
+ def magenta ; color(:magenta) ; end
211
+ def yellow ; color(:yellow) ; end
212
+ def cyan ; color(:cyan) ; end
213
+
214
+ def bold ; ansi(:bold) ; end
215
+ def underline ; ansi(:underline) ; end
216
+
217
+ def red! ; color!(:red) ; end
218
+ def green! ; color!(:green) ; end
219
+ def blue! ; color!(:blue) ; end
220
+ def black! ; color!(:black) ; end
221
+ def magenta! ; color!(:magenta) ; end
222
+ def yellow! ; color!(:yellow) ; end
223
+ def cyan! ; color!(:cyan) ; end
224
+
225
+ def bold! ; ansi!(:bold) ; end
226
+ def underline! ; ansi!(:underline) ; end
227
+
228
+ private
229
+
230
+ #
231
+ def shift_marks(index, delta, marks=nil)
232
+ new_marks = []
233
+ (marks || @marks).each do |(i, c)|
234
+ case i <=> index
235
+ when -1
236
+ new_marks << [i, c]
237
+ when 0, 1
238
+ new_marks << [i+delta, c]
239
+ end
240
+ end
241
+ new_marks
242
+ end
243
+
244
+ #
245
+ def shift_marks!(index, delta)
246
+ @marks.replace(shift_marks(index, delta))
247
+ end
248
+
249
+ end
@@ -0,0 +1,43 @@
1
+ #
2
+ # This library is based of HighLine's SystemExtensions
3
+ # by James Edward Gray II.
4
+ #
5
+ # Copyright 2006 Gray Productions. All rights reserved.
6
+ #
7
+ # This is Free Software. See LICENSE and COPYING for details.
8
+
9
+ module ANSI
10
+
11
+ #
12
+ module Terminal
13
+
14
+ module_function
15
+
16
+ modes = %w{win32 termios curses stty}
17
+
18
+ #
19
+ # This section builds character reading and terminal size functions
20
+ # to suit the proper platform we're running on. Be warned: Here be
21
+ # dragons!
22
+ #
23
+ begin
24
+ require 'ansi/terminal/' + (mode = modes.pop)
25
+ CHARACTER_MODE = mode
26
+ rescue LoadError
27
+ retry
28
+ end
29
+
30
+ # Get the width of the terminal window.
31
+ def terminal_width
32
+ terminal_size[0]
33
+ end
34
+
35
+ # Get the height of the terminal window.
36
+ def terminal_height
37
+ terminal_size[1]
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
@@ -0,0 +1,27 @@
1
+ module ANSI
2
+
3
+ module Terminal
4
+ require 'curses'
5
+
6
+ module_function
7
+
8
+ #CHARACTER_MODE = "curses" # For Debugging purposes only.
9
+
10
+ #
11
+ # Curses savvy getc().
12
+ #
13
+ #
14
+ def get_character(input = STDIN)
15
+ Curses.getch()
16
+ end
17
+
18
+ def terminal_size
19
+ Curses.init_screen
20
+ w, r = Curses.cols, Curses.rows
21
+ Curses.close_screen
22
+ return r, w
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,55 @@
1
+ module ANSI
2
+
3
+ module Terminal
4
+
5
+ module_function
6
+
7
+ #CHARACTER_MODE = "stty" # For Debugging purposes only.
8
+
9
+ #
10
+ # Unix savvy getc(). (Second choice.)
11
+ #
12
+ # *WARNING*: This method requires the external "stty" program!
13
+ #
14
+ def get_character( input = STDIN )
15
+ raw_no_echo_mode
16
+
17
+ begin
18
+ input.getc
19
+ ensure
20
+ restore_mode
21
+ end
22
+ end
23
+
24
+ #
25
+ # Switched the input mode to raw and disables echo.
26
+ #
27
+ # *WARNING*: This method requires the external "stty" program!
28
+ #
29
+ def raw_no_echo_mode
30
+ @state = `stty -g`
31
+ system "stty raw -echo cbreak isig"
32
+ end
33
+
34
+ #
35
+ # Restores a previously saved input mode.
36
+ #
37
+ # *WARNING*: This method requires the external "stty" program!
38
+ #
39
+ def restore_mode
40
+ system "stty #{@state}"
41
+ end
42
+
43
+ # A Unix savvy method to fetch the console columns, and rows.
44
+ def terminal_size
45
+ if /solaris/ =~ RUBY_PLATFORM and
46
+ `stty` =~ /\brows = (\d+).*\bcolumns = (\d+)/
47
+ [$2, $1].map { |c| x.to_i }
48
+ else
49
+ `stty size`.split.map { |x| x.to_i }.reverse
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,63 @@
1
+ module ANSI
2
+
3
+ module Terminal
4
+ require "termios" # Unix, first choice.
5
+
6
+ module_function
7
+
8
+ #CHARACTER_MODE = "termios" # For Debugging purposes only.
9
+
10
+ #
11
+ # Unix savvy getc(). (First choice.)
12
+ #
13
+ # *WARNING*: This method requires the "termios" library!
14
+ #
15
+ def get_character( input = STDIN )
16
+ old_settings = Termios.getattr(input)
17
+
18
+ new_settings = old_settings.dup
19
+ new_settings.c_lflag &= ~(Termios::ECHO | Termios::ICANON)
20
+ new_settings.c_cc[Termios::VMIN] = 1
21
+
22
+ begin
23
+ Termios.setattr(input, Termios::TCSANOW, new_settings)
24
+ input.getc
25
+ ensure
26
+ Termios.setattr(input, Termios::TCSANOW, old_settings)
27
+ end
28
+ end
29
+
30
+ # A Unix savvy method to fetch the console columns, and rows.
31
+ def terminal_size
32
+ if /solaris/ =~ RUBY_PLATFORM and
33
+ `stty` =~ /\brows = (\d+).*\bcolumns = (\d+)/
34
+ [$2, $1].map { |c| x.to_i }
35
+ else
36
+ `stty size`.split.map { |x| x.to_i }.reverse
37
+ end
38
+ end
39
+
40
+ # Console screen width (taken from progress bar)
41
+ #
42
+ # NOTE: Don't know how portable #screen_width is.
43
+ # TODO: How to fit in to system?
44
+ #
45
+ def screen_width(out=STDERR)
46
+ default_width = ENV['COLUMNS'] || 76
47
+ begin
48
+ tiocgwinsz = 0x5413
49
+ data = [0, 0, 0, 0].pack("SSSS")
50
+ if out.ioctl(tiocgwinsz, data) >= 0 then
51
+ rows, cols, xpixels, ypixels = data.unpack("SSSS")
52
+ if cols >= 0 then cols else default_width end
53
+ else
54
+ default_width
55
+ end
56
+ rescue Exception
57
+ default_width
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,107 @@
1
+ module ANSI
2
+
3
+ module Terminal
4
+ # Cygwin will look like Windows, but we want to treat it like a Posix OS:
5
+ raise LoadError, "Cygwin is a Posix OS." if RUBY_PLATFORM =~ /\bcygwin\b/i
6
+
7
+ require "Win32API" # See if we're on Windows.
8
+
9
+ module_function
10
+
11
+ #CHARACTER_MODE = "Win32API" # For Debugging purposes only.
12
+
13
+ #
14
+ # Windows savvy getc().
15
+ #
16
+ #
17
+ def get_character( input = STDIN )
18
+ @stdin_handle ||= GetStdHandle(STD_INPUT_HANDLE)
19
+
20
+ begin
21
+ SetConsoleEcho(@stdin_handle, false)
22
+ input.getc
23
+ ensure
24
+ SetConsoleEcho(@stdin_handle, true)
25
+ end
26
+ end
27
+
28
+ # A Windows savvy method to fetch the console columns, and rows.
29
+ def terminal_size
30
+ stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE)
31
+
32
+ bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy =
33
+ GetConsoleScreenBufferInfo(stdout_handle)
34
+ return right - left + 1, bottom - top + 1
35
+ end
36
+
37
+ # windows savvy console echo toggler
38
+ def SetConsoleEcho( console_handle, on )
39
+ mode = GetConsoleMode(console_handle)
40
+
41
+ # toggle the console echo bit
42
+ if on
43
+ mode |= ENABLE_ECHO_INPUT
44
+ else
45
+ mode &= ~ENABLE_ECHO_INPUT
46
+ end
47
+
48
+ ok = SetConsoleMode(console_handle, mode)
49
+ end
50
+
51
+ # win32 console APIs
52
+
53
+ STD_INPUT_HANDLE = -10
54
+ STD_OUTPUT_HANDLE = -11
55
+ STD_ERROR_HANDLE = -12
56
+
57
+ ENABLE_PROCESSED_INPUT = 0x0001
58
+ ENABLE_LINE_INPUT = 0x0002
59
+ ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
60
+ ENABLE_ECHO_INPUT = 0x0004
61
+ ENABLE_WINDOW_INPUT = 0x0008
62
+ ENABLE_MOUSE_INPUT = 0x0010
63
+ ENABLE_INSERT_MODE = 0x0020
64
+ ENABLE_QUICK_EDIT_MODE = 0x0040
65
+
66
+ @@apiGetStdHandle = nil
67
+ @@apiGetConsoleMode = nil
68
+ @@apiSetConsoleMode = nil
69
+ @@apiGetConsoleScreenBufferInfo = nil
70
+
71
+ def GetStdHandle( handle_type )
72
+ @@apiGetStdHandle ||= Win32API.new( "kernel32", "GetStdHandle",
73
+ ['L'], 'L' )
74
+
75
+ @@apiGetStdHandle.call( handle_type )
76
+ end
77
+
78
+ def GetConsoleMode( console_handle )
79
+ @@apiGetConsoleMode ||= Win32API.new( "kernel32", "GetConsoleMode",
80
+ ['L', 'P'], 'I' )
81
+
82
+ mode = ' ' * 4
83
+ @@apiGetConsoleMode.call(console_handle, mode)
84
+ mode.unpack('L')[0]
85
+ end
86
+
87
+ def SetConsoleMode( console_handle, mode )
88
+ @@apiSetConsoleMode ||= Win32API.new( "kernel32", "SetConsoleMode",
89
+ ['L', 'L'], 'I' )
90
+
91
+ @@apiSetConsoleMode.call(console_handle, mode) != 0
92
+ end
93
+
94
+ def GetConsoleScreenBufferInfo( console_handle )
95
+ @@apiGetConsoleScreenBufferInfo ||=
96
+ Win32API.new( "kernel32", "GetConsoleScreenBufferInfo",
97
+ ['L', 'P'], 'L' )
98
+
99
+ format = 'SSSSSssssSS'
100
+ buf = ([0] * format.size).pack(format)
101
+ @@apiGetConsoleScreenBufferInfo.call(console_handle, buf)
102
+ buf.unpack(format)
103
+ end
104
+
105
+ end
106
+
107
+ end