tabulo 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -3
- data/CHANGELOG.md +8 -0
- data/README.md +140 -88
- data/VERSION +1 -1
- data/assets/social_media_preview/table.png +0 -0
- data/lib/tabulo/border.rb +56 -101
- data/lib/tabulo/cell.rb +27 -16
- data/lib/tabulo/column.rb +28 -5
- data/lib/tabulo/table.rb +98 -79
- data/lib/tabulo/util.rb +20 -0
- data/lib/tabulo/version.rb +1 -1
- metadata +3 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.6.0
|
Binary file
|
data/lib/tabulo/border.rb
CHANGED
@@ -3,107 +3,62 @@ module Tabulo
|
|
3
3
|
# @!visibility private
|
4
4
|
class Border
|
5
5
|
|
6
|
+
Style = Struct.new(
|
7
|
+
:corner_top_left, :corner_top_right, :corner_bottom_right, :corner_bottom_left,
|
8
|
+
:edge_top, :edge_right, :edge_bottom, :edge_left,
|
9
|
+
:tee_top, :tee_right, :tee_bottom, :tee_left,
|
10
|
+
:divider_vertical, :divider_horizontal, :intersection)
|
11
|
+
|
6
12
|
STYLES = {
|
7
|
-
ascii:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
corner_top_right: "",
|
57
|
-
corner_bottom_right: "",
|
58
|
-
corner_bottom_left: "",
|
59
|
-
edge_top: "─",
|
60
|
-
edge_right: "",
|
61
|
-
edge_bottom: "─",
|
62
|
-
edge_left: "",
|
63
|
-
tee_top: " ",
|
64
|
-
tee_right: "",
|
65
|
-
tee_bottom: " ",
|
66
|
-
tee_left: "",
|
67
|
-
divider_vertical: " ",
|
68
|
-
divider_horizontal: "─",
|
69
|
-
intersection: " ",
|
70
|
-
},
|
71
|
-
markdown: {
|
72
|
-
corner_top_left: "",
|
73
|
-
corner_top_right: "",
|
74
|
-
corner_bottom_right: "",
|
75
|
-
corner_bottom_left: "",
|
76
|
-
edge_top: "",
|
77
|
-
edge_right: "|",
|
78
|
-
edge_bottom: "",
|
79
|
-
edge_left: "|",
|
80
|
-
tee_top: "",
|
81
|
-
tee_right: "|",
|
82
|
-
tee_bottom: "",
|
83
|
-
tee_left: "|",
|
84
|
-
divider_vertical: "|",
|
85
|
-
divider_horizontal: "-",
|
86
|
-
intersection: "|",
|
87
|
-
},
|
88
|
-
modern: {
|
89
|
-
corner_top_left: "┌",
|
90
|
-
corner_top_right: "┐",
|
91
|
-
corner_bottom_right: "┘",
|
92
|
-
corner_bottom_left: "└",
|
93
|
-
edge_top: "─",
|
94
|
-
edge_right: "│",
|
95
|
-
edge_bottom: "─",
|
96
|
-
edge_left: "│",
|
97
|
-
tee_top: "┬",
|
98
|
-
tee_right: "┤",
|
99
|
-
tee_bottom: "┴",
|
100
|
-
tee_left: "├",
|
101
|
-
divider_vertical: "│",
|
102
|
-
divider_horizontal: "─",
|
103
|
-
intersection: "┼",
|
104
|
-
},
|
105
|
-
blank: {
|
106
|
-
},
|
13
|
+
ascii:
|
14
|
+
Style.new(
|
15
|
+
"+", "+", "+", "+",
|
16
|
+
"-", "|", "-", "|",
|
17
|
+
"+", "+", "+", "+",
|
18
|
+
"|", "-", "+",
|
19
|
+
),
|
20
|
+
classic:
|
21
|
+
Style.new(
|
22
|
+
"+", "+", "", "",
|
23
|
+
"-", "|", "", "|",
|
24
|
+
"+", "+", "", "+",
|
25
|
+
"|", "-", "+",
|
26
|
+
),
|
27
|
+
reduced_ascii:
|
28
|
+
Style.new(
|
29
|
+
"", "", "", "",
|
30
|
+
"-", "", "-", "",
|
31
|
+
" ", "", " ", "",
|
32
|
+
" ", "-", " ",
|
33
|
+
),
|
34
|
+
reduced_modern:
|
35
|
+
Style.new(
|
36
|
+
"", "", "", "",
|
37
|
+
"─", "", "─", "",
|
38
|
+
" ", "", " ", "",
|
39
|
+
" ", "─", " ",
|
40
|
+
),
|
41
|
+
markdown:
|
42
|
+
Style.new(
|
43
|
+
"", "", "", "",
|
44
|
+
"", "|", "", "|",
|
45
|
+
"", "|", "", "|",
|
46
|
+
"|", "-", "|",
|
47
|
+
),
|
48
|
+
modern:
|
49
|
+
Style.new(
|
50
|
+
"┌", "┐", "┘", "└",
|
51
|
+
"─", "│", "─", "│",
|
52
|
+
"┬", "┤", "┴", "├",
|
53
|
+
"│", "─", "┼",
|
54
|
+
),
|
55
|
+
blank:
|
56
|
+
Style.new(
|
57
|
+
"", "", "", "",
|
58
|
+
"", "", "", "",
|
59
|
+
"", "", "", "",
|
60
|
+
"", "", "",
|
61
|
+
),
|
107
62
|
}
|
108
63
|
|
109
64
|
# @!visibility private
|
@@ -147,7 +102,7 @@ module Tabulo
|
|
147
102
|
|
148
103
|
def self.options(kind)
|
149
104
|
opts = STYLES[kind]
|
150
|
-
return opts if opts
|
105
|
+
return opts.to_h if opts
|
151
106
|
raise InvalidBorderError
|
152
107
|
end
|
153
108
|
|
data/lib/tabulo/cell.rb
CHANGED
@@ -13,7 +13,9 @@ module Tabulo
|
|
13
13
|
alignment:,
|
14
14
|
cell_data:,
|
15
15
|
formatter:,
|
16
|
+
left_padding:,
|
16
17
|
padding_character:,
|
18
|
+
right_padding:,
|
17
19
|
styler:,
|
18
20
|
truncation_indicator:,
|
19
21
|
value:,
|
@@ -21,8 +23,10 @@ module Tabulo
|
|
21
23
|
|
22
24
|
@alignment = alignment
|
23
25
|
@cell_data = cell_data
|
24
|
-
@padding_character = padding_character
|
25
26
|
@formatter = formatter
|
27
|
+
@left_padding = left_padding
|
28
|
+
@padding_character = padding_character
|
29
|
+
@right_padding = right_padding
|
26
30
|
@styler = styler
|
27
31
|
@truncation_indicator = truncation_indicator
|
28
32
|
@value = value
|
@@ -35,12 +39,12 @@ module Tabulo
|
|
35
39
|
end
|
36
40
|
|
37
41
|
# @!visibility private
|
38
|
-
def padded_truncated_subcells(target_height
|
39
|
-
total_padding_amount =
|
42
|
+
def padded_truncated_subcells(target_height)
|
43
|
+
total_padding_amount = @left_padding + @right_padding
|
40
44
|
truncated = (height > target_height)
|
41
45
|
(0...target_height).map do |subcell_index|
|
42
46
|
append_truncator = (truncated && (total_padding_amount != 0) && (subcell_index + 1 == target_height))
|
43
|
-
padded_subcell(subcell_index,
|
47
|
+
padded_subcell(subcell_index, append_truncator)
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
@@ -60,8 +64,11 @@ module Tabulo
|
|
60
64
|
end
|
61
65
|
end
|
62
66
|
|
63
|
-
def apply_styler(content)
|
64
|
-
|
67
|
+
def apply_styler(content, line_index)
|
68
|
+
case @styler.arity
|
69
|
+
when 4
|
70
|
+
@styler.call(@value, content, @cell_data, line_index)
|
71
|
+
when 3
|
65
72
|
@styler.call(@value, content, @cell_data)
|
66
73
|
else
|
67
74
|
@styler.call(@value, content)
|
@@ -72,13 +79,13 @@ module Tabulo
|
|
72
79
|
@subcells ||= calculate_subcells
|
73
80
|
end
|
74
81
|
|
75
|
-
def padded_subcell(subcell_index,
|
76
|
-
lpad = @padding_character *
|
82
|
+
def padded_subcell(subcell_index, append_truncator)
|
83
|
+
lpad = @padding_character * @left_padding
|
77
84
|
rpad =
|
78
85
|
if append_truncator
|
79
|
-
styled_truncation_indicator + padding(
|
86
|
+
styled_truncation_indicator(subcell_index) + padding(@right_padding - 1)
|
80
87
|
else
|
81
|
-
padding(
|
88
|
+
padding(@right_padding)
|
82
89
|
end
|
83
90
|
inner = subcell_index < height ? subcells[subcell_index] : padding(@width)
|
84
91
|
"#{lpad}#{inner}#{rpad}"
|
@@ -88,31 +95,35 @@ module Tabulo
|
|
88
95
|
@padding_character * amount
|
89
96
|
end
|
90
97
|
|
91
|
-
def styled_truncation_indicator
|
92
|
-
apply_styler(@truncation_indicator)
|
98
|
+
def styled_truncation_indicator(line_index)
|
99
|
+
apply_styler(@truncation_indicator, line_index)
|
93
100
|
end
|
94
101
|
|
95
102
|
def calculate_subcells
|
103
|
+
line_index = 0
|
96
104
|
formatted_content.split($/, -1).flat_map do |substr|
|
97
105
|
subsubcells, subsubcell, subsubcell_width = [], String.new(""), 0
|
98
106
|
|
99
107
|
substr.scan(/\X/).each do |grapheme_cluster|
|
100
108
|
grapheme_cluster_width = Unicode::DisplayWidth.of(grapheme_cluster)
|
101
109
|
if subsubcell_width + grapheme_cluster_width > @width
|
102
|
-
subsubcells << style_and_align_cell_content(subsubcell)
|
110
|
+
subsubcells << style_and_align_cell_content(subsubcell, line_index)
|
103
111
|
subsubcell_width = 0
|
104
112
|
subsubcell.clear
|
113
|
+
line_index += 1
|
105
114
|
end
|
106
115
|
|
107
116
|
subsubcell << grapheme_cluster
|
108
117
|
subsubcell_width += grapheme_cluster_width
|
109
118
|
end
|
110
119
|
|
111
|
-
subsubcells << style_and_align_cell_content(subsubcell)
|
120
|
+
subsubcells << style_and_align_cell_content(subsubcell, line_index)
|
121
|
+
line_index += 1
|
122
|
+
subsubcells
|
112
123
|
end
|
113
124
|
end
|
114
125
|
|
115
|
-
def style_and_align_cell_content(content)
|
126
|
+
def style_and_align_cell_content(content, line_index)
|
116
127
|
padding = Util.max(@width - Unicode::DisplayWidth.of(content), 0)
|
117
128
|
left_padding, right_padding =
|
118
129
|
case real_alignment
|
@@ -125,7 +136,7 @@ module Tabulo
|
|
125
136
|
[padding, 0]
|
126
137
|
end
|
127
138
|
|
128
|
-
"#{' ' * left_padding}#{apply_styler(content)}#{' ' * right_padding}"
|
139
|
+
"#{' ' * left_padding}#{apply_styler(content, line_index)}#{' ' * right_padding}"
|
129
140
|
end
|
130
141
|
|
131
142
|
def real_alignment
|
data/lib/tabulo/column.rb
CHANGED
@@ -6,6 +6,8 @@ module Tabulo
|
|
6
6
|
attr_accessor :width
|
7
7
|
attr_reader :header
|
8
8
|
attr_reader :index
|
9
|
+
attr_reader :left_padding
|
10
|
+
attr_reader :right_padding
|
9
11
|
|
10
12
|
def initialize(
|
11
13
|
align_body:,
|
@@ -15,7 +17,9 @@ module Tabulo
|
|
15
17
|
header:,
|
16
18
|
header_styler:,
|
17
19
|
index:,
|
20
|
+
left_padding:,
|
18
21
|
padding_character:,
|
22
|
+
right_padding:,
|
19
23
|
styler:,
|
20
24
|
truncation_indicator:,
|
21
25
|
width:)
|
@@ -26,12 +30,19 @@ module Tabulo
|
|
26
30
|
@formatter = formatter
|
27
31
|
@header = header
|
28
32
|
@index = index
|
33
|
+
@left_padding = left_padding
|
34
|
+
@right_padding = right_padding
|
29
35
|
|
30
36
|
@header_styler =
|
31
|
-
if header_styler
|
32
|
-
|
33
|
-
|
34
|
-
|
37
|
+
if header_styler
|
38
|
+
case header_styler.arity
|
39
|
+
when 3
|
40
|
+
-> (_, str, cell_data, line_index) { header_styler.call(str, cell_data.column_index, line_index) }
|
41
|
+
when 2
|
42
|
+
-> (_, str, cell_data) { header_styler.call(str, cell_data.column_index) }
|
43
|
+
else
|
44
|
+
-> (_, str) { header_styler.call(str) }
|
45
|
+
end
|
35
46
|
else
|
36
47
|
-> (_, str) { str }
|
37
48
|
end
|
@@ -43,14 +54,16 @@ module Tabulo
|
|
43
54
|
end
|
44
55
|
|
45
56
|
def header_cell
|
46
|
-
if @header_styler.arity
|
57
|
+
if @header_styler.arity >= 3
|
47
58
|
cell_data = CellData.new(nil, nil, @index)
|
48
59
|
end
|
49
60
|
Cell.new(
|
50
61
|
alignment: @align_header,
|
51
62
|
cell_data: cell_data,
|
52
63
|
formatter: -> (s) { s },
|
64
|
+
left_padding: @left_padding,
|
53
65
|
padding_character: @padding_character,
|
66
|
+
right_padding: @right_padding,
|
54
67
|
styler: @header_styler,
|
55
68
|
truncation_indicator: @truncation_indicator,
|
56
69
|
value: @header,
|
@@ -66,7 +79,9 @@ module Tabulo
|
|
66
79
|
alignment: @align_body,
|
67
80
|
cell_data: cell_data,
|
68
81
|
formatter: @formatter,
|
82
|
+
left_padding: @left_padding,
|
69
83
|
padding_character: @padding_character,
|
84
|
+
right_padding: @right_padding,
|
70
85
|
styler: @styler,
|
71
86
|
truncation_indicator: @truncation_indicator,
|
72
87
|
value: body_cell_value(source, row_index: row_index, column_index: column_index),
|
@@ -82,6 +97,14 @@ module Tabulo
|
|
82
97
|
end
|
83
98
|
end
|
84
99
|
|
100
|
+
def padded_width
|
101
|
+
width + total_padding
|
102
|
+
end
|
103
|
+
|
104
|
+
def total_padding
|
105
|
+
@left_padding + @right_padding
|
106
|
+
end
|
107
|
+
|
85
108
|
private
|
86
109
|
|
87
110
|
def body_cell_data_required?
|
data/lib/tabulo/table.rb
CHANGED
@@ -76,7 +76,8 @@ module Tabulo
|
|
76
76
|
# either side of each column. If passed an Integer, then the given amount of padding is
|
77
77
|
# applied to each side of each column. If passed a two-element Array, then the first element of the
|
78
78
|
# Array indicates the amount of padding to apply to the left of each column, and the second
|
79
|
-
# element indicates the amount to apply to the right.
|
79
|
+
# element indicates the amount to apply to the right. This setting can be overridden for
|
80
|
+
# individual columns using the `padding` option of {#add_column}.
|
80
81
|
# @param [Integer, nil] column_width The default column width for columns in this
|
81
82
|
# table, not excluding padding. If <tt>nil</tt>, then {DEFAULT_COLUMN_WIDTH} will be used.
|
82
83
|
# @param [nil, #to_proc] formatter (:to_s.to_proc) The default formatter for columns in this
|
@@ -99,14 +100,26 @@ module Tabulo
|
|
99
100
|
# will cause it to cease being valid Markdown when rendered, since Markdown engines do not generally
|
100
101
|
# support adding a caption element (i.e. title) to tables.
|
101
102
|
# @param [nil, #to_proc] title_styler (nil) A lambda or other callable object that will
|
102
|
-
# determine the colors or other styling applied to the table title.
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
103
|
+
# determine the colors or other styling applied to the table title. Can be passed
|
104
|
+
# <tt>nil</tt>, or can be passed a callable that takes either 1 or 2 parametes:
|
105
|
+
# * If passed <tt>nil</tt>, then no additional styling will be applied to the title.
|
106
|
+
# * If passed a callable, then that callable will be called for each line of
|
107
|
+
# the title, and the resulting string rendered in place of that line.
|
108
|
+
# The extra width of the string returned by the <tt>title_styler</tt> is not taken into
|
109
|
+
# consideration by the internal table and cell width calculations involved in rendering the
|
110
|
+
# table. Thus it can be used to apply ANSI escape codes to title content, to color the
|
111
|
+
# content for example, without breaking the table formatting.
|
112
|
+
# * If the passed callable takes 1 parameter, then the first parameter is a string
|
113
|
+
# representing a single line within the title. For example, if the title
|
114
|
+
# is wrapped over three lines, then the <tt>title_styler</tt> will be called
|
115
|
+
# three times, once for each line of content.
|
116
|
+
# * If the passed callable takes 2 parameters, then the first parameter is as above, and the
|
117
|
+
# second parameter is an Integer representing the index of the line within the
|
118
|
+
# title that is currently being styled. For example, if the title is wrapped over 3
|
119
|
+
# lines, then the callable will be called first with a line index of 0, to style the first line,
|
120
|
+
# then with a line index of 1, to style the second line, and finally with a line index of 2, for
|
121
|
+
# the third and final wrapped line of the cell.
|
122
|
+
#
|
110
123
|
# @param [nil, String] truncation_indicator Determines the character used to indicate that a
|
111
124
|
# cell's content has been truncated. If omitted or passed <tt>nil</tt>,
|
112
125
|
# defaults to {DEFAULT_TRUNCATION_INDICATOR}. If passed something other than <tt>nil</tt> or
|
@@ -142,7 +155,7 @@ module Tabulo
|
|
142
155
|
@column_padding = (column_padding || DEFAULT_COLUMN_PADDING)
|
143
156
|
|
144
157
|
@left_column_padding, @right_column_padding =
|
145
|
-
Array === @column_padding ? @column_padding : [@column_padding, @column_padding]
|
158
|
+
(Array === @column_padding ? @column_padding : [@column_padding, @column_padding])
|
146
159
|
|
147
160
|
@column_width = (column_width || DEFAULT_COLUMN_WIDTH)
|
148
161
|
@formatter = formatter
|
@@ -210,7 +223,7 @@ module Tabulo
|
|
210
223
|
# the column's label will also be used as its header text.
|
211
224
|
# @param [nil, #to_proc] header_styler (nil) A lambda or other callable object that will
|
212
225
|
# determine the colors or other styling applied to the header content. Can be passed
|
213
|
-
# <tt>nil</tt>, or can be passed a callable that takes
|
226
|
+
# <tt>nil</tt>, or can be passed a callable that takes 1, 2 or 3 parameters:
|
214
227
|
# * If passed <tt>nil</tt>, then no additional styling will be applied to the cell content
|
215
228
|
# (other than what was already applied by the <tt>formatter</tt>).
|
216
229
|
# * If passed a callable, then that callable will be called for each line of content within
|
@@ -227,9 +240,22 @@ module Tabulo
|
|
227
240
|
# second parameter is an Integer representing the positional index of this header's {Column},
|
228
241
|
# with the leftmost column having index 0, the next having index 1 etc.. This can be
|
229
242
|
# used, for example, to apply different styles to alternating {Column}s.
|
243
|
+
# * If the passed callable takes 3 parameters, then the first and second parameters are as above,
|
244
|
+
# and the third parameter is an Integer representing the index of the line within the
|
245
|
+
# header cell that is currently being styled. For example, if the cell content is wrapped over 3
|
246
|
+
# lines, then the callable will be called first with a line index of 0, to style the first line,
|
247
|
+
# then with a line index of 1, to style the second line, and finally with a line index of 2, for
|
248
|
+
# the third and final wrapped line of the cell.
|
230
249
|
#
|
231
250
|
# Note that if the header content is truncated, then any <tt>header_styler</tt> will be applied to the
|
232
251
|
# truncation indicator character as well as to the truncated content.
|
252
|
+
# @param [nil, Integer, Array] padding (nil) Determines the amount of blank space with which to
|
253
|
+
# pad either side of the column. If passed nil, then the `column_padding` setting of the
|
254
|
+
# {Table} will determine the column's padding. (See {#initialize}.) Otherwise, this option
|
255
|
+
# overrides, for this column, the `column_padding` that was set at the table level: if passed an Integer,
|
256
|
+
# then the given amount of padding is applied to either side of the column; or if passed a two-element Array,
|
257
|
+
# then the first element of the Array indicates the amount of padding to apply to the left of the column,
|
258
|
+
# and the second element indicates the amount to apply to the right.
|
233
259
|
# @param [nil, #to_proc] styler (nil) A lambda or other callable object that will determine
|
234
260
|
# the colors or other styling applied to the formatted value of the cell. Can be passed
|
235
261
|
# <tt>nil</tt>, or can be passed a callable that takes either 2 or 3 parameters:
|
@@ -253,6 +279,12 @@ module Tabulo
|
|
253
279
|
# {CellData#row_index} attribute can be inspected to determine whether the {Cell} is an
|
254
280
|
# odd- or even-numbered {Row}, to arrange for different styling to be applied to
|
255
281
|
# alternating rows. See the documentation for {CellData} for more.
|
282
|
+
# * If the passed callable takes 4 parameters, then the first three parameters are as above,
|
283
|
+
# and the fourth parameter is an Integer representing the index of the line within the
|
284
|
+
# cell that is currently being styled. For example, if the cell content is wrapped over 3
|
285
|
+
# lines, then the callable will be called first with a line index of 0, to style the first
|
286
|
+
# line, then with a line index of 1, to style the second line, and finally with a line
|
287
|
+
# index of 2, to style the third and final wrapped line of the cell.
|
256
288
|
#
|
257
289
|
# Note that if the content of a cell is truncated, then the whatever styling is applied by the
|
258
290
|
# <tt>styler</tt> to the cell content will also be applied to the truncation indicator character.
|
@@ -273,10 +305,17 @@ module Tabulo
|
|
273
305
|
# Table. (This is case-sensitive, but is insensitive to whether a String or Symbol is passed
|
274
306
|
# to the label parameter.)
|
275
307
|
def add_column(label, align_body: nil, align_header: nil, before: nil, formatter: nil,
|
276
|
-
header: nil, header_styler: nil, styler: nil, width: nil, &extractor)
|
308
|
+
header: nil, header_styler: nil, padding: nil, styler: nil, width: nil, &extractor)
|
277
309
|
|
278
310
|
column_label = normalize_column_label(label)
|
279
311
|
|
312
|
+
left_padding, right_padding =
|
313
|
+
if padding
|
314
|
+
Array === padding ? padding : [padding, padding]
|
315
|
+
else
|
316
|
+
[@left_column_padding, @right_column_padding]
|
317
|
+
end
|
318
|
+
|
280
319
|
if column_registry.include?(column_label)
|
281
320
|
raise InvalidColumnLabelError, "Column label already used in this table."
|
282
321
|
end
|
@@ -289,7 +328,9 @@ module Tabulo
|
|
289
328
|
header: (header || label).to_s,
|
290
329
|
header_styler: header_styler || @header_styler,
|
291
330
|
index: column_registry.count,
|
331
|
+
left_padding: left_padding,
|
292
332
|
padding_character: PADDING_CHARACTER,
|
333
|
+
right_padding: right_padding,
|
293
334
|
styler: styler || @styler,
|
294
335
|
truncation_indicator: @truncation_indicator,
|
295
336
|
width: width || @column_width,
|
@@ -331,7 +372,7 @@ module Tabulo
|
|
331
372
|
if column_registry.any?
|
332
373
|
bottom_edge = horizontal_rule(:bottom)
|
333
374
|
rows = map(&:to_s)
|
334
|
-
bottom_edge.empty? ? join_lines(rows) : join_lines(rows + [bottom_edge])
|
375
|
+
bottom_edge.empty? ? Util.join_lines(rows) : Util.join_lines(rows + [bottom_edge])
|
335
376
|
else
|
336
377
|
""
|
337
378
|
end
|
@@ -389,7 +430,7 @@ module Tabulo
|
|
389
430
|
# It may be that `:top`, `:middle` and `:bottom` all look the same. Whether
|
390
431
|
# this is the case depends on the characters used for the table border.
|
391
432
|
def horizontal_rule(position = :bottom)
|
392
|
-
column_widths = get_columns.map { |column| column.width +
|
433
|
+
column_widths = get_columns.map { |column| column.width + column.total_padding }
|
393
434
|
@border_instance.horizontal_rule(column_widths, position)
|
394
435
|
end
|
395
436
|
|
@@ -425,22 +466,27 @@ module Tabulo
|
|
425
466
|
# Table will refuse to shrink itself.
|
426
467
|
# @return [Table] the Table itself
|
427
468
|
def pack(max_table_width: :auto)
|
428
|
-
get_columns.each { |column| column.width = wrapped_width(column.header) }
|
469
|
+
get_columns.each { |column| column.width = Util.wrapped_width(column.header) }
|
429
470
|
|
430
471
|
@sources.each_with_index do |source, row_index|
|
431
472
|
get_columns.each_with_index do |column, column_index|
|
432
473
|
cell = column.body_cell(source, row_index: row_index, column_index: column_index)
|
433
|
-
cell_width = wrapped_width(cell.formatted_content)
|
474
|
+
cell_width = Util.wrapped_width(cell.formatted_content)
|
434
475
|
column.width = Util.max(column.width, cell_width)
|
435
476
|
end
|
436
477
|
end
|
437
478
|
|
438
|
-
if max_table_width
|
439
|
-
shrink_to(max_table_width == :auto ? TTY::Screen.width : max_table_width)
|
440
|
-
end
|
479
|
+
shrink_to(max_table_width == :auto ? TTY::Screen.width : max_table_width) if max_table_width
|
441
480
|
|
442
481
|
if @title
|
443
|
-
|
482
|
+
border_edge_width = (@border == :blank ? 0 : 2)
|
483
|
+
columns = get_columns
|
484
|
+
expand_to(
|
485
|
+
Unicode::DisplayWidth.of(@title) +
|
486
|
+
columns.first.left_padding +
|
487
|
+
columns.last.right_padding +
|
488
|
+
border_edge_width
|
489
|
+
)
|
444
490
|
end
|
445
491
|
|
446
492
|
self
|
@@ -530,33 +576,14 @@ module Tabulo
|
|
530
576
|
inner = format_row(cells, @wrap_body_cells_to)
|
531
577
|
|
532
578
|
if @title && header == :top
|
533
|
-
|
534
|
-
horizontal_rule(:
|
535
|
-
formatted_title,
|
536
|
-
horizontal_rule(:title_bottom),
|
537
|
-
formatted_header,
|
538
|
-
horizontal_rule(:middle),
|
539
|
-
inner
|
540
|
-
].reject(&:empty?))
|
579
|
+
Util.condense_lines([horizontal_rule(:title_top), formatted_title, horizontal_rule(:title_bottom),
|
580
|
+
formatted_header, horizontal_rule(:middle), inner])
|
541
581
|
elsif header == :top
|
542
|
-
|
543
|
-
horizontal_rule(:top),
|
544
|
-
formatted_header,
|
545
|
-
horizontal_rule(:middle),
|
546
|
-
inner
|
547
|
-
].reject(&:empty?))
|
582
|
+
Util.condense_lines([horizontal_rule(:top), formatted_header, horizontal_rule(:middle), inner])
|
548
583
|
elsif header
|
549
|
-
|
550
|
-
horizontal_rule(:middle),
|
551
|
-
formatted_header,
|
552
|
-
horizontal_rule(:middle),
|
553
|
-
inner
|
554
|
-
].reject(&:empty?))
|
584
|
+
Util.condense_lines([horizontal_rule(:middle), formatted_header, horizontal_rule(:middle), inner])
|
555
585
|
elsif divider
|
556
|
-
|
557
|
-
horizontal_rule(:middle),
|
558
|
-
inner
|
559
|
-
].reject(&:empty?))
|
586
|
+
Util.condense_lines([horizontal_rule(:middle), inner])
|
560
587
|
else
|
561
588
|
inner
|
562
589
|
end
|
@@ -593,23 +620,34 @@ module Tabulo
|
|
593
620
|
# @visibility private
|
594
621
|
def formatted_title
|
595
622
|
columns = get_columns
|
596
|
-
|
597
|
-
basic_width = columns.inject(0) { |total_width, column| total_width + column.width }
|
623
|
+
|
598
624
|
extra_for_internal_dividers = (@border == :blank ? 0 : 1)
|
599
|
-
|
600
|
-
|
601
|
-
|
625
|
+
|
626
|
+
title_cell_width = columns.inject(0) do |total_width, column|
|
627
|
+
total_width + column.padded_width + extra_for_internal_dividers
|
628
|
+
end
|
629
|
+
|
630
|
+
title_cell_width -= (columns.first.left_padding + columns.last.right_padding + extra_for_internal_dividers)
|
631
|
+
|
602
632
|
styler =
|
603
633
|
if @title_styler
|
604
|
-
|
634
|
+
case @title_styler.arity
|
635
|
+
when 1
|
636
|
+
-> (_val, str) { @title_styler.call(str) }
|
637
|
+
when 2
|
638
|
+
-> (_val, str, _cell_data, line_index) { @title_styler.call(str, line_index) }
|
639
|
+
end
|
605
640
|
else
|
606
|
-
-> (
|
641
|
+
-> (_val, str) { str }
|
607
642
|
end
|
643
|
+
|
608
644
|
title_cell = Cell.new(
|
609
645
|
alignment: @align_title,
|
610
646
|
cell_data: nil,
|
611
647
|
formatter: -> (s) { s },
|
648
|
+
left_padding: columns.first.left_padding,
|
612
649
|
padding_character: PADDING_CHARACTER,
|
650
|
+
right_padding: columns.last.right_padding,
|
613
651
|
styler: styler,
|
614
652
|
truncation_indicator: @truncation_indicator,
|
615
653
|
value: @title,
|
@@ -619,13 +657,13 @@ module Tabulo
|
|
619
657
|
max_cell_height = cells.map(&:height).max
|
620
658
|
row_height = ([nil, max_cell_height].compact.min || 1)
|
621
659
|
subcell_stacks = cells.map do |cell|
|
622
|
-
cell.padded_truncated_subcells(row_height
|
660
|
+
cell.padded_truncated_subcells(row_height)
|
623
661
|
end
|
624
662
|
subrows = subcell_stacks.transpose.map do |subrow_components|
|
625
663
|
@border_instance.join_cell_contents(subrow_components)
|
626
664
|
end
|
627
665
|
|
628
|
-
join_lines(subrows)
|
666
|
+
Util.join_lines(subrows)
|
629
667
|
end
|
630
668
|
|
631
669
|
# @!visibility private
|
@@ -642,10 +680,9 @@ module Tabulo
|
|
642
680
|
def expand_to(min_table_width)
|
643
681
|
columns = get_columns
|
644
682
|
num_columns = columns.count
|
645
|
-
|
646
|
-
total_padding = num_columns * total_column_padding
|
683
|
+
total_columns_padded_width = columns.inject(0) { |sum, column| sum + column.padded_width }
|
647
684
|
total_borders = num_columns + 1
|
648
|
-
unadjusted_table_width =
|
685
|
+
unadjusted_table_width = total_columns_padded_width + total_borders
|
649
686
|
required_increase = Util.max(min_table_width - unadjusted_table_width, 0)
|
650
687
|
|
651
688
|
required_increase.times do
|
@@ -661,10 +698,10 @@ module Tabulo
|
|
661
698
|
def shrink_to(max_table_width)
|
662
699
|
columns = get_columns
|
663
700
|
num_columns = columns.count
|
664
|
-
|
665
|
-
total_padding =
|
701
|
+
total_columns_padded_width = columns.inject(0) { |sum, column| sum + column.padded_width }
|
702
|
+
total_padding = columns.inject(0) { |sum, column| sum + column.total_padding }
|
666
703
|
total_borders = num_columns + 1
|
667
|
-
unadjusted_table_width =
|
704
|
+
unadjusted_table_width = total_columns_padded_width + total_borders
|
668
705
|
|
669
706
|
# Ensure max table width is at least wide enough to accommodate table borders and padding
|
670
707
|
# and one character of content.
|
@@ -681,11 +718,6 @@ module Tabulo
|
|
681
718
|
end
|
682
719
|
end
|
683
720
|
|
684
|
-
# @!visibility private
|
685
|
-
def total_column_padding
|
686
|
-
@left_column_padding + @right_column_padding
|
687
|
-
end
|
688
|
-
|
689
721
|
# @!visibility private
|
690
722
|
#
|
691
723
|
# Formats a single header row or body row as a String.
|
@@ -703,18 +735,13 @@ module Tabulo
|
|
703
735
|
max_cell_height = cells.map(&:height).max
|
704
736
|
row_height = ([wrap_cells_to, max_cell_height].compact.min || 1)
|
705
737
|
subcell_stacks = cells.map do |cell|
|
706
|
-
cell.padded_truncated_subcells(row_height
|
738
|
+
cell.padded_truncated_subcells(row_height)
|
707
739
|
end
|
708
740
|
subrows = subcell_stacks.transpose.map do |subrow_components|
|
709
741
|
@border_instance.join_cell_contents(subrow_components)
|
710
742
|
end
|
711
743
|
|
712
|
-
join_lines(subrows)
|
713
|
-
end
|
714
|
-
|
715
|
-
# @!visibility private
|
716
|
-
def join_lines(lines)
|
717
|
-
lines.join($/) # join strings with cross-platform newline
|
744
|
+
Util.join_lines(subrows)
|
718
745
|
end
|
719
746
|
|
720
747
|
# @!visibility private
|
@@ -732,13 +759,5 @@ module Tabulo
|
|
732
759
|
c
|
733
760
|
end
|
734
761
|
|
735
|
-
# @!visibility private
|
736
|
-
# @return [Integer] the length of the longest segment of str when split by newlines
|
737
|
-
def wrapped_width(str)
|
738
|
-
segments = str.split($/)
|
739
|
-
segments.inject(1) do |longest_length_so_far, segment|
|
740
|
-
Util.max(longest_length_so_far, Unicode::DisplayWidth.of(segment))
|
741
|
-
end
|
742
|
-
end
|
743
762
|
end
|
744
763
|
end
|