ohboyohboyohboy-monocle-print 1.1.1
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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/History.txt +4 -0
- data/Manifest.txt +21 -0
- data/README.txt +40 -0
- data/Rakefile +21 -0
- data/lib/monocle-print.rb +87 -0
- data/lib/monocle-print/atomic.rb +461 -0
- data/lib/monocle-print/geometry.rb +66 -0
- data/lib/monocle-print/graphics.rb +86 -0
- data/lib/monocle-print/graphics/registry.rb +63 -0
- data/lib/monocle-print/layout.rb +58 -0
- data/lib/monocle-print/list.rb +138 -0
- data/lib/monocle-print/output-device.rb +529 -0
- data/lib/monocle-print/presentation.rb +132 -0
- data/lib/monocle-print/progress.rb +192 -0
- data/lib/monocle-print/table.rb +229 -0
- data/lib/monocle-print/table/column.rb +98 -0
- data/lib/monocle-print/table/members.rb +252 -0
- data/lib/monocle-print/table/segments.rb +62 -0
- data/lib/monocle-print/terminal-escapes.rb +205 -0
- data/lib/monocle-print/utils.rb +23 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 86040373aa1a0083834327588b976a16f75d9a71
|
4
|
+
data.tar.gz: cf64e4578d808060fc9f9b1bc32690f21d1c648e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 15bef20ee331f6d2c9c896562c5e9dd23183d136d73be3c6dcf06e0fbb4632c8363a659ca6745c40d081441ac791d845b6ed0b88189ac7150c6e349caee70a0e
|
7
|
+
data.tar.gz: cc1c985d702a06c2dc5262eca2208c29d60993987c69c811169eb189599a25b961f471656989cd0e4a8f6b97e5e9823e8493507e682b654549767ee66a65ea2f
|
data/Gemfile
ADDED
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Gemfile
|
2
|
+
History.txt
|
3
|
+
Manifest.txt
|
4
|
+
Rakefile
|
5
|
+
README.txt
|
6
|
+
lib/monocle-print.rb
|
7
|
+
lib/monocle-print/layout.rb
|
8
|
+
lib/monocle-print/table/segments.rb
|
9
|
+
lib/monocle-print/table/members.rb
|
10
|
+
lib/monocle-print/table/column.rb
|
11
|
+
lib/monocle-print/output-device.rb
|
12
|
+
lib/monocle-print/graphics.rb
|
13
|
+
lib/monocle-print/table.rb
|
14
|
+
lib/monocle-print/graphics/registry.rb
|
15
|
+
lib/monocle-print/list.rb
|
16
|
+
lib/monocle-print/progress.rb
|
17
|
+
lib/monocle-print/presentation.rb
|
18
|
+
lib/monocle-print/utils.rb
|
19
|
+
lib/monocle-print/terminal-escapes.rb
|
20
|
+
lib/monocle-print/geometry.rb
|
21
|
+
lib/monocle-print/atomic.rb
|
data/README.txt
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
= ohboyohboyohboy-monocle-print
|
2
|
+
|
3
|
+
* http://ohboyohboyohboy.org
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Fancy console output tools
|
8
|
+
|
9
|
+
== REQUIREMENTS:
|
10
|
+
|
11
|
+
* ruby (>= 1.8.7)
|
12
|
+
|
13
|
+
== INSTALL:
|
14
|
+
|
15
|
+
sudo gem install ohboyohboyohboy-monocle-print
|
16
|
+
|
17
|
+
== LICENSE:
|
18
|
+
|
19
|
+
(The MIT License)
|
20
|
+
|
21
|
+
Copyright (c) 2012-2018 Kyle Yetter (kyle@ohboyohboyohboy.org)
|
22
|
+
|
23
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
24
|
+
a copy of this software and associated documentation files (the
|
25
|
+
'Software'), to deal in the Software without restriction, including
|
26
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
27
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
28
|
+
permit persons to whom the Software is furnished to do so, subject to
|
29
|
+
the following conditions:
|
30
|
+
|
31
|
+
The above copyright notice and this permission notice shall be
|
32
|
+
included in all copies or substantial portions of the Software.
|
33
|
+
|
34
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
35
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
36
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
37
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
38
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
39
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
40
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
#
|
4
|
+
|
5
|
+
PROJECT_NAME = "ohboyohboyohboy-monocle-print"
|
6
|
+
|
7
|
+
require "rubygems"
|
8
|
+
require "hoe"
|
9
|
+
|
10
|
+
# Hoe.plugin :compiler
|
11
|
+
# Hoe.plugin :gem_prelude_sucks
|
12
|
+
# Hoe.plugin :inline
|
13
|
+
# Hoe.plugin :minitest
|
14
|
+
# Hoe.plugin :racc
|
15
|
+
# Hoe.plugin :rcov
|
16
|
+
# Hoe.plugin :rubyforge
|
17
|
+
|
18
|
+
Hoe.spec PROJECT_NAME do
|
19
|
+
developer( "Kyle Yetter", "kyle@ohboyohboyohboy.org")
|
20
|
+
license "MIT" # this should match the license in the README
|
21
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'io/console'
|
6
|
+
rescue LoadError
|
7
|
+
# ignore
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'delegate'
|
11
|
+
autoload :StringIO, 'stringio' unless defined?( StringIO )
|
12
|
+
|
13
|
+
module MonoclePrint
|
14
|
+
VERSION = '1.1.1'
|
15
|
+
|
16
|
+
def self.version
|
17
|
+
VERSION
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.included( kl )
|
21
|
+
super
|
22
|
+
kl.extend( self )
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.library_path( *args )
|
26
|
+
File.join( File.dirname( __FILE__ ), *args )
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.stdout( options = {}, &block )
|
30
|
+
OutputDevice.stdout( options, &block )
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.stderr( options = {}, &block )
|
34
|
+
OutputDevice.stderr( options, &block )
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.buffer( options = {}, &block )
|
38
|
+
OutputDevice.buffer( options, &block )
|
39
|
+
end
|
40
|
+
|
41
|
+
module_function
|
42
|
+
|
43
|
+
def Line( obj )
|
44
|
+
SingleLine === obj ? obj : SingleLine.new( obj.to_s )
|
45
|
+
end
|
46
|
+
|
47
|
+
def Output( dev )
|
48
|
+
OutputDevice === dev ? dev : OutputDevice.new( dev )
|
49
|
+
end
|
50
|
+
|
51
|
+
def Text( obj )
|
52
|
+
case obj
|
53
|
+
when Text then obj
|
54
|
+
when nil then Text.new( '' )
|
55
|
+
else Text.new( obj.to_s )
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def Style( obj )
|
60
|
+
Graphics === obj ? obj : Graphics.style( obj )
|
61
|
+
end
|
62
|
+
|
63
|
+
def Rectangle( obj )
|
64
|
+
case obj
|
65
|
+
when Rectangle then obj
|
66
|
+
when Array then Rectangle.new( *obj )
|
67
|
+
when Hash then Rectangle.create( obj )
|
68
|
+
else Rectangle.new( obj )
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
$LOAD_PATH.unshift( MonoclePrint.library_path )
|
75
|
+
|
76
|
+
%w(
|
77
|
+
utils
|
78
|
+
geometry
|
79
|
+
presentation
|
80
|
+
terminal-escapes
|
81
|
+
atomic
|
82
|
+
graphics
|
83
|
+
output-device
|
84
|
+
progress
|
85
|
+
table
|
86
|
+
list
|
87
|
+
).each { | lib | require "monocle-print/#{ lib }" }
|
@@ -0,0 +1,461 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: ascii
|
3
|
+
#
|
4
|
+
# author: Kyle Yetter
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'monocle-print'
|
8
|
+
|
9
|
+
module MonoclePrint
|
10
|
+
if RUBY_VERSION =~ /^1\.8/
|
11
|
+
MULTIBYTE_CHARACTER = %r<
|
12
|
+
[\xC2-\xDF][\x80-\xBF]
|
13
|
+
| [\xE0-\xEF][\x80-\xBF]{2}
|
14
|
+
| [\xF0-\xF4][\x80-\xBF]{3}
|
15
|
+
>x
|
16
|
+
|
17
|
+
ONE_BYTE = 0x20 .. 0x7E
|
18
|
+
TWO_BYTES = 0xC2 .. 0xDF
|
19
|
+
THREE_BYTES = 0xE0 .. 0xEF
|
20
|
+
FOUR_BYTES = 0xF0 .. 0xF4
|
21
|
+
end
|
22
|
+
|
23
|
+
COLOR_ESCAPE = /\e\[[\d:;]*?m/
|
24
|
+
|
25
|
+
class SingleLine < ::String
|
26
|
+
include MonoclePrint
|
27
|
+
|
28
|
+
def self.clear_cache
|
29
|
+
@@width.clear
|
30
|
+
@@invisible_size.clear
|
31
|
+
return( self )
|
32
|
+
end
|
33
|
+
|
34
|
+
@@width = {}
|
35
|
+
@@invisible_size = {}
|
36
|
+
|
37
|
+
if RUBY_VERSION =~ /^1\.8/
|
38
|
+
|
39
|
+
def char_byte( n )
|
40
|
+
n.zero? and return( 0 )
|
41
|
+
seen = byte = 0
|
42
|
+
while c = self[ byte ] and seen < n
|
43
|
+
step =
|
44
|
+
case c
|
45
|
+
when ONE_BYTE then seen += 1; 1
|
46
|
+
when ?\e
|
47
|
+
self[ byte, size ] =~ /^#{COLOR_ESCAPE}/ ? $~.end(0) : 1
|
48
|
+
when TWO_BYTES then seen += 1; 2
|
49
|
+
when THREE_BYTES then seen += 1; 3
|
50
|
+
when FOUR_BYTES then seen += 1; 4
|
51
|
+
else 1
|
52
|
+
end
|
53
|
+
byte += step
|
54
|
+
end
|
55
|
+
return( byte )
|
56
|
+
end
|
57
|
+
|
58
|
+
def width
|
59
|
+
@@width[ hash ] ||= begin
|
60
|
+
(temp = bleach).gsub!( MULTIBYTE_CHARACTER, ' ' )
|
61
|
+
temp.size
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
else
|
66
|
+
|
67
|
+
def char_byte( n )
|
68
|
+
seen = byte = 0
|
69
|
+
if esc = self =~ COLOR_ESCAPE and esc < n
|
70
|
+
return( $~.end(0) + $'.char_byte( n - esc ) )
|
71
|
+
else n
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def width
|
76
|
+
@@width[ hash ] ||= bleach.length
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def align( alignment, width, fill = ' ' )
|
82
|
+
dup.align!( alignment, width, fill )
|
83
|
+
end
|
84
|
+
|
85
|
+
def align!( alignment, width, fill = ' ' )
|
86
|
+
case alignment.to_sym
|
87
|
+
when :left then left!( width, fill )
|
88
|
+
when :center then center!( width, fill )
|
89
|
+
when :right then right!( width, fill )
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def blank?
|
94
|
+
empty? or self =~ /^(\s|#{COLOR_ESCAPE})*$/
|
95
|
+
end
|
96
|
+
|
97
|
+
def bleach
|
98
|
+
gsub( COLOR_ESCAPE, '' )
|
99
|
+
end
|
100
|
+
|
101
|
+
def bleach!
|
102
|
+
gsub!( COLOR_ESCAPE, '' )
|
103
|
+
end
|
104
|
+
|
105
|
+
def center!( w, fill = ' ' )
|
106
|
+
w > width or return( self )
|
107
|
+
if fill.length == 1
|
108
|
+
replace( center( w + invisible_size, fill ) )
|
109
|
+
else fill = self.class.new( fill )
|
110
|
+
even, odd = ( width - w ).divmod( 2 )
|
111
|
+
insert( 0, fill.tile( even ) )
|
112
|
+
self << fill.tile( even + odd )
|
113
|
+
end
|
114
|
+
self
|
115
|
+
end
|
116
|
+
|
117
|
+
def divide_at( len )
|
118
|
+
pos = char_byte( len )
|
119
|
+
return( [ self[ 0, pos ], self[ pos, size ] ] )
|
120
|
+
end
|
121
|
+
|
122
|
+
def each_escape
|
123
|
+
block_given? or return( enum_for( :each_escape ) )
|
124
|
+
scan( COLOR_ESCAPE ) do |esc|
|
125
|
+
yield( esc )
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def escapes
|
130
|
+
each_escape.inject( self.class.new ) do | escs, esc |
|
131
|
+
escs << esc
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def indent( n )
|
136
|
+
dup.indent!( n )
|
137
|
+
end
|
138
|
+
|
139
|
+
def indent!( num_spaces )
|
140
|
+
if num_spaces < 0
|
141
|
+
remaining_indent = Utils.at_least( level_of_indent + num_spaces, 0 )
|
142
|
+
lstrip!
|
143
|
+
indent!( remaining_indent )
|
144
|
+
else
|
145
|
+
insert( 0, ' ' * num_spaces )
|
146
|
+
end
|
147
|
+
return( self )
|
148
|
+
end
|
149
|
+
|
150
|
+
def invisible_size
|
151
|
+
@@invisible_size[ hash ] ||= size - width
|
152
|
+
end
|
153
|
+
|
154
|
+
def left!( w, fill = ' ' )
|
155
|
+
w > width or return( self )
|
156
|
+
if fill.length == 1
|
157
|
+
replace( ljust( w + invisible_size, fill ) )
|
158
|
+
else fill = self.class.new( fill )
|
159
|
+
insert( 0, fill.tile( width - w ) )
|
160
|
+
end
|
161
|
+
self
|
162
|
+
end
|
163
|
+
|
164
|
+
def level_of_indent
|
165
|
+
self =~ /^(\s+)/ ? $1.length : 0
|
166
|
+
end
|
167
|
+
|
168
|
+
def pad!( left, right = left )
|
169
|
+
right!( width + left )
|
170
|
+
left!( width + right )
|
171
|
+
end
|
172
|
+
|
173
|
+
def partial(len)
|
174
|
+
self[ 0, char_byte( len ) ]
|
175
|
+
end
|
176
|
+
|
177
|
+
def right!( w, fill = ' ' )
|
178
|
+
w > width or return( self )
|
179
|
+
if fill.length == 1
|
180
|
+
replace( rjust( w + invisible_size, fill ) )
|
181
|
+
else fill = self.class.new( fill )
|
182
|
+
self.insert( 0, fill.tile( w - width ) )
|
183
|
+
end
|
184
|
+
self
|
185
|
+
end
|
186
|
+
|
187
|
+
def tile( size )
|
188
|
+
width == 0 and return( )
|
189
|
+
full, partial = size.divmod( width )
|
190
|
+
self * full << partial( partial )
|
191
|
+
end
|
192
|
+
|
193
|
+
def truncate( w, tail = nil )
|
194
|
+
dup.truncate!( w, tail )
|
195
|
+
end
|
196
|
+
|
197
|
+
def truncate!( w, tail = nil )
|
198
|
+
width > w or return( self )
|
199
|
+
if tail then tail = Line( tail )
|
200
|
+
return( partial( w - tail.width ) << tail )
|
201
|
+
else
|
202
|
+
return( partial( w ) )
|
203
|
+
end
|
204
|
+
return( self )
|
205
|
+
end
|
206
|
+
|
207
|
+
def words
|
208
|
+
strip.split(/\s+/)
|
209
|
+
end
|
210
|
+
|
211
|
+
def height
|
212
|
+
1
|
213
|
+
end
|
214
|
+
|
215
|
+
def wrap( w )
|
216
|
+
if width > w
|
217
|
+
words = split( /\s+/ ).inject( [] ) do | words, word |
|
218
|
+
while word.width > w
|
219
|
+
frag, word = word.divide_at( w )
|
220
|
+
words << frag
|
221
|
+
end
|
222
|
+
words << word
|
223
|
+
end
|
224
|
+
|
225
|
+
line = words.shift || self.class.new
|
226
|
+
text = Text.new
|
227
|
+
w -= 1
|
228
|
+
while word = words.shift
|
229
|
+
if line.width + word.width > w
|
230
|
+
text << line
|
231
|
+
line = word
|
232
|
+
else
|
233
|
+
line << ' ' << word
|
234
|
+
end
|
235
|
+
end
|
236
|
+
text << line
|
237
|
+
return( text )
|
238
|
+
else
|
239
|
+
return( Text( self.dup ) )
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
class Text < Array
|
245
|
+
def self.clear_cache
|
246
|
+
@@width.clear
|
247
|
+
@@uniform.clear
|
248
|
+
return( self )
|
249
|
+
end
|
250
|
+
|
251
|
+
include MonoclePrint
|
252
|
+
@@width = {}
|
253
|
+
@@uniform = {}
|
254
|
+
|
255
|
+
def initialize( lines = nil, default = nil )
|
256
|
+
case lines
|
257
|
+
when Fixnum
|
258
|
+
if block_given?
|
259
|
+
super( lines ) { | i | Line( yield( i ) ) }
|
260
|
+
else
|
261
|
+
default = Line( default )
|
262
|
+
super( lines, default )
|
263
|
+
end
|
264
|
+
when Text then super( lines )
|
265
|
+
when Array
|
266
|
+
super( lines.join( $/ ).map { | l | Line( l.chomp! || l ) } )
|
267
|
+
when SingleLine then super(1, lines)
|
268
|
+
when nil then super()
|
269
|
+
else
|
270
|
+
super( lines.to_s.lines.map { |l| Line( l.chomp! || l ) } )
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def initialize_copy( orig )
|
275
|
+
for line in orig
|
276
|
+
self << line.dup
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def align( alignment, width, fill = ' ' )
|
281
|
+
dup.align!( alignment, width, fill )
|
282
|
+
end
|
283
|
+
|
284
|
+
def align!( alignment, width, fill = ' ' )
|
285
|
+
# if the width argument is less than the full block width of the text,
|
286
|
+
# make it the full block width to ensure uniform width
|
287
|
+
width = Utils.at_least( width, self.width )
|
288
|
+
|
289
|
+
if empty?
|
290
|
+
self << SingleLine.new( ' ' * width )
|
291
|
+
else
|
292
|
+
map! do | line |
|
293
|
+
line.align( alignment, width, fill )
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
return( self )
|
298
|
+
end
|
299
|
+
|
300
|
+
def bleach
|
301
|
+
dup.bleach!
|
302
|
+
end
|
303
|
+
|
304
|
+
def bleach!
|
305
|
+
each { | l | l.bleach! }
|
306
|
+
end
|
307
|
+
|
308
|
+
def fix
|
309
|
+
dup.fix!
|
310
|
+
end
|
311
|
+
|
312
|
+
def fix!
|
313
|
+
align!( :left, width )
|
314
|
+
end
|
315
|
+
|
316
|
+
def fixed_indent!( level = 0 )
|
317
|
+
level = Utils.at_least( level, 0 )
|
318
|
+
offset = level - level_of_indent
|
319
|
+
indent!( offset )
|
320
|
+
end
|
321
|
+
|
322
|
+
def fixed_indent( level = 0 )
|
323
|
+
dup.fixed_indent!( level )
|
324
|
+
end
|
325
|
+
|
326
|
+
def level_of_indent
|
327
|
+
level = nil
|
328
|
+
levels = map { | line | line.blank? ? nil : line.level_of_indent }
|
329
|
+
levels.compact!
|
330
|
+
levels.min || 0
|
331
|
+
end
|
332
|
+
|
333
|
+
def indent!( spaces )
|
334
|
+
for line in self do line.indent!( spaces ) end
|
335
|
+
self
|
336
|
+
end
|
337
|
+
|
338
|
+
def indent( spaces )
|
339
|
+
dup.indent!( spaces )
|
340
|
+
end
|
341
|
+
|
342
|
+
def frame( graphic_style )
|
343
|
+
dup.frame!( graphic_style )
|
344
|
+
end
|
345
|
+
|
346
|
+
def frame!( graphic_style )
|
347
|
+
top = graphic_style.box_top( width )
|
348
|
+
bottom = graphic_style.box_bottom( width )
|
349
|
+
for line in self
|
350
|
+
line.insert( 0, graphic_style.v )
|
351
|
+
line.insert( -1, graphic_style.v )
|
352
|
+
end
|
353
|
+
unshift( top )
|
354
|
+
push( bottom )
|
355
|
+
return( self )
|
356
|
+
end
|
357
|
+
|
358
|
+
def inspect
|
359
|
+
digits = Math.log10( Utils.at_least( length, 1 ) ).floor + 1
|
360
|
+
$/ + each_with_index.map do | line, i |
|
361
|
+
line_no = i.to_s.rjust( digits )
|
362
|
+
"#{ line_no } | #{ line }"
|
363
|
+
end.join( $/ ) + $/
|
364
|
+
end
|
365
|
+
|
366
|
+
def juxtapose( text, joint = ' ' )
|
367
|
+
dup.juxtapose!( text, joint )
|
368
|
+
end
|
369
|
+
|
370
|
+
def juxtapose!( text, joint = ' ' )
|
371
|
+
text = self.class.new( text ).fix!.valign!( :top, height )
|
372
|
+
valign!( :top, text.height ); fix!
|
373
|
+
|
374
|
+
zip( text ) do | left, right |
|
375
|
+
left << joint.to_s << right
|
376
|
+
end
|
377
|
+
return( self )
|
378
|
+
end
|
379
|
+
|
380
|
+
def pad( padding )
|
381
|
+
dup.pad!( padding )
|
382
|
+
end
|
383
|
+
|
384
|
+
def pad!( padding )
|
385
|
+
padding.top.times { unshift( Line('') ) }
|
386
|
+
padding.bottom.times { push( Line('') ) }
|
387
|
+
w = width
|
388
|
+
for line in self
|
389
|
+
line.left!( w )
|
390
|
+
line.pad!( padding.left, padding.right )
|
391
|
+
end
|
392
|
+
self
|
393
|
+
end
|
394
|
+
|
395
|
+
def reflow( paragraph_style = true )
|
396
|
+
if paragraph_style
|
397
|
+
cursor, new = 0, self.class.new
|
398
|
+
text = self.to_s
|
399
|
+
while text =~ /\n\s*\n/
|
400
|
+
before, text = $`, $'
|
401
|
+
new << `#{ before.gsub!( /[ \t]*\n/, ' ' ) || before }`
|
402
|
+
new << ``
|
403
|
+
end
|
404
|
+
new << `#{ text.gsub!( /[ \t]*\n/, ' ' ) || text }`
|
405
|
+
return( new )
|
406
|
+
else
|
407
|
+
text = self.to_s
|
408
|
+
text.strip!
|
409
|
+
text.gsub!( /\s+/, ' ' )
|
410
|
+
self.class.new( text )
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
def reflow!( paragraph_style = true )
|
415
|
+
replace( reflow( paragraph_style ) )
|
416
|
+
end
|
417
|
+
|
418
|
+
def wrap( width )
|
419
|
+
reflow.inject( self.class.new ) do | wrapped, line |
|
420
|
+
wrapped.concat( line.wrap( width ) )
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
def to_s
|
425
|
+
join( $/ )
|
426
|
+
end
|
427
|
+
|
428
|
+
def uniform?
|
429
|
+
@@uniform.fetch( hash ) do | h |
|
430
|
+
@@uniform[ h ] = all? { | l | l.width == width }
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
def valign!( alignment, h, filler = '' )
|
435
|
+
filler = Line( filler )
|
436
|
+
h > height or return( self )
|
437
|
+
case alignment
|
438
|
+
when :top
|
439
|
+
fill( filler, height, h - height )
|
440
|
+
when :bottom
|
441
|
+
( h - height ).times { unshift( filler ) }
|
442
|
+
when :center
|
443
|
+
even, odd = ( h - height ).divmod( 2 )
|
444
|
+
even.times { push( filler ); unshift( filler ) }
|
445
|
+
odd == 1 and push( filler )
|
446
|
+
end
|
447
|
+
return( self )
|
448
|
+
end
|
449
|
+
|
450
|
+
def width
|
451
|
+
@@width[ hash ] ||= ( map { | line | line.width }.max || 0 )
|
452
|
+
end
|
453
|
+
|
454
|
+
alias height length
|
455
|
+
alias | juxtapose
|
456
|
+
def `(str) #` # comment here cos my editor's colorizing freaks out
|
457
|
+
SingleLine.new( str )
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
end
|