Commit 0f50c39
chamard
·
2026-09-05 09:01:37 -0400 EDT
parent 5afa7bc
fix: let line limit control scrollback retention (#254)
1 files changed,
+35,
-5
+35,
-5
| ... | ... | @@ -214,6 +214,15 @@ pub fn clientLoop(client_sock_fd: i32, env_str: []const u8) !ClientResult { | |
| 214 | 214 | } | |
| 215 | 215 | } | |
| 216 | 216 | ||
| 217 | + | fn initTerminal(gpa: std.mem.Allocator, io: std.Io, size: ipc.Resize, cfg: *const Cfg) !ghostty_vt.Terminal { | |
| 218 | + | return ghostty_vt.Terminal.init(io, gpa, .{ | |
| 219 | + | .cols = size.cols, | |
| 220 | + | .rows = size.rows, | |
| 221 | + | .max_scrollback_lines = cfg.max_scrollback_lines, | |
| 222 | + | .max_scrollback_bytes = null, // Let the line limit control scrollback. | |
| 223 | + | }); | |
| 224 | + | } | |
| 225 | + | ||
| 217 | 226 | /// dameonLoop is what the daemon runs to send and receive ipc commands from its corresponding | |
| 218 | 227 | /// clients. It uses poll() as its non-blocking mechanism. | |
| 219 | 228 | fn daemonLoop(daemon: *Daemon, gpa: std.mem.Allocator, io: std.Io, server_sock_fd: lib_posix.socket_t, pty_fd: i32) !void { |
| ... | ... | @@ -225,11 +234,7 @@ fn daemonLoop(daemon: *Daemon, gpa: std.mem.Allocator, io: std.Io, server_sock_f | |
| 225 | 234 | defer poll_fds.deinit(gpa); | |
| 226 | 235 | ||
| 227 | 236 | const init_size = ipc.getTerminalSize(pty_fd); | |
| 228 | - | var term = try ghostty_vt.Terminal.init(io, gpa, .{ | |
| 229 | - | .cols = init_size.cols, | |
| 230 | - | .rows = init_size.rows, | |
| 231 | - | .max_scrollback_lines = daemon.cfg.max_scrollback_lines, | |
| 232 | - | }); | |
| 237 | + | var term = try initTerminal(gpa, io, init_size, daemon.cfg); | |
| 233 | 238 | defer term.deinit(gpa); | |
| 234 | 239 | var vt_stream = term.vtStream(); | |
| 235 | 240 | defer vt_stream.deinit(); |
| ... | ... | @@ -1354,6 +1359,31 @@ pub const Daemon = struct { | |
| 1354 | 1359 | } | |
| 1355 | 1360 | }; | |
| 1356 | 1361 | ||
| 1362 | + | test "terminal retains the configured scrollback without the default byte cap" { | |
| 1363 | + | const alloc = std.testing.allocator; | |
| 1364 | + | const cfg = Cfg{ .socket_dir = "", .log_dir = "" }; | |
| 1365 | + | var term = try initTerminal(alloc, std.testing.io, .{ .cols = 80, .rows = 24 }, &cfg); | |
| 1366 | + | defer term.deinit(alloc); | |
| 1367 | + | var stream = term.vtStream(); | |
| 1368 | + | defer stream.deinit(); | |
| 1369 | + | ||
| 1370 | + | stream.nextSlice("first line\r\n"); | |
| 1371 | + | for (1..cfg.max_scrollback_lines) |_| stream.nextSlice("more output\r\n"); | |
| 1372 | + | ||
| 1373 | + | const history = util.serializeTerminal(alloc, &term, .plain) orelse return error.TestUnexpectedNull; | |
| 1374 | + | defer alloc.free(history); | |
| 1375 | + | try std.testing.expect(std.mem.startsWith(u8, history, "first line\n")); | |
| 1376 | + | ||
| 1377 | + | // Exceed the limit comfortably because Ghostty prunes whole pages. | |
| 1378 | + | for (0..cfg.max_scrollback_lines) |_| stream.nextSlice("more output\r\n"); | |
| 1379 | + | stream.nextSlice("latest line\r\n"); | |
| 1380 | + | ||
| 1381 | + | const pruned_history = util.serializeTerminal(alloc, &term, .plain) orelse return error.TestUnexpectedNull; | |
| 1382 | + | defer alloc.free(pruned_history); | |
| 1383 | + | try std.testing.expect(std.mem.indexOf(u8, pruned_history, "first line") == null); | |
| 1384 | + | try std.testing.expect(std.mem.indexOf(u8, pruned_history, "latest line") != null); | |
| 1385 | + | } | |
| 1386 | + | ||
| 1357 | 1387 | fn testDaemon() Daemon { | |
| 1358 | 1388 | return .{ | |
| 1359 | 1389 | .cfg = undefined, |