natty-ui 0.9.2 → 0.9.4
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/examples/3bit-colors.rb +2 -2
- data/examples/demo.rb +1 -1
- data/examples/illustration.rb +4 -2
- data/examples/table.rb +17 -12
- data/lib/natty-ui/ansi/constants.rb +75 -0
- data/lib/natty-ui/ansi.rb +6 -7
- data/lib/natty-ui/ansi_wrapper.rb +23 -33
- data/lib/natty-ui/line_animation/default.rb +1 -1
- data/lib/natty-ui/line_animation/matrix.rb +1 -1
- data/lib/natty-ui/line_animation/rainbow.rb +1 -1
- data/lib/natty-ui/line_animation/type_writer.rb +5 -25
- data/lib/natty-ui/line_animation.rb +2 -8
- data/lib/natty-ui/preload.rb +9 -0
- data/lib/natty-ui/text/east_asian_width.rb +1275 -0
- data/lib/natty-ui/text.rb +213 -0
- data/lib/natty-ui/version.rb +1 -1
- data/lib/natty-ui/wrapper/ask.rb +1 -1
- data/lib/natty-ui/wrapper/element.rb +1 -1
- data/lib/natty-ui/wrapper/horizontal_rule.rb +1 -1
- data/lib/natty-ui/wrapper/list_in_columns.rb +6 -8
- data/lib/natty-ui/wrapper/message.rb +1 -1
- data/lib/natty-ui/wrapper/query.rb +0 -1
- data/lib/natty-ui/wrapper/request.rb +1 -1
- data/lib/natty-ui/wrapper/section.rb +2 -2
- data/lib/natty-ui/wrapper/table.rb +10 -18
- data/lib/natty-ui/wrapper.rb +3 -22
- data/lib/natty-ui.rb +22 -80
- metadata +8 -6
- data/lib/natty-ui/ansi_constants.rb +0 -77
- data/lib/natty-ui/line_animation/test.rb +0 -29
- /data/lib/natty-ui/{features.rb → wrapper/features.rb} +0 -0
@@ -0,0 +1,213 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'ansi'
|
4
|
+
|
5
|
+
module NattyUI
|
6
|
+
module Text
|
7
|
+
class << self
|
8
|
+
def plain(str) = Ansi.blemish(plain_but_ansi(str))
|
9
|
+
|
10
|
+
def plain_but_ansi(str)
|
11
|
+
(str = str.to_s).empty? and return str
|
12
|
+
str.gsub(/(\[\[((?~\]\]))\]\])/) do
|
13
|
+
match = Regexp.last_match[2]
|
14
|
+
next match.size == 1 ? nil : "[[#{match[1..]}]]" if match[0] == '/'
|
15
|
+
Ansi.try_convert(match) ? nil : "[[#{match}]]"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def embellish(str)
|
20
|
+
(str = str.to_s).empty? and return str
|
21
|
+
reset = false
|
22
|
+
str =
|
23
|
+
str.gsub(/(\[\[((?~\]\]))\]\])/) do
|
24
|
+
match = Regexp.last_match[2]
|
25
|
+
if match[0] == '/'
|
26
|
+
next "[[#{match[1..]}]]" if match.size > 1
|
27
|
+
reset = false
|
28
|
+
next Ansi::RESET
|
29
|
+
end
|
30
|
+
ansi = Ansi.try_convert(match)
|
31
|
+
ansi ? reset = ansi : "[[#{match}]]"
|
32
|
+
end
|
33
|
+
reset ? "#{str}#{Ansi::RESET}" : str
|
34
|
+
end
|
35
|
+
|
36
|
+
def width(str)
|
37
|
+
return 0 if (str = plain(str)).empty?
|
38
|
+
str = str.encode(UTF_8) if str.encoding != UTF_8
|
39
|
+
width = 0
|
40
|
+
in_zero_width = false
|
41
|
+
str.scan(WIDTH_SCANNER) do |np_start, np_end, _csi, _osc, gc|
|
42
|
+
if in_zero_width
|
43
|
+
in_zero_width = false if np_end
|
44
|
+
next
|
45
|
+
end
|
46
|
+
next in_zero_width = true if np_start
|
47
|
+
width += char_width(gc) if gc
|
48
|
+
end
|
49
|
+
width
|
50
|
+
end
|
51
|
+
|
52
|
+
def char_width(char)
|
53
|
+
ord = char.ord
|
54
|
+
return SPECIAL_CHARS[ord] || 2 if ord <= 0x1f
|
55
|
+
return 1 if ord <= 0x7e
|
56
|
+
size = EastAsianWidth[ord]
|
57
|
+
return @ambiguous_char_width if size == -1
|
58
|
+
if size == 1 && char.size >= 2
|
59
|
+
sco = char[1].ord
|
60
|
+
# Halfwidth Dakuten Handakuten
|
61
|
+
return sco == 0xff9e || sco == 0xff9f ? 2 : 1
|
62
|
+
end
|
63
|
+
size
|
64
|
+
end
|
65
|
+
|
66
|
+
def simple_each_line(strs, &block)
|
67
|
+
strs.each { _1.to_s.each_line(chomp: true, &block) }
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def each_line(strs, max_width)
|
72
|
+
return if (max_width = max_width.to_i) <= 0
|
73
|
+
strs.each do |str|
|
74
|
+
str
|
75
|
+
.to_s
|
76
|
+
.each_line(chomp: true) do |line|
|
77
|
+
next yield(line) if line.empty?
|
78
|
+
current = String.new(encoding: line.encoding)
|
79
|
+
seq = current.dup
|
80
|
+
width = 0
|
81
|
+
in_zero_width = false
|
82
|
+
line = line.encode(UTF_8) if line.encoding != UTF_8
|
83
|
+
line.scan(WIDTH_SCANNER) do |np_start, np_end, csi, osc, gc|
|
84
|
+
next in_zero_width = (current << "\1") if np_start
|
85
|
+
next in_zero_width = !(current << "\2") if np_end
|
86
|
+
next (current << osc) && (seq << osc) if osc
|
87
|
+
if csi
|
88
|
+
current << csi
|
89
|
+
next if in_zero_width
|
90
|
+
next seq.clear if csi == "\e[m" || csi == "\e[0m"
|
91
|
+
next seq << csi
|
92
|
+
end
|
93
|
+
next current << gc if in_zero_width
|
94
|
+
cw = char_width(gc)
|
95
|
+
if (width += cw) > max_width
|
96
|
+
yield(current)
|
97
|
+
width = cw
|
98
|
+
current = seq.dup
|
99
|
+
end
|
100
|
+
current << gc
|
101
|
+
end
|
102
|
+
yield(current)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
nil
|
106
|
+
end
|
107
|
+
|
108
|
+
def each_line_with_size(strs, max_width)
|
109
|
+
return if (max_width = max_width.to_i) <= 0
|
110
|
+
strs.each do |str|
|
111
|
+
str
|
112
|
+
.to_s
|
113
|
+
.each_line(chomp: true) do |line|
|
114
|
+
next yield(line, 0) if line.empty?
|
115
|
+
current = String.new(encoding: line.encoding)
|
116
|
+
seq = current.dup
|
117
|
+
width = 0
|
118
|
+
in_zero_width = false
|
119
|
+
line = line.encode(UTF_8) if line.encoding != UTF_8
|
120
|
+
line.scan(WIDTH_SCANNER) do |np_start, np_end, csi, osc, gc|
|
121
|
+
next in_zero_width = (current << "\1") if np_start
|
122
|
+
next in_zero_width = !(current << "\2") if np_end
|
123
|
+
next (current << osc) && (seq << osc) if osc
|
124
|
+
if csi
|
125
|
+
current << csi
|
126
|
+
next if in_zero_width
|
127
|
+
next seq.clear if csi == "\e[m" || csi == "\e[0m"
|
128
|
+
next seq << csi
|
129
|
+
end
|
130
|
+
next current << gc if in_zero_width
|
131
|
+
cw = char_width(gc)
|
132
|
+
if (width += cw) > max_width
|
133
|
+
yield(current, width - cw)
|
134
|
+
width = cw
|
135
|
+
current = seq.dup
|
136
|
+
end
|
137
|
+
current << gc
|
138
|
+
end
|
139
|
+
yield(current, width)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
nil
|
143
|
+
end
|
144
|
+
|
145
|
+
def as_lines(strs, max_width)
|
146
|
+
ret = []
|
147
|
+
each_line(strs, max_width) { ret << _1 }
|
148
|
+
ret
|
149
|
+
end
|
150
|
+
|
151
|
+
def prepare_print(args, kwargs, screen_columns, &cvt)
|
152
|
+
prefix = kwargs[:prefix] and prefix = prefix.empty? ? '' : cvt[prefix]
|
153
|
+
suffix = kwargs[:suffix] and suffix = suffix.empty? ? '' : cvt[suffix]
|
154
|
+
return ["#{prefix}#{suffix}"] if args.empty?
|
155
|
+
ret = []
|
156
|
+
each_line(
|
157
|
+
args.map!(&cvt),
|
158
|
+
kwargs.fetch(:max_width) do
|
159
|
+
screen_columns.call -
|
160
|
+
kwargs.fetch(:prefix_width) { width(prefix) } -
|
161
|
+
kwargs.fetch(:suffix_width) { width(suffix) }
|
162
|
+
end
|
163
|
+
) { ret << "#{prefix}#{_1}#{suffix}" }
|
164
|
+
ret
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
UTF_8 = Encoding::UTF_8
|
169
|
+
|
170
|
+
WIDTH_SCANNER = /\G(?:(\1)|(\2)|(#{Ansi::CSI})|(#{Ansi::OSC})|(\X))/
|
171
|
+
|
172
|
+
SPECIAL_CHARS = {
|
173
|
+
0x00 => 0,
|
174
|
+
0x01 => 1,
|
175
|
+
0x02 => 1,
|
176
|
+
0x03 => 1,
|
177
|
+
0x04 => 1,
|
178
|
+
0x05 => 0,
|
179
|
+
0x06 => 1,
|
180
|
+
0x07 => 0,
|
181
|
+
0x08 => 0,
|
182
|
+
0x09 => 8,
|
183
|
+
0x0a => 0,
|
184
|
+
0x0b => 0,
|
185
|
+
0x0c => 0,
|
186
|
+
0x0d => 0,
|
187
|
+
0x0e => 0,
|
188
|
+
0x0f => 0,
|
189
|
+
0x10 => 1,
|
190
|
+
0x11 => 1,
|
191
|
+
0x12 => 1,
|
192
|
+
0x13 => 1,
|
193
|
+
0x14 => 1,
|
194
|
+
0x15 => 1,
|
195
|
+
0x16 => 1,
|
196
|
+
0x17 => 1,
|
197
|
+
0x18 => 1,
|
198
|
+
0x19 => 1,
|
199
|
+
0x1a => 1,
|
200
|
+
0x1b => 1,
|
201
|
+
0x1c => 1,
|
202
|
+
0x1d => 1,
|
203
|
+
0x1e => 1,
|
204
|
+
0x1f => 1
|
205
|
+
}.compare_by_identity.freeze
|
206
|
+
|
207
|
+
autoload(:EastAsianWidth, File.join(__dir__, 'text', 'east_asian_width'))
|
208
|
+
|
209
|
+
@ambiguous_char_width = 1
|
210
|
+
end
|
211
|
+
|
212
|
+
private_constant :Text
|
213
|
+
end
|
data/lib/natty-ui/version.rb
CHANGED
data/lib/natty-ui/wrapper/ask.rb
CHANGED
@@ -57,9 +57,7 @@ module NattyUI
|
|
57
57
|
return @parent if list.empty?
|
58
58
|
list.flatten!
|
59
59
|
cvt = cvt(glyph, list.size)
|
60
|
-
list.map!
|
61
|
-
Item.new(item = cvt[item], NattyUI.display_width(item))
|
62
|
-
end
|
60
|
+
list.map! { |item| Item.new(item = cvt[item], Text.width(item)) }
|
63
61
|
if compact
|
64
62
|
each_compacted(list, available_width - 1) { @parent.puts(_1) }
|
65
63
|
else
|
@@ -71,27 +69,27 @@ module NattyUI
|
|
71
69
|
def cvt(glyph, size)
|
72
70
|
case glyph
|
73
71
|
when nil, false
|
74
|
-
->(s) {
|
72
|
+
->(s) { Text.embellish(s) }
|
75
73
|
when :hex
|
76
74
|
pad = size.to_s(16).size
|
77
75
|
glyph = 0
|
78
76
|
lambda do |s|
|
79
|
-
"#{(glyph += 1).to_s(16).rjust(pad, '0')} #{
|
77
|
+
"#{(glyph += 1).to_s(16).rjust(pad, '0')} #{Text.embellish(s)}"
|
80
78
|
end
|
81
79
|
when Integer
|
82
80
|
pad = (glyph + size).to_s.size
|
83
81
|
glyph -= 1
|
84
|
-
->(s) { "#{(glyph += 1).to_s.rjust(pad)} #{
|
82
|
+
->(s) { "#{(glyph += 1).to_s.rjust(pad)} #{Text.embellish(s)}" }
|
85
83
|
when Symbol
|
86
84
|
lambda do |s|
|
87
85
|
"#{
|
88
86
|
t = glyph
|
89
87
|
glyph = glyph.succ
|
90
88
|
t
|
91
|
-
} #{
|
89
|
+
} #{Text.embellish(s)}"
|
92
90
|
end
|
93
91
|
else
|
94
|
-
->(s) { "#{glyph} #{
|
92
|
+
->(s) { "#{glyph} #{Text.embellish(s)}" }
|
95
93
|
end
|
96
94
|
end
|
97
95
|
|
@@ -103,9 +103,9 @@ module NattyUI
|
|
103
103
|
def initialize(
|
104
104
|
parent,
|
105
105
|
prefix:,
|
106
|
-
prefix_width:
|
106
|
+
prefix_width: Text.width(prefix),
|
107
107
|
suffix: nil,
|
108
|
-
suffix_width:
|
108
|
+
suffix_width: Text.width(suffix)
|
109
109
|
)
|
110
110
|
super(parent)
|
111
111
|
@prefix = prefix
|
@@ -53,8 +53,8 @@ module NattyUI
|
|
53
53
|
# %w[kiwi 1.5$ Newzeeland]
|
54
54
|
# )
|
55
55
|
def table(*table, type: :simple)
|
56
|
-
|
57
|
-
yield(table
|
56
|
+
return _element(:Table, table, type) unless block_given?
|
57
|
+
yield(table = Table.new(table))
|
58
58
|
_element(:Table, table.rows, type)
|
59
59
|
end
|
60
60
|
|
@@ -74,7 +74,7 @@ module NattyUI
|
|
74
74
|
# # kiwi: 1.5$
|
75
75
|
#
|
76
76
|
def pairs(seperator = ': ', **kwargs)
|
77
|
-
_element(:Pairs,
|
77
|
+
_element(:Pairs, kwargs.to_a, seperator)
|
78
78
|
end
|
79
79
|
|
80
80
|
class Table
|
@@ -93,11 +93,7 @@ module NattyUI
|
|
93
93
|
self
|
94
94
|
end
|
95
95
|
|
96
|
-
def initialize(
|
97
|
-
@rows = []
|
98
|
-
args.each { add_row(*_1) }
|
99
|
-
kwargs.each_pair { add_row(*_1) }
|
100
|
-
end
|
96
|
+
def initialize(rows) = (@rows = rows)
|
101
97
|
end
|
102
98
|
private_constant :Table
|
103
99
|
end
|
@@ -143,7 +139,7 @@ module NattyUI
|
|
143
139
|
rows,
|
144
140
|
@parent.available_width - 1,
|
145
141
|
seperator,
|
146
|
-
|
142
|
+
Text.plain(seperator)[-1] == ' '
|
147
143
|
) { @parent.puts(_1) }
|
148
144
|
@parent
|
149
145
|
end
|
@@ -170,7 +166,7 @@ module NattyUI
|
|
170
166
|
|
171
167
|
def self.each_simple_line(rows, max_width, col_div, first_right)
|
172
168
|
return if rows.empty?
|
173
|
-
gen = new(rows, max_width,
|
169
|
+
gen = new(rows, max_width, Text.width(col_div))
|
174
170
|
return unless gen.ok?
|
175
171
|
gen.aligns[0] = :right if first_right
|
176
172
|
gen.each { yield(_1.join(col_div)) }
|
@@ -182,7 +178,7 @@ module NattyUI
|
|
182
178
|
@rows =
|
183
179
|
rows.map do |row|
|
184
180
|
row.map do |col|
|
185
|
-
col =
|
181
|
+
col = Text.embellish(col).each_line(chomp: true).to_a
|
186
182
|
col.empty? ? col << '' : col
|
187
183
|
end
|
188
184
|
end
|
@@ -218,7 +214,7 @@ module NattyUI
|
|
218
214
|
private
|
219
215
|
|
220
216
|
def align(str, width, alignment)
|
221
|
-
return str unless (width -=
|
217
|
+
return str unless (width -= Text.width(str)).positive?
|
222
218
|
return str + (' ' * width) if alignment == :left
|
223
219
|
(' ' * width) << str
|
224
220
|
end
|
@@ -239,9 +235,7 @@ module NattyUI
|
|
239
235
|
diff.each do |col_idx|
|
240
236
|
adjust_to = adjusted[col_idx]
|
241
237
|
next if matrix[row_idx][col_idx] <= adjust_to
|
242
|
-
|
243
|
-
ary.pop if ary.last.empty?
|
244
|
-
row[col_idx] = ary
|
238
|
+
row[col_idx] = Text.as_lines(row[col_idx], adjust_to)
|
245
239
|
end
|
246
240
|
end
|
247
241
|
adjusted
|
@@ -249,9 +243,7 @@ module NattyUI
|
|
249
243
|
|
250
244
|
def create_matrix
|
251
245
|
ret =
|
252
|
-
@rows.map
|
253
|
-
row.map { |col| col.map { NattyUI.display_width(_1) }.max }
|
254
|
-
end
|
246
|
+
@rows.map { |row| row.map { |col| col.map { Text.width(_1) }.max } }
|
255
247
|
cc = ret.max_by(&:size).size
|
256
248
|
ret.each { (add = cc - _1.size).nonzero? and _1.fill(0, _1.size, add) }
|
257
249
|
end
|
data/lib/natty-ui/wrapper.rb
CHANGED
@@ -51,9 +51,8 @@ module NattyUI
|
|
51
51
|
# @param [#to_s] ... objects to print
|
52
52
|
# @return [Wrapper] itself
|
53
53
|
def puts(*args, **kwargs)
|
54
|
-
args = prepare_print(args, kwargs)
|
54
|
+
@stream.puts(args = prepare_print(args, kwargs))
|
55
55
|
@lines_written += args.size
|
56
|
-
@stream.puts(args)
|
57
56
|
@stream.flush
|
58
57
|
self
|
59
58
|
end
|
@@ -64,9 +63,8 @@ module NattyUI
|
|
64
63
|
# @param [#to_s] ... objects to print
|
65
64
|
# @return [Wrapper] itself
|
66
65
|
def print(*args, **kwargs)
|
67
|
-
args = prepare_print(args, kwargs)
|
66
|
+
@stream.print(*(args = prepare_print(args, kwargs)))
|
68
67
|
@lines_written += args.size - 1
|
69
|
-
@stream.print(*args)
|
70
68
|
@stream.flush
|
71
69
|
self
|
72
70
|
end
|
@@ -168,24 +166,7 @@ module NattyUI
|
|
168
166
|
protected
|
169
167
|
|
170
168
|
def prepare_print(args, kwargs)
|
171
|
-
|
172
|
-
end
|
173
|
-
|
174
|
-
def _prepare_print(args, kwargs, &cvt)
|
175
|
-
prefix = kwargs[:prefix] and prefix = prefix.empty? ? '' : cvt[prefix]
|
176
|
-
suffix = kwargs[:suffix] and suffix = suffix.empty? ? '' : cvt[suffix]
|
177
|
-
return ["#{prefix}#{suffix}"] if args.empty?
|
178
|
-
NattyUI
|
179
|
-
.each_line(
|
180
|
-
*args.map!(&cvt),
|
181
|
-
max_width:
|
182
|
-
kwargs.fetch(:max_width) do
|
183
|
-
screen_columns -
|
184
|
-
kwargs.fetch(:prefix_width) { NattyUI.display_width(prefix) } -
|
185
|
-
kwargs.fetch(:suffix_width) { NattyUI.display_width(suffix) }
|
186
|
-
end
|
187
|
-
)
|
188
|
-
.map { "#{prefix}#{_1}#{suffix}" }
|
169
|
+
Text.prepare_print(args, kwargs, -> { screen_columns }) { Text.plain(_1) }
|
189
170
|
end
|
190
171
|
|
191
172
|
def temp_func
|
data/lib/natty-ui.rb
CHANGED
@@ -1,14 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
# load the Reline::Unicode part only
|
5
|
-
# @!visibility private
|
6
|
-
module Reline
|
7
|
-
# @!visibility private
|
8
|
-
def self.ambiguous_width = 1
|
9
|
-
end
|
10
|
-
require 'reline/unicode'
|
11
|
-
end
|
3
|
+
require_relative 'natty-ui/text'
|
12
4
|
require_relative 'natty-ui/wrapper'
|
13
5
|
require_relative 'natty-ui/ansi_wrapper'
|
14
6
|
|
@@ -54,24 +46,24 @@ module NattyUI
|
|
54
46
|
wrapper_class(stream, ansi).__send__(:new, stream)
|
55
47
|
end
|
56
48
|
|
57
|
-
# Test if the given `stream` can be used for
|
49
|
+
# Test if the given `stream` can be used for input
|
58
50
|
#
|
59
51
|
# @param [IO] stream IO instance to test
|
60
52
|
# @return [Boolean] whether if the given stream is usable
|
61
|
-
def
|
62
|
-
(stream.is_a?(IO) && !stream.closed? && stream.stat.
|
63
|
-
(stream.is_a?(StringIO) && !stream.
|
53
|
+
def valid_in?(stream)
|
54
|
+
(stream.is_a?(IO) && !stream.closed? && stream.stat.readable?) ||
|
55
|
+
(stream.is_a?(StringIO) && !stream.closed_read?)
|
64
56
|
rescue StandardError
|
65
57
|
false
|
66
58
|
end
|
67
59
|
|
68
|
-
# Test if the given `stream` can be used for
|
60
|
+
# Test if the given `stream` can be used for output
|
69
61
|
#
|
70
62
|
# @param [IO] stream IO instance to test
|
71
63
|
# @return [Boolean] whether if the given stream is usable
|
72
|
-
def
|
73
|
-
(stream.is_a?(IO) && !stream.closed? && stream.stat.
|
74
|
-
(stream.is_a?(StringIO) && !stream.
|
64
|
+
def valid_out?(stream)
|
65
|
+
(stream.is_a?(IO) && !stream.closed? && stream.stat.writable?) ||
|
66
|
+
(stream.is_a?(StringIO) && !stream.closed_write?)
|
75
67
|
rescue StandardError
|
76
68
|
false
|
77
69
|
end
|
@@ -80,23 +72,7 @@ module NattyUI
|
|
80
72
|
#
|
81
73
|
# @param [#to_s] str string to edit
|
82
74
|
# @return [String] edited string
|
83
|
-
def embellish(str)
|
84
|
-
return +'' if (str = str.to_s).empty?
|
85
|
-
reset = false
|
86
|
-
str =
|
87
|
-
str.gsub(/(\[\[((?~\]\]))\]\])/) do
|
88
|
-
match = Regexp.last_match[2]
|
89
|
-
if match[0] == '/'
|
90
|
-
next "[[#{match[1..]}]]" if match.size > 1
|
91
|
-
reset = false
|
92
|
-
Ansi::RESET
|
93
|
-
else
|
94
|
-
ansi = Ansi.try_convert(match)
|
95
|
-
ansi ? reset = ansi : "[[#{match}]]"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
reset ? "#{str}#{Ansi::RESET}" : str
|
99
|
-
end
|
75
|
+
def embellish(str) = Text.embellish(str)
|
100
76
|
|
101
77
|
# Remove embedded attribute descriptions from given string.
|
102
78
|
#
|
@@ -104,14 +80,7 @@ module NattyUI
|
|
104
80
|
# @param [:keep,:remove] ansi keep or remove ANSI codes too
|
105
81
|
# @return [String] edited string
|
106
82
|
def plain(str, ansi: :keep)
|
107
|
-
|
108
|
-
str =
|
109
|
-
str.gsub(/(\[\[((?~\]\]))\]\])/) do
|
110
|
-
match = Regexp.last_match[2]
|
111
|
-
next match.size == 1 ? nil : "[[#{match[1..]}]]" if match[0] == '/'
|
112
|
-
Ansi.try_convert(match) ? nil : "[[#{match}]]"
|
113
|
-
end
|
114
|
-
ansi == :keep ? str : Ansi.blemish(str)
|
83
|
+
ansi == :keep ? Text.plain_but_ansi(str) : Text.plain(str)
|
115
84
|
end
|
116
85
|
|
117
86
|
# Calculate monospace (display) width of given String.
|
@@ -119,54 +88,24 @@ module NattyUI
|
|
119
88
|
#
|
120
89
|
# @param [#to_s] str string to calculate
|
121
90
|
# @return [Integer] the display size
|
122
|
-
def display_width(str)
|
123
|
-
str = plain(str).encode(Encoding::UTF_8)
|
124
|
-
return 0 if str.empty?
|
125
|
-
width = 0
|
126
|
-
in_zero_width = false
|
127
|
-
str.scan(Ansi::WIDTH_SCANNER) do |np_start, np_end, _csi, _osc, gc|
|
128
|
-
if in_zero_width
|
129
|
-
in_zero_width = false if np_end
|
130
|
-
next
|
131
|
-
end
|
132
|
-
next in_zero_width = true if np_start
|
133
|
-
width += Reline::Unicode.get_mbchar_width(gc) if gc
|
134
|
-
end
|
135
|
-
width
|
136
|
-
end
|
91
|
+
def display_width(str) = Text.width(str)
|
137
92
|
|
138
93
|
# Convert given arguments into strings and yield each line.
|
139
94
|
# Optionally limit the line width to given `max_width`.
|
140
95
|
#
|
141
96
|
# @overload each_line(..., max_width: nil)
|
142
|
-
# @param [#to_s] ... objects to
|
97
|
+
# @param [#to_s] ... objects converted to text lines
|
143
98
|
# @param [#to_i, nil] max_width maximum line width
|
144
99
|
# @yieldparam [String] line string line
|
145
100
|
# @return [nil]
|
146
101
|
# @overload each_line(..., max_width: nil)
|
147
|
-
# @param [#to_s] ... objects to
|
102
|
+
# @param [#to_s] ... objects converted to text lines
|
148
103
|
# @param [#to_i, nil] max_width maximum line width
|
149
104
|
# @return [Enumerator] line enumerator
|
150
105
|
def each_line(*strs, max_width: nil, &block)
|
151
106
|
return to_enum(__method__, *strs, max_width: max_width) unless block
|
152
|
-
|
153
|
-
|
154
|
-
return nil
|
155
|
-
end
|
156
|
-
return if (max_width = max_width.to_i) <= 0
|
157
|
-
strs.each do |str|
|
158
|
-
str
|
159
|
-
.to_s
|
160
|
-
.each_line(chomp: true) do |line|
|
161
|
-
next yield(line) if line.empty?
|
162
|
-
lines, _height = Reline::Unicode.split_by_width(line, max_width)
|
163
|
-
lines.compact!
|
164
|
-
next if lines.empty?
|
165
|
-
lines.pop if lines[-1].empty?
|
166
|
-
lines.each(&block)
|
167
|
-
end
|
168
|
-
end
|
169
|
-
nil
|
107
|
+
return Text.simple_each_line(strs, &block) unless max_width
|
108
|
+
Text.each_line(strs, max_width, &block)
|
170
109
|
end
|
171
110
|
|
172
111
|
# Read next raw key (keyboard input) from {in_stream}.
|
@@ -219,11 +158,14 @@ module NattyUI
|
|
219
158
|
# Instance for standard error output.
|
220
159
|
StdErr = stderr_is_stdout? ? StdOut : new(STDERR)
|
221
160
|
|
161
|
+
dir = __dir__
|
162
|
+
autoload(:LineAnimation, File.join(dir, 'natty-ui', 'line_animation'))
|
163
|
+
autoload(:KEY_MAP, File.join(dir, 'natty-ui', 'key_map'))
|
164
|
+
|
165
|
+
private_constant :LineAnimation, :KEY_MAP
|
166
|
+
|
222
167
|
@element = StdOut
|
223
168
|
self.in_stream = STDIN
|
224
|
-
|
225
|
-
autoload(:KEY_MAP, File.join(__dir__, 'natty-ui', 'key_map'))
|
226
|
-
private_constant :KEY_MAP
|
227
169
|
end
|
228
170
|
|
229
171
|
# @!visibility private
|