tabulo 2.5.0 → 2.6.0
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/.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
|