ansi 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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