table_tennis 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +4 -2
- data/Gemfile +3 -1
- data/README.md +27 -2
- data/Rakefile +2 -0
- data/demo.tape +23 -0
- data/justfile +42 -50
- data/lib/table_tennis/config.rb +2 -0
- data/lib/table_tennis/stage/format.rb +23 -6
- data/lib/table_tennis/stage/layout.rb +22 -18
- data/lib/table_tennis/stage/painter.rb +15 -2
- data/lib/table_tennis/stage/render.rb +10 -3
- data/lib/table_tennis/table_data.rb +25 -11
- data/lib/table_tennis/theme.rb +33 -4
- data/lib/table_tennis/util/colors.rb +8 -0
- data/lib/table_tennis/util/strings.rb +6 -0
- data/lib/table_tennis/version.rb +1 -1
- data/screenshots/dark.png +0 -0
- data/screenshots/droids.png +0 -0
- data/screenshots/hope.png +0 -0
- data/screenshots/light.png +0 -0
- data/screenshots/link.png +0 -0
- data/screenshots/row_numbers.png +0 -0
- data/screenshots/scales.png +0 -0
- data/screenshots/themes.png +0 -0
- data/table_tennis.gemspec +2 -1
- metadata +7 -5
- data/Gemfile.lock +0 -122
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '009a0c9bbb140d308d55441602f96a534e51d989259c70b2e3cc2661213252cb'
|
4
|
+
data.tar.gz: 6422661f90e1e6e15ddb67c0fff92d6d1c1f3d3454e95a57ae79652d83b981ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af82ae82579222df273e5244c2dde9137547cce527b4c5e4b900feeb7129beb7d9b5d9ea80ae849f62ac79befd14c38be0f8e8c311a651f8dc63660b1953190a
|
7
|
+
data.tar.gz: 7eae197b20fe45f0b65f9987097e38233dac3ea113fcabbfd27011340a18910f9c8d701a3352cb482819f266391e98a0daaeec0857e966982e6d6ffcf2aa2121
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -3,6 +3,8 @@ gemspec
|
|
3
3
|
|
4
4
|
group :development, :test do
|
5
5
|
gem "amazing_print"
|
6
|
+
gem "image_optim"
|
7
|
+
gem "image_optim_pack"
|
6
8
|
gem "minitest"
|
7
9
|
gem "minitest-hooks"
|
8
10
|
gem "mocha"
|
@@ -10,5 +12,5 @@ group :development, :test do
|
|
10
12
|
gem "pry"
|
11
13
|
gem "rake"
|
12
14
|
gem "simplecov", require: false
|
13
|
-
gem "standard", require: false
|
15
|
+
gem "standard", require: false, platform: :mri
|
14
16
|
end
|
data/README.md
CHANGED
@@ -29,6 +29,7 @@ gem "table_tennis"
|
|
29
29
|
- auto-layout to fit your terminal window
|
30
30
|
- auto-format floats and dates
|
31
31
|
- auto-color numeric columns
|
32
|
+
- auto-link markdown links
|
32
33
|
- titles, row numbers, zebra stripes...
|
33
34
|
|
34
35
|
### Themes
|
@@ -42,10 +43,10 @@ TableTennis examines the background color of your terminal to pick either the da
|
|
42
43
|
Construct your table with an array of rows. Rows are hashes, ActiveRecord objects, structs, Data records, or anything that responds to `to_h`. It also supports oddballs like arrays (as rows) or even a single hash.
|
43
44
|
|
44
45
|
```ruby
|
45
|
-
puts TableTennis.new([{a: "hello", b: "world"}, {a: "foo", b: "bar"})
|
46
|
+
puts TableTennis.new([{a: "hello", b: "world"}, {a: "foo", b: "bar"}])
|
46
47
|
puts TableTennis.new(Recipe.all.to_a) # activerecord
|
47
48
|
puts TableTennis.new(array_of_structs) # these use to_h
|
48
|
-
puts TableTennis.new([[1,2],[3,4]]
|
49
|
+
puts TableTennis.new([[1,2],[3,4]]) # array of arrays
|
49
50
|
puts TableTennis.new(authors[0]) # single hash
|
50
51
|
```
|
51
52
|
|
@@ -79,6 +80,7 @@ options = {
|
|
79
80
|
| `row_numbers` | `false` | Show row numbers in the table. |
|
80
81
|
| `save` | ─ | If you set this to a file path, TableTennis will save your table as a CSV file too. Useful if you want to do something else with the data. |
|
81
82
|
| `search` | ─ | string/regex to highlight in output |
|
83
|
+
| `separators` | `true` | Include column and header separators in output. |
|
82
84
|
| `strftime` | see → | strftime string for formatting Date/Time objects. The default is `"%Y-%m-%d"`, which looks like `2025-04-21` |
|
83
85
|
| `theme` | nil | When unset, will be autodetected based on terminal background color. If autodetect fails the theme defaults to :dark. You can also manually specify `:dark`, `:light` or `:ansi`. If colors are turned off this setting has no effect.|
|
84
86
|
| `title` | ─ | Add a title line to the table. |
|
@@ -113,6 +115,16 @@ puts TableTennis.new(rows, row_numbers: true, zebra: true)
|
|
113
115
|
| - | - | - |
|
114
116
|
|  |  |  |
|
115
117
|
|
118
|
+
### Links
|
119
|
+
|
120
|
+
Modern terminals support **hyperlinks** in the text stream. If your table includes markdown-style links, TableTennis will use [OSC 8](https://github.com/Alhadis/OSC8-Adoption) to render them. For example:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
puts TableTennis.new([{site: "[search](https://google.com)"}])
|
124
|
+
```
|
125
|
+
|
126
|
+

|
127
|
+
|
116
128
|
### Advanced Usage
|
117
129
|
|
118
130
|
TableTennis can be configured a few different ways:
|
@@ -149,8 +161,21 @@ We love CSV tools and use them all the time! Here are a few that we rely on:
|
|
149
161
|
- [Terminal::Table](https://github.com/tj/terminal-table) - wonderful rubygem for pretty printing tables, great for non-hash data like confusion matrices
|
150
162
|
- [visidata](https://www.visidata.org) - the best for poking around large files, it does everything
|
151
163
|
|
164
|
+
### Changelog
|
165
|
+
|
166
|
+
#### 0.0.5 (April '25)
|
167
|
+
|
168
|
+
- Support for markdown style links in the cells
|
169
|
+
|
170
|
+
#### 0.0.4 (April '25)
|
171
|
+
|
172
|
+
- Separators can be turned off with `separators: false`.
|
173
|
+
- Headers are in color now. Tweaked a few of the dark colors as well.
|
174
|
+
- Tricky math to better protect narrow columns with auto-layout.
|
175
|
+
|
152
176
|
### Special Thanks
|
153
177
|
|
154
178
|
- [termbg](https://github.com/dalance/termbg) and [termenv](https://github.com/muesli/termenv) for showing how to safely detect the terminal background color. These libraries are widely used for Rust/Go, but as far as I know nothing similar exists for Ruby.
|
155
179
|
- The [Paint gem](https://github.com/janlelis/paint) for help with ansi colors.
|
180
|
+
- I copied the header color themes from [tabiew](https://github.com/shshemi/tabiew). Great project!
|
156
181
|
- Google Sheets for providing nice color scales
|
data/Rakefile
CHANGED
data/demo.tape
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#
|
2
|
+
# script for vhs (https://github.com/charmbracelet/vhs)
|
3
|
+
#
|
4
|
+
|
5
|
+
# vhs settings
|
6
|
+
Output "/tmp/demo.gif"
|
7
|
+
Set FontFamily Menlo
|
8
|
+
Set FontSize 28
|
9
|
+
Set Width 1520
|
10
|
+
Set Height 1100
|
11
|
+
Set Margin 0
|
12
|
+
Set Padding 0
|
13
|
+
|
14
|
+
# fire up the app
|
15
|
+
Hide
|
16
|
+
Type@1ms "tennis --clear --demo 3 --title 'Star Wars People' --zebra --columns name,height,homeworld,species,films --color-scale height"
|
17
|
+
Enter
|
18
|
+
Wait+Screen /Star/
|
19
|
+
Show
|
20
|
+
|
21
|
+
# now take the screenshot
|
22
|
+
Sleep 0.5
|
23
|
+
Screenshot "/tmp/dark.png"
|
data/justfile
CHANGED
@@ -1,79 +1,71 @@
|
|
1
|
+
default: test
|
1
2
|
|
2
|
-
#
|
3
|
-
|
4
|
-
|
5
|
-
#
|
6
|
-
# dev
|
7
|
-
#
|
3
|
+
# check repo - lint & test
|
4
|
+
check: lint test
|
8
5
|
|
9
|
-
|
10
|
-
|
6
|
+
# for ci. don't bother linting on windows
|
7
|
+
ci:
|
8
|
+
@if [[ "{{os()}}" != "windows" ]]; then just lint ; fi
|
9
|
+
@just test
|
11
10
|
|
12
|
-
check
|
11
|
+
# check test coverage
|
12
|
+
coverage:
|
13
|
+
COVERAGE=1 just test
|
14
|
+
open /tmp/coverage/index.html
|
13
15
|
|
14
|
-
|
16
|
+
# format with rubocop
|
17
|
+
format: (lint "-a")
|
15
18
|
|
19
|
+
gem-local:
|
20
|
+
@just _banner rake install:local...
|
21
|
+
bundle exec rake install:local
|
16
22
|
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
# this will tag, build and push to rubygems
|
24
|
+
gem-push: check
|
25
|
+
@just _banner rake release...
|
26
|
+
rake release
|
20
27
|
|
28
|
+
# optimize images
|
21
29
|
image_optim:
|
22
|
-
|
30
|
+
@# advpng/pngout are slow. consider --verbose as well
|
31
|
+
@bundle exec image_optim --allow-lossy --svgo-precision=1 --no-advpng --no-pngout -r .
|
23
32
|
|
24
|
-
lint
|
25
|
-
|
26
|
-
|
33
|
+
# lint with rubocop
|
34
|
+
lint *ARGS:
|
35
|
+
@just _banner lint...
|
36
|
+
bundle exec rubocop {{ARGS}}
|
27
37
|
|
38
|
+
# start pry with the lib loaded
|
28
39
|
pry:
|
29
40
|
bundle exec pry -I lib -r table_tennis.rb
|
30
41
|
|
42
|
+
# run tennis repeatedly
|
31
43
|
tennis-watch *ARGS:
|
32
44
|
@watchexec --stop-timeout=0 --clear clear tennis {{ARGS}}
|
33
45
|
|
46
|
+
# run tests
|
34
47
|
test *ARGS:
|
35
|
-
@just
|
48
|
+
@just _banner rake test {{ARGS}}
|
36
49
|
@bundle exec rake test {{ARGS}}
|
37
50
|
|
51
|
+
# run tests repeatedly
|
38
52
|
test-watch *ARGS:
|
39
|
-
|
53
|
+
watchexec --stop-timeout=0 --clear clear just test "{{ARGS}}"
|
40
54
|
|
41
|
-
#
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
COVERAGE=1 just test
|
47
|
-
open /tmp/coverage/index.html
|
48
|
-
|
49
|
-
#
|
50
|
-
# gem tasks
|
51
|
-
#
|
52
|
-
|
53
|
-
# you can test locally from another project by dropping gem file into vendor/cache
|
54
|
-
gem-push:
|
55
|
-
@just banner bundle install and verify git...
|
56
|
-
bundle install
|
57
|
-
#@just check-git-status
|
58
|
-
@just banner gem build...
|
59
|
-
gem build table_tennis.gemspec
|
60
|
-
@just banner tag...
|
61
|
-
git tag -a "v{{gemver}}" -m "Tagging {{gemver}}"
|
62
|
-
git push --tags
|
63
|
-
@just banner gem push...
|
64
|
-
gem push "table_tennis-{{gemver}}.gem"
|
55
|
+
# create sceenshot using vhs
|
56
|
+
vhs:
|
57
|
+
@just _banner "running vhs..."
|
58
|
+
vhs demo.tape
|
59
|
+
magick /tmp/dark.png -crop 1448x1004+18+16 screenshots/dark.png
|
65
60
|
|
66
61
|
#
|
67
62
|
# util
|
68
63
|
#
|
69
64
|
|
70
|
-
|
71
|
-
|
72
|
-
|
65
|
+
_banner *ARGS: (_message BG_GREEN ARGS)
|
66
|
+
_warning *ARGS: (_message BG_YELLOW ARGS)
|
67
|
+
_fatal *ARGS: (_message BG_RED ARGS)
|
73
68
|
@exit 1
|
74
|
-
|
69
|
+
_message color *ARGS:
|
75
70
|
@msg=$(printf "[%s] %s" $(date +%H:%M:%S) "{{ARGS}}") ; \
|
76
71
|
printf "{{color+BOLD+WHITE}}%-72s{{ NORMAL }}\n" "$msg"
|
77
|
-
|
78
|
-
check-git-status:
|
79
|
-
@if [ ! -z "$(git status --porcelain)" ]; then just fatal "git status is dirty, bailing."; fi
|
data/lib/table_tennis/config.rb
CHANGED
@@ -18,6 +18,7 @@ module TableTennis
|
|
18
18
|
row_numbers: false, # show line numbers?
|
19
19
|
save: nil, # csv file path to save the table when created
|
20
20
|
search: nil, # string/regex to highlight in output
|
21
|
+
separators: true, # if true, show separators between columns
|
21
22
|
strftime: nil, # string for formatting dates
|
22
23
|
theme: nil, # :dark, :light or :ansi. :dark is the default
|
23
24
|
title: nil, # string for table title, if any
|
@@ -51,6 +52,7 @@ module TableTennis
|
|
51
52
|
placeholder: :str,
|
52
53
|
row_numbers: :bool,
|
53
54
|
save: :str,
|
55
|
+
separators: :bool,
|
54
56
|
strftime: :str,
|
55
57
|
title: :str,
|
56
58
|
titleize: :bool,
|
@@ -16,14 +16,23 @@ module TableTennis
|
|
16
16
|
fn || :fn_default
|
17
17
|
end
|
18
18
|
|
19
|
-
rows.each do |row|
|
20
|
-
row.each_index do
|
21
|
-
value = row[
|
19
|
+
rows.each.with_index do |row, r|
|
20
|
+
row.each_index do |c|
|
21
|
+
value = row[c]
|
22
22
|
# Try to format using the column fn. This can return nil. For
|
23
23
|
# example, a float column and value is nil, not a float, etc.
|
24
|
-
|
24
|
+
str = send(fns[c], value)
|
25
|
+
|
25
26
|
# If the column formatter failed, use the default formatter
|
26
|
-
|
27
|
+
str ||= fn_default(value) || config.placeholder
|
28
|
+
|
29
|
+
# look for markdown-style links
|
30
|
+
if (link = detect_link(str))
|
31
|
+
str, data.links[[r, c]] = link
|
32
|
+
end
|
33
|
+
|
34
|
+
# done
|
35
|
+
row[c] = str
|
27
36
|
end
|
28
37
|
end
|
29
38
|
end
|
@@ -61,7 +70,7 @@ module TableTennis
|
|
61
70
|
# default formatting. cleanup whitespace
|
62
71
|
def fn_default(value)
|
63
72
|
return if value.nil?
|
64
|
-
str =
|
73
|
+
str = value.is_a?(String) ? value : value.to_s
|
65
74
|
str = str.strip.gsub("\n", "\\n").gsub("\r", "\\r") if str.match?(/\s/)
|
66
75
|
return if str.empty?
|
67
76
|
str
|
@@ -91,6 +100,14 @@ module TableTennis
|
|
91
100
|
x
|
92
101
|
end
|
93
102
|
|
103
|
+
def detect_link(str)
|
104
|
+
# fail fast, for speed
|
105
|
+
return unless str.length >= 6 && str[0] == "["
|
106
|
+
if str =~ /^\[([^\]]+)\]\(([^\)]+)\)$/
|
107
|
+
[$1, $2]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
94
111
|
# str to_xxx that are resistant to commas
|
95
112
|
def to_f(str) = str.delete(",").to_f
|
96
113
|
def to_i(str) = str.delete(",").to_i
|
@@ -28,8 +28,7 @@ module TableTennis
|
|
28
28
|
# some math
|
29
29
|
#
|
30
30
|
|
31
|
-
|
32
|
-
AUTOLAYOUT_FUDGE = 2
|
31
|
+
FUDGE = 2
|
33
32
|
|
34
33
|
# Fit columns into terminal width. This is copied from the very simple HTML
|
35
34
|
# table column algorithm. Returns a hash of column name to width.
|
@@ -37,30 +36,35 @@ module TableTennis
|
|
37
36
|
# set provisional widths
|
38
37
|
columns.each { _1.width = _1.measure }
|
39
38
|
|
40
|
-
#
|
39
|
+
# How much space is available, and do we already fit?
|
41
40
|
screen_width = IO.console.winsize[1]
|
42
|
-
available = screen_width - chrome_width -
|
41
|
+
available = screen_width - chrome_width - FUDGE
|
43
42
|
return if available >= data_width
|
44
43
|
|
45
|
-
#
|
46
|
-
|
47
|
-
|
44
|
+
# We don't fit, so we are going to shrink (truncate) some columns.
|
45
|
+
# Potentially all the way down to a lower bound. But what is the lower
|
46
|
+
# bound? It's nice to have a generous value so that narrow columns have
|
47
|
+
# a shot at avoiding truncation. That isn't always possible, though.
|
48
|
+
lower_bound = (available / columns.length).clamp(2, 10)
|
48
49
|
|
49
|
-
#
|
50
|
-
#
|
51
|
-
|
52
|
-
|
50
|
+
# Calculate a "min" and a "max" for each column, then allocate available
|
51
|
+
# space proportionally to each column. This is similar to the algorithm
|
52
|
+
# for HTML tables.
|
53
|
+
min = max = columns.map(&:width)
|
54
|
+
min = min.map { [_1, lower_bound].min }
|
53
55
|
|
54
|
-
#
|
55
|
-
|
56
|
+
# W = difference between the available space and the minimum table width
|
57
|
+
# D = difference between maximum and minimum table width
|
58
|
+
# ratio = W / D
|
59
|
+
# col.width = col.min + ((col.max - col.min) * ratio)
|
60
|
+
ratio = (available - min.sum).to_f / (max.sum - min.sum)
|
61
|
+
if ratio <= 0
|
62
|
+
# even min doesn't fit, we gotta overflow
|
56
63
|
columns.each.with_index { _1.width = min[_2] }
|
57
64
|
return
|
58
65
|
end
|
59
|
-
|
60
|
-
|
61
|
-
columns.each.with_index do
|
62
|
-
# width = min + (delta * W / D)
|
63
|
-
_1.width = min[_2] + ((max[_2] - min[_2]) * w / d.to_f).to_i
|
66
|
+
columns.zip(min, max).each do |column, min, max|
|
67
|
+
column.width = min + ((max - min) * ratio).to_i
|
64
68
|
end
|
65
69
|
|
66
70
|
# because we always round down, there might be some extra space to distribute
|
@@ -16,6 +16,7 @@ module TableTennis
|
|
16
16
|
def run
|
17
17
|
return if !config.color
|
18
18
|
paint_title if config.title
|
19
|
+
paint_headers
|
19
20
|
paint_row_numbers if config.row_numbers
|
20
21
|
paint_rows if config.mark || config.zebra
|
21
22
|
paint_columns if config.color_scales
|
@@ -32,6 +33,14 @@ module TableTennis
|
|
32
33
|
set_style(r: :title, style: :title)
|
33
34
|
end
|
34
35
|
|
36
|
+
NHEADER_COLORS = 6
|
37
|
+
|
38
|
+
def paint_headers
|
39
|
+
columns.each.with_index do |column, c|
|
40
|
+
set_style(r: :header, c:, style: :"header#{c % Theme::NHEADER_COLORS}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
35
44
|
def paint_row_numbers
|
36
45
|
set_style(c: 0, style: :chrome)
|
37
46
|
end
|
@@ -127,8 +136,12 @@ module TableTennis
|
|
127
136
|
|
128
137
|
def mark_style(user_mark)
|
129
138
|
case user_mark
|
130
|
-
when String, Symbol
|
131
|
-
|
139
|
+
when String, Symbol
|
140
|
+
# pick a nice fg color
|
141
|
+
[Util::Colors.contrast(user_mark), user_mark]
|
142
|
+
when Array
|
143
|
+
# a Paint array
|
144
|
+
user_mark
|
132
145
|
else; :mark # default
|
133
146
|
end
|
134
147
|
end
|
@@ -32,12 +32,12 @@ module TableTennis
|
|
32
32
|
if config.title
|
33
33
|
io.puts render_separator(NW, BAR, NE)
|
34
34
|
io.puts render_title
|
35
|
-
io.puts render_separator(W, N, E)
|
35
|
+
io.puts render_separator(W, N, E) if config.separators
|
36
36
|
else
|
37
37
|
io.puts render_separator(NW, N, NE)
|
38
38
|
end
|
39
39
|
io.puts render_row(:header)
|
40
|
-
io.puts render_separator(W, C, E)
|
40
|
+
io.puts render_separator(W, C, E) if config.separators
|
41
41
|
rows.each_index { io.puts render_row(_1) }
|
42
42
|
io.puts render_separator(SW, S, SE)
|
43
43
|
end
|
@@ -59,9 +59,10 @@ module TableTennis
|
|
59
59
|
|
60
60
|
# assemble line by rendering cells
|
61
61
|
enum = (r != :header) ? rows[r].each : columns.map(&:header)
|
62
|
+
joiner = config.separators ? " #{pipe} " : " "
|
62
63
|
line = enum.map.with_index do |value, c|
|
63
64
|
render_cell(value, r, c, row_style)
|
64
|
-
end.join(
|
65
|
+
end.join(joiner)
|
65
66
|
line = "#{pipe} #{line} #{pipe}"
|
66
67
|
|
67
68
|
# afterward, apply row color
|
@@ -82,6 +83,11 @@ module TableTennis
|
|
82
83
|
# add ansi codes for search
|
83
84
|
value = value.gsub(search) { paint(_1, :search) } if search
|
84
85
|
|
86
|
+
# add ansi codes for links
|
87
|
+
if config.color && (link = data.links[[r, c]])
|
88
|
+
value = theme.link(value, link)
|
89
|
+
end
|
90
|
+
|
85
91
|
# pad and paint
|
86
92
|
if whitespace > 0
|
87
93
|
spaces = " " * whitespace
|
@@ -95,6 +101,7 @@ module TableTennis
|
|
95
101
|
end
|
96
102
|
|
97
103
|
def render_separator(l, m, r)
|
104
|
+
m = "" if !config.separators
|
98
105
|
line = [].tap do |buf|
|
99
106
|
columns.each.with_index do |column, c|
|
100
107
|
buf << ((c == 0) ? l : m)
|
@@ -21,7 +21,7 @@ module TableTennis
|
|
21
21
|
prepend MemoWise
|
22
22
|
include Util::Inspectable
|
23
23
|
|
24
|
-
attr_accessor :config, :input_rows, :styles
|
24
|
+
attr_accessor :config, :input_rows, :links, :styles
|
25
25
|
|
26
26
|
def initialize(rows:, config: nil)
|
27
27
|
@config, @input_rows = config, rows
|
@@ -38,15 +38,20 @@ module TableTennis
|
|
38
38
|
raise ArgumentError, "input_rows must be an array of hash-like objects, not #{input_rows.class}"
|
39
39
|
end
|
40
40
|
|
41
|
+
@links = {}
|
41
42
|
@styles = {}
|
42
43
|
end
|
43
44
|
|
44
45
|
# Lazily calculate the list of columns.
|
45
46
|
def columns
|
46
47
|
names = config&.columns
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
if !fat_rows.empty?
|
49
|
+
names ||= {}.tap do |memo|
|
50
|
+
fat_rows.each { |row| row.each_key { memo[_1] = 1 } }
|
51
|
+
end.keys
|
52
|
+
else
|
53
|
+
names = []
|
54
|
+
end
|
50
55
|
names.each do |name|
|
51
56
|
if !fat_rows.any? { _1.key?(name) }
|
52
57
|
raise ArgumentError, "specified column `#{name}` not found in any row of input data"
|
@@ -88,13 +93,22 @@ module TableTennis
|
|
88
93
|
|
89
94
|
# layout math
|
90
95
|
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
|
96
|
+
# with separators
|
97
|
+
# |•xxxx•|•xxxx•|•xxxx•|•xxxx•|•xxxx•|•xxxx•|•xxxx•|•xxxx•|
|
98
|
+
# ↑↑ ↑ ↑
|
99
|
+
# 12 3 <- three chrome chars per column │
|
100
|
+
# │
|
101
|
+
# extra chrome char at the end
|
102
|
+
# without
|
103
|
+
# |•xxxx••xxxx••xxxx••xxxx••xxxx••xxxx••xxxx••xxxx•|
|
104
|
+
# ↑ ↑ ↑
|
105
|
+
# 1 2 <- two chrome chars per column │
|
106
|
+
# │
|
107
|
+
# extra chrome char at beginning and end
|
108
|
+
def chrome_width
|
109
|
+
config.separators ? (columns.length * 3 + 1) : (columns.length * 2 + 2)
|
110
|
+
end
|
111
|
+
memo_wise :chrome_width
|
98
112
|
|
99
113
|
# for debugging
|
100
114
|
def debug(str)
|
data/lib/table_tennis/theme.rb
CHANGED
@@ -5,27 +5,49 @@ module TableTennis
|
|
5
5
|
prepend MemoWise
|
6
6
|
|
7
7
|
RESET = Paint::NOTHING
|
8
|
+
OSC_8 = "\e]8;;"
|
9
|
+
ST = "\e\\"
|
10
|
+
|
11
|
+
NHEADER_COLORS = 6
|
8
12
|
THEMES = {
|
9
13
|
dark: {
|
10
|
-
title: "
|
14
|
+
title: ["blue-400", :bold],
|
11
15
|
chrome: "gray-500",
|
12
16
|
cell: "gray-200",
|
17
|
+
header0: ["#ff6188", :bold],
|
18
|
+
header1: ["#fc9867", :bold],
|
19
|
+
header2: ["#ffd866", :bold],
|
20
|
+
header3: ["#a9dc76", :bold],
|
21
|
+
header4: ["#78dce8", :bold],
|
22
|
+
header5: ["#ab9df2", :bold],
|
13
23
|
mark: %w[white blue-500],
|
14
24
|
search: %w[black yellow-300],
|
15
|
-
zebra: %w[white #
|
25
|
+
zebra: %w[white #222],
|
16
26
|
},
|
17
27
|
light: {
|
18
|
-
title: "blue-600",
|
28
|
+
title: ["blue-600", :bold],
|
19
29
|
chrome: "#bbb",
|
20
30
|
cell: "gray-800",
|
31
|
+
header0: ["#ee4066", :bold],
|
32
|
+
header1: ["#da7645", :bold],
|
33
|
+
header2: ["#ddb644", :bold],
|
34
|
+
header3: ["#87ba54", :bold],
|
35
|
+
header4: ["#56bac6", :bold],
|
36
|
+
header5: ["#897bd0", :bold],
|
21
37
|
mark: %w[white blue-500],
|
22
38
|
search: %w[black yellow-300],
|
23
39
|
zebra: %w[black gray-200],
|
24
40
|
},
|
25
41
|
ansi: {
|
26
|
-
title:
|
42
|
+
title: %i[green bold],
|
27
43
|
chrome: %i[faint default],
|
28
44
|
cell: :default,
|
45
|
+
header0: nil, # not supported
|
46
|
+
header1: nil, # not supported
|
47
|
+
header2: nil, # not supported
|
48
|
+
header3: nil, # not supported
|
49
|
+
header4: nil, # not supported
|
50
|
+
header5: nil, # not supported
|
29
51
|
mark: %i[white blue],
|
30
52
|
search: %i[white magenta],
|
31
53
|
zebra: nil, # not supported
|
@@ -50,6 +72,7 @@ module TableTennis
|
|
50
72
|
if value.is_a?(Symbol) && THEME_KEYS.include?(value)
|
51
73
|
value = THEMES[name][value]
|
52
74
|
end
|
75
|
+
|
53
76
|
# turn value(s) into colors
|
54
77
|
colors = Array(value).map { Util::Colors.get(_1) }
|
55
78
|
return if colors == [] || colors == [nil]
|
@@ -75,6 +98,12 @@ module TableTennis
|
|
75
98
|
end
|
76
99
|
memo_wise :paint
|
77
100
|
|
101
|
+
# use osc 8 to create a terminal hyperlink. underline too
|
102
|
+
def link(str, link)
|
103
|
+
linked = "#{OSC_8}#{link}#{ST}#{str}#{OSC_8}#{ST}"
|
104
|
+
Paint[linked, :underline]
|
105
|
+
end
|
106
|
+
|
78
107
|
# for debugging, mostly
|
79
108
|
def self.info
|
80
109
|
sample = if !Config.detect_color?
|
@@ -439,6 +439,14 @@ module TableTennis
|
|
439
439
|
|
440
440
|
# is this color dark?
|
441
441
|
def dark?(color)
|
442
|
+
color = get(color)
|
443
|
+
return true if !color
|
444
|
+
|
445
|
+
# take a guess at symbols too
|
446
|
+
if color.is_a?(Symbol)
|
447
|
+
return !%i[white gray].include?(color)
|
448
|
+
end
|
449
|
+
|
442
450
|
luma = luma(color)
|
443
451
|
!luma || luma < DARK_LUMA
|
444
452
|
end
|
@@ -22,6 +22,12 @@ module TableTennis
|
|
22
22
|
simple?(text) ? text.length : Unicode::DisplayWidth.of(text)
|
23
23
|
end
|
24
24
|
|
25
|
+
def hyperlink(value)
|
26
|
+
if value =~ /^\[([^\]]*)\]\(([^\)]*)\)$/
|
27
|
+
[$1, $2]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
25
31
|
# truncate a string based on the display width of the grapheme clusters.
|
26
32
|
# Should handle emojis and international characters
|
27
33
|
def truncate(text, stop)
|
data/lib/table_tennis/version.rb
CHANGED
data/screenshots/dark.png
CHANGED
Binary file
|
data/screenshots/droids.png
CHANGED
Binary file
|
data/screenshots/hope.png
CHANGED
Binary file
|
data/screenshots/light.png
CHANGED
Binary file
|
Binary file
|
data/screenshots/row_numbers.png
CHANGED
Binary file
|
data/screenshots/scales.png
CHANGED
Binary file
|
data/screenshots/themes.png
CHANGED
Binary file
|
data/table_tennis.gemspec
CHANGED
@@ -7,10 +7,11 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.email = "amd@gurge.com"
|
8
8
|
|
9
9
|
s.summary = "Stylish tables in your terminal."
|
10
|
-
s.homepage = "
|
10
|
+
s.homepage = "https://github.com/gurgeous/table_tennis"
|
11
11
|
s.license = "MIT"
|
12
12
|
s.required_ruby_version = ">= 3.0.0"
|
13
13
|
s.metadata = {
|
14
|
+
"homepage_uri" => s.homepage,
|
14
15
|
"rubygems_mfa_required" => "true",
|
15
16
|
"source_code_uri" => s.homepage,
|
16
17
|
}
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: table_tennis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Doppelt
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-04-
|
10
|
+
date: 2025-04-28 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: csv
|
@@ -88,10 +88,10 @@ files:
|
|
88
88
|
- ".gitignore"
|
89
89
|
- ".rubocop.yml"
|
90
90
|
- Gemfile
|
91
|
-
- Gemfile.lock
|
92
91
|
- LICENSE
|
93
92
|
- README.md
|
94
93
|
- Rakefile
|
94
|
+
- demo.tape
|
95
95
|
- justfile
|
96
96
|
- lib/table_tennis.rb
|
97
97
|
- lib/table_tennis/column.rb
|
@@ -116,16 +116,18 @@ files:
|
|
116
116
|
- screenshots/droids.png
|
117
117
|
- screenshots/hope.png
|
118
118
|
- screenshots/light.png
|
119
|
+
- screenshots/link.png
|
119
120
|
- screenshots/row_numbers.png
|
120
121
|
- screenshots/scales.png
|
121
122
|
- screenshots/themes.png
|
122
123
|
- table_tennis.gemspec
|
123
|
-
homepage:
|
124
|
+
homepage: https://github.com/gurgeous/table_tennis
|
124
125
|
licenses:
|
125
126
|
- MIT
|
126
127
|
metadata:
|
128
|
+
homepage_uri: https://github.com/gurgeous/table_tennis
|
127
129
|
rubygems_mfa_required: 'true'
|
128
|
-
source_code_uri:
|
130
|
+
source_code_uri: https://github.com/gurgeous/table_tennis
|
129
131
|
rdoc_options: []
|
130
132
|
require_paths:
|
131
133
|
- lib
|
data/Gemfile.lock
DELETED
@@ -1,122 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
table_tennis (0.0.3)
|
5
|
-
csv (~> 3.3)
|
6
|
-
ffi (~> 1.17)
|
7
|
-
memo_wise (~> 1.11)
|
8
|
-
paint (~> 2.3)
|
9
|
-
unicode-display_width (~> 3.1)
|
10
|
-
|
11
|
-
GEM
|
12
|
-
remote: https://rubygems.org/
|
13
|
-
specs:
|
14
|
-
amazing_print (1.7.2)
|
15
|
-
ast (2.4.3)
|
16
|
-
coderay (1.1.3)
|
17
|
-
csv (3.3.2)
|
18
|
-
docile (1.4.1)
|
19
|
-
ffi (1.17.1)
|
20
|
-
ffi (1.17.1-aarch64-linux-gnu)
|
21
|
-
ffi (1.17.1-aarch64-linux-musl)
|
22
|
-
ffi (1.17.1-arm-linux-gnu)
|
23
|
-
ffi (1.17.1-arm-linux-musl)
|
24
|
-
ffi (1.17.1-arm64-darwin)
|
25
|
-
ffi (1.17.1-x86-linux-gnu)
|
26
|
-
ffi (1.17.1-x86-linux-musl)
|
27
|
-
ffi (1.17.1-x86_64-darwin)
|
28
|
-
ffi (1.17.1-x86_64-linux-gnu)
|
29
|
-
ffi (1.17.1-x86_64-linux-musl)
|
30
|
-
json (2.10.2)
|
31
|
-
language_server-protocol (3.17.0.4)
|
32
|
-
lint_roller (1.1.0)
|
33
|
-
memo_wise (1.11.0)
|
34
|
-
method_source (1.1.0)
|
35
|
-
minitest (5.25.5)
|
36
|
-
minitest-hooks (1.5.2)
|
37
|
-
minitest (> 5.3)
|
38
|
-
mocha (2.7.1)
|
39
|
-
ruby2_keywords (>= 0.0.5)
|
40
|
-
ostruct (0.6.1)
|
41
|
-
paint (2.3.0)
|
42
|
-
parallel (1.26.3)
|
43
|
-
parser (3.3.7.4)
|
44
|
-
ast (~> 2.4.1)
|
45
|
-
racc
|
46
|
-
prism (1.4.0)
|
47
|
-
pry (0.15.2)
|
48
|
-
coderay (~> 1.1)
|
49
|
-
method_source (~> 1.0)
|
50
|
-
racc (1.8.1)
|
51
|
-
rainbow (3.1.1)
|
52
|
-
rake (13.2.1)
|
53
|
-
regexp_parser (2.10.0)
|
54
|
-
rubocop (1.73.2)
|
55
|
-
json (~> 2.3)
|
56
|
-
language_server-protocol (~> 3.17.0.2)
|
57
|
-
lint_roller (~> 1.1.0)
|
58
|
-
parallel (~> 1.10)
|
59
|
-
parser (>= 3.3.0.2)
|
60
|
-
rainbow (>= 2.2.2, < 4.0)
|
61
|
-
regexp_parser (>= 2.9.3, < 3.0)
|
62
|
-
rubocop-ast (>= 1.38.0, < 2.0)
|
63
|
-
ruby-progressbar (~> 1.7)
|
64
|
-
unicode-display_width (>= 2.4.0, < 4.0)
|
65
|
-
rubocop-ast (1.43.0)
|
66
|
-
parser (>= 3.3.7.2)
|
67
|
-
prism (~> 1.4)
|
68
|
-
rubocop-performance (1.24.0)
|
69
|
-
lint_roller (~> 1.1)
|
70
|
-
rubocop (>= 1.72.1, < 2.0)
|
71
|
-
rubocop-ast (>= 1.38.0, < 2.0)
|
72
|
-
ruby-progressbar (1.13.0)
|
73
|
-
ruby2_keywords (0.0.5)
|
74
|
-
simplecov (0.22.0)
|
75
|
-
docile (~> 1.1)
|
76
|
-
simplecov-html (~> 0.11)
|
77
|
-
simplecov_json_formatter (~> 0.1)
|
78
|
-
simplecov-html (0.13.1)
|
79
|
-
simplecov_json_formatter (0.1.4)
|
80
|
-
standard (1.47.0)
|
81
|
-
language_server-protocol (~> 3.17.0.2)
|
82
|
-
lint_roller (~> 1.0)
|
83
|
-
rubocop (~> 1.73.0)
|
84
|
-
standard-custom (~> 1.0.0)
|
85
|
-
standard-performance (~> 1.7)
|
86
|
-
standard-custom (1.0.2)
|
87
|
-
lint_roller (~> 1.0)
|
88
|
-
rubocop (~> 1.50)
|
89
|
-
standard-performance (1.7.0)
|
90
|
-
lint_roller (~> 1.1)
|
91
|
-
rubocop-performance (~> 1.24.0)
|
92
|
-
unicode-display_width (3.1.4)
|
93
|
-
unicode-emoji (~> 4.0, >= 4.0.4)
|
94
|
-
unicode-emoji (4.0.4)
|
95
|
-
|
96
|
-
PLATFORMS
|
97
|
-
aarch64-linux-gnu
|
98
|
-
aarch64-linux-musl
|
99
|
-
arm-linux-gnu
|
100
|
-
arm-linux-musl
|
101
|
-
arm64-darwin
|
102
|
-
ruby
|
103
|
-
x86-linux-gnu
|
104
|
-
x86-linux-musl
|
105
|
-
x86_64-darwin
|
106
|
-
x86_64-linux-gnu
|
107
|
-
x86_64-linux-musl
|
108
|
-
|
109
|
-
DEPENDENCIES
|
110
|
-
amazing_print
|
111
|
-
minitest
|
112
|
-
minitest-hooks
|
113
|
-
mocha
|
114
|
-
ostruct
|
115
|
-
pry
|
116
|
-
rake
|
117
|
-
simplecov
|
118
|
-
standard
|
119
|
-
table_tennis!
|
120
|
-
|
121
|
-
BUNDLED WITH
|
122
|
-
2.6.5
|