highline 2.0.0.pre.develop.9 → 2.0.0.pre.develop.11
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/.rubocop.yml +59 -5
- data/.travis.yml +9 -4
- data/Changelog.md +11 -0
- data/Gemfile +12 -19
- data/Rakefile +5 -11
- data/examples/ansi_colors.rb +6 -11
- data/examples/asking_for_arrays.rb +4 -3
- data/examples/basic_usage.rb +29 -22
- data/examples/color_scheme.rb +11 -10
- data/examples/get_character.rb +6 -5
- data/examples/limit.rb +2 -1
- data/examples/menus.rb +11 -11
- data/examples/overwrite.rb +7 -6
- data/examples/page_and_wrap.rb +5 -4
- data/examples/password.rb +2 -1
- data/examples/repeat_entry.rb +7 -5
- data/examples/trapping_eof.rb +2 -1
- data/examples/using_readline.rb +2 -1
- data/highline.gemspec +25 -25
- data/lib/highline.rb +103 -111
- data/lib/highline/builtin_styles.rb +45 -41
- data/lib/highline/color_scheme.rb +32 -28
- data/lib/highline/compatibility.rb +3 -3
- data/lib/highline/custom_errors.rb +2 -1
- data/lib/highline/import.rb +8 -11
- data/lib/highline/list.rb +4 -8
- data/lib/highline/list_renderer.rb +207 -201
- data/lib/highline/menu.rb +75 -63
- data/lib/highline/menu/item.rb +2 -0
- data/lib/highline/paginator.rb +5 -6
- data/lib/highline/question.rb +38 -36
- data/lib/highline/question/answer_converter.rb +2 -2
- data/lib/highline/question_asker.rb +15 -17
- data/lib/highline/simulate.rb +11 -13
- data/lib/highline/statement.rb +12 -10
- data/lib/highline/string.rb +9 -8
- data/lib/highline/string_extensions.rb +30 -14
- data/lib/highline/style.rb +68 -45
- data/lib/highline/template_renderer.rb +5 -5
- data/lib/highline/terminal.rb +24 -31
- data/lib/highline/terminal/io_console.rb +2 -2
- data/lib/highline/terminal/ncurses.rb +4 -3
- data/lib/highline/terminal/unix_stty.rb +12 -9
- data/lib/highline/version.rb +1 -1
- data/lib/highline/wrapper.rb +12 -11
- metadata +34 -43
- data/test/acceptance/acceptance.rb +0 -62
- data/test/acceptance/acceptance_test.rb +0 -69
- data/test/acceptance/at_color_output_using_erb_templates.rb +0 -17
- data/test/acceptance/at_echo_false.rb +0 -23
- data/test/acceptance/at_readline.rb +0 -37
- data/test/io_console_compatible.rb +0 -37
- data/test/string_methods.rb +0 -35
- data/test/test_answer_converter.rb +0 -26
- data/test/test_color_scheme.rb +0 -94
- data/test/test_helper.rb +0 -22
- data/test/test_highline.rb +0 -1627
- data/test/test_import.rb +0 -55
- data/test/test_list.rb +0 -60
- data/test/test_menu.rb +0 -749
- data/test/test_paginator.rb +0 -73
- data/test/test_question_asker.rb +0 -20
- data/test/test_simulator.rb +0 -24
- data/test/test_string_extension.rb +0 -72
- data/test/test_string_highline.rb +0 -42
- data/test/test_style.rb +0 -613
- data/test/test_wrapper.rb +0 -188
@@ -1,4 +1,4 @@
|
|
1
|
-
#coding: utf-8
|
1
|
+
# coding: utf-8
|
2
2
|
|
3
3
|
class HighLine
|
4
4
|
# Builtin Styles that are included at HighLine initialization.
|
@@ -23,17 +23,19 @@ class HighLine
|
|
23
23
|
blink: "\e[5m",
|
24
24
|
reverse: "\e[7m",
|
25
25
|
concealed: "\e[8m"
|
26
|
-
}
|
26
|
+
}.freeze
|
27
27
|
|
28
28
|
STYLE_LIST.each do |style_name, code|
|
29
|
-
|
29
|
+
style = String(style_name).upcase
|
30
30
|
|
31
|
-
|
32
|
-
|
31
|
+
const_set style, code
|
32
|
+
const_set style + "_STYLE",
|
33
|
+
Style.new(name: style_name, code: code, builtin: true)
|
33
34
|
end
|
34
35
|
|
35
36
|
# Basic Style names like CLEAR, BOLD, UNDERLINE
|
36
|
-
STYLES = %w
|
37
|
+
STYLES = %w[CLEAR RESET BOLD DARK UNDERLINE
|
38
|
+
UNDERSCORE BLINK REVERSE CONCEALED].freeze
|
37
39
|
|
38
40
|
# A Hash with the basic colors an their ANSI escape codes.
|
39
41
|
COLOR_LIST = {
|
@@ -48,33 +50,35 @@ class HighLine
|
|
48
50
|
gray: { code: "\e[37m", rgb: [192, 192, 192] },
|
49
51
|
grey: { code: "\e[37m", rgb: [192, 192, 192] },
|
50
52
|
none: { code: "\e[38m", rgb: [0, 0, 0] }
|
51
|
-
}
|
53
|
+
}.freeze
|
52
54
|
|
53
55
|
COLOR_LIST.each do |color_name, attributes|
|
54
|
-
|
56
|
+
color = String(color_name).upcase
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
style = Style.new(
|
59
|
+
name: color_name,
|
60
|
+
code: attributes[:code],
|
61
|
+
rgb: attributes[:rgb],
|
62
|
+
builtin: true
|
63
|
+
)
|
62
64
|
|
63
|
-
|
65
|
+
const_set color + "_STYLE", style
|
64
66
|
end
|
65
67
|
|
66
68
|
# The builtin styles basic colors like black, red, green.
|
67
|
-
BASIC_COLORS =
|
69
|
+
BASIC_COLORS =
|
70
|
+
%w[BLACK RED GREEN YELLOW BLUE
|
71
|
+
MAGENTA CYAN WHITE GRAY GREY NONE].freeze
|
68
72
|
|
69
73
|
colors = BASIC_COLORS.dup
|
70
74
|
BASIC_COLORS.each do |color|
|
71
75
|
bright_color = "BRIGHT_#{color}"
|
72
76
|
colors << bright_color
|
73
|
-
const_set bright_color +
|
77
|
+
const_set bright_color + "_STYLE", const_get(color + "_STYLE").bright
|
74
78
|
|
75
79
|
light_color = "LIGHT_#{color}"
|
76
80
|
colors << light_color
|
77
|
-
const_set light_color +
|
81
|
+
const_set light_color + "_STYLE", const_get(color + "_STYLE").light
|
78
82
|
end
|
79
83
|
|
80
84
|
# The builtin styles' colors like LIGHT_RED and BRIGHT_BLUE.
|
@@ -86,11 +90,10 @@ class HighLine
|
|
86
90
|
const_set "ON_#{color}", const_get("ON_#{color}_STYLE").code
|
87
91
|
end
|
88
92
|
|
89
|
-
ON_NONE_STYLE.rgb = [255,255,255] # Override; white background
|
93
|
+
ON_NONE_STYLE.rgb = [255, 255, 255] # Override; white background
|
90
94
|
|
91
95
|
# BuiltinStyles class methods to be extended.
|
92
96
|
module ClassMethods
|
93
|
-
|
94
97
|
# Regexp to match against RGB style constant names.
|
95
98
|
RGB_COLOR_PATTERN = /^(ON_)?(RGB_)([A-F0-9]{6})(_STYLE)?$/
|
96
99
|
|
@@ -98,27 +101,28 @@ class HighLine
|
|
98
101
|
# builtin constants (without explicitly defining them)
|
99
102
|
# @param name [Symbol] missing constant name
|
100
103
|
def const_missing(name)
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
104
|
+
raise NameError, "Bad color or uninitialized constant #{name}" unless
|
105
|
+
name.to_s =~ RGB_COLOR_PATTERN
|
106
|
+
|
107
|
+
on = Regexp.last_match(1)
|
108
|
+
suffix = Regexp.last_match(4)
|
109
|
+
|
110
|
+
code_name = if suffix
|
111
|
+
Regexp.last_match(1).to_s +
|
112
|
+
Regexp.last_match(2) +
|
113
|
+
Regexp.last_match(3)
|
114
|
+
else
|
115
|
+
name.to_s
|
116
|
+
end
|
117
|
+
|
118
|
+
style_name = code_name + "_STYLE"
|
119
|
+
style = Style.rgb(Regexp.last_match(3))
|
120
|
+
style = style.on if on
|
121
|
+
|
122
|
+
const_set(style_name, style)
|
123
|
+
const_set(code_name, style.code)
|
124
|
+
|
125
|
+
suffix ? style : style.code
|
122
126
|
end
|
123
127
|
end
|
124
128
|
end
|
@@ -8,7 +8,6 @@
|
|
8
8
|
#
|
9
9
|
# This is Free Software. See LICENSE and COPYING for details
|
10
10
|
|
11
|
-
|
12
11
|
class HighLine
|
13
12
|
#
|
14
13
|
# ColorScheme objects encapsulate a named set of colors to be used in the
|
@@ -19,7 +18,8 @@ class HighLine
|
|
19
18
|
#
|
20
19
|
# A ColorScheme contains named sets of HighLine color constants.
|
21
20
|
#
|
22
|
-
# @example Instantiating a color scheme, applying it to HighLine,
|
21
|
+
# @example Instantiating a color scheme, applying it to HighLine,
|
22
|
+
# and using it:
|
23
23
|
# ft = HighLine::ColorScheme.new do |cs|
|
24
24
|
# cs[:headline] = [ :bold, :yellow, :on_black ]
|
25
25
|
# cs[:horizontal_line] = [ :bold, :white ]
|
@@ -49,15 +49,15 @@ class HighLine
|
|
49
49
|
# constants.
|
50
50
|
#
|
51
51
|
# @param h [Hash]
|
52
|
-
def initialize(
|
53
|
-
@scheme =
|
52
|
+
def initialize(h = nil)
|
53
|
+
@scheme = {}
|
54
54
|
load_from_hash(h) if h
|
55
55
|
yield self if block_given?
|
56
56
|
end
|
57
57
|
|
58
58
|
# Load multiple colors from key/value pairs.
|
59
59
|
# @param h [Hash]
|
60
|
-
def load_from_hash(
|
60
|
+
def load_from_hash(h)
|
61
61
|
h.each_pair do |color_tag, constants|
|
62
62
|
self[color_tag] = constants
|
63
63
|
end
|
@@ -66,20 +66,20 @@ class HighLine
|
|
66
66
|
# Does this color scheme include the given tag name?
|
67
67
|
# @param color_tag [#to_sym]
|
68
68
|
# @return [Boolean]
|
69
|
-
def include?(
|
69
|
+
def include?(color_tag)
|
70
70
|
@scheme.keys.include?(to_symbol(color_tag))
|
71
71
|
end
|
72
72
|
|
73
73
|
# Allow the scheme to be accessed like a Hash.
|
74
74
|
# @param color_tag [#to_sym]
|
75
75
|
# @return [Style]
|
76
|
-
def [](
|
76
|
+
def [](color_tag)
|
77
77
|
@scheme[to_symbol(color_tag)]
|
78
78
|
end
|
79
79
|
|
80
80
|
# Retrieve the original form of the scheme
|
81
81
|
# @param color_tag [#to_sym]
|
82
|
-
def definition(
|
82
|
+
def definition(color_tag)
|
83
83
|
style = @scheme[to_symbol(color_tag)]
|
84
84
|
style && style.list
|
85
85
|
end
|
@@ -93,29 +93,33 @@ class HighLine
|
|
93
93
|
# Allow the scheme to be set like a Hash.
|
94
94
|
# @param color_tag [#to_sym]
|
95
95
|
# @param constants [Array<Symbol>] Array of Style symbols
|
96
|
-
def []=(
|
97
|
-
@scheme[to_symbol(color_tag)] =
|
98
|
-
|
96
|
+
def []=(color_tag, constants)
|
97
|
+
@scheme[to_symbol(color_tag)] =
|
98
|
+
HighLine::Style.new(name: color_tag.to_s.downcase.to_sym,
|
99
|
+
list: constants,
|
100
|
+
no_index: true)
|
99
101
|
end
|
100
102
|
|
101
103
|
# Retrieve the color scheme hash (in original definition format)
|
102
104
|
# @return [Hash] scheme as Hash. It may be reused in a new ColorScheme.
|
103
105
|
def to_hash
|
104
|
-
@scheme.
|
106
|
+
@scheme.each_with_object({}) do |pair, hsh|
|
107
|
+
key, value = pair
|
108
|
+
hsh[key] = value.list
|
109
|
+
end
|
105
110
|
end
|
106
111
|
|
107
|
-
|
108
112
|
private
|
109
113
|
|
110
114
|
# Return a normalized representation of a color name.
|
111
|
-
def to_symbol(
|
115
|
+
def to_symbol(t)
|
112
116
|
t.to_s.downcase
|
113
117
|
end
|
114
118
|
|
115
119
|
# Return a normalized representation of a color setting.
|
116
|
-
def to_constant(
|
120
|
+
def to_constant(v)
|
117
121
|
v = v.to_s if v.is_a?(Symbol)
|
118
|
-
if v.is_a?(::String)
|
122
|
+
if v.is_a?(::String)
|
119
123
|
HighLine.const_get(v.upcase)
|
120
124
|
else
|
121
125
|
v
|
@@ -125,23 +129,23 @@ class HighLine
|
|
125
129
|
|
126
130
|
# A sample ColorScheme.
|
127
131
|
class SampleColorScheme < ColorScheme
|
132
|
+
SAMPLE_SCHEME = {
|
133
|
+
critical: [:yellow, :on_red],
|
134
|
+
error: [:bold, :red],
|
135
|
+
warning: [:bold, :yellow],
|
136
|
+
notice: [:bold, :magenta],
|
137
|
+
info: [:bold, :cyan],
|
138
|
+
debug: [:bold, :green],
|
139
|
+
row_even: [:cyan],
|
140
|
+
row_odd: [:magenta]
|
141
|
+
}.freeze
|
128
142
|
#
|
129
143
|
# Builds the sample scheme with settings for <tt>:critical</tt>,
|
130
144
|
# <tt>:error</tt>, <tt>:warning</tt>, <tt>:notice</tt>, <tt>:info</tt>,
|
131
145
|
# <tt>:debug</tt>, <tt>:row_even</tt>, and <tt>:row_odd</tt> colors.
|
132
146
|
#
|
133
|
-
def initialize(
|
134
|
-
|
135
|
-
:critical => [ :yellow, :on_red ],
|
136
|
-
:error => [ :bold, :red ],
|
137
|
-
:warning => [ :bold, :yellow ],
|
138
|
-
:notice => [ :bold, :magenta ],
|
139
|
-
:info => [ :bold, :cyan ],
|
140
|
-
:debug => [ :bold, :green ],
|
141
|
-
:row_even => [ :cyan ],
|
142
|
-
:row_odd => [ :magenta ]
|
143
|
-
}
|
144
|
-
super(scheme)
|
147
|
+
def initialize(_h = nil)
|
148
|
+
super(SAMPLE_SCHEME)
|
145
149
|
end
|
146
150
|
end
|
147
151
|
end
|
@@ -4,13 +4,13 @@ unless STDIN.respond_to? :getbyte
|
|
4
4
|
# HighLine adds #getbyte alias to #getc when #getbyte is not available.
|
5
5
|
class IO
|
6
6
|
# alias to #getc when #getbyte is not available
|
7
|
-
|
7
|
+
alias getbyte getc
|
8
8
|
end
|
9
9
|
|
10
10
|
# HighLine adds #getbyte alias to #getc when #getbyte is not available.
|
11
11
|
class StringIO
|
12
12
|
# alias to #getc when #getbyte is not available
|
13
|
-
|
13
|
+
alias getbyte getc
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -18,6 +18,6 @@ unless "".respond_to? :each_line
|
|
18
18
|
# HighLine adds #each_line alias to #each when each_line is not available.
|
19
19
|
class String
|
20
20
|
# alias to #each when each_line is not available.
|
21
|
-
|
21
|
+
alias each_line each
|
22
22
|
end
|
23
23
|
end
|
data/lib/highline/import.rb
CHANGED
@@ -10,22 +10,19 @@
|
|
10
10
|
require "highline"
|
11
11
|
require "forwardable"
|
12
12
|
|
13
|
-
$terminal = HighLine.new
|
14
|
-
|
15
13
|
#
|
16
14
|
# <tt>require "highline/import"</tt> adds shortcut methods to Kernel, making
|
17
15
|
# {HighLine#agree}, {HighLine#ask}, {HighLine#choose} and {HighLine#say}
|
18
16
|
# globally available. This is handy for
|
19
|
-
# quick and dirty input and output. These methods use
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# Otherwise, these methods are identical to their {HighLine} counterparts,
|
23
|
-
# class for detailed explanations.
|
17
|
+
# quick and dirty input and output. These methods use HighLine.default_instance
|
18
|
+
# which is initialized to use <tt>$stdin</tt> and <tt>$stdout</tt> (you are free
|
19
|
+
# to change this).
|
20
|
+
# Otherwise, these methods are identical to their {HighLine} counterparts,
|
21
|
+
# see that class for detailed explanations.
|
24
22
|
#
|
25
23
|
module Kernel
|
26
24
|
extend Forwardable
|
27
|
-
|
28
|
-
:use_color=, :use_color?, :reset_use_color
|
25
|
+
def_instance_delegators :HighLine, :agree, :ask, :choose, :say
|
29
26
|
end
|
30
27
|
|
31
28
|
# When requiring 'highline/import' HighLine adds {#or_ask} to Object so
|
@@ -41,11 +38,11 @@ class Object
|
|
41
38
|
# @param details [lambda] block to be called with the question
|
42
39
|
# instance as argument.
|
43
40
|
# @return [String] answer
|
44
|
-
def or_ask(
|
41
|
+
def or_ask(*args, &details)
|
45
42
|
ask(*args) do |question|
|
46
43
|
question.first_answer = String(self)
|
47
44
|
|
48
|
-
|
45
|
+
yield(question) if details
|
49
46
|
end
|
50
47
|
end
|
51
48
|
end
|
data/lib/highline/list.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
class HighLine
|
4
|
-
|
5
4
|
# List class with some convenience methods like {#col_down}.
|
6
5
|
class List
|
7
|
-
|
8
6
|
# Original given *items* argument.
|
9
7
|
# It's frozen at initialization time and
|
10
8
|
# all later transformations will happen on {#list}.
|
@@ -33,7 +31,6 @@ class HighLine
|
|
33
31
|
|
34
32
|
attr_reader :transpose_mode
|
35
33
|
|
36
|
-
|
37
34
|
# Content are distributed first by column in col down mode.
|
38
35
|
# @return [Boolean]
|
39
36
|
#
|
@@ -69,7 +66,8 @@ class HighLine
|
|
69
66
|
build
|
70
67
|
end
|
71
68
|
|
72
|
-
# Transpose the (already sliced by rows) list,
|
69
|
+
# Transpose the (already sliced by rows) list,
|
70
|
+
# turning its rows into columns.
|
73
71
|
# @return [self]
|
74
72
|
def transpose
|
75
73
|
first_row = @list[0]
|
@@ -141,9 +139,7 @@ class HighLine
|
|
141
139
|
|
142
140
|
# Set the {#row_join_string}.
|
143
141
|
# @see #row_join_string
|
144
|
-
|
145
|
-
@row_join_string = string
|
146
|
-
end
|
142
|
+
attr_writer :row_join_string
|
147
143
|
|
148
144
|
# Returns the row join string size.
|
149
145
|
# Useful for calculating the actual size of
|
@@ -178,4 +174,4 @@ class HighLine
|
|
178
174
|
row.compact.join(row_join_string) + "\n"
|
179
175
|
end
|
180
176
|
end
|
181
|
-
end
|
177
|
+
end
|
@@ -1,251 +1,257 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
|
7
|
-
|
8
|
-
# This class is a utility for quickly and easily laying out lists
|
9
|
-
# to be used by HighLine.
|
10
|
-
#
|
11
|
-
class HighLine::ListRenderer
|
12
|
-
# Items list
|
13
|
-
# @return [Array]
|
14
|
-
attr_reader :items
|
15
|
-
|
16
|
-
# @return [Symbol] the current mode the List is being rendered
|
17
|
-
# @see #initialize for more details see mode parameter of #initialize
|
18
|
-
attr_reader :mode
|
19
|
-
|
20
|
-
# Changes the behaviour of some modes. Example, in :inline mode
|
21
|
-
# the option is treated as the 'end separator' (defaults to " or ")
|
22
|
-
# @return option parameter that changes the behaviour of some modes.
|
23
|
-
attr_reader :option
|
24
|
-
|
25
|
-
# @return [HighLine] context
|
26
|
-
attr_reader :highline
|
27
|
-
|
28
|
-
# The only required parameters are _items_ and _highline_.
|
29
|
-
# @param items [Array] the Array of items to list
|
30
|
-
# @param mode [Symbol] controls how that list is formed
|
31
|
-
# @param option has different effects, depending on the _mode_.
|
32
|
-
# @param highline [HighLine] a HighLine instance to direct the output to.
|
33
|
-
#
|
34
|
-
# Recognized modes are:
|
3
|
+
require "highline/template_renderer"
|
4
|
+
require "highline/wrapper"
|
5
|
+
require "highline/list"
|
6
|
+
|
7
|
+
class HighLine
|
35
8
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# _option_ is the number of columns to be
|
39
|
-
# used. When absent, columns will be
|
40
|
-
# determined based on _wrap_at_ or a
|
41
|
-
# default of 80 characters.
|
42
|
-
# <tt>:columns_down</tt>:: Identical to <tt>:columns_across</tt>,
|
43
|
-
# save flow goes down.
|
44
|
-
# <tt>:uneven_columns_across</tt>:: Like <tt>:columns_across</tt> but each
|
45
|
-
# column is sized independently.
|
46
|
-
# <tt>:uneven_columns_down</tt>:: Like <tt>:columns_down</tt> but each
|
47
|
-
# column is sized independently.
|
48
|
-
# <tt>:inline</tt>:: All _items_ are placed on a single line.
|
49
|
-
# The last two _items_ are separated by
|
50
|
-
# _option_ or a default of " or ". All
|
51
|
-
# other _items_ are separated by ", ".
|
52
|
-
# <tt>:rows</tt>:: The default mode. Each of the _items_ is
|
53
|
-
# placed on its own line. The _option_
|
54
|
-
# parameter is ignored in this mode.
|
9
|
+
# This class is a utility for quickly and easily laying out lists
|
10
|
+
# to be used by HighLine.
|
55
11
|
#
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
@
|
62
|
-
@mode
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
85
88
|
end
|
86
|
-
end
|
87
89
|
|
88
|
-
|
90
|
+
private
|
89
91
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
92
|
+
def render_list_items(items)
|
93
|
+
items.to_ary.map do |item|
|
94
|
+
item = String(item)
|
95
|
+
template = ERB.new(item, nil, "%")
|
96
|
+
template_renderer =
|
97
|
+
HighLine::TemplateRenderer.new(template, self, highline)
|
98
|
+
template_renderer.render
|
99
|
+
end
|
96
100
|
end
|
97
|
-
end
|
98
101
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
+
def list_default_mode
|
103
|
+
items.map { |i| "#{i}\n" }.join
|
104
|
+
end
|
102
105
|
|
103
|
-
|
104
|
-
|
106
|
+
def list_inline_mode
|
107
|
+
end_separator = option || " or "
|
105
108
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
109
|
+
if items.size == 1
|
110
|
+
items.first
|
111
|
+
else
|
112
|
+
items[0..-2].join(", ") + "#{end_separator}#{items.last}"
|
113
|
+
end
|
110
114
|
end
|
111
|
-
end
|
112
115
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
+
def list_columns_across_mode
|
117
|
+
HighLine::List.new(right_padded_items, cols: col_count).to_s
|
118
|
+
end
|
116
119
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
+
def list_columns_down_mode
|
121
|
+
HighLine::List.new(
|
122
|
+
right_padded_items,
|
123
|
+
cols: col_count,
|
124
|
+
col_down: true
|
125
|
+
).to_s
|
126
|
+
end
|
120
127
|
|
121
|
-
|
122
|
-
|
128
|
+
def list_uneven_columns_mode(list = nil)
|
129
|
+
list ||= HighLine::List.new(items)
|
123
130
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
131
|
+
col_max = option || items.size
|
132
|
+
col_max.downto(1) do |column_count|
|
133
|
+
list.cols = column_count
|
134
|
+
widths = get_col_widths(list)
|
128
135
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
136
|
+
if column_count == 1 || # last guess
|
137
|
+
inside_line_size_limit?(widths) || # good guess
|
138
|
+
option # defined by user
|
139
|
+
return pad_uneven_rows(list, widths)
|
140
|
+
end
|
133
141
|
end
|
134
142
|
end
|
135
|
-
end
|
136
143
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
144
|
+
def list_uneven_columns_down_mode
|
145
|
+
list = HighLine::List.new(items, col_down: true)
|
146
|
+
list_uneven_columns_mode(list)
|
147
|
+
end
|
141
148
|
|
142
|
-
|
143
|
-
|
144
|
-
|
149
|
+
def pad_uneven_rows(list, widths)
|
150
|
+
right_padded_list = Array(list).map do |row|
|
151
|
+
right_pad_row(row.compact, widths)
|
152
|
+
end
|
153
|
+
stringfy_list(right_padded_list)
|
145
154
|
end
|
146
|
-
stringfy_list(right_padded_list)
|
147
|
-
end
|
148
155
|
|
149
|
-
|
150
|
-
|
151
|
-
|
156
|
+
def stringfy_list(list)
|
157
|
+
list.map { |row| row_to_s(row) }.join
|
158
|
+
end
|
152
159
|
|
153
|
-
|
154
|
-
|
155
|
-
|
160
|
+
def row_to_s(row)
|
161
|
+
row.compact.join(row_join_string) + "\n"
|
162
|
+
end
|
156
163
|
|
157
|
-
|
158
|
-
|
159
|
-
|
164
|
+
def right_pad_row(row, widths)
|
165
|
+
row.zip(widths).map do |field, width|
|
166
|
+
right_pad_field(field, width)
|
167
|
+
end
|
160
168
|
end
|
161
|
-
end
|
162
169
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
170
|
+
def right_pad_field(field, width)
|
171
|
+
field = String(field) # nil protection
|
172
|
+
pad_size = width - actual_length(field)
|
173
|
+
field + (pad_char * pad_size)
|
174
|
+
end
|
168
175
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
176
|
+
def get_col_widths(lines)
|
177
|
+
lines = transpose(lines)
|
178
|
+
get_segment_widths(lines)
|
179
|
+
end
|
173
180
|
|
174
|
-
|
175
|
-
|
176
|
-
|
181
|
+
def get_row_widths(lines)
|
182
|
+
get_segment_widths(lines)
|
183
|
+
end
|
177
184
|
|
178
|
-
|
179
|
-
|
180
|
-
|
185
|
+
def get_segment_widths(lines)
|
186
|
+
lines.map do |col|
|
187
|
+
actual_lengths_for(col).max
|
188
|
+
end
|
181
189
|
end
|
182
|
-
end
|
183
190
|
|
184
|
-
|
185
|
-
|
186
|
-
|
191
|
+
def actual_lengths_for(line)
|
192
|
+
line.map do |item|
|
193
|
+
actual_length(item)
|
194
|
+
end
|
187
195
|
end
|
188
|
-
end
|
189
196
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
197
|
+
def transpose(lines)
|
198
|
+
lines = Array(lines)
|
199
|
+
first_line = lines.shift
|
200
|
+
first_line.zip(*lines)
|
201
|
+
end
|
195
202
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
203
|
+
def inside_line_size_limit?(widths)
|
204
|
+
line_size = widths.reduce(0) { |sum, n| sum + n + row_join_str_size }
|
205
|
+
line_size <= line_size_limit + row_join_str_size
|
206
|
+
end
|
200
207
|
|
201
|
-
|
202
|
-
|
203
|
-
|
208
|
+
def actual_length(text)
|
209
|
+
HighLine::Wrapper.actual_length text
|
210
|
+
end
|
204
211
|
|
205
|
-
|
206
|
-
|
207
|
-
|
212
|
+
def items_max_length
|
213
|
+
@items_max_length ||= max_length(items)
|
214
|
+
end
|
208
215
|
|
209
|
-
|
210
|
-
|
211
|
-
|
216
|
+
def max_length(items)
|
217
|
+
items.map { |item| actual_length(item) }.max
|
218
|
+
end
|
212
219
|
|
213
|
-
|
214
|
-
|
215
|
-
|
220
|
+
def line_size_limit
|
221
|
+
@line_size_limit ||= (highline.wrap_at || 80)
|
222
|
+
end
|
216
223
|
|
217
|
-
|
218
|
-
|
219
|
-
|
224
|
+
def row_join_string
|
225
|
+
@row_join_string ||= " "
|
226
|
+
end
|
220
227
|
|
221
|
-
|
222
|
-
@row_join_string = string
|
223
|
-
end
|
228
|
+
attr_writer :row_join_string
|
224
229
|
|
225
|
-
|
226
|
-
|
227
|
-
|
230
|
+
def row_join_str_size
|
231
|
+
row_join_string.size
|
232
|
+
end
|
228
233
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
234
|
+
def col_count_calculate
|
235
|
+
(line_size_limit + row_join_str_size) /
|
236
|
+
(items_max_length + row_join_str_size)
|
237
|
+
end
|
233
238
|
|
234
|
-
|
235
|
-
|
236
|
-
|
239
|
+
def col_count
|
240
|
+
option || col_count_calculate
|
241
|
+
end
|
237
242
|
|
238
|
-
|
239
|
-
|
240
|
-
|
243
|
+
def right_padded_items
|
244
|
+
items.map do |item|
|
245
|
+
right_pad_field(item, items_max_length)
|
246
|
+
end
|
241
247
|
end
|
242
|
-
end
|
243
248
|
|
244
|
-
|
245
|
-
|
246
|
-
|
249
|
+
def pad_char
|
250
|
+
" "
|
251
|
+
end
|
247
252
|
|
248
|
-
|
249
|
-
|
253
|
+
def row_count
|
254
|
+
(items.count / col_count.to_f).ceil
|
255
|
+
end
|
250
256
|
end
|
251
|
-
end
|
257
|
+
end
|