terminal-table 1.8.0 → 3.0.2
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 +5 -5
- data/.github/workflows/ci.yml +28 -0
- data/.gitignore +5 -0
- data/History.rdoc +57 -0
- data/README.md +417 -0
- data/examples/data.csv +4 -0
- data/examples/examples.rb +0 -0
- data/examples/examples_unicode.rb +89 -0
- data/examples/issue100.rb +34 -0
- data/examples/issue111.rb +4 -0
- data/examples/issue118.rb +36 -0
- data/examples/issue95.rb +42 -0
- data/examples/show_csv_table.rb +34 -0
- data/examples/strong_separator.rb +23 -0
- data/lib/terminal-table/cell.rb +8 -8
- data/lib/terminal-table/row.rb +18 -4
- data/lib/terminal-table/separator.rb +56 -4
- data/lib/terminal-table/style.rb +218 -13
- data/lib/terminal-table/table.rb +47 -18
- data/lib/terminal-table/util.rb +13 -0
- data/lib/terminal-table/version.rb +1 -1
- data/lib/terminal-table.rb +2 -2
- data/terminal-table.gemspec +3 -3
- metadata +28 -19
- data/README.rdoc +0 -247
data/examples/issue95.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'colorize'
|
3
|
+
require_relative '../lib/terminal-table.rb'
|
4
|
+
|
5
|
+
original_sample_data = [
|
6
|
+
["Sep 2016", 33, [-38, -53.52], 46, [-25, -35.21]],
|
7
|
+
["Oct 2016", 35, [2, 6.06], 50, [4, 8.69]]
|
8
|
+
]
|
9
|
+
|
10
|
+
table = Terminal::Table.new headings: ["Month".cyan,"Monthly IT".cyan,"IT Difference OPM".cyan,
|
11
|
+
"Monthly OOT".cyan,"OOT Difference OPM".cyan], rows: original_sample_data
|
12
|
+
|
13
|
+
table.style = { padding_left: 2, padding_right: 2, border_x: "-".blue, border_y: "|".blue, border_i: "+".blue }
|
14
|
+
|
15
|
+
puts table
|
16
|
+
|
17
|
+
puts ""
|
18
|
+
puts "^ good table"
|
19
|
+
puts "v wonky table"
|
20
|
+
puts ""
|
21
|
+
|
22
|
+
split_column_sample_data = [
|
23
|
+
["Sep 2016", 33, -38, -53.52, 46, -25, -35.21],
|
24
|
+
["Oct 2016", 35, 2, 6.06, 50, 4, 8.69]
|
25
|
+
]
|
26
|
+
|
27
|
+
table = Terminal::Table.new headings: ["Month".cyan,"Monthly IT".cyan,
|
28
|
+
{value: "IT Difference OPM".cyan, colspan: 2}, "Monthly OOT".cyan,
|
29
|
+
{value: "OOT Difference OPM".cyan, colspan: 2}], rows: split_column_sample_data
|
30
|
+
|
31
|
+
table.style = { padding_left: 2, padding_right: 2, border_x: "-".blue, border_y: "|".blue, border_i: "+".blue }
|
32
|
+
|
33
|
+
puts table
|
34
|
+
|
35
|
+
|
36
|
+
table = Terminal::Table.new headings: ["Month","Monthly IT",
|
37
|
+
{value: "IT Difference OPM", colspan: 2}, "Monthly OOT",
|
38
|
+
{value: "OOT Difference OPM", colspan: 2}], rows: split_column_sample_data
|
39
|
+
|
40
|
+
table.style = { padding_left: 2, padding_right: 2, border_x: "-".blue, border_y: "|".cyan, border_i: "+" }
|
41
|
+
|
42
|
+
puts table
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "csv"
|
4
|
+
$LOAD_PATH << "#{__dir__}/../lib"
|
5
|
+
require "terminal-table"
|
6
|
+
|
7
|
+
#
|
8
|
+
# Usage:
|
9
|
+
# ./show_csv_table.rb data.csv
|
10
|
+
# cat data.csv | ./show_csv_table.rb
|
11
|
+
# cat data.csv | ./show_csv_table.rb -
|
12
|
+
#
|
13
|
+
#
|
14
|
+
# Reads a CSV from $stdin if no argument given, or argument is '-'
|
15
|
+
# otherwise interprets first cmdline argument as the CSV filename
|
16
|
+
#
|
17
|
+
use_stdin = ARGV[0].nil? || (ARGV[0] == '-')
|
18
|
+
io_object = use_stdin ? $stdin : File.open(ARGV[0], 'r')
|
19
|
+
csv = CSV.new(io_object)
|
20
|
+
|
21
|
+
#
|
22
|
+
# Convert to an array for use w/ terminal-table
|
23
|
+
# The assumption is that this is a pretty small spreadsheet.
|
24
|
+
#
|
25
|
+
csv_array = csv.to_a
|
26
|
+
|
27
|
+
user_table = Terminal::Table.new do |v|
|
28
|
+
v.style = { :border => :unicode_round } # >= v3.0.0
|
29
|
+
v.title = "Some Title"
|
30
|
+
v.headings = csv_array[0]
|
31
|
+
v.rows = csv_array[1..-1]
|
32
|
+
end
|
33
|
+
|
34
|
+
puts user_table
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative "../lib/terminal-table"
|
3
|
+
|
4
|
+
#
|
5
|
+
# An example of how to manually add separators with non-default
|
6
|
+
# border_type to enable a footer row.
|
7
|
+
#
|
8
|
+
table = Terminal::Table.new do |t|
|
9
|
+
# set the style
|
10
|
+
t.style = { border: :unicode_thick_edge }
|
11
|
+
|
12
|
+
# header row
|
13
|
+
t.headings = ['fruit', 'count']
|
14
|
+
|
15
|
+
# some row data
|
16
|
+
t.add_row ['apples', 7]
|
17
|
+
t.add_row ['bananas', 19]
|
18
|
+
t.add_separator border_type: :strong
|
19
|
+
# footer row
|
20
|
+
t.add_row ['total', 26]
|
21
|
+
end
|
22
|
+
|
23
|
+
puts table.render
|
data/lib/terminal-table/cell.rb
CHANGED
@@ -57,7 +57,7 @@ module Terminal
|
|
57
57
|
def render(line = 0)
|
58
58
|
left = " " * @table.style.padding_left
|
59
59
|
right = " " * @table.style.padding_right
|
60
|
-
display_width = Unicode::DisplayWidth.of(
|
60
|
+
display_width = Unicode::DisplayWidth.of(Util::ansi_escape(lines[line]))
|
61
61
|
render_width = lines[line].to_s.size - display_width + width
|
62
62
|
align("#{left}#{lines[line]}#{right}", alignment, render_width + @table.cell_padding)
|
63
63
|
end
|
@@ -68,7 +68,7 @@ module Terminal
|
|
68
68
|
# removes all ANSI escape sequences (e.g. color)
|
69
69
|
|
70
70
|
def value_for_column_width_recalc
|
71
|
-
lines.map{ |s|
|
71
|
+
lines.map{ |s| Util::ansi_escape(s) }.max_by{ |s| Unicode::DisplayWidth.of(s) }
|
72
72
|
end
|
73
73
|
|
74
74
|
##
|
@@ -82,12 +82,12 @@ module Terminal
|
|
82
82
|
inner_width + padding
|
83
83
|
end
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
85
|
+
def inspect
|
86
|
+
fields = %i[alignment colspan index value width].map do |name|
|
87
|
+
val = self.instance_variable_get('@'+name.to_s)
|
88
|
+
"@#{name}=#{val.inspect}"
|
89
|
+
end.join(', ')
|
90
|
+
return "#<#{self.class} #{fields}>"
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
data/lib/terminal-table/row.rb
CHANGED
@@ -12,7 +12,7 @@ module Terminal
|
|
12
12
|
##
|
13
13
|
# Initialize with _width_ and _options_.
|
14
14
|
|
15
|
-
def initialize table, array = []
|
15
|
+
def initialize table, array = [], **_kwargs
|
16
16
|
@cell_index = 0
|
17
17
|
@table = table
|
18
18
|
@cells = []
|
@@ -36,17 +36,31 @@ module Terminal
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def render
|
39
|
-
|
39
|
+
vleft, vcenter, vright = @table.style.vertical
|
40
40
|
(0...height).to_a.map do |line|
|
41
|
-
|
41
|
+
vleft + cells.map do |cell|
|
42
42
|
cell.render(line)
|
43
|
-
end.join(
|
43
|
+
end.join(vcenter) + vright
|
44
44
|
end.join("\n")
|
45
45
|
end
|
46
46
|
|
47
47
|
def number_of_columns
|
48
48
|
@cells.collect(&:colspan).inject(0, &:+)
|
49
49
|
end
|
50
|
+
|
51
|
+
# used to find indices where we have table '+' crossings.
|
52
|
+
# in cases where the colspan > 1, then we will skip over some numbers
|
53
|
+
# if colspan is always 1, then the list should be incrementing by 1.
|
54
|
+
#
|
55
|
+
# skip 0 entry, because it's the left side.
|
56
|
+
# skip last entry, because it's the right side.
|
57
|
+
# we only care about "+/T" style crossings.
|
58
|
+
def crossings
|
59
|
+
idx = 0
|
60
|
+
@cells[0...-1].map { |c| idx += c.colspan }
|
61
|
+
end
|
62
|
+
|
50
63
|
end
|
64
|
+
|
51
65
|
end
|
52
66
|
end
|
@@ -2,13 +2,65 @@ module Terminal
|
|
2
2
|
class Table
|
3
3
|
class Separator < Row
|
4
4
|
|
5
|
+
##
|
6
|
+
# `prevrow`, `nextrow` contain references to adjacent rows.
|
7
|
+
#
|
8
|
+
# `border_type` is a symbol used to control which type of border is used
|
9
|
+
# on the separator (:top for top-edge, :bot for bottom-edge,
|
10
|
+
# :div for interior, and :strong for emphasized-interior)
|
11
|
+
#
|
12
|
+
# `implicit` is false for user-added separators, and true for
|
13
|
+
# implicit/auto-generated separators.
|
14
|
+
|
15
|
+
def initialize(*args, border_type: :div, implicit: false)
|
16
|
+
super
|
17
|
+
@prevrow, @nextrow = nil, nil
|
18
|
+
@border_type = border_type
|
19
|
+
@implicit = implicit
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_accessor :border_type
|
23
|
+
attr_reader :implicit
|
24
|
+
|
5
25
|
def render
|
6
|
-
|
7
|
-
|
26
|
+
left_edge, ctrflat, ctrud, right_edge, ctrdn, ctrup = @table.style.horizontal(border_type)
|
27
|
+
|
28
|
+
prev_crossings = @prevrow.respond_to?(:crossings) ? @prevrow.crossings : []
|
29
|
+
next_crossings = @nextrow.respond_to?(:crossings) ? @nextrow.crossings : []
|
30
|
+
rval = [left_edge]
|
31
|
+
numcols = @table.number_of_columns
|
32
|
+
(0...numcols).each do |idx|
|
33
|
+
rval << ctrflat * (@table.column_width(idx) + @table.cell_padding)
|
34
|
+
pcinc = prev_crossings.include?(idx+1)
|
35
|
+
ncinc = next_crossings.include?(idx+1)
|
36
|
+
border_center = if pcinc && ncinc
|
37
|
+
ctrud
|
38
|
+
elsif pcinc
|
39
|
+
ctrup
|
40
|
+
elsif ncinc
|
41
|
+
ctrdn
|
42
|
+
elsif !ctrud.empty?
|
43
|
+
# special case if the center-up-down intersection is empty
|
44
|
+
# which happens when verticals/intersections are removed. in that case
|
45
|
+
# we do not want to replace with a flat element so return empty-string in else block
|
46
|
+
ctrflat
|
47
|
+
else
|
48
|
+
''
|
49
|
+
end
|
50
|
+
rval << border_center if idx < numcols-1
|
8
51
|
end
|
9
|
-
|
10
|
-
|
52
|
+
|
53
|
+
rval << right_edge
|
54
|
+
rval.join
|
55
|
+
end
|
56
|
+
|
57
|
+
# Save off neighboring rows, so that we can use them later in determining
|
58
|
+
# which types of table edges to use.
|
59
|
+
def save_adjacent_rows(prevrow, nextrow)
|
60
|
+
@prevrow = prevrow
|
61
|
+
@nextrow = nextrow
|
11
62
|
end
|
63
|
+
|
12
64
|
end
|
13
65
|
end
|
14
66
|
end
|
data/lib/terminal-table/style.rb
CHANGED
@@ -1,5 +1,171 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'forwardable'
|
3
|
+
|
1
4
|
module Terminal
|
2
5
|
class Table
|
6
|
+
|
7
|
+
class Border
|
8
|
+
|
9
|
+
attr_accessor :data, :top, :bottom, :left, :right
|
10
|
+
def initialize
|
11
|
+
@top, @bottom, @left, @right = true, true, true, true
|
12
|
+
end
|
13
|
+
def []=(key, val)
|
14
|
+
@data[key] = val
|
15
|
+
end
|
16
|
+
def [](key)
|
17
|
+
@data[key]
|
18
|
+
end
|
19
|
+
def initialize_dup(other)
|
20
|
+
super
|
21
|
+
@data = other.data.dup
|
22
|
+
end
|
23
|
+
def remove_verticals
|
24
|
+
self.class.const_get("VERTICALS").each { |key| @data[key] = "" }
|
25
|
+
self.class.const_get("INTERSECTIONS").each { |key| @data[key] = "" }
|
26
|
+
end
|
27
|
+
def remove_horizontals
|
28
|
+
self.class.const_get("HORIZONTALS").each { |key| @data[key] = "" }
|
29
|
+
end
|
30
|
+
|
31
|
+
# If @left, return the edge else empty-string.
|
32
|
+
def maybeleft(key) ; @left ? @data[key] : '' ; end
|
33
|
+
|
34
|
+
# If @right, return the edge else empty-string.
|
35
|
+
def mayberight(key) ; @right ? @data[key] : '' ; end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class AsciiBorder < Border
|
40
|
+
HORIZONTALS = %i[x]
|
41
|
+
VERTICALS = %i[y]
|
42
|
+
INTERSECTIONS = %i[i]
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
super
|
46
|
+
@data = { x: "-", y: "|", i: "+" }
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get vertical border elements
|
50
|
+
# @return [Array] 3-element list of [left, center, right]
|
51
|
+
def vertical
|
52
|
+
[maybeleft(:y), @data[:y], mayberight(:y)] # left, center, right
|
53
|
+
end
|
54
|
+
|
55
|
+
# Get horizontal border elements
|
56
|
+
# @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up]
|
57
|
+
def horizontal(_type)
|
58
|
+
x, i = @data[:x], @data[:i]
|
59
|
+
[maybeleft(:i), x, i, mayberight(:i), i, i]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class MarkdownBorder < AsciiBorder
|
64
|
+
def initialize
|
65
|
+
super
|
66
|
+
@top, @bottom = false, false
|
67
|
+
@data = { x: "-", y: "|", i: "|" }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class UnicodeBorder < Border
|
72
|
+
|
73
|
+
ALLOWED_SEPARATOR_BORDER_STYLES = %i[
|
74
|
+
top bot
|
75
|
+
div dash dot3 dot4
|
76
|
+
thick thick_dash thick_dot3 thick_dot4
|
77
|
+
heavy heavy_dash heavy_dot3 heavy_dot4
|
78
|
+
bold bold_dash bold_dot3 bold_dot4
|
79
|
+
double
|
80
|
+
]
|
81
|
+
|
82
|
+
HORIZONTALS = %i[x sx ax bx nx bx_dot3 bx_dot4 bx_dash x_dot3 x_dot4 x_dash]
|
83
|
+
VERTICALS = %i[y yw ye]
|
84
|
+
INTERSECTIONS = %i[nw n ne nd
|
85
|
+
aw ai ae ad au
|
86
|
+
bw bi be bd bu
|
87
|
+
w i e dn up
|
88
|
+
sw s se su]
|
89
|
+
def initialize
|
90
|
+
super
|
91
|
+
@data = {
|
92
|
+
nil => nil,
|
93
|
+
nw: "┌", nx: "─", n: "┬", ne: "┐",
|
94
|
+
yw: "│", y: "│", ye: "│",
|
95
|
+
aw: "╞", ax: "═", ai: "╪", ae: "╡", ad: '╤', au: "╧", # double
|
96
|
+
bw: "┝", bx: "━", bi: "┿", be: "┥", bd: '┯', bu: "┷", # heavy/bold/thick
|
97
|
+
w: "├", x: "─", i: "┼", e: "┤", dn: "┬", up: "┴", # normal div
|
98
|
+
sw: "└", sx: "─", s: "┴", se: "┘",
|
99
|
+
# alternative dots/dashes
|
100
|
+
x_dot4: '┈', x_dot3: '┄', x_dash: '╌',
|
101
|
+
bx_dot4: '┉', bx_dot3: '┅', bx_dash: '╍',
|
102
|
+
}
|
103
|
+
end
|
104
|
+
# Get vertical border elements
|
105
|
+
# @return [Array] 3-element list of [left, center, right]
|
106
|
+
def vertical
|
107
|
+
[maybeleft(:yw), @data[:y], mayberight(:ye)]
|
108
|
+
end
|
109
|
+
|
110
|
+
# Get horizontal border elements
|
111
|
+
# @return [Array] a 6 element list of: [i-left, horizontal-bar, i-up/down, i-right, i-down, i-up]
|
112
|
+
def horizontal(type)
|
113
|
+
raise ArgumentError, "Border type is #{type.inspect}, must be one of #{ALLOWED_SEPARATOR_BORDER_STYLES.inspect}" unless ALLOWED_SEPARATOR_BORDER_STYLES.include?(type)
|
114
|
+
lookup = case type
|
115
|
+
when :top
|
116
|
+
[:nw, :nx, :n, :ne, :n, nil]
|
117
|
+
when :bot
|
118
|
+
[:sw, :sx, :s, :se, nil, :s]
|
119
|
+
when :double
|
120
|
+
# typically used for the separator below the heading row or above a footer row)
|
121
|
+
[:aw, :ax, :ai, :ae, :ad, :au]
|
122
|
+
when :thick, :thick_dash, :thick_dot3, :thick_dot4,
|
123
|
+
:heavy, :heavy_dash, :heavy_dot3, :heavy_dot4,
|
124
|
+
:bold, :bold_dash, :bold_dot3, :bold_dot4
|
125
|
+
# alternate thick/bold border
|
126
|
+
xref = type.to_s.sub(/^(thick|heavy|bold)/,'bx').to_sym
|
127
|
+
[:bw, xref, :bi, :be, :bd, :bu]
|
128
|
+
when :dash, :dot3, :dot4
|
129
|
+
# alternate thin dividers
|
130
|
+
xref = "x_#{type}".to_sym
|
131
|
+
[:w, xref, :i, :e, :dn, :up]
|
132
|
+
else # :div (center, non-emphasized)
|
133
|
+
[:w, :x, :i, :e, :dn, :up]
|
134
|
+
end
|
135
|
+
rval = lookup.map { |key| @data.fetch(key) }
|
136
|
+
rval[0] = '' unless @left
|
137
|
+
rval[3] = '' unless @right
|
138
|
+
rval
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Unicode Border With rounded edges
|
143
|
+
class UnicodeRoundBorder < UnicodeBorder
|
144
|
+
def initialize
|
145
|
+
super
|
146
|
+
@data.merge!({nw: '╭', ne: '╮', sw: '╰', se: '╯'})
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Unicode Border with thick outer edges
|
151
|
+
class UnicodeThickEdgeBorder < UnicodeBorder
|
152
|
+
def initialize
|
153
|
+
super
|
154
|
+
@data = {
|
155
|
+
nil => nil,
|
156
|
+
nw: "┏", nx: "━", n: "┯", ne: "┓", nd: nil,
|
157
|
+
yw: "┃", y: "│", ye: "┃",
|
158
|
+
aw: "┣", ax: "═", ai: "╪", ae: "┫", ad: '╤', au: "╧", # double
|
159
|
+
bw: "┣", bx: "━", bi: "┿", be: "┫", bd: '┯', bu: "┷", # heavy/bold/thick
|
160
|
+
w: "┠", x: "─", i: "┼", e: "┨", dn: "┬", up: "┴", # normal div
|
161
|
+
sw: "┗", sx: "━", s: "┷", se: "┛", su: nil,
|
162
|
+
# alternative dots/dashes
|
163
|
+
x_dot4: '┈', x_dot3: '┄', x_dash: '╌',
|
164
|
+
bx_dot4: '┉', bx_dot3: '┅', bx_dash: '╍',
|
165
|
+
}
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
3
169
|
# A Style object holds all the formatting information for a Table object
|
4
170
|
#
|
5
171
|
# To create a table with a certain style, use either the constructor
|
@@ -22,20 +188,51 @@ module Terminal
|
|
22
188
|
# Terminal::Table::Style.defaults = {:width => 80}
|
23
189
|
#
|
24
190
|
class Style
|
191
|
+
extend Forwardable
|
192
|
+
def_delegators :@border, :vertical, :horizontal, :remove_verticals, :remove_horizontals
|
193
|
+
|
25
194
|
@@defaults = {
|
26
|
-
:
|
27
|
-
:border_top => true, :border_bottom => true,
|
195
|
+
:border => AsciiBorder.new,
|
28
196
|
:padding_left => 1, :padding_right => 1,
|
29
197
|
:margin_left => '',
|
30
198
|
:width => nil, :alignment => nil,
|
31
|
-
:all_separators => false
|
199
|
+
:all_separators => false,
|
32
200
|
}
|
33
201
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
202
|
+
## settors/gettor for legacy ascii borders
|
203
|
+
def border_x=(val) ; @border[:x] = val ; end
|
204
|
+
def border_y=(val) ; @border[:y] = val ; end
|
205
|
+
def border_i=(val) ; @border[:i] = val ; end
|
206
|
+
def border_y ; @border[:y] ; end
|
207
|
+
def border_y_width ; Util::ansi_escape(@border[:y]).length ; end
|
208
|
+
|
209
|
+
# Accessor for instance of Border
|
210
|
+
attr_reader :border
|
211
|
+
def border=(val)
|
212
|
+
if val.is_a? Symbol
|
213
|
+
# convert symbol name like :foo_bar to get class FooBarBorder
|
214
|
+
klass_str = val.to_s.split('_').collect(&:capitalize).join + "Border"
|
215
|
+
begin
|
216
|
+
klass = Terminal::Table::const_get(klass_str)
|
217
|
+
@border = klass.new
|
218
|
+
rescue NameError
|
219
|
+
raise "Cannot lookup class Terminal::Table::#{klass_str} from symbol #{val.inspect}"
|
220
|
+
end
|
221
|
+
else
|
222
|
+
@border = val
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def border_top=(val) ; @border.top = val ; end
|
227
|
+
def border_bottom=(val) ; @border.bottom = val ; end
|
228
|
+
def border_left=(val) ; @border.left = val ; end
|
229
|
+
def border_right=(val) ; @border.right = val ; end
|
230
|
+
|
231
|
+
def border_top ; @border.top ; end
|
232
|
+
def border_bottom ; @border.bottom ; end
|
233
|
+
def border_left ; @border.left ; end
|
234
|
+
def border_right ; @border.right ; end
|
235
|
+
|
39
236
|
|
40
237
|
attr_accessor :padding_left
|
41
238
|
attr_accessor :padding_right
|
@@ -47,23 +244,30 @@ module Terminal
|
|
47
244
|
|
48
245
|
attr_accessor :all_separators
|
49
246
|
|
50
|
-
|
247
|
+
|
51
248
|
def initialize options = {}
|
52
249
|
apply self.class.defaults.merge(options)
|
53
250
|
end
|
54
251
|
|
55
252
|
def apply options
|
56
|
-
options.each
|
253
|
+
options.each do |m, v|
|
254
|
+
__send__ "#{m}=", v
|
255
|
+
end
|
57
256
|
end
|
58
|
-
|
257
|
+
|
59
258
|
class << self
|
60
259
|
def defaults
|
61
|
-
@@defaults
|
260
|
+
klass_defaults = @@defaults.dup
|
261
|
+
# border is an object that needs to be duplicated on instantiation,
|
262
|
+
# otherwise everything will be referencing the same object-id.
|
263
|
+
klass_defaults[:border] = klass_defaults[:border].dup
|
264
|
+
klass_defaults
|
62
265
|
end
|
63
|
-
|
266
|
+
|
64
267
|
def defaults= options
|
65
268
|
@@defaults = defaults.merge(options)
|
66
269
|
end
|
270
|
+
|
67
271
|
end
|
68
272
|
|
69
273
|
def on_change attr
|
@@ -74,6 +278,7 @@ module Terminal
|
|
74
278
|
yield attr.to_sym, value
|
75
279
|
end
|
76
280
|
end
|
281
|
+
|
77
282
|
end
|
78
283
|
end
|
79
284
|
end
|
data/lib/terminal-table/table.rb
CHANGED
@@ -7,9 +7,10 @@ module Terminal
|
|
7
7
|
attr_reader :headings
|
8
8
|
|
9
9
|
##
|
10
|
-
# Generates a ASCII table with the given _options_.
|
10
|
+
# Generates a ASCII/Unicode table with the given _options_.
|
11
11
|
|
12
12
|
def initialize options = {}, &block
|
13
|
+
@elaborated = false
|
13
14
|
@headings = []
|
14
15
|
@rows = []
|
15
16
|
@column_widths = []
|
@@ -26,9 +27,8 @@ module Terminal
|
|
26
27
|
# Align column _n_ to the given _alignment_ of :center, :left, or :right.
|
27
28
|
|
28
29
|
def align_column n, alignment
|
29
|
-
|
30
|
-
column(n).
|
31
|
-
cell = r[i][n]
|
30
|
+
# nil forces the column method to return the cell itself
|
31
|
+
column(n, nil).each do |cell|
|
32
32
|
cell.alignment = alignment unless cell.alignment?
|
33
33
|
end
|
34
34
|
end
|
@@ -46,12 +46,12 @@ module Terminal
|
|
46
46
|
##
|
47
47
|
# Add a separator.
|
48
48
|
|
49
|
-
def add_separator
|
50
|
-
|
49
|
+
def add_separator(border_type: :div)
|
50
|
+
@rows << Separator.new(self, border_type: border_type)
|
51
51
|
end
|
52
52
|
|
53
53
|
def cell_spacing
|
54
|
-
cell_padding + style.
|
54
|
+
cell_padding + style.border_y_width
|
55
55
|
end
|
56
56
|
|
57
57
|
def cell_padding
|
@@ -118,28 +118,57 @@ module Terminal
|
|
118
118
|
end
|
119
119
|
|
120
120
|
##
|
121
|
-
#
|
121
|
+
# Elaborate rows to form an Array of Rows and Separators with adjacency properties added.
|
122
|
+
#
|
123
|
+
# This is separated from the String rendering so that certain features may be tweaked
|
124
|
+
# before the String is built.
|
122
125
|
|
123
|
-
def
|
124
|
-
|
125
|
-
buffer = style.border_top ? [
|
126
|
+
def elaborate_rows
|
127
|
+
|
128
|
+
buffer = style.border_top ? [Separator.new(self, border_type: :top, implicit: true)] : []
|
126
129
|
unless @title.nil?
|
127
130
|
buffer << Row.new(self, [title_cell_options])
|
128
|
-
buffer <<
|
131
|
+
buffer << Separator.new(self, implicit: true)
|
129
132
|
end
|
130
133
|
@headings.each do |row|
|
131
134
|
unless row.cells.empty?
|
132
135
|
buffer << row
|
133
|
-
buffer <<
|
136
|
+
buffer << Separator.new(self, border_type: :double, implicit: true)
|
134
137
|
end
|
135
138
|
end
|
136
139
|
if style.all_separators
|
137
|
-
|
140
|
+
@rows.each_with_index do |row, idx|
|
141
|
+
# last separator is bottom, others are :div
|
142
|
+
border_type = (idx == @rows.size - 1) ? :bot : :div
|
143
|
+
buffer << row
|
144
|
+
buffer << Separator.new(self, border_type: border_type, implicit: true)
|
145
|
+
end
|
138
146
|
else
|
139
147
|
buffer += @rows
|
140
|
-
buffer <<
|
148
|
+
buffer << Separator.new(self, border_type: :bot, implicit: true) if style.border_bottom
|
149
|
+
end
|
150
|
+
|
151
|
+
# After all implicit Separators are inserted we need to save off the
|
152
|
+
# adjacent rows so that we can decide what type of intersections to use
|
153
|
+
# based on column spans in the adjacent row(s).
|
154
|
+
buffer.each_with_index do |r, idx|
|
155
|
+
if r.is_a?(Separator)
|
156
|
+
prev_row = idx > 0 ? buffer[idx - 1] : nil
|
157
|
+
next_row = buffer.fetch(idx + 1, nil)
|
158
|
+
r.save_adjacent_rows(prev_row, next_row)
|
159
|
+
end
|
141
160
|
end
|
142
|
-
|
161
|
+
|
162
|
+
@elaborated = true
|
163
|
+
@rows = buffer
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# Render the table.
|
168
|
+
|
169
|
+
def render
|
170
|
+
elaborate_rows unless @elaborated
|
171
|
+
@rows.map { |r| style.margin_left + r.render.rstrip }.join("\n")
|
143
172
|
end
|
144
173
|
alias :to_s :render
|
145
174
|
|
@@ -181,7 +210,7 @@ module Terminal
|
|
181
210
|
private
|
182
211
|
|
183
212
|
def columns_width
|
184
|
-
column_widths.inject(0) { |s, i| s + i + cell_spacing } + style.
|
213
|
+
column_widths.inject(0) { |s, i| s + i + cell_spacing } + style.border_y_width
|
185
214
|
end
|
186
215
|
|
187
216
|
def recalc_column_widths
|
@@ -300,7 +329,7 @@ module Terminal
|
|
300
329
|
|
301
330
|
full_width = dp[n_cols][0].keys.first
|
302
331
|
unless style.width.nil?
|
303
|
-
new_width = style.width - space_width - style.
|
332
|
+
new_width = style.width - space_width - style.border_y_width
|
304
333
|
if new_width < full_width
|
305
334
|
raise "Table width exceeds wanted width " +
|
306
335
|
"of #{style.width} characters."
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Terminal
|
2
|
+
class Table
|
3
|
+
module Util
|
4
|
+
# removes all ANSI escape sequences (e.g. color)
|
5
|
+
def ansi_escape(line)
|
6
|
+
line.to_s.gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, '').
|
7
|
+
gsub(/\x1b(\[|\(|\))[;?0-9]*[0-9A-Za-z]/, '').
|
8
|
+
gsub(/(\x03|\x1a)/, '')
|
9
|
+
end
|
10
|
+
module_function :ansi_escape
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/terminal-table.rb
CHANGED
@@ -21,6 +21,6 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
23
|
|
24
|
-
%w(cell row separator style table table_helper version).each do |file|
|
25
|
-
|
24
|
+
%w(cell row separator style table table_helper util version).each do |file|
|
25
|
+
require_relative "./terminal-table/#{file}"
|
26
26
|
end
|