diff --git a/lib/thor/shell/basic.rb b/lib/thor/shell/basic.rb index 0708ca59..e20f132c 100644 --- a/lib/thor/shell/basic.rb +++ b/lib/thor/shell/basic.rb @@ -175,6 +175,7 @@ def print_in_columns(array) # ==== Options # indent:: Indent the first column by indent value. # colwidth:: Force the first column to colwidth spaces wide. + # borders:: Adds ascii borders. # def print_table(array, options = {}) # rubocop:disable Metrics/MethodLength printer = TablePrinter.new(stdout, options) diff --git a/lib/thor/shell/table_printer.rb b/lib/thor/shell/table_printer.rb index e1ab6fd1..53d349a0 100644 --- a/lib/thor/shell/table_printer.rb +++ b/lib/thor/shell/table_printer.rb @@ -4,12 +4,15 @@ 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) @@ -17,33 +20,33 @@ def print(array) 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 @@ -51,8 +54,11 @@ def prepare(array) 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 @@ -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 diff --git a/spec/shell/basic_spec.rb b/spec/shell/basic_spec.rb index f9f34c85..ee3889bf 100644 --- a/spec/shell/basic_spec.rb +++ b/spec/shell/basic_spec.rb @@ -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