Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for printing tables with borders #855

Merged
merged 1 commit into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/thor/shell/basic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ def print_in_columns(array)
# ==== Options
# indent<Integer>:: Indent the first column by indent value.
# colwidth<Integer>:: Force the first column to colwidth spaces wide.
# borders<Boolean>:: Adds ascii borders.
#
def print_table(array, options = {}) # rubocop:disable Metrics/MethodLength
printer = TablePrinter.new(stdout, options)
Expand Down
65 changes: 51 additions & 14 deletions lib/thor/shell/table_printer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,61 @@
class Thor
module Shell
class TablePrinter < ColumnPrinter
BORDER_SEPARATOR = :separator

def initialize(stdout, options = {})
super
@formats = []
@maximas = []
@colwidth = options[:colwidth]
@truncate = options[:truncate] == true ? Terminal.terminal_width : options[:truncate]
@padding = 1
end

def print(array)
return if array.empty?

prepare(array)

print_border_separator if options[:borders]

array.each do |row|
if options[:borders] && row == BORDER_SEPARATOR
print_border_separator
next
end

sentence = "".dup

row.each_with_index do |column, index|
maxima = @maximas[index]

f = if column.is_a?(Numeric)
if index == row.size - 1
# Don't output 2 trailing spaces when printing the last column
"%#{maxima}s"
else
"%#{maxima}s "
end
else
@formats[index]
end
sentence << f % column.to_s
sentence << format_cell(column, row.size, index)
end

sentence = truncate(sentence)
sentence << "|" if options[:borders]
stdout.puts sentence

end
print_border_separator if options[:borders]
end

private

def prepare(array)
array = array.reject{|row| row == BORDER_SEPARATOR }

@formats << "%-#{@colwidth + 2}s".dup if @colwidth
start = @colwidth ? 1 : 0

colcount = array.max { |a, b| a.size <=> b.size }.size

start.upto(colcount - 1) do |index|
maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max

@maximas << maxima
@formats << if index == colcount - 1
@formats << if options[:borders]
"%-#{maxima}s".dup
elsif index == colcount - 1
# Don't output 2 trailing spaces when printing the last column
"%-s".dup
else
Expand All @@ -64,6 +70,37 @@ def prepare(array)
@formats << "%s"
end

def format_cell(column, row_size, index)
maxima = @maximas[index]

f = if column.is_a?(Numeric)
if options[:borders]
# With borders we handle padding separately
"%#{maxima}s"
elsif index == row_size - 1
# Don't output 2 trailing spaces when printing the last column
"%#{maxima}s"
else
"%#{maxima}s "
end
else
@formats[index]
end

cell = "".dup
cell << "|" + " " * @padding if options[:borders]
cell << f % column.to_s
cell << " " * @padding if options[:borders]
cell
end

def print_border_separator
top = @maximas.map do |maxima|
" " * @indent + "+" + "-" * (maxima + 2 * @padding)
end
stdout.puts top.join + "+"
end

def truncate(string)
return string unless @truncate
as_unicode do
Expand Down
38 changes: 38 additions & 0 deletions spec/shell/basic_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,44 @@ def #456 Lanç...
Erik 1234567890123 green
TABLE
end

it "prints a table with borders" do
content = capture(:stdout) { shell.print_table(@table, borders: true) }
expect(content).to eq(<<-TABLE)
+-----+------+-------------+
| abc | #123 | first three |
| | #0 | empty |
| xyz | #786 | last three |
+-----+------+-------------+
TABLE
end

it "prints a table with borders and separators" do
@table.insert(1, :separator)
content = capture(:stdout) { shell.print_table(@table, borders: true) }
expect(content).to eq(<<-TABLE)
+-----+------+-------------+
| abc | #123 | first three |
+-----+------+-------------+
| | #0 | empty |
| xyz | #786 | last three |
+-----+------+-------------+
TABLE
end

it "prints a table with borders and small numbers and right-aligns them" do
table = [
["Name", "Number", "Color"], # rubocop: disable Style/WordArray
["Erik", 1, "green"]
]
content = capture(:stdout) { shell.print_table(table, borders: true) }
expect(content).to eq(<<-TABLE)
+------+--------+-------+
| Name | Number | Color |
| Erik | 1 | green |
+------+--------+-------+
TABLE
end
end

describe "#file_collision" do
Expand Down
Loading