762b7aa461df
size the man pager to the real terminal height via TIOCGWINSZ instead of a fixed 22-row guess; bump v3.4.10
- time
- 2026-07-02 00:01 UTC · 3 months ago
- machine
- koh2
- agent
- claude-sonnet-5
- session
- man pager terminal height
- parent
- 0feb174dd569
↓ nina-762b7aa4.tar.gzatlas at this save
files changed2 files
VERSION+1 −1src/man.zig+32 −8src/man.zig+32 −8
@@ -487,15 +487,37 @@487487 488488 // --- Pager state ---489489 490-const PAGE_ROWS: u16 = 22; // visible content rows490+// Visible content rows. Set once per run from the real terminal height, so491+// the pager fills the window instead of a fixed guess that left half the492+// screen unused on tall terminals. Falls back to this default when the493+// window size can't be read.494+var PAGE_ROWS: u16 = 22;495+const MIN_PAGE_ROWS: u16 = 5;491496 const COL_WIDTH: u16 = 96; // wrap width for paragraphs492497 // Frame geometry. The header paints exactly HEADER_ROWS lines and the footer493498 // FOOTER_ROWS; redraw arithmetic depends on these being right, so name them494499 // instead of magic +2 guesses that drift.495500 const HEADER_ROWS: u16 = 3;496501 const FOOTER_ROWS: u16 = 1;497-const FRAME_ROWS: u16 = HEADER_ROWS + PAGE_ROWS + FOOTER_ROWS;502+fn frameRows() u16 {503+ return HEADER_ROWS + PAGE_ROWS + FOOTER_ROWS;504+}498505 506+// Reads the terminal's row count via TIOCGWINSZ and converts it to a content507+// row budget by subtracting the fixed header/footer. Any failure (not a tty,508+// ioctl error, absurdly small window) falls back to the default PAGE_ROWS.509+fn detectPageRows(io: std.Io) u16 {510+ var ws: std.posix.winsize = .{ .row = 0, .col = 0, .xpixel = 0, .ypixel = 0 };511+ const result = io.operate(.{ .device_io_control = .{512+ .file = std.Io.File.stdout(),513+ .code = std.posix.T.IOCGWINSZ,514+ .arg = &ws,515+ } }) catch return PAGE_ROWS;516+ if (result.device_io_control < 0) return PAGE_ROWS;517+ if (ws.row <= HEADER_ROWS + FOOTER_ROWS + MIN_PAGE_ROWS) return PAGE_ROWS;518+ return ws.row - HEADER_ROWS - FOOTER_ROWS;519+}520+499521 pub fn run(io: std.Io, topic: []const u8) !void {500522 // Plain fallback for non-tty (piped, redirected). Print every section in501523 // order with a colored chapter divider — same vibe, but no interactivity.@@ -508,6 +530,8 @@508530 try rawModeEnable(STDIN_FD, &saved);509531 defer rawModeDisable(STDIN_FD, saved);510532 533+ PAGE_ROWS = detectPageRows(io);534+511535 const start_section: usize = if (topic.len == 0) 0 else blk: {512536 for (MAN_PAGE, 0..) |sec, i| {513537 if (std.mem.eql(u8, sec.id, topic)) break :blk i;@@ -628,11 +652,11 @@628652 // Clear the whole inline frame on exit — header included — so the shell629653 // prompt lands on a clean line. The old PAGE_ROWS + 2 count missed the top630654 // two header rows, leaving them stranded above the prompt.631- output.cursorUp(FRAME_ROWS);632- for (0..FRAME_ROWS) |_| {655+ output.cursorUp(frameRows());656+ for (0..frameRows()) |_| {633657 output.raw("\x1b[K\n");634658 }635- output.cursorUp(FRAME_ROWS);659+ output.cursorUp(frameRows());636660 output.flush();637661 }638662 @@ -872,9 +896,9 @@872896 }873897 874898 fn showHelp() void {875- output.cursorUp(FRAME_ROWS);876- for (0..FRAME_ROWS) |_| output.raw("\x1b[K\n");877- output.cursorUp(FRAME_ROWS);899+ output.cursorUp(frameRows());900+ for (0..frameRows()) |_| output.raw("\x1b[K\n");901+ output.cursorUp(frameRows());878902 drawHeader();879903 drawHelpBody();880904 drawFooter(0, MAN_PAGE.len, 0, 1);