natty-ui 0.29.0 → 0.31.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/README.md +5 -4
- data/examples/cols.rb +4 -6
- data/examples/hbars.rb +2 -3
- data/examples/illustration.rb +13 -14
- data/examples/info.rb +9 -13
- data/examples/named-colors.rb +3 -5
- data/examples/tables.rb +5 -8
- data/examples/tasks.rb +14 -14
- data/examples/themes.rb +51 -0
- data/lib/natty-ui/attributes.rb +37 -38
- data/lib/natty-ui/features.rb +111 -73
- data/lib/natty-ui/hbars_renderer.rb +51 -43
- data/lib/natty-ui/progress.rb +5 -7
- data/lib/natty-ui/shell_command.rb +132 -0
- data/lib/natty-ui/table.rb +47 -12
- data/lib/natty-ui/table_renderer.rb +3 -2
- data/lib/natty-ui/theme.rb +326 -282
- data/lib/natty-ui/utils.rb +3 -3
- data/lib/natty-ui/vbars_renderer.rb +1 -0
- data/lib/natty-ui/version.rb +1 -1
- data/lib/natty-ui.rb +3 -1
- metadata +9 -7
data/lib/natty-ui/features.rb
CHANGED
|
@@ -37,52 +37,61 @@ module NattyUI
|
|
|
37
37
|
# @return [Features]
|
|
38
38
|
# itself
|
|
39
39
|
def puts(*text, **options)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
max_width *= Terminal.columns
|
|
47
|
-
elsif max_width < 0
|
|
48
|
-
max_width += Terminal.columns
|
|
49
|
-
end
|
|
50
|
-
end
|
|
40
|
+
if options.empty?
|
|
41
|
+
bbcode = true
|
|
42
|
+
max_width = Terminal.columns
|
|
43
|
+
else
|
|
44
|
+
bbcode = true if (bbcode = options[:bbcode]).nil?
|
|
45
|
+
ignore_newline = options[:eol] == false || options[:ignore_newline]
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
0
|
|
47
|
+
if (max_width = options[:max_width]).nil?
|
|
48
|
+
return self if (max_width = Terminal.columns).zero?
|
|
49
|
+
elsif max_width < 1
|
|
50
|
+
if max_width > 0
|
|
51
|
+
max_width *= Terminal.columns
|
|
52
|
+
elsif max_width < 0
|
|
53
|
+
max_width += Terminal.columns
|
|
54
|
+
else
|
|
55
|
+
return self
|
|
56
|
+
end
|
|
58
57
|
end
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
prefix_width =
|
|
60
|
+
if (prefix = options[:prefix])
|
|
61
|
+
prefix = Ansi.bbcode(prefix) if bbcode
|
|
62
|
+
options[:prefix_width] || Text.width(prefix, bbcode: false)
|
|
63
|
+
else
|
|
64
|
+
0
|
|
65
|
+
end
|
|
65
66
|
|
|
66
|
-
if
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
67
|
+
if (first_line = options[:first_line_prefix])
|
|
68
|
+
first_line = Ansi.bbcode(first_line) if bbcode
|
|
69
|
+
first_line_width =
|
|
70
|
+
options[:first_line_prefix_width] ||
|
|
71
|
+
Text.width(first_line, bbcode: false)
|
|
72
|
+
|
|
73
|
+
if prefix_width < first_line_width
|
|
74
|
+
prefix_next = "#{prefix}#{' ' * (first_line_width - prefix_width)}"
|
|
75
|
+
prefix = first_line
|
|
76
|
+
prefix_width = first_line_width
|
|
77
|
+
else
|
|
78
|
+
prefix_next = prefix
|
|
79
|
+
prefix =
|
|
80
|
+
if first_line_width < prefix_width
|
|
81
|
+
first_line + (' ' * (prefix_width - first_line_width))
|
|
82
|
+
else
|
|
83
|
+
first_line
|
|
84
|
+
end
|
|
85
|
+
end
|
|
78
86
|
end
|
|
79
|
-
end
|
|
80
87
|
|
|
81
|
-
|
|
88
|
+
max_width -= prefix_width
|
|
82
89
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
90
|
+
if (suffix = options[:suffix])
|
|
91
|
+
suffix = Ansi.bbcode(suffix) if bbcode
|
|
92
|
+
max_width -=
|
|
93
|
+
options[:suffix_width] || Text.width(suffix, bbcode: false)
|
|
94
|
+
end
|
|
86
95
|
end
|
|
87
96
|
|
|
88
97
|
return self if max_width <= 0
|
|
@@ -93,12 +102,14 @@ module NattyUI
|
|
|
93
102
|
limit: max_width,
|
|
94
103
|
bbcode: bbcode,
|
|
95
104
|
ansi: Terminal.ansi?,
|
|
96
|
-
ignore_newline:
|
|
105
|
+
ignore_newline: ignore_newline
|
|
97
106
|
)
|
|
98
107
|
|
|
108
|
+
@__eol ||= Terminal.ansi? ? "\e[m\n" : "\n"
|
|
109
|
+
|
|
99
110
|
if (align = options[:align]).nil?
|
|
100
111
|
lines.each do |line|
|
|
101
|
-
Terminal.print(prefix, line, suffix, __eol, bbcode: false)
|
|
112
|
+
Terminal.print(prefix, line, suffix, @__eol, bbcode: false)
|
|
102
113
|
@lines_written += 1
|
|
103
114
|
prefix, prefix_next = prefix_next, nil if prefix_next
|
|
104
115
|
end
|
|
@@ -118,7 +129,7 @@ module NattyUI
|
|
|
118
129
|
' ' * (max_width - width),
|
|
119
130
|
line,
|
|
120
131
|
suffix,
|
|
121
|
-
__eol,
|
|
132
|
+
@__eol,
|
|
122
133
|
bbcode: false
|
|
123
134
|
)
|
|
124
135
|
@lines_written += 1
|
|
@@ -133,7 +144,7 @@ module NattyUI
|
|
|
133
144
|
line,
|
|
134
145
|
' ' * (space - lw),
|
|
135
146
|
suffix,
|
|
136
|
-
__eol,
|
|
147
|
+
@__eol,
|
|
137
148
|
bbcode: false
|
|
138
149
|
)
|
|
139
150
|
@lines_written += 1
|
|
@@ -146,7 +157,7 @@ module NattyUI
|
|
|
146
157
|
line,
|
|
147
158
|
' ' * (max_width - width),
|
|
148
159
|
suffix,
|
|
149
|
-
__eol,
|
|
160
|
+
@__eol,
|
|
150
161
|
bbcode: false
|
|
151
162
|
)
|
|
152
163
|
@lines_written += 1
|
|
@@ -301,7 +312,7 @@ module NattyUI
|
|
|
301
312
|
#
|
|
302
313
|
# @return (see puts)
|
|
303
314
|
def space(count = 1)
|
|
304
|
-
puts("\n" * count)
|
|
315
|
+
(count = count.to_i).positive? ? puts("\n" * count) : self
|
|
305
316
|
end
|
|
306
317
|
|
|
307
318
|
# Print given items as list (like 'ls' command).
|
|
@@ -474,10 +485,11 @@ module NattyUI
|
|
|
474
485
|
# ui.hbars 1..10, style: :blue, width: 0.5
|
|
475
486
|
#
|
|
476
487
|
# @param values [#to_a, Array<Numeric>] values to print
|
|
477
|
-
# @param
|
|
488
|
+
# @param min [#to_f] start value
|
|
489
|
+
# @param max [#to_f] end value
|
|
478
490
|
# @param normalize [true, false] whether the values should be normalized
|
|
479
|
-
# @param
|
|
480
|
-
# @param
|
|
491
|
+
# @param text [true, false] whether the values should be printed too
|
|
492
|
+
# @param width [:auto, :min, Integer] with of each bar
|
|
481
493
|
# @param style [Symbol, Array<Symbol>, nil] bar drawing style
|
|
482
494
|
# @param text_style [Symbol, Array<Symbol>, nil] text style
|
|
483
495
|
#
|
|
@@ -486,8 +498,10 @@ module NattyUI
|
|
|
486
498
|
# @return (see puts)
|
|
487
499
|
def hbars(
|
|
488
500
|
values,
|
|
489
|
-
|
|
501
|
+
min: nil,
|
|
502
|
+
max: nil,
|
|
490
503
|
normalize: false,
|
|
504
|
+
text: true,
|
|
491
505
|
width: :auto,
|
|
492
506
|
style: nil,
|
|
493
507
|
text_style: nil
|
|
@@ -497,12 +511,9 @@ module NattyUI
|
|
|
497
511
|
raise(ArgumentError, 'values can not be negative')
|
|
498
512
|
end
|
|
499
513
|
style = text_style = nil unless Terminal.ansi?
|
|
500
|
-
|
|
501
|
-
if
|
|
502
|
-
|
|
503
|
-
else
|
|
504
|
-
puts(*HBarsRenderer.lines_bars_only(values, size, normalize, style))
|
|
505
|
-
end
|
|
514
|
+
renderer = HBarsRenderer.new(values, min, max)
|
|
515
|
+
renderer.with_text(text_style) if text
|
|
516
|
+
puts(*renderer.lines(Utils.as_size(3..columns, width), style, normalize))
|
|
506
517
|
end
|
|
507
518
|
|
|
508
519
|
# Dynamically display a task progress.
|
|
@@ -567,6 +578,31 @@ module NattyUI
|
|
|
567
578
|
block ? __with(progress, &block) : progress
|
|
568
579
|
end
|
|
569
580
|
|
|
581
|
+
# Run a shell program
|
|
582
|
+
#
|
|
583
|
+
# @return [Process::Status] when command was executed
|
|
584
|
+
# @return [nil] in error case (like command not found)
|
|
585
|
+
def sh(*cmd, shell: false, input: nil)
|
|
586
|
+
m = (theme = Theme.current).mark(:sh_out)
|
|
587
|
+
opts_out = {
|
|
588
|
+
bbcode: false,
|
|
589
|
+
prefix: "#{m}#{theme.sh_out_style}",
|
|
590
|
+
prefix_width: m.width
|
|
591
|
+
}
|
|
592
|
+
m = theme.mark(:sh_err)
|
|
593
|
+
opts_err = {
|
|
594
|
+
bbcode: false,
|
|
595
|
+
prefix: "#{m}#{theme.sh_err_style}",
|
|
596
|
+
prefix_width: m.width
|
|
597
|
+
}
|
|
598
|
+
ShellCommand.call(*cmd, shell: shell, input: input) do |line, kind|
|
|
599
|
+
puts(
|
|
600
|
+
line.gsub("\t", ' ').gsub(/[[:space:]]/, ' '),
|
|
601
|
+
**(kind == :error ? opts_err : opts_out)
|
|
602
|
+
)
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
|
|
570
606
|
#
|
|
571
607
|
# @!endgroup
|
|
572
608
|
#
|
|
@@ -945,43 +981,45 @@ module NattyUI
|
|
|
945
981
|
end
|
|
946
982
|
end
|
|
947
983
|
end
|
|
948
|
-
|
|
949
|
-
def __eol
|
|
950
|
-
@__eol ||= Terminal.ansi? ? "\e[m\n" : "\n"
|
|
951
|
-
end
|
|
952
984
|
end
|
|
953
985
|
|
|
954
986
|
dir = __dir__
|
|
955
|
-
autoload :Choice, "#{dir}/choice.rb"
|
|
956
|
-
autoload :DumbChoice, "#{dir}/dumb_choice.rb"
|
|
957
|
-
autoload :DumbOptions, "#{dir}/dumb_options.rb"
|
|
958
|
-
autoload :CompactLSRenderer, "#{dir}/ls_renderer.rb"
|
|
959
987
|
autoload :Framed, "#{dir}/framed.rb"
|
|
960
|
-
autoload :HBarsRenderer, "#{dir}/hbars_renderer.rb"
|
|
961
|
-
autoload :LSRenderer, "#{dir}/ls_renderer.rb"
|
|
962
|
-
autoload :Options, "#{dir}/options.rb"
|
|
963
|
-
autoload :Progress, "#{dir}/progress.rb"
|
|
964
|
-
autoload :DumbProgress, "#{dir}/progress.rb"
|
|
965
988
|
autoload :Section, "#{dir}/section.rb"
|
|
989
|
+
autoload :ShellCommand, "#{dir}/shell_command.rb"
|
|
966
990
|
autoload :Table, "#{dir}/table.rb"
|
|
967
991
|
autoload :Task, "#{dir}/task.rb"
|
|
968
992
|
autoload :Temporary, "#{dir}/temporary.rb"
|
|
969
993
|
autoload :Theme, "#{dir}/theme.rb"
|
|
970
994
|
autoload :Utils, "#{dir}/utils.rb"
|
|
995
|
+
|
|
996
|
+
autoload :Choice, "#{dir}/choice.rb"
|
|
997
|
+
autoload :DumbChoice, "#{dir}/dumb_choice.rb"
|
|
998
|
+
autoload :Options, "#{dir}/options.rb"
|
|
999
|
+
autoload :DumbOptions, "#{dir}/dumb_options.rb"
|
|
1000
|
+
autoload :Progress, "#{dir}/progress.rb"
|
|
1001
|
+
autoload :DumbProgress, "#{dir}/progress.rb"
|
|
1002
|
+
|
|
1003
|
+
autoload :CompactLSRenderer, "#{dir}/ls_renderer.rb"
|
|
1004
|
+
autoload :HBarsRenderer, "#{dir}/hbars_renderer.rb"
|
|
1005
|
+
autoload :LSRenderer, "#{dir}/ls_renderer.rb"
|
|
971
1006
|
autoload :VBarsRenderer, "#{dir}/vbars_renderer.rb"
|
|
972
1007
|
|
|
973
1008
|
private_constant(
|
|
1009
|
+
:Framed,
|
|
1010
|
+
:ShellCommand,
|
|
1011
|
+
:Utils,
|
|
1012
|
+
# -
|
|
974
1013
|
:Choice,
|
|
975
1014
|
:DumbChoice,
|
|
1015
|
+
:Options,
|
|
976
1016
|
:DumbOptions,
|
|
1017
|
+
:Progress,
|
|
1018
|
+
:DumbProgress,
|
|
1019
|
+
# -
|
|
977
1020
|
:CompactLSRenderer,
|
|
978
|
-
:Framed,
|
|
979
1021
|
:HBarsRenderer,
|
|
980
1022
|
:LSRenderer,
|
|
981
|
-
:Options,
|
|
982
|
-
:Progress,
|
|
983
|
-
:DumbProgress,
|
|
984
|
-
:Utils,
|
|
985
1023
|
:VBarsRenderer
|
|
986
1024
|
)
|
|
987
1025
|
end
|
|
@@ -3,54 +3,62 @@
|
|
|
3
3
|
require_relative 'utils'
|
|
4
4
|
|
|
5
5
|
module NattyUI
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if style
|
|
20
|
-
bos = Ansi[*style]
|
|
21
|
-
eos = Ansi::RESET
|
|
22
|
-
end
|
|
23
|
-
vals = normalize ? normalize!(vals, size) : adjust!(vals, size)
|
|
24
|
-
texts.map.with_index do |str, idx|
|
|
25
|
-
"#{bots}#{' ' * (tw - str.width)}#{str}#{eots}#{bos}▕#{
|
|
26
|
-
'▆' * vals[idx]
|
|
27
|
-
}#{eos}"
|
|
28
|
-
end
|
|
29
|
-
end
|
|
6
|
+
class HBarsRenderer
|
|
7
|
+
def initialize(values, min, max)
|
|
8
|
+
@values = values
|
|
9
|
+
@min = min
|
|
10
|
+
@max = max
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def with_text(style)
|
|
14
|
+
@texts = @values.map { Str.new(_1) }
|
|
15
|
+
@texts_width = @texts.max_by(&:width).width
|
|
16
|
+
@texts.map!(&format_text(style, @texts_width))
|
|
17
|
+
self
|
|
18
|
+
end
|
|
30
19
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
end
|
|
36
|
-
width -= 1
|
|
37
|
-
vals = normalize ? normalize!(vals, width) : adjust!(vals, width)
|
|
38
|
-
vals.map { "#{bos}▕#{'▆' * _1}#{eos}" }
|
|
20
|
+
def lines(width, style, normalize)
|
|
21
|
+
if @texts
|
|
22
|
+
width -= @texts_width
|
|
23
|
+
return @texts if width < 3
|
|
39
24
|
end
|
|
25
|
+
width -= 1
|
|
26
|
+
fmt = format(style)
|
|
27
|
+
vals = normalize ? normalized(width, &fmt) : adjusted(width, &fmt)
|
|
28
|
+
vals = @texts.zip(vals).map!(&:join) if @texts
|
|
29
|
+
vals
|
|
30
|
+
end
|
|
40
31
|
|
|
41
|
-
|
|
32
|
+
private
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
def format_text(style, width)
|
|
35
|
+
return ->(l) { "#{' ' * (width - l.width)}#{l}" } unless style
|
|
36
|
+
bot = Ansi[*style]
|
|
37
|
+
eot = Ansi::RESET
|
|
38
|
+
->(l) { "#{bot}#{' ' * (width - l.width)}#{l}#{eot}" }
|
|
39
|
+
end
|
|
47
40
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
41
|
+
def format(style)
|
|
42
|
+
return ->(l) { "▕#{'▆' * l}" } unless style
|
|
43
|
+
bos = Ansi[*style]
|
|
44
|
+
eos = Ansi::RESET
|
|
45
|
+
->(l) { "#{bos}▕#{'▆' * l}#{eos}" }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def adjusted(size)
|
|
49
|
+
vals = @values.map(&:to_f)
|
|
50
|
+
max = vals.max
|
|
51
|
+
max = @max.to_f if @max&.>(max)
|
|
52
|
+
vals.map! { yield(((_1 / max) * size).round) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def normalized(size)
|
|
56
|
+
vals = @values.map(&:to_f)
|
|
57
|
+
min, max = vals.minmax
|
|
58
|
+
min = @min.to_f if @min&.<(min)
|
|
59
|
+
max = @max.to_f if @max&.>(max)
|
|
60
|
+
max -= min
|
|
61
|
+
vals.map! { yield((((_1 - min) / max) * size).round) }
|
|
54
62
|
end
|
|
55
63
|
end
|
|
56
64
|
|
data/lib/natty-ui/progress.rb
CHANGED
|
@@ -100,8 +100,10 @@ module NattyUI
|
|
|
100
100
|
@pin_line = NattyUI.lines_written
|
|
101
101
|
@style = Theme.current.task_style
|
|
102
102
|
cm = Theme.current.mark(:current)
|
|
103
|
-
@
|
|
104
|
-
|
|
103
|
+
@redraw_opts = {
|
|
104
|
+
first_line_prefix: "#{cm} #{@style}",
|
|
105
|
+
first_line_prefix_width: cm.width + 1
|
|
106
|
+
}
|
|
105
107
|
max ? self.max = max : redraw
|
|
106
108
|
end
|
|
107
109
|
|
|
@@ -111,11 +113,7 @@ module NattyUI
|
|
|
111
113
|
curr = bar ? [@title, bar] : [@title]
|
|
112
114
|
return if @last == curr
|
|
113
115
|
@pin_line = NattyUI.back_to_line(@pin_line) if @last
|
|
114
|
-
@parent.puts(
|
|
115
|
-
*curr,
|
|
116
|
-
first_line_prefix: @flp,
|
|
117
|
-
first_line_prefix_width: @flpw
|
|
118
|
-
)
|
|
116
|
+
@parent.puts(*curr, **@redraw_opts)
|
|
119
117
|
@last = curr
|
|
120
118
|
end
|
|
121
119
|
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
module NattyUI
|
|
2
|
+
module ShellCommand
|
|
3
|
+
class << self
|
|
4
|
+
def call(*cmd, shell: false, input: nil, **options)
|
|
5
|
+
options = options.except(:in, :out, :err)
|
|
6
|
+
env = (cmd[0].is_a?(Hash) ? cmd.shift.dup : {}).freeze
|
|
7
|
+
if shell
|
|
8
|
+
cmd = cmd.map! { _escape(it) }.join(' ')
|
|
9
|
+
elsif cmd.size == 1 && cmd[0].include?(' ')
|
|
10
|
+
cmd = cmd[0]
|
|
11
|
+
end
|
|
12
|
+
cmd.freeze
|
|
13
|
+
input = Input.for(input)
|
|
14
|
+
ret = nil
|
|
15
|
+
with_io(options, input) do |cio, out_r, err_r, in_w|
|
|
16
|
+
thread = Process.detach(Process.spawn(env, *cmd, options))
|
|
17
|
+
begin
|
|
18
|
+
cio.each(&:close)
|
|
19
|
+
read = [out_r, err_r]
|
|
20
|
+
write = [in_w] if in_w
|
|
21
|
+
while !read.empty? || write
|
|
22
|
+
rr, wr, = IO.select(read, write)
|
|
23
|
+
if rr.include?(out_r)
|
|
24
|
+
begin
|
|
25
|
+
yield(out_r.readline(chomp: true), :output)
|
|
26
|
+
rescue SystemCallError, IOError
|
|
27
|
+
read.delete(out_r)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
if rr.include?(err_r)
|
|
31
|
+
begin
|
|
32
|
+
yield(err_r.readline(chomp: true), :error)
|
|
33
|
+
rescue SystemCallError, IOError
|
|
34
|
+
read.delete(err_r)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
next if wr.empty?
|
|
38
|
+
begin
|
|
39
|
+
next if input.call(in_w)
|
|
40
|
+
rescue SystemCallError, IOError
|
|
41
|
+
# nop
|
|
42
|
+
end
|
|
43
|
+
in_w.close
|
|
44
|
+
write = nil
|
|
45
|
+
end
|
|
46
|
+
ensure
|
|
47
|
+
ret = thread.join.value
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
ret
|
|
51
|
+
rescue SystemCallError, IOError
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def with_io(options, input)
|
|
58
|
+
IO.pipe do |out_r, out_w|
|
|
59
|
+
IO.pipe do |err_r, err_w|
|
|
60
|
+
cio = [options[:out] = out_w, options[:err] = err_w]
|
|
61
|
+
return yield(cio, out_r, err_r) unless input
|
|
62
|
+
IO.pipe do |in_r, in_w|
|
|
63
|
+
cio << (options[:in] = in_r)
|
|
64
|
+
in_w.sync = true
|
|
65
|
+
yield(cio, out_r, err_r, in_w)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def _escape(str)
|
|
72
|
+
return +"''" if str.empty?
|
|
73
|
+
str = str.dup
|
|
74
|
+
str.gsub!(%r{[^A-Za-z0-9_\-.,:+/@\n]}, "\\\\\\&")
|
|
75
|
+
str.gsub!("\n", "'\n'")
|
|
76
|
+
str
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
module Input
|
|
81
|
+
def self.for(obj)
|
|
82
|
+
return unless obj
|
|
83
|
+
return CopyWriter.new(obj) if obj.respond_to?(:readpartial)
|
|
84
|
+
return CopyWriter.new(obj.to_io) if obj.respond_to?(:to_io)
|
|
85
|
+
return ArrayWriter.new(obj) if obj.is_a?(Array)
|
|
86
|
+
return EnumerableWriter.new(obj) if obj.respond_to?(:each)
|
|
87
|
+
return ArrayWriter.new(obj.to_a) if obj.respond_to?(:to_a)
|
|
88
|
+
Writer.new(obj)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class Writer
|
|
92
|
+
def call(io) = (io.write(_next) if @obj)
|
|
93
|
+
def initialize(obj) = (@obj = obj)
|
|
94
|
+
def _next = (_, @obj = @obj, nil).first
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class CopyWriter < Writer
|
|
98
|
+
def call(io) = (IO.copy_stream(_next, io) if @obj)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class ArrayWriter
|
|
102
|
+
def call(io) = io.write(@ry[@idx += 1] || return)
|
|
103
|
+
|
|
104
|
+
def initialize(ary)
|
|
105
|
+
@ry = ary
|
|
106
|
+
@idx = -1
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
class EnumerableWriter
|
|
111
|
+
def call(io)
|
|
112
|
+
io.write(@enum.next)
|
|
113
|
+
rescue StopIteration
|
|
114
|
+
false
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def initialize(enum)
|
|
118
|
+
@enum =
|
|
119
|
+
if enum.respond_to?(:enum_for)
|
|
120
|
+
enum.enum_for(:each)
|
|
121
|
+
else
|
|
122
|
+
Enumerator.new { |y| enum.each { y << it } }
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private_constant :Input
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private_constant :ShellCommand
|
|
132
|
+
end
|
data/lib/natty-ui/table.rb
CHANGED
|
@@ -9,8 +9,8 @@ module NattyUI
|
|
|
9
9
|
# Collection of rows and columns used by {Features.table}.
|
|
10
10
|
#
|
|
11
11
|
class Table
|
|
12
|
-
class Column < NattyUI::Attributes
|
|
13
|
-
include
|
|
12
|
+
class Column < NattyUI::Attributes
|
|
13
|
+
include Width
|
|
14
14
|
|
|
15
15
|
# @return [Integer] column index
|
|
16
16
|
attr_reader :index
|
|
@@ -22,6 +22,12 @@ module NattyUI
|
|
|
22
22
|
def to_s = "#{super.chop} @index:#{@index} @width:#{width.inspect}>"
|
|
23
23
|
alias inspect to_s
|
|
24
24
|
|
|
25
|
+
def assign(**attributes)
|
|
26
|
+
return self if attributes.empty?
|
|
27
|
+
@parent.each_cell_of(@index) { _1.attributes.merge!(**attributes) }
|
|
28
|
+
self
|
|
29
|
+
end
|
|
30
|
+
|
|
25
31
|
private
|
|
26
32
|
|
|
27
33
|
def find_width
|
|
@@ -137,6 +143,12 @@ module NattyUI
|
|
|
137
143
|
self
|
|
138
144
|
end
|
|
139
145
|
|
|
146
|
+
def assign(**attributes)
|
|
147
|
+
return self if attributes.empty?
|
|
148
|
+
@cells.each { _1.attributes.merge!(**attributes) }
|
|
149
|
+
self
|
|
150
|
+
end
|
|
151
|
+
|
|
140
152
|
private
|
|
141
153
|
|
|
142
154
|
def respond_to_missing?(name, _)
|
|
@@ -169,19 +181,42 @@ module NattyUI
|
|
|
169
181
|
class Cell
|
|
170
182
|
include TextWithAttributes
|
|
171
183
|
|
|
172
|
-
class Attributes < NattyUI::Attributes
|
|
173
|
-
prepend
|
|
174
|
-
prepend
|
|
175
|
-
prepend
|
|
176
|
-
prepend
|
|
177
|
-
prepend
|
|
184
|
+
class Attributes < NattyUI::Attributes
|
|
185
|
+
prepend Width
|
|
186
|
+
prepend Padding
|
|
187
|
+
prepend Align
|
|
188
|
+
prepend Vertical
|
|
189
|
+
prepend Style
|
|
190
|
+
|
|
191
|
+
# Whether the text's line breaks are processed.
|
|
192
|
+
#
|
|
193
|
+
# @return [true, false]
|
|
194
|
+
attr_reader :eol
|
|
195
|
+
|
|
196
|
+
# @attribute [w] eol
|
|
197
|
+
def eol=(value)
|
|
198
|
+
@eol = value ? true : false
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
protected
|
|
202
|
+
|
|
203
|
+
def _assign(opt)
|
|
204
|
+
@eol = opt[:eol]
|
|
205
|
+
@eol = true if @eol.nil?
|
|
206
|
+
super
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def _store(opt)
|
|
210
|
+
opt[:eol] = false unless @eol
|
|
211
|
+
super
|
|
212
|
+
end
|
|
178
213
|
end
|
|
179
214
|
end
|
|
180
215
|
|
|
181
|
-
class Attributes < NattyUI::Attributes
|
|
182
|
-
prepend
|
|
183
|
-
prepend
|
|
184
|
-
prepend
|
|
216
|
+
class Attributes < NattyUI::Attributes
|
|
217
|
+
prepend Border
|
|
218
|
+
prepend BorderStyle
|
|
219
|
+
prepend Position
|
|
185
220
|
|
|
186
221
|
# Whether the table has a border around.
|
|
187
222
|
#
|
|
@@ -115,12 +115,12 @@ module NattyUI
|
|
|
115
115
|
@align = att.align
|
|
116
116
|
@vertical = att.vertical
|
|
117
117
|
@style = att.style_bbcode
|
|
118
|
-
@text = width_corrected(cell.text, width)
|
|
118
|
+
@text = width_corrected(cell.text, width, att.eol == false)
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
private
|
|
122
122
|
|
|
123
|
-
def width_corrected(text, width)
|
|
123
|
+
def width_corrected(text, width, ignore_newline)
|
|
124
124
|
@width, @padding[3], @padding[1] =
|
|
125
125
|
WidthFinder.adjust(width, @padding[3], @padding[1])
|
|
126
126
|
@empty = @style ? "#{@style}#{' ' * width}[/]" : ' ' * width
|
|
@@ -130,6 +130,7 @@ module NattyUI
|
|
|
130
130
|
*text,
|
|
131
131
|
limit: @width,
|
|
132
132
|
bbcode: true,
|
|
133
|
+
ignore_newline: ignore_newline,
|
|
133
134
|
ansi: Terminal.ansi?
|
|
134
135
|
).map(&txt_fmt),
|
|
135
136
|
Array.new(@padding[2], @empty)
|