agent: move terminal width query into its only user, Spinner

Terminal.columns() had exactly one caller (Spinner's line renderer) and
Terminal itself never used it. Moving it breaks the Spinner<->Terminal
import cycle and stops Spinner type-checking against the isocline
@cImport for one ioctl.
This commit is contained in:
Adrià Arrufat
2026-07-15 11:36:31 +02:00
parent 44193a6fca
commit 772a3fc7db
2 changed files with 16 additions and 17 deletions

View File

@@ -19,7 +19,6 @@
const std = @import("std");
const lp = @import("lightpanda");
const log = lp.log;
const Terminal = @import("Terminal.zig");
const ansi = @import("ansi.zig");
const truncateUtf8 = @import("../string.zig").truncateUtf8;
@@ -291,7 +290,7 @@ fn renderLocked(self: *Spinner) void {
// (glyph, two spaces, `[`, `]`) around prefix+name+args. `\r` and
// ANSI escapes are zero-width, so they don't count toward wrap.
const decoration_cells: usize = 5 + prefix.len + name.len;
const cols: usize = Terminal.columns() orelse 80;
const cols: usize = columns() orelse 80;
// Reserve one extra cell so the line is strictly less than `cols`:
// auto-wrap (DECAWM) terminals advance past a row that exactly fills
// the width.
@@ -316,6 +315,21 @@ fn renderLocked(self: *Spinner) void {
/// Returns the byte length of `bytes` that fits in `max_cells` cells, rounded
/// down to a whole UTF-8 codepoint. Multi-cell glyphs (CJK, wide emoji) count
/// as 1 — args are typically ASCII, so the approximation is good enough.
/// Current terminal width in columns, queried via TIOCGWINSZ on stderr.
/// Null when stderr isn't a tty, the ioctl fails, or the kernel reports 0
/// (some pseudo-ttys leave the field unset). Cheap enough to call per render
/// frame; picks up resizes without SIGWINCH plumbing.
fn columns() ?u16 {
var ws: std.posix.winsize = undefined;
// bitcast via c_uint: on archs where `_IOR` sets the direction bit
// (MIPS/PPC/SPARC), `IOCGWINSZ` exceeds i32 range, so a plain @intCast
// panics; the bitcast preserves the bit pattern.
const req: c_int = @bitCast(@as(c_uint, std.posix.T.IOCGWINSZ));
const rc = std.c.ioctl(std.posix.STDERR_FILENO, req, &ws);
if (rc != 0 or ws.col == 0) return null;
return ws.col;
}
fn truncToCells(bytes: []const u8, max_cells: usize) usize {
var cells: usize = 0;
var i: usize = 0;

View File

@@ -948,21 +948,6 @@ fn logSink(bytes: []const u8) void {
_ = std.posix.write(std.posix.STDERR_FILENO, bytes) catch {};
}
/// Current terminal width in columns, queried via TIOCGWINSZ on stderr.
/// Null when stderr isn't a tty, the ioctl fails, or the kernel reports 0
/// (some pseudo-ttys leave the field unset). Cheap enough to call per render
/// frame; picks up resizes without SIGWINCH plumbing.
pub fn columns() ?u16 {
var ws: std.posix.winsize = undefined;
// bitcast via c_uint: on archs where `_IOR` sets the direction bit
// (MIPS/PPC/SPARC), `IOCGWINSZ` exceeds i32 range, so a plain @intCast
// panics; the bitcast preserves the bit pattern.
const req: c_int = @bitCast(@as(c_uint, std.posix.T.IOCGWINSZ));
const rc = std.c.ioctl(std.posix.STDERR_FILENO, req, &ws);
if (rc != 0 or ws.col == 0) return null;
return ws.col;
}
/// Erase the frame after an empty submit. The bars collapse on submit, leaving
/// the spacing and prompt lines with the cursor one line below; move up two,
/// clear to end of screen.