tty 0.0.10 → 0.0.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.
- data/README.md +75 -19
- data/lib/tty.rb +7 -0
- data/lib/tty/shell/question.rb +3 -3
- data/lib/tty/shell/response.rb +5 -5
- data/lib/tty/table.rb +51 -19
- data/lib/tty/table/column_set.rb +42 -1
- data/lib/tty/table/columns.rb +167 -0
- data/lib/tty/table/field.rb +2 -2
- data/lib/tty/table/indentation.rb +54 -0
- data/lib/tty/table/operation/escape.rb +1 -1
- data/lib/tty/table/operation/filter.rb +0 -1
- data/lib/tty/table/operation/padding.rb +95 -0
- data/lib/tty/table/operation/wrapped.rb +6 -3
- data/lib/tty/table/operations.rb +3 -2
- data/lib/tty/table/orientation/horizontal.rb +27 -1
- data/lib/tty/table/orientation/vertical.rb +17 -1
- data/lib/tty/table/padder.rb +142 -0
- data/lib/tty/table/renderer/basic.rb +101 -31
- data/lib/tty/table/validatable.rb +0 -7
- data/lib/tty/terminal/color.rb +36 -20
- data/lib/tty/text/truncation.rb +16 -1
- data/lib/tty/text/wrapping.rb +31 -10
- data/lib/tty/version.rb +1 -1
- data/spec/tty/shell/question/argument_spec.rb +1 -1
- data/spec/tty/shell/question/modify_spec.rb +2 -2
- data/spec/tty/shell/response/read_email_spec.rb +0 -1
- data/spec/tty/table/border/ascii/rendering_spec.rb +34 -7
- data/spec/tty/table/border/null/rendering_spec.rb +34 -7
- data/spec/tty/table/column_set/extract_widths_spec.rb +1 -1
- data/spec/tty/table/column_set/widths_from_spec.rb +52 -0
- data/spec/tty/table/columns/enforce_spec.rb +68 -0
- data/spec/tty/table/columns/widths_spec.rb +33 -0
- data/spec/tty/table/indentation/insert_indent_spec.rb +27 -0
- data/spec/tty/table/operation/wrapped/call_spec.rb +2 -1
- data/spec/tty/table/operation/wrapped/wrap_spec.rb +3 -2
- data/spec/tty/table/operations/new_spec.rb +3 -5
- data/spec/tty/table/orientation_spec.rb +68 -22
- data/spec/tty/table/padder/parse_spec.rb +45 -0
- data/spec/tty/table/padding_spec.rb +120 -0
- data/spec/tty/table/renderer/ascii/indentation_spec.rb +41 -0
- data/spec/tty/table/renderer/ascii/padding_spec.rb +61 -0
- data/spec/tty/table/renderer/ascii/resizing_spec.rb +114 -0
- data/spec/tty/table/renderer/basic/alignment_spec.rb +18 -19
- data/spec/tty/table/renderer/basic/coloring_spec.rb +45 -0
- data/spec/tty/table/renderer/basic/indentation_spec.rb +46 -0
- data/spec/tty/table/renderer/basic/options_spec.rb +2 -2
- data/spec/tty/table/renderer/basic/padding_spec.rb +52 -0
- data/spec/tty/table/renderer/basic/resizing_spec.rb +96 -0
- data/spec/tty/table/renderer/render_spec.rb +36 -0
- data/spec/tty/table/renderer/unicode/indentation_spec.rb +41 -0
- data/spec/tty/table/renderer/unicode/padding_spec.rb +61 -0
- data/spec/tty/table/renderer_spec.rb +19 -0
- data/spec/tty/table/rotate_spec.rb +20 -6
- data/spec/tty/text/truncation/truncate_spec.rb +18 -3
- data/spec/tty/text/wrapping/wrap_spec.rb +24 -7
- metadata +56 -18
@@ -0,0 +1,142 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
|
6
|
+
# A class responsible for processing table field padding
|
7
|
+
class Padder
|
8
|
+
include TTY::Equatable
|
9
|
+
|
10
|
+
attr_reader :padding
|
11
|
+
|
12
|
+
# Initialize a Padder
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
def initialize(padding)
|
16
|
+
@padding = padding
|
17
|
+
end
|
18
|
+
|
19
|
+
# Parse padding options
|
20
|
+
#
|
21
|
+
# @param [Object] value
|
22
|
+
#
|
23
|
+
# @return [TTY::Padder]
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def self.parse(value = nil)
|
27
|
+
return value if value.kind_of?(self)
|
28
|
+
|
29
|
+
padding = if value.class <= Numeric
|
30
|
+
[value, value, value, value]
|
31
|
+
elsif value.nil?
|
32
|
+
[]
|
33
|
+
elsif value.size == 2
|
34
|
+
[value[0], value[1], value[0], value[1]]
|
35
|
+
elsif value.size == 4
|
36
|
+
value
|
37
|
+
else
|
38
|
+
raise ArgumentError, 'Wrong :padding parameter, must be an array'
|
39
|
+
end
|
40
|
+
new(padding)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Top padding
|
44
|
+
#
|
45
|
+
# @return [Integer]
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def top
|
49
|
+
@padding[0].to_i
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set top padding
|
53
|
+
#
|
54
|
+
# @param [Integer] val
|
55
|
+
#
|
56
|
+
# @api public
|
57
|
+
def top=(value)
|
58
|
+
@padding[0] = value
|
59
|
+
end
|
60
|
+
|
61
|
+
# Right padding
|
62
|
+
#
|
63
|
+
# @return [Integer]
|
64
|
+
#
|
65
|
+
# @api public
|
66
|
+
def right
|
67
|
+
@padding[1].to_i
|
68
|
+
end
|
69
|
+
|
70
|
+
# Set right padding
|
71
|
+
#
|
72
|
+
# @param [Integer] val
|
73
|
+
#
|
74
|
+
# @api public
|
75
|
+
def right=(value)
|
76
|
+
@padding[1] = value
|
77
|
+
end
|
78
|
+
|
79
|
+
# Bottom padding
|
80
|
+
#
|
81
|
+
# @return [Integer]
|
82
|
+
#
|
83
|
+
# @api public
|
84
|
+
def bottom
|
85
|
+
@padding[2].to_i
|
86
|
+
end
|
87
|
+
|
88
|
+
# Set bottom padding
|
89
|
+
#
|
90
|
+
# @param [Integer] value
|
91
|
+
#
|
92
|
+
# @api public
|
93
|
+
def bottom=(value)
|
94
|
+
@padding[2] = value
|
95
|
+
end
|
96
|
+
|
97
|
+
# Left padding
|
98
|
+
#
|
99
|
+
# @return [Integer]
|
100
|
+
#
|
101
|
+
# @api public
|
102
|
+
def left
|
103
|
+
@padding[3].to_i
|
104
|
+
end
|
105
|
+
|
106
|
+
# Set left padding
|
107
|
+
#
|
108
|
+
# @param [Integer] value
|
109
|
+
#
|
110
|
+
# @api public
|
111
|
+
def left=(value)
|
112
|
+
@padding[3] = value
|
113
|
+
end
|
114
|
+
|
115
|
+
# Check if padding is set
|
116
|
+
#
|
117
|
+
# @api public
|
118
|
+
def empty?
|
119
|
+
padding.empty?
|
120
|
+
end
|
121
|
+
|
122
|
+
# Check if vertical padding is applied
|
123
|
+
#
|
124
|
+
# @return [Boolean]
|
125
|
+
#
|
126
|
+
# @api public
|
127
|
+
def vertical?
|
128
|
+
top.nonzero? or bottom.nonzero?
|
129
|
+
end
|
130
|
+
|
131
|
+
# Check if horizontal padding is applied
|
132
|
+
#
|
133
|
+
# @return [Boolean]
|
134
|
+
#
|
135
|
+
# @api public
|
136
|
+
def horizontal?
|
137
|
+
left.nonzero? or right.nonzero?
|
138
|
+
end
|
139
|
+
|
140
|
+
end # Padder
|
141
|
+
end # Table
|
142
|
+
end # TTY
|
@@ -16,7 +16,7 @@ module TTY
|
|
16
16
|
#
|
17
17
|
# @api public
|
18
18
|
attr_reader :table
|
19
|
-
private :table
|
19
|
+
# private :table
|
20
20
|
|
21
21
|
# Table border to be rendered
|
22
22
|
#
|
@@ -57,6 +57,36 @@ module TTY
|
|
57
57
|
# @api public
|
58
58
|
attr_accessor :multiline
|
59
59
|
|
60
|
+
# The table indentation value
|
61
|
+
#
|
62
|
+
# @return [Integer]
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
attr_accessor :indent
|
66
|
+
|
67
|
+
# The table totabl width
|
68
|
+
#
|
69
|
+
# @return [Integer]
|
70
|
+
#
|
71
|
+
# @api public
|
72
|
+
attr_accessor :width
|
73
|
+
|
74
|
+
# The table resizing behaviour. If true the algorithm will automatically
|
75
|
+
# expand or shrink table to fit the terminal width or specified width.
|
76
|
+
# By default its false.
|
77
|
+
#
|
78
|
+
# @return [Integer]
|
79
|
+
#
|
80
|
+
# @api public
|
81
|
+
attr_accessor :resize
|
82
|
+
|
83
|
+
# The table padding settings
|
84
|
+
#
|
85
|
+
# @return [TTY::Table::Padder]
|
86
|
+
#
|
87
|
+
# @api public
|
88
|
+
attr_reader :padding
|
89
|
+
|
60
90
|
# Initialize a Renderer
|
61
91
|
#
|
62
92
|
# @param [Hash] options
|
@@ -64,31 +94,39 @@ module TTY
|
|
64
94
|
# used to format table individual column alignment
|
65
95
|
# @option options [String] :column_widths
|
66
96
|
# used to format table individula column width
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
97
|
+
# @option options [Integer] :indent
|
98
|
+
# indent the first column by indent value
|
99
|
+
# @option options [Integer,Array] :padding
|
100
|
+
# add padding to table fields
|
70
101
|
#
|
71
102
|
# @return [TTY::Table::Renderer::Basic]
|
72
103
|
#
|
73
104
|
# @api private
|
74
|
-
def initialize(table, options={})
|
75
|
-
validate_rendering_options!(options)
|
105
|
+
def initialize(table, options = {})
|
76
106
|
@table = table || (raise ArgumentRequired, "Expected TTY::Table instance, got #{table.inspect}")
|
77
107
|
@multiline = options.fetch(:multiline) { false }
|
78
108
|
@operations = TTY::Table::Operations.new(table)
|
79
|
-
|
80
|
-
@operations.
|
81
|
-
@operations.run_operations(:escape)
|
109
|
+
if not multiline
|
110
|
+
@operations.add(:escape, Operation::Escape.new)
|
82
111
|
end
|
83
|
-
|
84
112
|
@border = TTY::Table::BorderOptions.from(options.delete(:border))
|
85
|
-
@column_widths =
|
86
|
-
ColumnSet.new(table).extract_widths
|
87
|
-
}).map(&:to_i)
|
113
|
+
@column_widths = options.fetch(:column_widths, nil)
|
88
114
|
@column_aligns = Array(options.delete(:column_aligns)).map(&:to_sym)
|
89
115
|
@filter = options.fetch(:filter) { proc { |val, row, col| val } }
|
90
116
|
@width = options.fetch(:width) { TTY.terminal.width }
|
91
117
|
@border_class = options.fetch(:border_class) { Border::Null }
|
118
|
+
@indent = options.fetch(:indent) { 0 }
|
119
|
+
@resize = options.fetch(:resize) { false }
|
120
|
+
@padding = TTY::Table::Padder.parse(options.fetch(:padding) { nil })
|
121
|
+
end
|
122
|
+
|
123
|
+
# Parses supplied column widths, if not present calculates natural widths
|
124
|
+
#
|
125
|
+
# @return [Array[Integer]]
|
126
|
+
#
|
127
|
+
# @api public
|
128
|
+
def column_widths
|
129
|
+
@column_widths = ColumnSet.widths_from(table, @column_widths)
|
92
130
|
end
|
93
131
|
|
94
132
|
# Store border characters, style and separator for the table rendering
|
@@ -113,10 +151,35 @@ module TTY
|
|
113
151
|
#
|
114
152
|
# @api private
|
115
153
|
def add_operations
|
116
|
-
operations.
|
117
|
-
|
118
|
-
operations.
|
119
|
-
operations.
|
154
|
+
operations.add(:alignment, Operation::AlignmentSet.new(column_aligns,
|
155
|
+
column_widths))
|
156
|
+
operations.add(:filter, Operation::Filter.new(filter))
|
157
|
+
operations.add(:truncation, Operation::Truncation.new(column_widths))
|
158
|
+
operations.add(:wrapping, Operation::Wrapped.new(column_widths, padding))
|
159
|
+
operations.add(:padding, Operation::Padding.new(padding, multiline))
|
160
|
+
end
|
161
|
+
|
162
|
+
# Initializes indentation
|
163
|
+
#
|
164
|
+
# @return [TTY::Table::Indentation]
|
165
|
+
#
|
166
|
+
# @api private
|
167
|
+
def indentation
|
168
|
+
@indentation ||= TTY::Table::Indentation.new(self)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Delegate indentation insertion
|
172
|
+
#
|
173
|
+
# @api public
|
174
|
+
def insert_indent(line)
|
175
|
+
indentation.insert_indent(line)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Return column contraints
|
179
|
+
#
|
180
|
+
# @api private
|
181
|
+
def columns_constraints
|
182
|
+
TTY::Table::Columns.new(self)
|
120
183
|
end
|
121
184
|
|
122
185
|
# Sets the output padding,
|
@@ -126,7 +189,7 @@ module TTY
|
|
126
189
|
#
|
127
190
|
# @api public
|
128
191
|
def padding=(value)
|
129
|
-
@padding =
|
192
|
+
@padding = TTY::Table::Padder.parse(value)
|
130
193
|
end
|
131
194
|
|
132
195
|
# Renders table
|
@@ -137,13 +200,15 @@ module TTY
|
|
137
200
|
def render
|
138
201
|
return if table.empty?
|
139
202
|
|
140
|
-
|
141
|
-
|
142
|
-
# TODO: Decide about table orientation
|
203
|
+
operations.run_operations(:escape) unless multiline
|
204
|
+
columns_constraints.enforce
|
143
205
|
add_operations
|
144
|
-
ops = [:
|
206
|
+
ops = [:alignment]
|
207
|
+
ops << :padding unless padding.empty?
|
145
208
|
multiline ? ops << :wrapping : ops << :truncation
|
209
|
+
ops << :filter
|
146
210
|
operations.run_operations(*ops)
|
211
|
+
|
147
212
|
render_data.compact.join("\n")
|
148
213
|
end
|
149
214
|
|
@@ -153,12 +218,16 @@ module TTY
|
|
153
218
|
#
|
154
219
|
# @api private
|
155
220
|
def render_data
|
156
|
-
first_row
|
157
|
-
data_border
|
158
|
-
header
|
221
|
+
first_row = table.first
|
222
|
+
data_border = border_class.new(column_widths, border)
|
223
|
+
header = render_header(first_row, data_border)
|
159
224
|
rows_with_border = render_rows(data_border)
|
160
225
|
|
161
|
-
|
226
|
+
if bottom_line = data_border.bottom_line
|
227
|
+
insert_indent(bottom_line)
|
228
|
+
end
|
229
|
+
|
230
|
+
[header, rows_with_border, bottom_line].compact
|
162
231
|
end
|
163
232
|
|
164
233
|
# Format the header if present
|
@@ -175,7 +244,8 @@ module TTY
|
|
175
244
|
def render_header(row, data_border)
|
176
245
|
top_line = data_border.top_line
|
177
246
|
if row.is_a?(TTY::Table::Header)
|
178
|
-
[top_line, data_border.row_line(row), data_border.separator]
|
247
|
+
header = [top_line, data_border.row_line(row), data_border.separator]
|
248
|
+
insert_indent(header.compact)
|
179
249
|
else
|
180
250
|
top_line
|
181
251
|
end
|
@@ -190,8 +260,8 @@ module TTY
|
|
190
260
|
#
|
191
261
|
# @api private
|
192
262
|
def render_rows(data_border)
|
193
|
-
rows
|
194
|
-
size
|
263
|
+
rows = table.rows
|
264
|
+
size = rows.size
|
195
265
|
rows.each_with_index.map do |row, index|
|
196
266
|
render_row(row, data_border, size != (index += 1))
|
197
267
|
end
|
@@ -213,9 +283,9 @@ module TTY
|
|
213
283
|
row_line = data_border.row_line(row)
|
214
284
|
|
215
285
|
if (border.separator == TTY::Table::Border::EACH_ROW) && is_last_row
|
216
|
-
[row_line, separator]
|
286
|
+
insert_indent([row_line, separator])
|
217
287
|
else
|
218
|
-
row_line
|
288
|
+
insert_indent(row_line)
|
219
289
|
end
|
220
290
|
end
|
221
291
|
|
@@ -29,13 +29,6 @@ module TTY
|
|
29
29
|
def assert_string_values(rows)
|
30
30
|
end
|
31
31
|
|
32
|
-
def validate_rendering_options!(options)
|
33
|
-
if (column_widths = options[:column_widths]) &&
|
34
|
-
(!column_widths.kind_of?(Array) || column_widths.empty?)
|
35
|
-
raise InvalidArgument, ":column_widths must be a non-empty array"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
32
|
# Check if options are of required type
|
40
33
|
#
|
41
34
|
# @api private
|
data/lib/tty/terminal/color.rb
CHANGED
@@ -16,28 +16,44 @@ module TTY
|
|
16
16
|
STYLES = %w[ BOLD CLEAR UNDERLINE ].freeze
|
17
17
|
|
18
18
|
# Escape codes for text color.
|
19
|
-
BLACK
|
20
|
-
RED
|
21
|
-
GREEN
|
22
|
-
YELLOW
|
23
|
-
BLUE
|
24
|
-
MAGENTA
|
25
|
-
CYAN
|
26
|
-
WHITE
|
27
|
-
|
28
|
-
|
19
|
+
BLACK = "\e[30m"
|
20
|
+
RED = "\e[31m"
|
21
|
+
GREEN = "\e[32m"
|
22
|
+
YELLOW = "\e[33m"
|
23
|
+
BLUE = "\e[34m"
|
24
|
+
MAGENTA = "\e[35m"
|
25
|
+
CYAN = "\e[36m"
|
26
|
+
WHITE = "\e[37m"
|
27
|
+
|
28
|
+
LIGHT_BLACK = "\e[90m"
|
29
|
+
LIGHT_RED = "\e[91m"
|
30
|
+
LIGHT_GREEN = "\e[92m"
|
31
|
+
LIGHT_YELLOW = "\e[93m"
|
32
|
+
LIGHT_BLUE = "\e[94m"
|
33
|
+
LIGHT_MAGENTA = "\e[95m"
|
34
|
+
LIGHT_CYAN = "\e[96m"
|
35
|
+
|
36
|
+
TEXT_COLORS = (constants.grep(/^[^ON_]*/) - STYLES).freeze
|
29
37
|
|
30
38
|
# Escape codes for background color.
|
31
|
-
ON_BLACK
|
32
|
-
ON_RED
|
33
|
-
ON_GREEN
|
34
|
-
ON_YELLOW
|
35
|
-
ON_BLUE
|
36
|
-
ON_MAGENTA
|
37
|
-
ON_CYAN
|
38
|
-
ON_WHITE
|
39
|
-
|
40
|
-
|
39
|
+
ON_BLACK = "\e[40m"
|
40
|
+
ON_RED = "\e[41m"
|
41
|
+
ON_GREEN = "\e[42m"
|
42
|
+
ON_YELLOW = "\e[43m"
|
43
|
+
ON_BLUE = "\e[44m"
|
44
|
+
ON_MAGENTA = "\e[45m"
|
45
|
+
ON_CYAN = "\e[46m"
|
46
|
+
ON_WHITE = "\e[47m"
|
47
|
+
|
48
|
+
ON_LIGHT_BLACK = "\e[100m"
|
49
|
+
ON_LIGHT_RED = "\e[101m"
|
50
|
+
ON_LIGHT_GREEN = "\e[102m"
|
51
|
+
ON_LIGHT_YELLOW = "\e[103m"
|
52
|
+
ON_LIGHT_BLUE = "\e[104m"
|
53
|
+
ON_LIGHT_MAGENTA = "\e[105m"
|
54
|
+
ON_LIGHT_CYAN = "\e[106m"
|
55
|
+
|
56
|
+
BACKGROUND_COLORS = constants.grep(/^ON_*/).freeze
|
41
57
|
|
42
58
|
attr_reader :enabled
|
43
59
|
|
data/lib/tty/text/truncation.rb
CHANGED
@@ -19,6 +19,8 @@ module TTY
|
|
19
19
|
|
20
20
|
attr_reader :trailing
|
21
21
|
|
22
|
+
attr_reader :escape
|
23
|
+
|
22
24
|
# Initialize a Truncation
|
23
25
|
#
|
24
26
|
# @param [String] text
|
@@ -36,6 +38,7 @@ module TTY
|
|
36
38
|
# @option options [Symbol] :length the desired length
|
37
39
|
# @option options [Symbol] :separator the character for splitting words
|
38
40
|
# @option options [Symbol] :trailing the character for ending sentence
|
41
|
+
# @option options [Symbol] :escape remove ANSI escape sequences
|
39
42
|
#
|
40
43
|
# @api private
|
41
44
|
def initialize(text, *args)
|
@@ -45,6 +48,7 @@ module TTY
|
|
45
48
|
@length = args[0] unless args.empty?
|
46
49
|
@separator = options.fetch(:separator) { nil }
|
47
50
|
@trailing = options.fetch(:trailing) { DEFAULT_TRAILING }
|
51
|
+
@escape = options.fetch(:escape) { true }
|
48
52
|
end
|
49
53
|
|
50
54
|
# Truncate a text
|
@@ -56,7 +60,7 @@ module TTY
|
|
56
60
|
return text unless length && length > 0
|
57
61
|
|
58
62
|
as_unicode do
|
59
|
-
chars = text.chars.to_a
|
63
|
+
chars = (escape ? escape_text : text).chars.to_a
|
60
64
|
return chars.join if chars.length <= length
|
61
65
|
stop = chars[0, length_without_trailing].rindex(separator)
|
62
66
|
|
@@ -66,6 +70,17 @@ module TTY
|
|
66
70
|
|
67
71
|
private
|
68
72
|
|
73
|
+
# Strip ANSI characters from the text
|
74
|
+
#
|
75
|
+
# @param [String] text
|
76
|
+
#
|
77
|
+
# @return [String]
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
def escape_text
|
81
|
+
TTY.terminal.color.remove text.dup
|
82
|
+
end
|
83
|
+
|
69
84
|
# Leave space for the trailing characters
|
70
85
|
#
|
71
86
|
# @return [Integer]
|