terminal_rb 0.7.0 → 0.9.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.
- checksums.yaml +4 -4
- data/lib/terminal/ansi.rb +35 -18
- data/lib/terminal/text.rb +114 -167
- data/lib/terminal/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 351b0ba282f1686000796ca789653308c722d201286ea7e1e6b99d2802db2b21
|
4
|
+
data.tar.gz: c25af6f120fc06e991b7f2a5f616141ac44d99a72a7d4569228d7822daa730a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31c49a47f6bf38aa08cb8e75e85fe5180d7d676b737392b38e52baebf1ff7669d9fb76d1037bfdb1e13a5683b36a434e41b4001ef2dfc56692ed1229cc7f044d
|
7
|
+
data.tar.gz: 2ba822e7bc9448d577f6f6649abb8b6c8f6fb9c44a76c86d9c9595c78cfd96e9dbda1451d6617e5c180e19a1c40dd0b7b09392fc1894dd3dbe858b7ab526c6da
|
data/lib/terminal/ansi.rb
CHANGED
@@ -154,7 +154,10 @@ module Terminal
|
|
154
154
|
#
|
155
155
|
# @param str [#to_s] string to be modified
|
156
156
|
# @return [String] string without ANSI attributes
|
157
|
-
def undecorate(str)
|
157
|
+
def undecorate(str)
|
158
|
+
str = str.to_s
|
159
|
+
str.index("\e") ? str.gsub(TEST, '') : str.dup
|
160
|
+
end
|
158
161
|
|
159
162
|
# Try to combine given ANSI attributes and colors.
|
160
163
|
# The attributes and colors have to be separated by given `separator``.
|
@@ -220,13 +223,13 @@ module Terminal
|
|
220
223
|
# @param str [#to_s] string to be modified
|
221
224
|
# @return [String] string with ANSI attributes
|
222
225
|
def bbcode(str)
|
223
|
-
str
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
226
|
+
str = str.to_s
|
227
|
+
return str.dup unless str.index('[')
|
228
|
+
str.gsub(BBCODE) do |match_str|
|
229
|
+
next match_str if (match = Regexp.last_match[1]).empty?
|
230
|
+
next "[#{match[1..]}]" if match[0] == '\\'
|
231
|
+
try_convert(match) || match_str
|
232
|
+
end
|
230
233
|
end
|
231
234
|
|
232
235
|
# Remove embedded BBCode-like attributes.
|
@@ -240,15 +243,15 @@ module Terminal
|
|
240
243
|
# @param str [#to_s] string to be modified
|
241
244
|
# @return [String] string without BBCode
|
242
245
|
def unbbcode(str)
|
243
|
-
str
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
246
|
+
str = str.to_s
|
247
|
+
return str.dup unless str.index('[')
|
248
|
+
str.gsub(BBCODE) do |match_str|
|
249
|
+
next match_str if (match = Regexp.last_match[1]).empty?
|
250
|
+
next "[#{match[1..]}]" if match[0] == '\\'
|
251
|
+
next match_str if (match = match.split).empty?
|
252
|
+
next if match.all? { ATTRIBUTES[_1] || COLORS[_1] || _color(_1) }
|
253
|
+
match_str
|
254
|
+
end
|
252
255
|
end
|
253
256
|
|
254
257
|
#
|
@@ -264,7 +267,21 @@ module Terminal
|
|
264
267
|
#
|
265
268
|
# @param str [#to_s] string to be modified
|
266
269
|
# @return [String] string without BBCode and ANSI control codes.
|
267
|
-
def plain(str)
|
270
|
+
def plain(str)
|
271
|
+
str = str.to_s
|
272
|
+
unless str.index('[')
|
273
|
+
return str.index("\e") ? str.gsub(TEST, '') : str.dup
|
274
|
+
end
|
275
|
+
str =
|
276
|
+
str.gsub(BBCODE) do |match_str|
|
277
|
+
next match_str if (match = Regexp.last_match[1]).empty?
|
278
|
+
next "[#{match[1..]}]" if match[0] == '\\'
|
279
|
+
next match_str if (match = match.split).empty?
|
280
|
+
next if match.all? { ATTRIBUTES[_1] || COLORS[_1] || _color(_1) }
|
281
|
+
match_str
|
282
|
+
end
|
283
|
+
str.index("\e") ? str.gsub!(TEST, '') : str
|
284
|
+
end
|
268
285
|
|
269
286
|
# Create nice colored text.
|
270
287
|
#
|
data/lib/terminal/text.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require_relative 'ansi'
|
4
4
|
|
5
5
|
module Terminal
|
6
|
+
# Text helper functions.
|
7
|
+
#
|
6
8
|
module Text
|
7
9
|
class << self
|
8
10
|
# Value for {width} of letters whose display width is not precisely
|
@@ -31,46 +33,85 @@ module Terminal
|
|
31
33
|
str = str.encode(ENC) if str.encoding != ENC
|
32
34
|
width = 0
|
33
35
|
str.scan(WIDTH_SCANNER) do |sp, gc|
|
34
|
-
next width +=
|
36
|
+
next width += char_width(gc) if gc
|
35
37
|
width += 1 if sp
|
36
38
|
end
|
37
39
|
width
|
38
40
|
end
|
39
41
|
|
40
42
|
# Iterate each line of given text.
|
41
|
-
# It can optionally
|
42
43
|
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
# @param
|
50
|
-
#
|
51
|
-
# @
|
52
|
-
#
|
53
|
-
# @
|
44
|
+
# @param [#to_s, ...] text
|
45
|
+
# text objects to process
|
46
|
+
# @param [#to_i, nil] limit
|
47
|
+
# optionally limit line size
|
48
|
+
# @param [true, false] bbcode
|
49
|
+
# whether to interprete embedded BBCode (see {Ansi.bbcode})
|
50
|
+
# @param [true, false] ansi
|
51
|
+
# whether to keep embedded ANSI control codes
|
52
|
+
# @param [true, false] ignore_newline
|
53
|
+
# wheter to ignore embedded line breaks (`"\r\n"` or `"\n"`)
|
54
|
+
# @yield [String] text line
|
55
|
+
# @return [Enumerator] when no block given
|
56
|
+
# @return [nil]
|
57
|
+
# @raise ArgumentError when a `limit` less than `1` is given
|
54
58
|
def each_line(
|
55
59
|
*text,
|
60
|
+
limit: nil,
|
56
61
|
bbcode: true,
|
57
62
|
ansi: true,
|
58
63
|
ignore_newline: false,
|
64
|
+
&block
|
65
|
+
)
|
66
|
+
if limit
|
67
|
+
limit = limit.to_i
|
68
|
+
raise(ArgumentError, "invalid limit - #{limit}") if limit < 1
|
69
|
+
if block_given?
|
70
|
+
lines_in(text, bbcode, ansi, ignore_newline, limit, &block)
|
71
|
+
else
|
72
|
+
to_enum(:lines_in, text, bbcode, ansi, ignore_newline, limit)
|
73
|
+
end
|
74
|
+
elsif block_given?
|
75
|
+
lines(text, bbcode, ansi, ignore_newline, &block)
|
76
|
+
else
|
77
|
+
to_enum(:lines, text, bbcode, ansi, ignore_newline)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
alias each each_line
|
81
|
+
|
82
|
+
# Iterate each line and it's display width of given text.
|
83
|
+
#
|
84
|
+
# @param (see each_line)
|
85
|
+
# @yield [String, Integer] text line and it's display width
|
86
|
+
# @return (see each_line)
|
87
|
+
# @raise (see each_line)
|
88
|
+
def each_line_with_size(
|
89
|
+
*text,
|
59
90
|
limit: nil,
|
60
|
-
|
91
|
+
bbcode: true,
|
92
|
+
ansi: true,
|
93
|
+
ignore_newline: false,
|
61
94
|
&block
|
62
95
|
)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
96
|
+
if limit
|
97
|
+
limit = limit.to_i
|
98
|
+
raise(ArgumentError, "invalid limit - #{limit}") if limit < 1
|
99
|
+
if block_given?
|
100
|
+
pairs_in(text, bbcode, ansi, ignore_newline, limit, &block)
|
101
|
+
else
|
102
|
+
to_enum(:pairs_in, text, bbcode, ansi, ignore_newline, limit)
|
103
|
+
end
|
104
|
+
elsif block_given?
|
105
|
+
pairs(text, bbcode, ansi, ignore_newline, &block)
|
106
|
+
else
|
107
|
+
to_enum(:pairs, text, bbcode, ansi, ignore_newline)
|
108
|
+
end
|
69
109
|
end
|
110
|
+
alias each_with_size each_line_with_size
|
111
|
+
|
112
|
+
private
|
70
113
|
|
71
|
-
|
72
|
-
# works for UTF-8 chars only!
|
73
|
-
def __char_width(char)
|
114
|
+
def char_width(char)
|
74
115
|
ord = char.ord
|
75
116
|
return CONTROL_CHAR_WIDTH[ord] || 2 if ord < 0x20
|
76
117
|
return 1 if ord < 0xa1
|
@@ -80,127 +121,39 @@ module Terminal
|
|
80
121
|
# Halfwidth Dakuten Handakuten
|
81
122
|
sco == 0xff9e || sco == 0xff9f ? 2 : 1
|
82
123
|
end
|
83
|
-
end
|
84
124
|
|
85
|
-
|
86
|
-
# Internally used by {Text.each_line}.
|
87
|
-
class Wrap
|
88
|
-
# Create a new instance which may support different conditions.
|
89
|
-
#
|
90
|
-
# @param [#to_s, ...] text
|
91
|
-
# text objects to process
|
92
|
-
# @param [true, false] bbcode
|
93
|
-
# whether to interprete embedded BBCode (see {Ansi.bbcode})
|
94
|
-
# @param [true, false] ansi
|
95
|
-
# whether to keep embedded ANSI control codes
|
96
|
-
# @param [true, false] ignore_newline
|
97
|
-
# wheter to ignore embedded line breaks (`"\r\n"` or `"\n"`)
|
98
|
-
def initialize(*text, bbcode: true, ansi: true, ignore_newline: false)
|
99
|
-
@parts = []
|
125
|
+
def each_part(text, bbcode, ansi, ignore_newline)
|
100
126
|
newline = ignore_newline ? :space : :nl
|
101
127
|
text.each do |txt|
|
102
128
|
txt = bbcode ? Ansi.bbcode(txt) : txt.to_s
|
103
|
-
next
|
129
|
+
next yield(:hard_nl) if txt.empty?
|
104
130
|
txt = txt.encode(ENC) if txt.encoding != ENC
|
105
131
|
word = nil
|
106
|
-
txt.scan(
|
107
|
-
|
132
|
+
txt.scan(SCAN_EXPR) do |nl, csi, osc, space, gc|
|
133
|
+
if gc
|
134
|
+
next word.add(gc, char_width(gc)) if word
|
135
|
+
next word = Word.new(gc, char_width(gc))
|
136
|
+
end
|
137
|
+
yield(word) if word
|
108
138
|
word = nil
|
109
|
-
next
|
110
|
-
next
|
139
|
+
next yield(:space) if space
|
140
|
+
next yield(newline) if nl
|
111
141
|
next unless ansi
|
112
|
-
next
|
113
|
-
next
|
114
|
-
|
142
|
+
next yield(:seq, osc) if osc
|
143
|
+
next yield(:seq_end) if csi == "\e[m" || csi == "\e[0m"
|
144
|
+
yield(:seq, csi)
|
115
145
|
end
|
116
|
-
|
146
|
+
yield(word) if word
|
147
|
+
yield(:hard_nl)
|
117
148
|
end
|
118
|
-
end
|
119
|
-
|
120
|
-
# Iterate each line of given text.
|
121
|
-
#
|
122
|
-
# @param [#to_i, nil] limit
|
123
|
-
# optionally limit line size
|
124
|
-
# @param [true, false] with_size
|
125
|
-
# whether to yield each line with it's display width
|
126
|
-
# @yield [String] line when `with_size` is false
|
127
|
-
# @yield [<String, Integer>] line and it's display width
|
128
|
-
# when `with_size` is true
|
129
|
-
# @return [Enumerator] when no block is given
|
130
|
-
# @return [nil] when block is given
|
131
|
-
# @raise ArgumentError when a `limit` less than `1` is given
|
132
|
-
def each_line(limit: nil, with_size: false, &block)
|
133
|
-
unless limit
|
134
|
-
return with_size ? pairs(&block) : lines(&block) if block_given?
|
135
|
-
return to_enum(with_size ? :pairs : :lines)
|
136
|
-
end
|
137
|
-
limit = limit.to_i
|
138
|
-
raise(ArgumentError, "invalid limit - #{limit}") if limit < 1
|
139
|
-
if block_given?
|
140
|
-
return with_size ? pairs_in(limit, &block) : lines_in(limit, &block)
|
141
|
-
end
|
142
|
-
to_enum(with_size ? :pairs_in : :lines_in, limit)
|
143
|
-
end
|
144
|
-
alias each each_line
|
145
|
-
|
146
|
-
# @!visibility private
|
147
|
-
def each_word(with_size: false, &block)
|
148
|
-
return with_size ? word_pairs(&block) : words(&block) if block_given?
|
149
|
-
to_enum(with_size ? :word_pairs : :words)
|
150
|
-
end
|
151
|
-
|
152
|
-
# @!visibility private
|
153
|
-
def to_str
|
154
|
-
str = EMPTY.dup
|
155
|
-
seq = EMPTY.dup
|
156
|
-
@parts.each do |part, opt|
|
157
|
-
next str << part if part.is_a?(Word)
|
158
|
-
next str << ' ' if part == :space
|
159
|
-
|
160
|
-
if part == :nl
|
161
|
-
str << "\n"
|
162
|
-
next str << seq.dup
|
163
|
-
end
|
164
|
-
|
165
|
-
if part == :seq
|
166
|
-
str << opt
|
167
|
-
next seq << opt
|
168
|
-
end
|
169
|
-
|
170
|
-
# :seq_end
|
171
|
-
next if seq.empty?
|
172
|
-
str << "\e[m"
|
173
|
-
seq.clear
|
174
|
-
end
|
175
|
-
str
|
176
|
-
end
|
177
|
-
alias to_s to_str
|
178
|
-
|
179
|
-
# @!visibility private
|
180
|
-
def to_a(limit: nil, with_size: false)
|
181
|
-
each_line(limit: limit, with_size: with_size).to_a
|
182
|
-
end
|
183
|
-
|
184
|
-
# @!visibility private
|
185
|
-
def to_ary = each_line.to_a
|
186
|
-
|
187
|
-
private
|
188
|
-
|
189
|
-
def words
|
190
|
-
@parts.each { yield(_1.to_s) if _1.is_a?(Word) }
|
191
149
|
nil
|
192
150
|
end
|
193
151
|
|
194
|
-
def
|
195
|
-
@parts.each { yield(_1.to_s, _1.size) if _1.is_a?(Word) }
|
196
|
-
nil
|
197
|
-
end
|
198
|
-
|
199
|
-
def lines
|
152
|
+
def lines(text, bbcode, ansi, ignore_newline)
|
200
153
|
current = EMPTY.dup
|
201
154
|
seq = EMPTY.dup
|
202
155
|
lws = nil
|
203
|
-
|
156
|
+
each_part(text, bbcode, ansi, ignore_newline) do |part, opt|
|
204
157
|
if part == :space
|
205
158
|
next if lws
|
206
159
|
current << ' '
|
@@ -239,12 +192,12 @@ module Terminal
|
|
239
192
|
nil
|
240
193
|
end
|
241
194
|
|
242
|
-
def lines_in(limit)
|
195
|
+
def lines_in(text, bbcode, ansi, ignore_newline, limit)
|
243
196
|
current = EMPTY.dup
|
244
197
|
seq = EMPTY.dup
|
245
198
|
width = 0
|
246
199
|
lws = nil
|
247
|
-
|
200
|
+
each_part(text, bbcode, ansi, ignore_newline) do |part, opt|
|
248
201
|
if part == :space
|
249
202
|
next if lws
|
250
203
|
if width.succ < limit
|
@@ -322,12 +275,12 @@ module Terminal
|
|
322
275
|
nil
|
323
276
|
end
|
324
277
|
|
325
|
-
def pairs
|
278
|
+
def pairs(text, bbcode, ansi, ignore_newline)
|
326
279
|
current = EMPTY.dup
|
327
280
|
seq = EMPTY.dup
|
328
281
|
width = 0
|
329
282
|
lws = nil
|
330
|
-
|
283
|
+
each_part(text, bbcode, ansi, ignore_newline) do |part, opt|
|
331
284
|
if part == :space
|
332
285
|
next if lws
|
333
286
|
current << ' '
|
@@ -371,12 +324,12 @@ module Terminal
|
|
371
324
|
nil
|
372
325
|
end
|
373
326
|
|
374
|
-
def pairs_in(limit)
|
327
|
+
def pairs_in(text, bbcode, ansi, ignore_newline, limit)
|
375
328
|
current = EMPTY.dup
|
376
329
|
seq = EMPTY.dup
|
377
330
|
width = 0
|
378
331
|
lws = nil
|
379
|
-
|
332
|
+
each_part(text, bbcode, ansi, ignore_newline) do |part, opt|
|
380
333
|
if part == :space
|
381
334
|
next if lws
|
382
335
|
if width.succ < limit
|
@@ -457,46 +410,40 @@ module Terminal
|
|
457
410
|
end
|
458
411
|
nil
|
459
412
|
end
|
413
|
+
end
|
460
414
|
|
461
|
-
|
462
|
-
attr_reader :to_str, :size, :chars
|
415
|
+
@ambiguous_char_width = 1
|
463
416
|
|
464
|
-
|
465
|
-
|
466
|
-
@size = Text.__char_width(char)
|
467
|
-
@chars = [[char, @size]]
|
468
|
-
end
|
417
|
+
class Word
|
418
|
+
attr_reader :to_str, :size, :chars
|
469
419
|
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
420
|
+
def initialize(char, size)
|
421
|
+
@to_str = char.dup
|
422
|
+
@size = size
|
423
|
+
@chars = [[char, size]]
|
424
|
+
end
|
425
|
+
|
426
|
+
def add(char, size)
|
427
|
+
@to_str << char
|
428
|
+
@size += size
|
429
|
+
@chars << [char, size]
|
430
|
+
nil
|
476
431
|
end
|
477
|
-
private_constant :Word
|
478
|
-
|
479
|
-
REGEXP =
|
480
|
-
/\G(?:
|
481
|
-
(\r?\n)
|
482
|
-
| (\e\[[\d;:\?]*[ABCDEFGHJKSTfminsuhl])
|
483
|
-
| (\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
|
484
|
-
| (\s+)
|
485
|
-
| (\X)
|
486
|
-
)/x
|
487
|
-
private_constant :REGEXP
|
488
|
-
|
489
|
-
ENC = Encoding::UTF_8
|
490
|
-
private_constant :ENC
|
491
|
-
|
492
|
-
EMPTY = String.new(encoding: ENC).freeze
|
493
|
-
private_constant :EMPTY
|
494
432
|
end
|
433
|
+
private_constant :Word
|
495
434
|
|
496
435
|
ENC = Encoding::UTF_8
|
497
|
-
|
436
|
+
EMPTY = String.new(encoding: ENC).freeze
|
437
|
+
private_constant :ENC, :EMPTY
|
498
438
|
|
499
|
-
|
439
|
+
SCAN_EXPR =
|
440
|
+
/\G(?:
|
441
|
+
(\r?\n)
|
442
|
+
| (\e\[[\d;:\?]*[ABCDEFGHJKSTfminsuhl])
|
443
|
+
| (\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
|
444
|
+
| (\s+)
|
445
|
+
| (\X)
|
446
|
+
)/x
|
500
447
|
|
501
448
|
WIDTH_SCANNER =
|
502
449
|
/\G(?:
|
@@ -505,7 +452,7 @@ module Terminal
|
|
505
452
|
| (\s+)
|
506
453
|
| (\X)
|
507
454
|
)/x
|
508
|
-
private_constant :WIDTH_SCANNER
|
455
|
+
private_constant :SCAN_EXPR, :WIDTH_SCANNER
|
509
456
|
|
510
457
|
CONTROL_CHAR_WIDTH = {
|
511
458
|
0x00 => 0,
|
data/lib/terminal/version.rb
CHANGED