Class: TableTennis::Stage::Render

Inherits:
Base
  • Object
show all
Defined in:
lib/table_tennis/stage/render.rb

Overview

This is the final stage of the rending pipeline - take our layout information, the table cells, and the painted styles to render the table as a string. This is the slowest part of rendering since there is a lot of ansi/string manipulation.

This is also where config.search is applied.

Constant Summary collapse

BOX =
[
  "╭─┬─╮", # 0
  "│ │ │", # 1
  "├─┼─┤", # 2
  "╰─┴─╯", # 3
]

Instance Attribute Summary

Attributes inherited from Base

#data

Instance Method Summary collapse

Methods inherited from Base

#initialize

Methods included from Util::Inspectable

#inspect

Constructor Details

This class inherits a constructor from TableTennis::Stage::Base

Instance Method Details

#paint(str, style) ⇒ Object

helpers



142
143
144
145
146
# File 'lib/table_tennis/stage/render.rb', line 142

def paint(str, style)
  # delegate painting to the theme, if color is enabled
  str = theme.paint(str, style) if config.color
  str
end

#pipeObject



148
# File 'lib/table_tennis/stage/render.rb', line 148

def pipe = paint(PIPE, :chrome)

#render_cell(value, r, c, default_cell_style) ⇒ Object



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
# File 'lib/table_tennis/stage/render.rb', line 72

def render_cell(value, r, c, default_cell_style)
  # calculate whitespace based on plaintext
  whitespace = columns[c].width - Util::Strings.width(value)

  # cell > column > default > cell (row styles are applied elsewhere)
  style = nil
  style ||= data.get_style(r:, c:)
  style ||= data.get_style(c:)
  style ||= default_cell_style
  style ||= :cell

  # add ansi codes for search
  value = search_cell(value) if search

  # add ansi codes for links
  if config.color && (link = data.links[[r, c]])
    value = theme.link(value, link)
  end

  # pad and paint
  if whitespace > 0
    spaces = " " * whitespace
    value = if columns[c].alignment == :left
      "#{value}#{spaces}"
    else
      "#{spaces}#{value}"
    end
  end
  paint(value, style)
end

#render_emptyObject



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/table_tennis/stage/render.rb', line 115

def render_empty
  title, body = config.title || "empty table", "no data"
  width = [title, body].map(&:length).max

  # helpers
  sep_row = ->(l, c, r) do
    paint("#{l}#{c * (width + 2)}#{r}", :chrome)
  end
  text_row = ->(str, style) do
    inner = paint(str.center(width), style)
    paint("#{pipe} #{inner} #{pipe}", Theme::BG)
  end

  # go
  [].tap do
    _1 << sep_row.call(NW, BAR, NE)
    _1 << text_row.call(title, data.get_style(r: :title) || :cell)
    _1 << sep_row.call(W, BAR, E)
    _1 << text_row.call(body, :chrome)
    _1 << sep_row.call(SW, BAR, SE)
  end.join("\n")
end

#render_row(r) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/table_tennis/stage/render.rb', line 57

def render_row(r)
  row_style = data.get_style(r:)

  # assemble line by rendering cells
  enum = (r != :header) ? rows[r].each : columns.map(&:header)
  joiner = config.separators ? " #{pipe} " : "  "
  line = enum.map.with_index do |value, c|
    render_cell(value, r, c, row_style)
  end.join(joiner)
  line = "#{pipe} #{line} #{pipe}"

  # afterward, apply row color
  paint(line, row_style || Theme::BG)
end

#render_separator(l, m, r) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
# File 'lib/table_tennis/stage/render.rb', line 103

def render_separator(l, m, r)
  m = "" if !config.separators
  line = [].tap do |buf|
    columns.each.with_index do |column, c|
      buf << ((c == 0) ? l : m)
      buf << (BAR * (column.width + 2))
    end
    buf << r
  end.join
  paint(paint(line, :chrome), Theme::BG)
end

#render_titleObject

render different parts of the table



49
50
51
52
53
54
55
# File 'lib/table_tennis/stage/render.rb', line 49

def render_title
  title_width = data.table_width - 4
  title = Util::Strings.truncate(config.title, title_width)
  title_style = data.get_style(r: :title) || :cell
  line = paint(Util::Strings.center(title, title_width), title_style || :cell)
  paint("#{pipe} #{line} #{pipe}", Theme::BG)
end

#run(io) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/table_tennis/stage/render.rb', line 25

def run(io)
  # edge case - empty
  if rows.empty? || columns.empty?
    io.puts render_empty
    return
  end

  if config.title
    io.puts render_separator(NW, BAR, NE)
    io.puts render_title
    io.puts render_separator(W, N, E) if config.separators
  else
    io.puts render_separator(NW, N, NE)
  end
  io.puts render_row(:header)
  io.puts render_separator(W, C, E) if config.separators
  rows.each_index { io.puts render_row(_1) }
  io.puts render_separator(SW, S, SE)
end

#searchObject



151
152
153
154
155
156
# File 'lib/table_tennis/stage/render.rb', line 151

def search
  case config.search
  when String then /#{Regexp.escape(config.search)}/i
  when Regexp then config.search
  end
end

#search_cell(value) ⇒ Object

add ansi codes for search



160
161
162
163
164
165
# File 'lib/table_tennis/stage/render.rb', line 160

def search_cell(value)
  return value if !value.match?(search)
  # edge case - we can't gsub a painted cell, it can mess up the escaping
  value = Util::Strings.unpaint(value)
  value.gsub(search) { paint(_1, :search) }
end