natty-ui 0.5.2 → 0.6.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 +4 -5
- data/examples/attributes.rb +165 -0
- data/examples/basic.rb +8 -9
- data/examples/illustration.png +0 -0
- data/examples/list_in_columns.rb +43 -0
- data/examples/progress.rb +45 -61
- data/examples/query.rb +27 -23
- data/lib/natty-ui/ansi.rb +47 -37
- data/lib/natty-ui/ansi_wrapper.rb +55 -22
- data/lib/natty-ui/features.rb +36 -0
- data/lib/natty-ui/{wrapper/mixins.rb → mixins.rb} +1 -1
- data/lib/natty-ui/version.rb +1 -1
- data/lib/natty-ui/wrapper/ask.rb +5 -3
- data/lib/natty-ui/wrapper/element.rb +6 -4
- data/lib/natty-ui/wrapper/framed.rb +1 -1
- data/lib/natty-ui/wrapper/heading.rb +1 -1
- data/lib/natty-ui/wrapper/list_in_columns.rb +81 -0
- data/lib/natty-ui/wrapper/message.rb +1 -1
- data/lib/natty-ui/wrapper/progress.rb +12 -7
- data/lib/natty-ui/wrapper/query.rb +4 -2
- data/lib/natty-ui/wrapper/request.rb +32 -0
- data/lib/natty-ui/wrapper/section.rb +9 -7
- data/lib/natty-ui/wrapper/task.rb +2 -2
- data/lib/natty-ui/wrapper.rb +19 -13
- data/lib/natty-ui.rb +68 -1
- metadata +10 -7
- data/examples/colors.rb +0 -7
- data/examples/illustration.svg +0 -1
- data/lib/natty-ui/wrapper/features.rb +0 -20
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'io/console'
|
4
|
-
require_relative 'wrapper'
|
5
3
|
require_relative 'ansi'
|
4
|
+
require_relative 'wrapper'
|
6
5
|
|
7
6
|
module NattyUI
|
8
7
|
class AnsiWrapper < Wrapper
|
@@ -110,18 +109,48 @@ module NattyUI
|
|
110
109
|
MSG = Ansi[:bold, 231].freeze
|
111
110
|
end
|
112
111
|
|
113
|
-
class Framed <
|
112
|
+
class Framed < Section
|
114
113
|
protected
|
115
114
|
|
115
|
+
def initialize(parent, title:, type:, **opts)
|
116
|
+
@parent = parent
|
117
|
+
title = title.to_s.tr("\r\n", '')
|
118
|
+
topl, topr, botl, botr, hor, vert = *components(type)
|
119
|
+
width = available_width
|
120
|
+
rcount = [width - _plain_width(title) - 6, 0].max
|
121
|
+
parent.puts(
|
122
|
+
"#{COLOR}#{topl}#{hor}#{hor}#{Ansi.reset} " \
|
123
|
+
"#{TITLE_ATTR}#{title}#{Ansi.reset} " \
|
124
|
+
"#{COLOR}#{hor * rcount}#{topr}#{Ansi.reset}"
|
125
|
+
)
|
126
|
+
@bottom = "#{COLOR}#{botl}#{hor * (width - 2)}#{botr}#{Ansi.reset}"
|
127
|
+
vert = "#{COLOR}#{vert}#{Ansi.reset}"
|
128
|
+
super(
|
129
|
+
parent,
|
130
|
+
prefix: "#{vert} ",
|
131
|
+
suffix:
|
132
|
+
"#{Ansi.cursor_right_aligned}" \
|
133
|
+
"#{Ansi.cursor_left(suffix_width)}#{vert}",
|
134
|
+
**opts
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
def suffix = "#{super} "
|
139
|
+
def finish = parent.puts(@bottom)
|
140
|
+
|
116
141
|
def components(type)
|
117
|
-
|
118
|
-
[
|
119
|
-
"#{Ansi[39]}#{top_start}#{Ansi[:bold, 231]}",
|
120
|
-
"#{Ansi[:reset, 39]}#{top_suffix}#{Ansi.reset}",
|
121
|
-
Ansi.embellish(left, 39),
|
122
|
-
Ansi.embellish(bottom, 39)
|
123
|
-
]
|
142
|
+
COMPONENTS[type] || raise(ArgumentError, "invalid frame type - #{type}")
|
124
143
|
end
|
144
|
+
|
145
|
+
COLOR = Ansi[39].freeze
|
146
|
+
TITLE_ATTR = Ansi[:bold, 231].freeze
|
147
|
+
COMPONENTS = {
|
148
|
+
rounded: %w[╭ ╮ ╰ ╯ ─ │],
|
149
|
+
simple: %w[┌ ┐ └ ┘ ─ │],
|
150
|
+
heavy: %w[┏ ┓ ┗ ┛ ━ ┃],
|
151
|
+
semi: %w[┍ ┑ ┕ ┙ ━ │],
|
152
|
+
double: %w[╔ ╗ ╚ ╝ ═ ║]
|
153
|
+
}.compare_by_identity.freeze
|
125
154
|
end
|
126
155
|
|
127
156
|
class Ask < Ask
|
@@ -136,6 +165,14 @@ module NattyUI
|
|
136
165
|
PREFIX = "#{Ansi[:bold, :italic, 220]}▶︎#{Ansi[:reset, 220]}".freeze
|
137
166
|
end
|
138
167
|
|
168
|
+
class Request < Request
|
169
|
+
def prompt(question) = "#{prefix}#{PREFIX} #{question}#{Ansi.reset} "
|
170
|
+
def finish = (wrapper.stream << FINISH).flush
|
171
|
+
|
172
|
+
PREFIX = "#{Ansi[:bold, :italic, 220]}▶︎#{Ansi[:reset, 220]}".freeze
|
173
|
+
FINISH = (Ansi.cursor_line_up + Ansi.line_erase_to_end).freeze
|
174
|
+
end
|
175
|
+
|
139
176
|
class Query < Query
|
140
177
|
protected
|
141
178
|
|
@@ -155,9 +192,9 @@ module NattyUI
|
|
155
192
|
class Progress < Progress
|
156
193
|
protected
|
157
194
|
|
158
|
-
def
|
195
|
+
def draw(title)
|
159
196
|
@prefix = "#{prefix}#{TITLE_PREFIX}#{title}#{Ansi.reset} "
|
160
|
-
(wrapper.stream << @prefix).flush
|
197
|
+
(wrapper.stream << @prefix << Ansi.cursor_hide).flush
|
161
198
|
@prefix = "#{Ansi.line_clear}#{@prefix}"
|
162
199
|
if @max_value
|
163
200
|
@prefix << BAR_COLOR
|
@@ -167,26 +204,21 @@ module NattyUI
|
|
167
204
|
end
|
168
205
|
end
|
169
206
|
|
170
|
-
def draw_final = (wrapper.stream << Ansi.line_clear).flush
|
171
|
-
|
172
207
|
def redraw
|
173
208
|
(wrapper.stream << @prefix << (@max_value ? fullbar : indicator)).flush
|
174
209
|
end
|
175
210
|
|
211
|
+
def end_draw = (wrapper.stream << ERASE).flush
|
176
212
|
def indicator = '─╲│╱'[(@indicator += 1) % 4]
|
213
|
+
# def indicator = '⣷⣯⣟⡿⢿⣻⣽⣾'[(@indicator += 1) % 8]
|
177
214
|
|
178
215
|
def fullbar
|
179
216
|
percent = @value / @max_value
|
180
217
|
count = (30 * percent).to_i
|
218
|
+
mv = max_value.to_i.to_s
|
181
219
|
"#{'█' * count}#{BAR_BACK}#{'▁' * (30 - count)}" \
|
182
|
-
"#{BAR_INK} #{
|
183
|
-
|
184
|
-
'%<value>.0f/%<max_value>.0f (%<percent>.2f%%)',
|
185
|
-
value: @value,
|
186
|
-
max_value: @max_value,
|
187
|
-
percent: percent * 100
|
188
|
-
)
|
189
|
-
}"
|
220
|
+
"#{BAR_INK} #{value.to_i.to_s.rjust(mv.size)}/#{mv} " \
|
221
|
+
"(#{(percent * 100).round(2).to_s.rjust(6)})"
|
190
222
|
end
|
191
223
|
|
192
224
|
TITLE_PREFIX = "#{Ansi[:bold, :italic, 117]}➔#{Ansi[:reset, 117]} ".freeze
|
@@ -194,6 +226,7 @@ module NattyUI
|
|
194
226
|
BAR_COLOR = Ansi[39, 295].freeze
|
195
227
|
BAR_BACK = Ansi[236, 492].freeze
|
196
228
|
BAR_INK = Ansi[:bold, 255, :on_default].freeze
|
229
|
+
ERASE = (Ansi.line_clear + Ansi.cursor_show).freeze
|
197
230
|
end
|
198
231
|
|
199
232
|
PAGE_BEGIN =
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NattyUI
|
4
|
+
#
|
5
|
+
# Features of {NattyUI} - methods to display natty elements.
|
6
|
+
#
|
7
|
+
module Features
|
8
|
+
# Print a horizontal rule
|
9
|
+
#
|
10
|
+
# @param [#to_s] symbol string to build the horizontal rule
|
11
|
+
# @return [Wrapper, Wrapper::Element] itself
|
12
|
+
def hr(symbol = '═')
|
13
|
+
symbol = symbol.to_s
|
14
|
+
size = _plain_width(symbol)
|
15
|
+
return self if size.zero?
|
16
|
+
msg = symbol * ((available_width - 1) / size)
|
17
|
+
return puts(msg, prefix: Ansi[39], suffix: Ansi.reset) if wrapper.ansi?
|
18
|
+
puts(msg)
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def _plain_width(str) = NattyUI.display_width(NattyUI.plain(str))
|
24
|
+
def _blemish_width(str) = NattyUI.display_width(Ansi.blemish(str))
|
25
|
+
|
26
|
+
def _element(type, *args)
|
27
|
+
wrapper.class.const_get(type).__send__(:new, self).__send__(:_call, *args)
|
28
|
+
end
|
29
|
+
|
30
|
+
def _section(owner, type, args, **opts, &block)
|
31
|
+
sec = wrapper.class.const_get(type).__send__(:new, owner, **opts)
|
32
|
+
sec.puts(*args) if args && !args.empty?
|
33
|
+
block ? sec.__send__(:_call, &block) : sec
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -12,7 +12,7 @@ module NattyUI
|
|
12
12
|
#
|
13
13
|
module ProgressAttributes
|
14
14
|
# @attribute [r] completed?
|
15
|
-
# @return [Boolean] whether the task completed
|
15
|
+
# @return [Boolean] whether the task completed successfully
|
16
16
|
def completed? = (@status == :completed)
|
17
17
|
|
18
18
|
# @attribute [r] failed?
|
data/lib/natty-ui/version.rb
CHANGED
data/lib/natty-ui/wrapper/ask.rb
CHANGED
@@ -17,11 +17,13 @@ module NattyUI
|
|
17
17
|
# when true
|
18
18
|
# sec.info('Yeah!!')
|
19
19
|
# when false
|
20
|
-
# sec.write("That's
|
20
|
+
# sec.write("That's pity!")
|
21
21
|
# else
|
22
22
|
# sec.failed('You should have an opinion!')
|
23
23
|
# end
|
24
24
|
#
|
25
|
+
# @see NattyUI.in_stream
|
26
|
+
#
|
25
27
|
# @param question [#to_s] Question to display
|
26
28
|
# @param yes [#to_s] chars which will be used to answer 'Yes'
|
27
29
|
# @param no [#to_s] chars which will be used to answer 'No'
|
@@ -65,9 +67,9 @@ module NattyUI
|
|
65
67
|
|
66
68
|
def grab(yes, no)
|
67
69
|
yes = yes.to_s.chars.uniq
|
70
|
+
raise(ArgumentError, ':yes can not be empty') if yes.empty?
|
68
71
|
no = no.to_s.chars.uniq
|
69
|
-
raise(ArgumentError, ':
|
70
|
-
raise(ArgumentError, ':no can not be emoty') if no.empty?
|
72
|
+
raise(ArgumentError, ':no can not be empty') if no.empty?
|
71
73
|
return yes, no if (yes & no).empty?
|
72
74
|
raise(ArgumentError, 'chars in :yes and :no can not be intersect')
|
73
75
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'features'
|
3
|
+
require_relative '../features'
|
4
4
|
|
5
5
|
module NattyUI
|
6
6
|
class Wrapper
|
@@ -24,8 +24,7 @@ module NattyUI
|
|
24
24
|
|
25
25
|
# Close the element.
|
26
26
|
#
|
27
|
-
# @return [Element] itself
|
28
|
-
# @return [nil] when used with a code block
|
27
|
+
# @return [Element] itself
|
29
28
|
def close = _close(:closed)
|
30
29
|
|
31
30
|
alias _to_s to_s
|
@@ -37,7 +36,10 @@ module NattyUI
|
|
37
36
|
protected
|
38
37
|
|
39
38
|
def prefix = "#{@parent.__send__(:prefix)}#{@prefix}"
|
40
|
-
def suffix = "#{@parent.__send__(:suffix)}
|
39
|
+
def suffix = "#{@suffix}#{@parent.__send__(:suffix)}"
|
40
|
+
def prefix_width = _blemish_width(prefix)
|
41
|
+
def suffix_width = _blemish_width(suffix)
|
42
|
+
def available_width = wrapper.screen_columns - prefix_width - suffix_width
|
41
43
|
def finish = nil
|
42
44
|
|
43
45
|
def wrapper
|
@@ -14,7 +14,7 @@ module NattyUI
|
|
14
14
|
# @param [Array<#to_s>] args more objects to print
|
15
15
|
# @param [Symbol] type frame type;
|
16
16
|
# valid types are `:rounded`, `:simple`, `:heavy`, `:semi`, `:double`
|
17
|
-
# @yieldparam [Wrapper::Framed]
|
17
|
+
# @yieldparam [Wrapper::Framed] framed the created section
|
18
18
|
# @return [Object] the result of the code block
|
19
19
|
# @return [Wrapper::Framed] itself, when no code block is given
|
20
20
|
def framed(title, *args, type: :rounded, &block)
|
@@ -7,7 +7,7 @@ module NattyUI
|
|
7
7
|
# Creates section with a H1 title.
|
8
8
|
#
|
9
9
|
# @param (see #information)
|
10
|
-
# @yieldparam [Wrapper::Heading]
|
10
|
+
# @yieldparam [Wrapper::Heading] heading the created section
|
11
11
|
# @return [Object] the result of the code block
|
12
12
|
# @return [Wrapper::Heading] itself, when no code block is given
|
13
13
|
def h1(title, *args, &block)
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'element'
|
4
|
+
|
5
|
+
module NattyUI
|
6
|
+
module Features
|
7
|
+
#
|
8
|
+
# Print items of a given list as columns.
|
9
|
+
# In the default compact format columns may have diffrent widths and the
|
10
|
+
# list items are ordered column-wise.
|
11
|
+
# The non-compact format prints all columns in same width and order the list
|
12
|
+
# items row-wise.
|
13
|
+
#
|
14
|
+
# @param [Array<#to_s>] args items to print
|
15
|
+
# @param [Boolean] compact whether to use compact format
|
16
|
+
# @return [Wrapper, Wrapper::Element] itself
|
17
|
+
def ls(*args, compact: true) = _element(:ListInColumns, args, compact)
|
18
|
+
end
|
19
|
+
|
20
|
+
class Wrapper
|
21
|
+
#
|
22
|
+
# An {Element} to print items of a given list as columns.
|
23
|
+
#
|
24
|
+
# @see Features#ls
|
25
|
+
class ListInColumns < Element
|
26
|
+
protected
|
27
|
+
|
28
|
+
def _call(list, compact)
|
29
|
+
list.flatten!
|
30
|
+
return parent if list.empty?
|
31
|
+
list.map! { |item| Item.new(item = item.to_s, _plain_width(item)) }
|
32
|
+
if compact
|
33
|
+
each_compacted(list, available_width) { |line| parent.puts(line) }
|
34
|
+
else
|
35
|
+
each(list, available_width) { |line| parent.puts(line) }
|
36
|
+
end
|
37
|
+
parent
|
38
|
+
end
|
39
|
+
|
40
|
+
def each(list, max_width)
|
41
|
+
width = list.max_by(&:width).width + 3
|
42
|
+
list.each_slice(max_width / width) do |slice|
|
43
|
+
yield(slice.map { |item| item.to_s(width) }.join)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def each_compacted(list, max_width)
|
48
|
+
found, widths = find_columns(list, max_width)
|
49
|
+
fill(found[-1], found[0].size)
|
50
|
+
found.transpose.each do |row|
|
51
|
+
row = row.each_with_index.map { |item, col| item&.to_s(widths[col]) }
|
52
|
+
yield(row.join)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_columns(list, max_width)
|
57
|
+
found = [list]
|
58
|
+
widths = [list.max_by(&:width).width]
|
59
|
+
1.upto(list.size - 1) do |slice_size|
|
60
|
+
candidate = list.each_slice(list.size / slice_size).to_a
|
61
|
+
cwidths = candidate.map { |ary| ary.max_by(&:width).width + 3 }
|
62
|
+
cwidths[-1] -= 3
|
63
|
+
break if cwidths.sum > max_width
|
64
|
+
found = candidate
|
65
|
+
widths = cwidths
|
66
|
+
end
|
67
|
+
[found, widths]
|
68
|
+
end
|
69
|
+
|
70
|
+
def fill(ary, size)
|
71
|
+
diff = size - ary.size
|
72
|
+
ary.fill(nil, ary.size, diff) if diff.positive?
|
73
|
+
end
|
74
|
+
|
75
|
+
Item =
|
76
|
+
Data.define(:str, :width) do
|
77
|
+
def to_s(in_width) = "#{str}#{' ' * (in_width - width)}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -10,7 +10,7 @@ module NattyUI
|
|
10
10
|
# @param [#to_s] title object to print as section title
|
11
11
|
# @param [Array<#to_s>] args more objects to print
|
12
12
|
# @param [#to_s] symbol symbol/prefix used for the title
|
13
|
-
# @yieldparam [Wrapper::Message]
|
13
|
+
# @yieldparam [Wrapper::Message] message the created section
|
14
14
|
# @return [Object] the result of the code block
|
15
15
|
# @return [Wrapper::Message] itself, when no code block is given
|
16
16
|
def message(title, *args, symbol: :default, &block)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'element'
|
4
|
-
require_relative 'mixins'
|
4
|
+
require_relative '../mixins'
|
5
5
|
|
6
6
|
module NattyUI
|
7
7
|
module Features
|
@@ -35,11 +35,11 @@ module NattyUI
|
|
35
35
|
@max_value = [0, max_value.to_f].max if max_value
|
36
36
|
@value = 0
|
37
37
|
@progress = 0
|
38
|
-
|
38
|
+
draw(title)
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
42
|
-
def
|
41
|
+
def draw(title) = (wrapper.stream << prefix << "➔ #{title} ").flush
|
42
|
+
def end_draw = (wrapper.stream << "\n")
|
43
43
|
|
44
44
|
def redraw
|
45
45
|
return (wrapper.stream << '.').flush unless @max_value
|
@@ -50,10 +50,15 @@ module NattyUI
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def finish
|
53
|
-
|
53
|
+
end_draw
|
54
54
|
return @parent.failed(*@final_text) if failed?
|
55
|
-
|
56
|
-
|
55
|
+
_section(
|
56
|
+
@parent,
|
57
|
+
:Message,
|
58
|
+
@final_text,
|
59
|
+
title: @final_text.shift,
|
60
|
+
symbol: @status = :completed
|
61
|
+
)
|
57
62
|
end
|
58
63
|
end
|
59
64
|
end
|
@@ -24,9 +24,11 @@ module NattyUI
|
|
24
24
|
# )
|
25
25
|
# # => 'a' or 'b' or 'c' or nil if user aborted
|
26
26
|
#
|
27
|
+
# @see NattyUI.in_stream
|
28
|
+
#
|
27
29
|
# @param question [#to_s] Question to display
|
28
30
|
# @param choices [#to_s] choices selectable via index (0..9)
|
29
|
-
# @param result [Symbol] defines how the result
|
31
|
+
# @param result [Symbol] defines how the result will be returned
|
30
32
|
# @param kw_choices [{Char => #to_s}] choices selectable with given char
|
31
33
|
# @return [Char] when `result` is configured as `:char`
|
32
34
|
# @return [#to_s] when `result` is configured as `:choice`
|
@@ -78,7 +80,7 @@ module NattyUI
|
|
78
80
|
.to_h
|
79
81
|
.merge!(kw_choices)
|
80
82
|
.transform_keys! { |k| [k.to_s[0], ' '].max }
|
81
|
-
.transform_values! { |v| v.to_s.tr("\r\n", ' ') }
|
83
|
+
.transform_values! { |v| v.to_s.tr("\r\n\t", ' ') }
|
82
84
|
end
|
83
85
|
end
|
84
86
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'element'
|
4
|
+
|
5
|
+
module NattyUI
|
6
|
+
module Features
|
7
|
+
# Request user input.
|
8
|
+
#
|
9
|
+
# @param question [#to_s] Question to display
|
10
|
+
# @return [String] the user input
|
11
|
+
# @return [nil] when input was aborted with `^C` or `^D`
|
12
|
+
def request(question) = _element(:Request, question)
|
13
|
+
end
|
14
|
+
|
15
|
+
class Wrapper
|
16
|
+
#
|
17
|
+
# An {Element} to request user input.
|
18
|
+
#
|
19
|
+
# @see Features#request
|
20
|
+
class Request < Element
|
21
|
+
protected
|
22
|
+
|
23
|
+
def _call(question)
|
24
|
+
NattyUI.readline(prompt(question), stream: wrapper.stream)
|
25
|
+
ensure
|
26
|
+
finish
|
27
|
+
end
|
28
|
+
|
29
|
+
def prompt(question) = "#{prefix}▶︎ #{question}: "
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -29,7 +29,7 @@ module NattyUI
|
|
29
29
|
|
30
30
|
class Wrapper
|
31
31
|
#
|
32
|
-
# Visual
|
32
|
+
# Visual {Element} to keep text lines together.
|
33
33
|
#
|
34
34
|
# A section can contain other elements and sections.
|
35
35
|
#
|
@@ -38,23 +38,25 @@ module NattyUI
|
|
38
38
|
class Section < Element
|
39
39
|
# Close the section.
|
40
40
|
#
|
41
|
-
# @return [Section] itself
|
42
|
-
# @return [nil] when used with a code block
|
41
|
+
# @return [Section] itself
|
43
42
|
def close = _close(:closed)
|
44
43
|
|
45
44
|
# Print given arguments as lines into the section.
|
45
|
+
# Optionally limit the line width to given `max_width`.
|
46
46
|
#
|
47
|
-
# @overload puts(
|
47
|
+
# @overload puts(..., max_width: nil)
|
48
48
|
# @param [#to_s] ... objects to print
|
49
|
+
# @param [Integer, nil] max_width maximum line width
|
49
50
|
# @comment @param [#to_s, nil] prefix line prefix
|
50
51
|
# @comment @param [#to_s, nil] suffix line suffix
|
51
52
|
# @return [Section] itself
|
52
|
-
def puts(*args, prefix: nil, suffix: nil)
|
53
|
+
def puts(*args, max_width: nil, prefix: nil, suffix: nil)
|
53
54
|
return self if @status
|
54
55
|
@parent.puts(
|
55
56
|
*args,
|
57
|
+
max_width: max_width,
|
56
58
|
prefix: prefix ? "#{@prefix}#{prefix}" : @prefix,
|
57
|
-
suffix: suffix ? "#{
|
59
|
+
suffix: suffix ? "#{suffix}#{@suffix}" : @suffix
|
58
60
|
)
|
59
61
|
self
|
60
62
|
end
|
@@ -66,7 +68,7 @@ module NattyUI
|
|
66
68
|
# @return [Section] itself
|
67
69
|
def space(lines = 1)
|
68
70
|
@parent.puts(
|
69
|
-
"\n" *
|
71
|
+
"\n" * [lines.to_i, 1].max,
|
70
72
|
prefix: @prefix,
|
71
73
|
suffix: @suffix
|
72
74
|
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'section'
|
4
|
-
require_relative 'mixins'
|
4
|
+
require_relative '../mixins'
|
5
5
|
|
6
6
|
module NattyUI
|
7
7
|
module Features
|
@@ -11,7 +11,7 @@ module NattyUI
|
|
11
11
|
# or {#failed}.
|
12
12
|
#
|
13
13
|
# @param (see #information)
|
14
|
-
# @yieldparam [Wrapper::Task]
|
14
|
+
# @yieldparam [Wrapper::Task] task the created section
|
15
15
|
# @return [Object] the result of the code block
|
16
16
|
# @return [Wrapper::Task] itself, when no code block is given
|
17
17
|
def task(title, *args, &block)
|
data/lib/natty-ui/wrapper.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'io/console'
|
4
4
|
require_relative 'wrapper/ask'
|
5
5
|
require_relative 'wrapper/framed'
|
6
6
|
require_relative 'wrapper/heading'
|
7
|
+
require_relative 'wrapper/list_in_columns'
|
7
8
|
require_relative 'wrapper/message'
|
8
9
|
require_relative 'wrapper/progress'
|
9
10
|
require_relative 'wrapper/query'
|
11
|
+
require_relative 'wrapper/request'
|
10
12
|
require_relative 'wrapper/section'
|
11
13
|
require_relative 'wrapper/task'
|
12
14
|
|
@@ -28,13 +30,13 @@ module NattyUI
|
|
28
30
|
# @return [[Integer, Integer]] screen size as rows and columns
|
29
31
|
def screen_size
|
30
32
|
return @stream.winsize if @ws
|
31
|
-
[ENV['LINES'].to_i.nonzero? ||
|
33
|
+
[ENV['LINES'].to_i.nonzero? || 24, ENV['COLUMNS'].to_i.nonzero? || 80]
|
32
34
|
end
|
33
35
|
|
34
36
|
# @attribute [r] screen_rows
|
35
37
|
# @return [Integer] number of screen rows
|
36
38
|
def screen_rows
|
37
|
-
@ws ? @stream.winsize[0] : (ENV['LINES'].to_i.nonzero? ||
|
39
|
+
@ws ? @stream.winsize[0] : (ENV['LINES'].to_i.nonzero? || 24)
|
38
40
|
end
|
39
41
|
|
40
42
|
# @attribute [r] screen_columns
|
@@ -46,24 +48,22 @@ module NattyUI
|
|
46
48
|
# @!group Tool functions
|
47
49
|
|
48
50
|
# Print given arguments as lines to the output stream.
|
51
|
+
# Optionally limit the line width to given `max_width`.
|
49
52
|
#
|
50
|
-
# @overload puts(
|
53
|
+
# @overload puts(..., max_width: nil)
|
51
54
|
# @param [#to_s] ... objects to print
|
55
|
+
# @param [Integer, nil] max_width maximum line width
|
52
56
|
# @comment @param [#to_s, nil] prefix line prefix
|
53
57
|
# @comment @param [#to_s, nil] suffix line suffix
|
54
58
|
# @return [Wrapper] itself
|
55
|
-
def puts(*args, prefix: nil, suffix: nil)
|
59
|
+
def puts(*args, max_width: nil, prefix: nil, suffix: nil)
|
56
60
|
if args.empty?
|
57
61
|
@stream.puts(embellish("#{prefix}#{suffix}"))
|
58
62
|
@lines_written += 1
|
59
63
|
else
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
io.each(chomp: true) do |line|
|
64
|
-
@stream.puts(embellish("#{prefix}#{line}#{suffix}"))
|
65
|
-
@lines_written += 1
|
66
|
-
end
|
64
|
+
NattyUI.each_line(*args, max_width: max_width) do |line|
|
65
|
+
@stream.puts(embellish("#{prefix}#{line}#{suffix}"))
|
66
|
+
@lines_written += 1
|
67
67
|
end
|
68
68
|
end
|
69
69
|
@stream.flush
|
@@ -154,7 +154,7 @@ module NattyUI
|
|
154
154
|
def initialize(stream)
|
155
155
|
@stream = stream
|
156
156
|
@lines_written = 0
|
157
|
-
@ws = stream.respond_to?(:winsize) && stream.winsize&.
|
157
|
+
@ws = stream.respond_to?(:winsize) && stream.winsize&.all?(&:positive?)
|
158
158
|
rescue Errno::ENOTTY
|
159
159
|
@ws = false
|
160
160
|
end
|
@@ -163,6 +163,12 @@ module NattyUI
|
|
163
163
|
def prefix = nil
|
164
164
|
alias suffix prefix
|
165
165
|
|
166
|
+
def prefix_width = 0
|
167
|
+
alias suffix_width prefix_width
|
168
|
+
alias width prefix_width
|
169
|
+
|
170
|
+
alias available_width screen_columns
|
171
|
+
|
166
172
|
private_class_method :new
|
167
173
|
end
|
168
174
|
end
|