summaryrefslogtreecommitdiff
path: root/src/Terminal.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Terminal.zig')
-rw-r--r--src/Terminal.zig50
1 files changed, 50 insertions, 0 deletions
diff --git a/src/Terminal.zig b/src/Terminal.zig
index 91c23305..76be29d0 100644
--- a/src/Terminal.zig
+++ b/src/Terminal.zig
@@ -659,6 +659,36 @@ test "replay restores terminal cells from the retained stream" {
try std.testing.expectEqualSlices(u8, try replayBytes(pane, gpa), try replayBytes(restored, gpa));
}
+test "a restored command pane comes back finished, its command not run again" {
+ if (comptime !enabled or pardes.isolated) return error.SkipZigTest;
+ const gpa = std.testing.allocator;
+ const p = try Pardes.init(gpa, .{ .tty_only = true, .cols = 60, .rows = 16 });
+ defer p.deinit();
+ while (p.nextEffect()) |_| {}
+ const cmd = p.freeSlot().?;
+ _ = try p.newCommand(cmd, "/tmp", "make test");
+ feedOutput(p, p.panes[cmd].?, "ok\r\n");
+ p.update(.{ .exited = .{ .pane = @intCast(cmd), .status = 2 } });
+ try dump.dumpState(p);
+ const restored = try dump.initFromDump(gpa, .{ .tty_only = true, .cols = 60, .rows = 16 }, p.dump_out.?);
+ defer restored.deinit();
+ var spawns: usize = 0;
+ while (restored.nextEffect()) |effect| switch (effect) {
+ .spawn => spawns += 1,
+ else => {},
+ };
+ try std.testing.expectEqual(@as(usize, 1), spawns); // the shell, not the command
+ const again = for (restored.panes) |slot| {
+ if (slot) |pane| if (pane.command != null) break pane;
+ } else return error.CommandPaneLost;
+ try std.testing.expectEqualStrings("make test", again.command.?);
+ try std.testing.expect(again.command_done);
+ try std.testing.expectEqual(@as(?u8, 2), again.command_status);
+ const text = try screenTextAlloc(again, gpa);
+ defer gpa.free(text);
+ try std.testing.expect(std.mem.indexOf(u8, text, "ok") != null);
+}
+
test "a restored terminal keeps its history above a marker and gets a shell where it was" {
if (comptime !enabled or pardes.isolated) return error.SkipZigTest;
const gpa = std.testing.allocator;
@@ -668,6 +698,7 @@ test "a restored terminal keeps its history above a marker and gets a shell wher
const pane = p.panes[0].?;
feedOutput(p, pane, "old output\r\n$ ls\r\n");
try pane.setOwnedCwd("/tmp");
+ pane.shell = try gpa.dupe(u8, "bash"); // `Tty bash`: it comes back as bash
try dump.dumpState(p);
const restored = try dump.initFromDump(gpa, .{ .tty_only = true, .cols = 40, .rows = 12 }, p.dump_out.?);
defer restored.deinit();
@@ -678,6 +709,8 @@ test "a restored terminal keeps its history above a marker and gets a shell wher
};
try std.testing.expect(spawned);
const again = restored.panes[0].?;
+ try std.testing.expectEqualStrings("bash", again.shell.?);
+ try std.testing.expect(again.command == null);
try std.testing.expectEqual(.spawn, again.pending_command.wait);
const text = try screenTextAlloc(again, gpa);
defer gpa.free(text);
@@ -961,6 +994,15 @@ pub const CommandOutput = struct {
/// What the newest finished command printed: the screen text between its
/// C and D marks, read from at most `max_rows` rows above its end, so a
/// command that printed the whole history costs no more than its tail.
+/// The shell exited in the middle of a command (its `exit 3`): no end mark
+/// will come, so the command's output ends where the cursor is.
+pub fn endOutputHere(pane: *Pane) void {
+ if (comptime !enabled) return;
+ const state = pane.terminal orelse return;
+ const marks = &state.stream.handler;
+ if (marks.phase == .running and marks.out_end == null) marks.pinCursor(&marks.out_end, true);
+}
+
pub fn commandOutput(pane: *Pane, gpa: std.mem.Allocator, max_rows: usize) !CommandOutput {
// Unless there is text to read, whether the nothing it printed is the
// whole of it.
@@ -1427,6 +1469,9 @@ pub fn dumpPane(
.collapsed = pane.collapsed,
.terminal = .{
.cwd = try arena.dupe(u8, pane.cwdSlice()),
+ .shell = try arena.dupe(u8, pane.shell orelse ""),
+ .command = try arena.dupe(u8, pane.command orelse ""),
+ .status = pane.command_status,
.stream = try arena.dupe(u8, text),
.stream_b64 = &.{},
.cursor = .{ .col = 0, .row = 0 },
@@ -1476,6 +1521,9 @@ pub fn dumpPane(
.collapsed = pane.collapsed,
.terminal = .{
.cwd = try arena.dupe(u8, pane.cwdSlice()),
+ .shell = try arena.dupe(u8, pane.shell orelse ""),
+ .command = try arena.dupe(u8, pane.command orelse ""),
+ .status = pane.command_status,
.stream = stream[0..len],
.stream_b64 = try dump.encodeBytes(arena, try screenVt(pane, arena)),
.stream_is_screen = true,
@@ -2111,6 +2159,8 @@ test "terminal Filter keys indexed truecolor OSC and background-only cells throu
defer p.deinit();
while (p.nextEffect()) |_| {}
const pane = p.panes[0].?;
+ pane.tag_expanded = false; // a one-row tag keeps this small screen's rows
+ p.sync();
try testing.expect(pane.tty_filter);
pane.tty_filter = false;