Skip to content

Commit 100d69a

Browse files
Copilotjiacai2050
andauthored
pretty-table: add Cell struct with per-cell styling and horizontal span (#63)
* Initial plan * Improve pretty-table: add cell alignment, symmetric padding, and row separators Co-authored-by: jiacai2050 <3848910+jiacai2050@users.noreply.github.com> * Add ANSI color support to pretty-table (per-cell, header, footer) Co-authored-by: jiacai2050 <3848910+jiacai2050@users.noreply.github.com> * Add Cell struct with per-cell styling and hspan to pretty-table Co-authored-by: jiacai2050 <3848910+jiacai2050@users.noreply.github.com> * Apply TigerStyle fixes and align with PR#62 simargs API Co-authored-by: jiacai2050 <3848910+jiacai2050@users.noreply.github.com> * Add TigerStyle skill file from PR#62 Co-authored-by: jiacai2050 <3848910+jiacai2050@users.noreply.github.com> * Fix comment punctuation in pretty-table-demo.zig (TigerStyle) Co-authored-by: jiacai2050 <3848910+jiacai2050@users.noreply.github.com> * Fix CI: add braces around for-if assignment in pretty-table tests Co-authored-by: jiacai2050 <3848910+jiacai2050@users.noreply.github.com> * Fix CI: correct for-loop blocks and fix hspan test data to match actual column widths Co-authored-by: jiacai2050 <3848910+jiacai2050@users.noreply.github.com> * Revert simargs.zig and all non-pretty-table changes; remove agent skill file Co-authored-by: jiacai2050 <3848910+jiacai2050@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jiacai2050 <3848910+jiacai2050@users.noreply.github.com>
1 parent 5ade8d1 commit 100d69a

File tree

4 files changed

+622
-82
lines changed

4 files changed

+622
-82
lines changed

docs/content/packages/pretty-table.org

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,28 @@
77

88
* Features
99
- Many box-drawing character to choose(=ascii=, =box=, =dos=).
10+
- Per-cell styling: foreground/background color, bold, italic.
11+
- Column spanning (=hspan=): a cell can span multiple columns.
1012

1113
* Usage
1214
See [[https://github.com/jiacai2050/zigcli/blob/main/examples/pretty-table-demo.zig][pretty-table-demo.zig]]
1315

1416
#+begin_src zig
1517
const t = Table(2){
16-
.header = [_]String{ "Language", "Files" },
17-
.rows = &[_][2]String{
18-
.{ "Zig", "3" },
19-
.{ "Python", "2" },
18+
.header = [_]Cell{ Cell.init("Language"), Cell.init("Files") },
19+
.rows = &[_][2]Cell{
20+
.{ Cell.init("Zig"), Cell.init("3") },
21+
.{ Cell.init("Python"), Cell.init("2") },
2022
},
21-
.footer = [2]String{ "Total", "5" },
23+
.footer = [2]Cell{ Cell.init("Total"), Cell.init("5") },
2224
.mode = .box, // or .ascii, .dos
2325
};
2426

25-
const out = std.io.getStdOut();
26-
try out.writer().print("{}", .{t});
27+
const out = std.fs.File.stdout();
28+
var buf: [1024]u8 = undefined;
29+
var writer = out.writer(&buf);
30+
try writer.interface.print("{f}", .{t});
31+
try writer.interface.flush();
2732
#+end_src
2833

2934
#+begin_src plaintext
@@ -32,9 +37,7 @@ See [[https://github.com/jiacai2050/zigcli/blob/main/examples/pretty-table-demo.
3237
├────────┼─────┤
3338
│Zig │3 │
3439
│Python │2 │
35-
│C │12 │
36-
│Ruby │5 │
3740
├────────┼─────┤
38-
│Total │22
41+
│Total │5
3942
└────────┴─────┘
4043
#+end_src

examples/pretty-table-demo.zig

Lines changed: 111 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,122 @@ const std = @import("std");
22

33
const Table = @import("pretty-table").Table;
44
const Separator = @import("pretty-table").Separator;
5-
const String = @import("pretty-table").String;
5+
const Cell = @import("pretty-table").Cell;
6+
const Align = @import("pretty-table").Align;
7+
const Color = @import("pretty-table").Color;
68

79
pub fn main() !void {
8-
const t = Table(2){
9-
.header = [_]String{ "Language", "Files" },
10-
.rows = &[_][2]String{
11-
.{ "Zig", "3" },
12-
.{ "Python", "2" },
13-
.{ "C", "12" },
14-
.{ "Ruby", "5" },
15-
},
16-
.footer = [2]String{ "Total", "22" },
10+
const out = std.fs.File.stdout();
11+
var buf: [4096]u8 = undefined;
12+
var writer = out.writer(&buf);
13+
14+
// Basic table with box-drawing borders.
15+
const basic = Table(2){
16+
.header = [_]Cell{ Cell.init("Language"), Cell.init("Files") },
17+
.rows = &[_][2]Cell{
18+
.{ Cell.init("Zig"), Cell.init("3") },
19+
.{ Cell.init("Python"), Cell.init("2") },
20+
.{ Cell.init("C"), Cell.init("12") },
21+
.{ Cell.init("Ruby"), Cell.init("5") },
22+
},
23+
.footer = [2]Cell{ Cell.init("Total"), Cell.init("22") },
1724
.mode = .box,
25+
.padding = 1,
1826
};
27+
try writer.interface.print("=== Box mode with padding ===\n{f}\n", .{basic});
1928

20-
const out = std.fs.File.stdout();
21-
var buf: [1024]u8 = undefined;
22-
var writer = out.writer(&buf);
29+
// Right-align numeric columns.
30+
const scores = Table(3){
31+
.header = [_]Cell{ Cell.init("Name"), Cell.init("Score"), Cell.init("Rank") },
32+
.rows = &[_][3]Cell{
33+
.{ Cell.init("Alice"), Cell.init("9800"), Cell.init("1") },
34+
.{ Cell.init("Bob"), Cell.init("7500"), Cell.init("2") },
35+
.{ Cell.init("Charlie"), Cell.init("5100"), Cell.init("3") },
36+
},
37+
.mode = .box,
38+
.padding = 1,
39+
.column_align = .{ .left, .right, .right },
40+
};
41+
try writer.interface.print("=== Right-aligned numeric columns ===\n{f}\n", .{scores});
42+
43+
// Center-aligned headers.
44+
const centered = Table(3){
45+
.header = [_]Cell{ Cell.init("CPU"), Cell.init("Memory"), Cell.init("Disk") },
46+
.rows = &[_][3]Cell{
47+
.{ Cell.init("45%"), Cell.init("62%"), Cell.init("30%") },
48+
.{ Cell.init("80%"), Cell.init("71%"), Cell.init("55%") },
49+
},
50+
.mode = .dos,
51+
.padding = 1,
52+
.column_align = .{ .center, .center, .center },
53+
};
54+
try writer.interface.print("=== Center-aligned (DOS mode) ===\n{f}\n", .{centered});
55+
56+
// Row separators between every data row.
57+
const with_seps = Table(2){
58+
.header = [_]Cell{ Cell.init("Step"), Cell.init("Status") },
59+
.rows = &[_][2]Cell{
60+
.{ Cell.init("Build"), Cell.init("OK") },
61+
.{ Cell.init("Test"), Cell.init("OK") },
62+
.{ Cell.init("Deploy"), Cell.init("PENDING") },
63+
},
64+
.padding = 1,
65+
.row_separator = true,
66+
};
67+
try writer.interface.print("=== Row separators ===\n{f}\n", .{with_seps});
68+
69+
// Per-cell styling: foreground colors, bold, italic, background color.
70+
const styled = Table(3){
71+
.header = [_]Cell{
72+
Cell.init("Service").withFg(.bright_white).withBold(),
73+
Cell.init("Status").withFg(.bright_white).withBold(),
74+
Cell.init("Uptime").withFg(.bright_white).withBold(),
75+
},
76+
.rows = &[_][3]Cell{
77+
.{
78+
Cell.init("web"),
79+
Cell.init("UP").withFg(.green),
80+
Cell.init("99.9%").withFg(.green),
81+
},
82+
.{ Cell.init("db"), Cell.init("UP").withFg(.green), Cell.init("99.5%").withFg(.green) },
83+
.{
84+
Cell.init("cache"),
85+
Cell.init("DOWN").withFg(.red).withBold(),
86+
Cell.init("0.0%").withFg(.red),
87+
},
88+
},
89+
.footer = [3]Cell{
90+
Cell.init("Summary").withFg(.yellow),
91+
Cell.init("2/3 UP").withFg(.yellow),
92+
Cell.init("").withFg(.yellow),
93+
},
94+
.mode = .box,
95+
.padding = 1,
96+
};
97+
try writer.interface.print("=== Per-cell styling ===\n{f}\n", .{styled});
98+
99+
// Column spanning (hspan): one cell spans multiple columns.
100+
const spanned = Table(4){
101+
.header = [_]Cell{
102+
Cell.init("Name"),
103+
Cell.init("Q1"),
104+
Cell.init("Q2"),
105+
Cell.init("Q3"),
106+
},
107+
.rows = &[_][4]Cell{
108+
.{ Cell.init("Alice"), Cell.init("90"), Cell.init("85"), Cell.init("92") },
109+
// "N/A" spans columns 1-3 (Q1, Q2, Q3).
110+
.{
111+
Cell.init("Bob"),
112+
Cell.init("N/A (on leave)").withHspan(3),
113+
Cell.span(),
114+
Cell.span(),
115+
},
116+
},
117+
.padding = 1,
118+
.mode = .box,
119+
};
120+
try writer.interface.print("=== Column spanning (hspan) ===\n{f}\n", .{spanned});
23121

24-
try writer.interface.print("{f}", .{t});
25122
try writer.interface.flush();
26123
}

src/bin/loc.zig

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const std = @import("std");
22
const Table = @import("pretty-table").Table;
3+
const Cell = @import("pretty-table").Cell;
34
const Separator = @import("pretty-table").Separator;
45
const simargs = @import("simargs");
56
const util = @import("util.zig");
@@ -125,14 +126,14 @@ const LinesOfCode = struct {
125126

126127
const header = b: {
127128
const fieldInfos = std.meta.fields(Column);
128-
var names: [fieldInfos.len][]const u8 = undefined;
129+
var names: [fieldInfos.len]Cell = undefined;
129130
for (fieldInfos, 0..) |field, i| {
130-
names[i] = [_]u8{std.ascii.toUpper(field.name[0])} ++ field.name[1..];
131+
names[i] = Cell.init([_]u8{std.ascii.toUpper(field.name[0])} ++ field.name[1..]);
131132
}
132133
break :b names;
133134
};
134135
const LOCTable = Table(Self.header.len);
135-
const LOCTableData = [Self.header.len][]const u8;
136+
const LOCTableData = [Self.header.len]Cell;
136137

137138
fn merge(self: *Self, other: Self) void {
138139
self.files += other.files;
@@ -163,14 +164,14 @@ const LinesOfCode = struct {
163164
}
164165

165166
fn toTableData(self: Self, allocator: std.mem.Allocator) Self.LOCTableData {
166-
return [_][]const u8{
167-
self.lang.toString(),
168-
Self.numToString(self.files, allocator),
169-
Self.numToString(self.codes + self.blanks + self.comments, allocator),
170-
Self.numToString(self.codes, allocator),
171-
Self.numToString(self.comments, allocator),
172-
Self.numToString(self.blanks, allocator),
173-
StringUtil.humanSize(allocator, self.size) catch unreachable,
167+
return [_]Cell{
168+
Cell.init(self.lang.toString()),
169+
Cell.init(Self.numToString(self.files, allocator)),
170+
Cell.init(Self.numToString(self.codes + self.blanks + self.comments, allocator)),
171+
Cell.init(Self.numToString(self.codes, allocator)),
172+
Cell.init(Self.numToString(self.comments, allocator)),
173+
Cell.init(Self.numToString(self.blanks, allocator)),
174+
Cell.init(StringUtil.humanSize(allocator, self.size) catch unreachable),
174175
};
175176
}
176177
};

0 commit comments

Comments
 (0)