highline 1.7.10 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +5 -0
- data/.rubocop.yml +84 -0
- data/.simplecov +5 -0
- data/.travis.yml +35 -9
- data/Changelog.md +214 -15
- data/Gemfile +16 -5
- data/README.md +202 -0
- data/Rakefile +8 -20
- data/appveyor.yml +37 -0
- data/examples/ansi_colors.rb +6 -11
- data/examples/asking_for_arrays.rb +6 -2
- data/examples/basic_usage.rb +31 -21
- data/examples/color_scheme.rb +11 -10
- data/examples/get_character.rb +8 -4
- data/examples/limit.rb +4 -0
- data/examples/menus.rb +16 -10
- data/examples/overwrite.rb +9 -5
- data/examples/page_and_wrap.rb +5 -4
- data/examples/password.rb +4 -0
- data/examples/repeat_entry.rb +10 -5
- data/examples/trapping_eof.rb +2 -1
- data/examples/using_readline.rb +2 -1
- data/highline.gemspec +25 -27
- data/lib/highline/builtin_styles.rb +129 -0
- data/lib/highline/color_scheme.rb +49 -32
- data/lib/highline/compatibility.rb +11 -4
- data/lib/highline/custom_errors.rb +57 -0
- data/lib/highline/import.rb +19 -12
- data/lib/highline/io_console_compatible.rb +37 -0
- data/lib/highline/list.rb +177 -0
- data/lib/highline/list_renderer.rb +261 -0
- data/lib/highline/menu/item.rb +32 -0
- data/lib/highline/menu.rb +306 -111
- data/lib/highline/paginator.rb +52 -0
- data/lib/highline/question/answer_converter.rb +103 -0
- data/lib/highline/question.rb +281 -131
- data/lib/highline/question_asker.rb +150 -0
- data/lib/highline/simulate.rb +24 -13
- data/lib/highline/statement.rb +88 -0
- data/lib/highline/string.rb +36 -0
- data/lib/highline/string_extensions.rb +83 -64
- data/lib/highline/style.rb +196 -63
- data/lib/highline/template_renderer.rb +62 -0
- data/lib/highline/terminal/io_console.rb +36 -0
- data/lib/highline/terminal/ncurses.rb +38 -0
- data/lib/highline/terminal/unix_stty.rb +51 -0
- data/lib/highline/terminal.rb +190 -0
- data/lib/highline/version.rb +3 -1
- data/lib/highline/wrapper.rb +53 -0
- data/lib/highline.rb +390 -788
- metadata +56 -35
- data/INSTALL +0 -59
- data/README.rdoc +0 -74
- data/lib/highline/system_extensions.rb +0 -254
- data/setup.rb +0 -1360
- data/test/string_methods.rb +0 -32
- data/test/tc_color_scheme.rb +0 -96
- data/test/tc_highline.rb +0 -1402
- data/test/tc_import.rb +0 -52
- data/test/tc_menu.rb +0 -439
- data/test/tc_simulator.rb +0 -33
- data/test/tc_string_extension.rb +0 -33
- data/test/tc_string_highline.rb +0 -38
- data/test/tc_style.rb +0 -578
@@ -0,0 +1,261 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "highline/template_renderer"
|
4
|
+
require "highline/wrapper"
|
5
|
+
require "highline/list"
|
6
|
+
|
7
|
+
class HighLine
|
8
|
+
#
|
9
|
+
# This class is a utility for quickly and easily laying out lists
|
10
|
+
# to be used by HighLine.
|
11
|
+
#
|
12
|
+
class ListRenderer
|
13
|
+
# Items list
|
14
|
+
# @return [Array]
|
15
|
+
attr_reader :items
|
16
|
+
|
17
|
+
# @return [Symbol] the current mode the List is being rendered
|
18
|
+
# @see #initialize for more details see mode parameter of #initialize
|
19
|
+
attr_reader :mode
|
20
|
+
|
21
|
+
# Changes the behaviour of some modes. Example, in :inline mode
|
22
|
+
# the option is treated as the 'end separator' (defaults to " or ")
|
23
|
+
# @return option parameter that changes the behaviour of some modes.
|
24
|
+
attr_reader :option
|
25
|
+
|
26
|
+
# @return [HighLine] context
|
27
|
+
attr_reader :highline
|
28
|
+
|
29
|
+
# The only required parameters are _items_ and _highline_.
|
30
|
+
# @param items [Array] the Array of items to list
|
31
|
+
# @param mode [Symbol] controls how that list is formed
|
32
|
+
# @param option has different effects, depending on the _mode_.
|
33
|
+
# @param highline [HighLine] a HighLine instance to direct the output to.
|
34
|
+
#
|
35
|
+
# Recognized modes are:
|
36
|
+
#
|
37
|
+
# <tt>:columns_across</tt>:: _items_ will be placed in columns,
|
38
|
+
# flowing from left to right. If given,
|
39
|
+
# _option_ is the number of columns to be
|
40
|
+
# used. When absent, columns will be
|
41
|
+
# determined based on _wrap_at_ or a
|
42
|
+
# default of 80 characters.
|
43
|
+
# <tt>:columns_down</tt>:: Identical to <tt>:columns_across</tt>,
|
44
|
+
# save flow goes down.
|
45
|
+
# <tt>:uneven_columns_across</tt>:: Like <tt>:columns_across</tt> but each
|
46
|
+
# column is sized independently.
|
47
|
+
# <tt>:uneven_columns_down</tt>:: Like <tt>:columns_down</tt> but each
|
48
|
+
# column is sized independently.
|
49
|
+
# <tt>:inline</tt>:: All _items_ are placed on a single
|
50
|
+
# line. The last two _items_ are
|
51
|
+
# separated by _option_ or a default of
|
52
|
+
# " or ". All other _items_ are
|
53
|
+
# separated by ", ".
|
54
|
+
# <tt>:rows</tt>:: The default mode. Each of the _items_
|
55
|
+
# is placed on its own line. The _option_
|
56
|
+
# parameter is ignored in this mode.
|
57
|
+
#
|
58
|
+
# Each member of the _items_ Array is passed through ERb and thus can
|
59
|
+
# contain their own expansions. Color escape expansions do not contribute to
|
60
|
+
# the final field width.
|
61
|
+
|
62
|
+
def initialize(items, mode = :rows, option = nil, highline)
|
63
|
+
@highline = highline
|
64
|
+
@mode = mode
|
65
|
+
@option = option
|
66
|
+
@items = render_list_items(items)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Render the list using the appropriate mode and options.
|
70
|
+
# @return [String] rendered list as String
|
71
|
+
def render
|
72
|
+
return "" if items.empty?
|
73
|
+
|
74
|
+
case mode
|
75
|
+
when :inline
|
76
|
+
list_inline_mode
|
77
|
+
when :columns_across
|
78
|
+
list_columns_across_mode
|
79
|
+
when :columns_down
|
80
|
+
list_columns_down_mode
|
81
|
+
when :uneven_columns_across
|
82
|
+
list_uneven_columns_mode
|
83
|
+
when :uneven_columns_down
|
84
|
+
list_uneven_columns_down_mode
|
85
|
+
else
|
86
|
+
list_default_mode
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def render_list_items(items)
|
93
|
+
items.to_ary.map do |item|
|
94
|
+
item = String(item)
|
95
|
+
template = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
|
96
|
+
ERB.new(item, trim_mode: "%")
|
97
|
+
else
|
98
|
+
ERB.new(item, nil, "%")
|
99
|
+
end
|
100
|
+
template_renderer =
|
101
|
+
HighLine::TemplateRenderer.new(template, self, highline)
|
102
|
+
template_renderer.render
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def list_default_mode
|
107
|
+
items.map { |i| "#{i}\n" }.join
|
108
|
+
end
|
109
|
+
|
110
|
+
def list_inline_mode
|
111
|
+
end_separator = option || " or "
|
112
|
+
|
113
|
+
if items.size == 1
|
114
|
+
items.first
|
115
|
+
else
|
116
|
+
items[0..-2].join(", ") + "#{end_separator}#{items.last}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def list_columns_across_mode
|
121
|
+
HighLine::List.new(right_padded_items, cols: col_count).to_s
|
122
|
+
end
|
123
|
+
|
124
|
+
def list_columns_down_mode
|
125
|
+
HighLine::List.new(
|
126
|
+
right_padded_items,
|
127
|
+
cols: col_count,
|
128
|
+
col_down: true
|
129
|
+
).to_s
|
130
|
+
end
|
131
|
+
|
132
|
+
def list_uneven_columns_mode(list = nil)
|
133
|
+
list ||= HighLine::List.new(items)
|
134
|
+
|
135
|
+
col_max = option || items.size
|
136
|
+
col_max.downto(1) do |column_count|
|
137
|
+
list.cols = column_count
|
138
|
+
widths = get_col_widths(list)
|
139
|
+
|
140
|
+
if column_count == 1 || # last guess
|
141
|
+
inside_line_size_limit?(widths) || # good guess
|
142
|
+
option # defined by user
|
143
|
+
return pad_uneven_rows(list, widths)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def list_uneven_columns_down_mode
|
149
|
+
list = HighLine::List.new(items, col_down: true)
|
150
|
+
list_uneven_columns_mode(list)
|
151
|
+
end
|
152
|
+
|
153
|
+
def pad_uneven_rows(list, widths)
|
154
|
+
right_padded_list = Array(list).map do |row|
|
155
|
+
right_pad_row(row.compact, widths)
|
156
|
+
end
|
157
|
+
stringfy_list(right_padded_list)
|
158
|
+
end
|
159
|
+
|
160
|
+
def stringfy_list(list)
|
161
|
+
list.map { |row| row_to_s(row) }.join
|
162
|
+
end
|
163
|
+
|
164
|
+
def row_to_s(row)
|
165
|
+
row.compact.join(row_join_string) + "\n"
|
166
|
+
end
|
167
|
+
|
168
|
+
def right_pad_row(row, widths)
|
169
|
+
row.zip(widths).map do |field, width|
|
170
|
+
right_pad_field(field, width)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def right_pad_field(field, width)
|
175
|
+
field = String(field) # nil protection
|
176
|
+
pad_size = width - actual_length(field)
|
177
|
+
field + (pad_char * pad_size)
|
178
|
+
end
|
179
|
+
|
180
|
+
def get_col_widths(lines)
|
181
|
+
lines = transpose(lines)
|
182
|
+
get_segment_widths(lines)
|
183
|
+
end
|
184
|
+
|
185
|
+
def get_row_widths(lines)
|
186
|
+
get_segment_widths(lines)
|
187
|
+
end
|
188
|
+
|
189
|
+
def get_segment_widths(lines)
|
190
|
+
lines.map do |col|
|
191
|
+
actual_lengths_for(col).max
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def actual_lengths_for(line)
|
196
|
+
line.map do |item|
|
197
|
+
actual_length(item)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def transpose(lines)
|
202
|
+
lines = Array(lines)
|
203
|
+
first_line = lines.shift
|
204
|
+
first_line.zip(*lines)
|
205
|
+
end
|
206
|
+
|
207
|
+
def inside_line_size_limit?(widths)
|
208
|
+
line_size = widths.reduce(0) { |sum, n| sum + n + row_join_str_size }
|
209
|
+
line_size <= line_size_limit + row_join_str_size
|
210
|
+
end
|
211
|
+
|
212
|
+
def actual_length(text)
|
213
|
+
HighLine::Wrapper.actual_length text
|
214
|
+
end
|
215
|
+
|
216
|
+
def items_max_length
|
217
|
+
@items_max_length ||= max_length(items)
|
218
|
+
end
|
219
|
+
|
220
|
+
def max_length(items)
|
221
|
+
items.map { |item| actual_length(item) }.max
|
222
|
+
end
|
223
|
+
|
224
|
+
def line_size_limit
|
225
|
+
@line_size_limit ||= (highline.wrap_at || 80)
|
226
|
+
end
|
227
|
+
|
228
|
+
def row_join_string
|
229
|
+
@row_join_string ||= " "
|
230
|
+
end
|
231
|
+
|
232
|
+
attr_writer :row_join_string
|
233
|
+
|
234
|
+
def row_join_str_size
|
235
|
+
row_join_string.size
|
236
|
+
end
|
237
|
+
|
238
|
+
def col_count_calculate
|
239
|
+
(line_size_limit + row_join_str_size) /
|
240
|
+
(items_max_length + row_join_str_size)
|
241
|
+
end
|
242
|
+
|
243
|
+
def col_count
|
244
|
+
option || col_count_calculate
|
245
|
+
end
|
246
|
+
|
247
|
+
def right_padded_items
|
248
|
+
items.map do |item|
|
249
|
+
right_pad_field(item, items_max_length)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def pad_char
|
254
|
+
" "
|
255
|
+
end
|
256
|
+
|
257
|
+
def row_count
|
258
|
+
(items.count / col_count.to_f).ceil
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class HighLine
|
4
|
+
class Menu < Question
|
5
|
+
# Represents an Item of a HighLine::Menu.
|
6
|
+
#
|
7
|
+
class Item
|
8
|
+
attr_reader :name, :text, :help, :action
|
9
|
+
|
10
|
+
#
|
11
|
+
# @param name [String] The name that is matched against the user input
|
12
|
+
# @param attributes [Hash] options Hash to tailor menu item to your needs
|
13
|
+
# @option attributes text: [String] The text that displays for that
|
14
|
+
# choice (defaults to name)
|
15
|
+
# @option attributes help: [String] help/hint string to be displayed.
|
16
|
+
# @option attributes action: [Block] a block that gets called when choice
|
17
|
+
# is selected
|
18
|
+
#
|
19
|
+
def initialize(name, attributes)
|
20
|
+
@name = name
|
21
|
+
@text = attributes[:text] || @name
|
22
|
+
@help = attributes[:help]
|
23
|
+
@action = attributes[:action]
|
24
|
+
end
|
25
|
+
|
26
|
+
def item_help
|
27
|
+
return {} unless help
|
28
|
+
{ name.to_s.downcase => help }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|