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,167 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
|
6
|
+
# A class responsible for enforcing column constraints
|
7
|
+
class Columns
|
8
|
+
|
9
|
+
attr_reader :table
|
10
|
+
|
11
|
+
attr_reader :renderer
|
12
|
+
|
13
|
+
MIN_WIDTH = 1
|
14
|
+
|
15
|
+
BORDER_WIDTH = 1
|
16
|
+
|
17
|
+
# Initialize a Columns
|
18
|
+
#
|
19
|
+
# @param [TTY::Table::Renderer]
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
def initialize(renderer)
|
23
|
+
@renderer = renderer
|
24
|
+
@table = renderer.table
|
25
|
+
end
|
26
|
+
|
27
|
+
# Estimate outside border size
|
28
|
+
#
|
29
|
+
# @return [Integer]
|
30
|
+
#
|
31
|
+
# @api public
|
32
|
+
def outside_border_size
|
33
|
+
renderer.border_class == TTY::Table::Border::Null ? 0 : 2
|
34
|
+
end
|
35
|
+
|
36
|
+
# Total border size
|
37
|
+
#
|
38
|
+
# @return [Integer]
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def border_size
|
42
|
+
BORDER_WIDTH * (table.column_size - 1) + outside_border_size
|
43
|
+
end
|
44
|
+
|
45
|
+
# Estimate minimum table width to be able to display content
|
46
|
+
#
|
47
|
+
# @return [Integer]
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
def minimum_width
|
51
|
+
table.column_size * MIN_WIDTH + border_size
|
52
|
+
end
|
53
|
+
|
54
|
+
# Return column's natural unconstrained widths
|
55
|
+
#
|
56
|
+
# @return [Integer]
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
def natural_width
|
60
|
+
renderer.column_widths.inject(0, &:+) + border_size
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return the constrained column widths. Account for table field widths
|
64
|
+
# and any user defined constraints on the table width.
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def enforce
|
68
|
+
assert_minimum_width
|
69
|
+
|
70
|
+
if not renderer.padding.empty?
|
71
|
+
renderer.column_widths = adjust_padding
|
72
|
+
end
|
73
|
+
|
74
|
+
if natural_width <= renderer.width
|
75
|
+
renderer.column_widths = expand if renderer.resize
|
76
|
+
else
|
77
|
+
if renderer.resize
|
78
|
+
renderer.column_widths = shrink
|
79
|
+
else
|
80
|
+
rotate
|
81
|
+
renderer.column_widths = ColumnSet.widths_from(table)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Adjust column widths to account for padding whitespace
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
def adjust_padding
|
90
|
+
padding = renderer.padding
|
91
|
+
column_size = table.column_size
|
92
|
+
|
93
|
+
(0...column_size).reduce([]) do |lengths, col|
|
94
|
+
lengths + [padding.left + renderer.column_widths[col] + padding.right]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Rotate table to vertical orientation and print information to stdout
|
99
|
+
#
|
100
|
+
# @api private
|
101
|
+
def rotate
|
102
|
+
TTY.shell.warn 'The table size exceeds the currently set width.' +
|
103
|
+
'To avoid error either. Defaulting to vertical orientation.'
|
104
|
+
table.orientation= :vertical
|
105
|
+
table.rotate
|
106
|
+
end
|
107
|
+
|
108
|
+
# Expand column widths to match the requested width
|
109
|
+
#
|
110
|
+
# @api private
|
111
|
+
def expand
|
112
|
+
column_size = table.column_size
|
113
|
+
ratio = ((renderer.width - natural_width) / column_size.to_f).floor
|
114
|
+
|
115
|
+
widths = (0...column_size).reduce([]) do |lengths, col|
|
116
|
+
lengths + [renderer.column_widths[col] + ratio]
|
117
|
+
end
|
118
|
+
distribute_extra_width(widths)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Shrink column widths to match the requested width
|
122
|
+
#
|
123
|
+
# @api private
|
124
|
+
def shrink
|
125
|
+
column_size = table.column_size
|
126
|
+
ratio = ((natural_width - renderer.width) / column_size.to_f).ceil
|
127
|
+
|
128
|
+
widths = (0...column_size).reduce([]) do |lengths, col|
|
129
|
+
lengths + [renderer.column_widths[col] - ratio]
|
130
|
+
end
|
131
|
+
distribute_extra_width(widths)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Assert minimum width for the table content
|
135
|
+
#
|
136
|
+
# @raise [TTY::ResizeError]
|
137
|
+
#
|
138
|
+
# @api private
|
139
|
+
def assert_minimum_width
|
140
|
+
width = renderer.width
|
141
|
+
if width <= minimum_width
|
142
|
+
raise ResizeError,
|
143
|
+
"Table's width is too small to contain the content " +
|
144
|
+
"(min width #{minimum_width}, currently set #{width})"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Distribute remaining width to meet the total width requirement.
|
149
|
+
#
|
150
|
+
# @param [Array[Integer]] widths
|
151
|
+
#
|
152
|
+
# @api private
|
153
|
+
def distribute_extra_width(widths)
|
154
|
+
column_size = table.column_size
|
155
|
+
extra_width = renderer.width - (widths.reduce(:+) + border_size)
|
156
|
+
per_field_width = extra_width / column_size
|
157
|
+
remaining_width = extra_width % column_size
|
158
|
+
extra = [1] * remaining_width + [0] * (column_size - remaining_width)
|
159
|
+
|
160
|
+
widths.map.with_index do |width, index|
|
161
|
+
width + per_field_width + extra[index]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end # Columns
|
166
|
+
end # Table
|
167
|
+
end # TTY
|
data/lib/tty/table/field.rb
CHANGED
@@ -77,7 +77,7 @@ module TTY
|
|
77
77
|
# @api public
|
78
78
|
def lines
|
79
79
|
escaped = value.to_s.scan(/(\\n|\\t|\\r)/)
|
80
|
-
escaped.empty? ? value.to_s.split(/\n
|
80
|
+
escaped.empty? ? value.to_s.split(/\n/, -1) : [value.to_s]
|
81
81
|
end
|
82
82
|
|
83
83
|
# If the string contains unescaped new lines then the longest token
|
@@ -87,7 +87,7 @@ module TTY
|
|
87
87
|
#
|
88
88
|
# @api public
|
89
89
|
def length
|
90
|
-
lines.max_by(&:length).size
|
90
|
+
(lines.max_by(&:length) || '').size
|
91
91
|
end
|
92
92
|
|
93
93
|
# Extract the number of lines this value spans
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
|
6
|
+
# A class responsible for indenting table representation
|
7
|
+
class Indentation
|
8
|
+
|
9
|
+
attr_reader :renderer
|
10
|
+
|
11
|
+
# Initialize an Indentation
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
def initialize(renderer)
|
15
|
+
@renderer = renderer
|
16
|
+
end
|
17
|
+
|
18
|
+
# Create indentation
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
def indentation
|
22
|
+
' ' * renderer.indent
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return a table part with indentation inserted
|
26
|
+
#
|
27
|
+
# @param [#map, #to_s] part
|
28
|
+
# the rendered table part
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def insert_indent(part)
|
32
|
+
if part.respond_to?(:to_a)
|
33
|
+
part.map { |line| insert_indentation(line) }
|
34
|
+
else
|
35
|
+
insert_indentation(part)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Insert indentation into a table renderd line
|
42
|
+
#
|
43
|
+
# @param [#to_a, #to_s] line
|
44
|
+
# the rendered table line
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
def insert_indentation(line)
|
48
|
+
line = line.is_a?(Array) ? line[0] : line
|
49
|
+
line.insert(0, indentation) if line
|
50
|
+
end
|
51
|
+
|
52
|
+
end # Indentation
|
53
|
+
end # Table
|
54
|
+
end # TTY
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
class Table
|
5
|
+
module Operation
|
6
|
+
|
7
|
+
# A class responsible for padding field with whitespace
|
8
|
+
class Padding
|
9
|
+
|
10
|
+
attr_reader :padding_top
|
11
|
+
|
12
|
+
attr_reader :padding_right
|
13
|
+
|
14
|
+
attr_reader :padding_bottom
|
15
|
+
|
16
|
+
attr_reader :padding_left
|
17
|
+
|
18
|
+
attr_reader :padding_width
|
19
|
+
|
20
|
+
attr_reader :multiline
|
21
|
+
|
22
|
+
# Initialize a Padding operation
|
23
|
+
#
|
24
|
+
# @param [TTY::Table::Padder]
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
def initialize(padding, multiline)
|
28
|
+
@padding_top = "\n" * padding.top
|
29
|
+
@padding_right = ' ' * padding.right
|
30
|
+
@padding_bottom = "\n" * padding.bottom
|
31
|
+
@padding_left = ' ' * padding.left
|
32
|
+
@padding_width = padding.left + padding.right
|
33
|
+
@multiline = multiline
|
34
|
+
end
|
35
|
+
|
36
|
+
# Apply padding to a field
|
37
|
+
#
|
38
|
+
# @param [TTY::Table::Field] field
|
39
|
+
# the table field
|
40
|
+
#
|
41
|
+
# @param [Integer] row
|
42
|
+
# the field row index
|
43
|
+
#
|
44
|
+
# @param [Integer] col
|
45
|
+
# the field column index
|
46
|
+
#
|
47
|
+
# @return [TTY::Table::Field]
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
def call(field, row, col)
|
51
|
+
text = field.value.to_s
|
52
|
+
|
53
|
+
text = multiline ? pad_multi_line(text) : pad_single_line(text)
|
54
|
+
text.insert(0, padding_top).insert(-1, padding_bottom)
|
55
|
+
|
56
|
+
field.value = text
|
57
|
+
end
|
58
|
+
|
59
|
+
# Apply padding to multi line text
|
60
|
+
#
|
61
|
+
# @param [String] text
|
62
|
+
#
|
63
|
+
# @return [String]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
def pad_multi_line(text)
|
67
|
+
text.split("\n", -1).map { |part| pad_around(part) }.join("\n")
|
68
|
+
end
|
69
|
+
|
70
|
+
# Apply padding to single line text
|
71
|
+
#
|
72
|
+
# @param [String] text
|
73
|
+
#
|
74
|
+
# @return [String]
|
75
|
+
#
|
76
|
+
# @api private
|
77
|
+
def pad_single_line(text)
|
78
|
+
pad_around(text.strip)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Apply padding to left and right side of string
|
82
|
+
#
|
83
|
+
# @param [String] text
|
84
|
+
#
|
85
|
+
# @return [String]
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
def pad_around(text)
|
89
|
+
text.insert(0, padding_left).insert(-1, padding_right)
|
90
|
+
end
|
91
|
+
|
92
|
+
end # Padding
|
93
|
+
end # Operation
|
94
|
+
end # Table
|
95
|
+
end # TTY
|
@@ -9,11 +9,14 @@ module TTY
|
|
9
9
|
|
10
10
|
attr_reader :widths
|
11
11
|
|
12
|
+
attr_reader :padding
|
13
|
+
|
12
14
|
# Initialize a Wrapped
|
13
15
|
#
|
14
16
|
# @api public
|
15
|
-
def initialize(widths)
|
16
|
-
@widths
|
17
|
+
def initialize(widths, padding)
|
18
|
+
@widths = widths
|
19
|
+
@padding = padding.padding
|
17
20
|
end
|
18
21
|
|
19
22
|
# Apply wrapping to a field
|
@@ -46,7 +49,7 @@ module TTY
|
|
46
49
|
#
|
47
50
|
# @api public
|
48
51
|
def wrap(string, width)
|
49
|
-
TTY::Text.wrap(string, width)
|
52
|
+
TTY::Text.wrap(string, width, padding: padding)
|
50
53
|
end
|
51
54
|
|
52
55
|
end # Wrapped
|
data/lib/tty/table/operations.rb
CHANGED
@@ -20,8 +20,9 @@ module TTY
|
|
20
20
|
# @return [Object]
|
21
21
|
#
|
22
22
|
# @api public
|
23
|
-
def initialize(table)
|
23
|
+
def initialize(table, renderer=nil)
|
24
24
|
@table = table
|
25
|
+
@renderer = renderer # will be available to each operation
|
25
26
|
end
|
26
27
|
|
27
28
|
# Available operations
|
@@ -43,7 +44,7 @@ module TTY
|
|
43
44
|
# @return [Hash]
|
44
45
|
#
|
45
46
|
# @api public
|
46
|
-
def
|
47
|
+
def add(type, object)
|
47
48
|
operations[type] << object
|
48
49
|
end
|
49
50
|
|
@@ -6,14 +6,40 @@ module TTY
|
|
6
6
|
# A class representing table orientation
|
7
7
|
class Orientation
|
8
8
|
|
9
|
+
# A class responsible for horizontal table transformation
|
9
10
|
class Horizontal < Orientation
|
10
11
|
|
11
12
|
def transform(table)
|
12
13
|
table.rotate_horizontal
|
13
14
|
end
|
14
15
|
|
15
|
-
|
16
|
+
# Slice vertical table data into horizontal
|
17
|
+
#
|
18
|
+
# @param [TTY::Table] table
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
def slice(table)
|
22
|
+
head, body, array_h, array_b = 4.times.map { [] }
|
23
|
+
index = 0
|
24
|
+
first_column = 0
|
25
|
+
second_column = 1
|
26
|
+
|
27
|
+
(0...table.original_columns * table.original_rows).each do |col_index|
|
28
|
+
row = table.rows[index]
|
29
|
+
array_h += [row[first_column]]
|
30
|
+
array_b += [row[second_column]]
|
16
31
|
|
32
|
+
if col_index % table.original_columns == 2
|
33
|
+
head << array_h
|
34
|
+
body << array_b
|
35
|
+
array_h, array_b = [], []
|
36
|
+
end
|
37
|
+
index += 1
|
38
|
+
end
|
39
|
+
[head, body]
|
40
|
+
end
|
41
|
+
|
42
|
+
end # Horizontal
|
17
43
|
end # Orientation
|
18
44
|
end # Table
|
19
45
|
end # TTY
|
@@ -6,14 +6,30 @@ module TTY
|
|
6
6
|
# A class representing table orientation
|
7
7
|
class Orientation
|
8
8
|
|
9
|
+
# A class responsible for vertical table transformation
|
9
10
|
class Vertical < Orientation
|
10
11
|
|
11
12
|
def transform(table)
|
12
13
|
table.rotate_vertical
|
13
14
|
end
|
14
15
|
|
15
|
-
|
16
|
+
# Slice horizontal table data into vertical
|
17
|
+
#
|
18
|
+
# @param [TTY::Table] table
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
def slice(table)
|
22
|
+
header = table.header
|
23
|
+
row_size = table.row_size
|
24
|
+
|
25
|
+
head = header ? header : (0..row_size).map { |n| (n + 1).to_s }
|
16
26
|
|
27
|
+
(0...row_size).inject([]) do |array, index|
|
28
|
+
array + head.zip(table.rows[index]).map { |row| table.to_row(row) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end # Vertical
|
17
33
|
end # Orientation
|
18
34
|
end # Table
|
19
35
|
end # TTY
|