summaryrefslogtreecommitdiff
path: root/lib/bundler/vendor/thor/lib/thor/shell/table_printer.rb
blob: 525f9ce5bbc504e7a65c894502009ce1ff781cd0 (plain)
1
2
3
4
5
6
7
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
require_relative "column_printer"
require_relative "terminal"

class Bundler::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|
            sentence << format_cell(column, row.size, index)
          end

          sentence = truncate(sentence)
          sentence << "|" if options[:borders]
          stdout.puts indentation + 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 options[:borders]
             "%-#{maxima}s".dup
          elsif index == colcount - 1
            # Don't output 2 trailing spaces when printing the last column
            "%-s".dup
          else
            "%-#{maxima + 2}s".dup
          end
        end

        @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
        separator = @maximas.map do |maxima|
          "+" + "-" * (maxima + 2 * @padding)
        end
        stdout.puts indentation + separator.join + "+"
      end

      def truncate(string)
        return string unless @truncate
        as_unicode do
          chars = string.chars.to_a
          if chars.length <= @truncate
            chars.join
          else
            chars[0, @truncate - 3 - @indent].join + "..."
          end
        end
      end

      def indentation
        " " * @indent
      end

      if "".respond_to?(:encode)
        def as_unicode
          yield
        end
      else
        def as_unicode
          old = $KCODE # rubocop:disable Style/GlobalVars
          $KCODE = "U" # rubocop:disable Style/GlobalVars
          yield
        ensure
          $KCODE = old # rubocop:disable Style/GlobalVars
        end
      end
    end
  end
end