Class: Railbow::Table::Renderer

Inherits:
Object
  • Object
show all
Includes:
Railbow::TextUtils
Defined in:
lib/railbow/table/renderer.rb

Constant Summary collapse

RESET =
"\e[0m"
WHITE =
"\e[97m"
GHOST_BG =

deep red/maroon background - stands out as abnormal

"\e[48;5;52m"
GHOST_FG =

warm pink foreground for contrast

"\e[38;5;217m"
DIMMED_FG =

muted grey - a row that is not currently in effect

"\e[38;5;242m"
MIN_LAST_COL =

The flexing last column never gets less than this from the terminal.

10
MIN_SHRINK_WIDTH =

Default floor for shrinkable columns that name no shrink_floor.

16

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Railbow::TextUtils

#display_width, #strip_ansi, #truncate_ansi, #truncate_str

Constructor Details

#initialize(columns:, theme:, compact: {}, aliases: {}, min_widths: nil, term_width: nil) ⇒ Renderer

min_widths raises the resolved width of each column to at least the given value, which is how several tables rendered in one run line their columns up with each other. An explicit compact maxw still wins.

term_width overrides the detected terminal width; render re-invokes itself through a reduced renderer when the budget drops a column, and the width it fits must be the same one this instance measured.



31
32
33
34
35
36
37
38
39
# File 'lib/railbow/table/renderer.rb', line 31

def initialize(columns:, theme:, compact: {}, aliases: {}, min_widths: nil, term_width: nil)
  @compact = compact
  @aliases = aliases
  @min_widths = min_widths
  @term_width = term_width
  @reverse_col_aliases = aliases[:columns]&.invert || {}
  @columns = apply_hidden_columns(columns)
  @theme = theme
end

Instance Attribute Details

#columnsObject (readonly)

Returns the value of attribute columns.



22
23
24
# File 'lib/railbow/table/renderer.rb', line 22

def columns
  @columns
end

#themeObject (readonly)

Returns the value of attribute theme.



22
23
24
# File 'lib/railbow/table/renderer.rb', line 22

def theme
  @theme
end

Instance Method Details

#column_widths(rows) ⇒ Object

The widths this table would resolve to on its own. Callers rendering several tables together take the per-column maximum and feed it back as min_widths.



44
45
46
47
48
# File 'lib/railbow/table/renderer.rb', line 44

def column_widths(rows)
  return [] if columns.empty?

  resolve_widths(prepare_rows(rows))
end

#fixed_width(widths) ⇒ Object

Width of everything left of the last column. The last column flexes to the terminal, so this is where furniture drawn around the table (a section rule, say) can stop without running past it.



53
54
55
56
57
# File 'lib/railbow/table/renderer.rb', line 53

def fixed_width(widths)
  return 0 if columns.empty? || widths.nil? || widths.empty?

  compute_prefix_width(widths, columns.size - 1)
end

#render(rows, separators: {}, highlight_rows: Set.new, ghost_rows: Set.new, dim_rows: Set.new, tick_rows: Set.new, tick_col: nil) ⇒ Object



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
# File 'lib/railbow/table/renderer.rb', line 59

def render(rows, separators: {}, highlight_rows: Set.new, ghost_rows: Set.new, dim_rows: Set.new, tick_rows: Set.new, tick_col: nil)
  return "" if columns.empty?

  rows = prepare_rows(rows)

  # Width budget: when even fully shrunk columns cannot bring the table
  # under the terminal width, sacrifice the most expendable column and
  # render without it. One column per pass - the reduced renderer asks
  # again with what is left.
  if (drop = drop_index_to_fit(rows))
    return dropped_renderer(drop).render(
      rows.map { |row| row.reject.with_index { |_, i| i == drop } },
      separators: separators, highlight_rows: highlight_rows, ghost_rows: ghost_rows,
      dim_rows: dim_rows, tick_rows: tick_rows, tick_col: shift_tick_col(tick_col, drop)
    )
  end

  resolved = resolve_widths(rows)
  shrink_to_fit!(resolved, rows)

  # In oneline mode, truncate non-sticky non-last columns at resolved width
  if @compact[:oneline]
    rows = rows.map { |row|
      row.each_with_index.map { |cell, i|
        col = columns[i]
        if col && i < columns.size - 1 && !col.sticky && !col.truncate
          truncate_str(cell.to_s, resolved[i])
        else
          cell
        end
      }
    }
  end
  lines = []
  lines << render_header(resolved) unless @compact[:noheader]
  rows.each_with_index do |row, i|
    tc = (tick_rows.include?(i) && tick_col) ? tick_col : nil
    if separators.key?(i) && theme.format_separator
      lines << render_separator_row(separators[i], resolved, tick_col: tc)
      tc = nil # tick already shown on separator row
    end
    formatted = render_row(row, resolved, tick_col: tc, highlight: highlight_rows.include?(i),
      ghost: ghost_rows.include?(i), dim: dim_rows.include?(i))
    lines << formatted
  end
  lines.join("\n")
end