From d6c877f55669869bc4c360e6d94563ded778d26d Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 30 Sep 2026 12:03:44 -0300 Subject: Help and Find from the workspace or a column tag act in the session's directory, not the keyboard pane's New, Tty and commands clicked in a tag no pane owns ran in the session's directory, but Help named its +Help after the pane with the keyboard, and Find walked that pane's directory. So the same click gave different results depending on which pane was focused. Both now take the session's directory from a header, through execDir as the others do. Co-Authored-By: Claude Opus 5.5 --- src/Output.zig | 6 ++++-- src/look.zig | 5 +++-- src/ninep/cols.zig | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Output.zig b/src/Output.zig index 65f6bfe4..545637b3 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -916,7 +916,7 @@ pub fn resetBody(p: *Pardes, pane: *Pane) void { fn openRead(p: *Pardes, id: usize, from: Origin, arg: []const u8, content: []u8) !void { errdefer p.gpa.free(content); - const pane = p.panes[id] orelse return error.MissingPane; + if (p.panes[id] == null) return error.MissingPane; for (p.panes, 0..) |slot, i| { const hp = slot orelse continue; const hf = if (hp.file) |*f| f else continue; @@ -928,7 +928,9 @@ fn openRead(p: *Pardes, id: usize, from: Origin, arg: []const u8, content: []u8) p.active = i; return; } - const dir = if (pane.file) |f| (std.fs.path.dirname(f.path) orelse "/") else pane.cwdSlice(); + // Where the word ran: the pane's directory, or from a column's or the + // workspace's tag, the session's. + const dir = p.execDir(id); const free = p.freeSlot() orelse return error.NoPaneSlots; _ = try open(p, free, dir, from, arg, content); exec.placeNew(p, id, id, free, if (from == .errors) .errors else .doc); diff --git a/src/look.zig b/src/look.zig index 65d001a9..36eae75c 100644 --- a/src/look.zig +++ b/src/look.zig @@ -583,8 +583,9 @@ pub fn runSearch(p: *Pardes, id: usize, pat_raw: []const u8, kind: Search, start if (pat.len == 0) return 0; const arena = p.scratch.allocator(); // where the pane lives: a file's directory, a shell's cwd — the walk - // root, and the directory the results buffer is named in. - const dir = Pardes.paneDir(pane); + // root, and the directory the results buffer is named in. From a + // column's or the workspace's tag, the session's. + const dir = if (p.exec_header) p.sessionDir() else Pardes.paneDir(pane); const out = try arena.alloc(u8, filesystem.search_max_output_bytes); var out_len: usize = 0; var nrows: usize = 0; diff --git a/src/ninep/cols.zig b/src/ninep/cols.zig index 1919122d..454cce4a 100644 --- a/src/ninep/cols.zig +++ b/src/ninep/cols.zig @@ -283,6 +283,24 @@ test "a pane's word written to /tagexec or a column's exec is refused, and acts try testing.expectEqual(tree.Status.ok, th.wr(p, col_exec, "New\n").reply.status); } +test "Help and Find at /tagexec act in the session's directory, not the keyboard pane's" { + const p = try th.withFile(testing.allocator, "x\n"); + defer p.deinit(); + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + try tmp.dir.writeFile(testing.io, .{ .sub_path = "needle.txt", .data = "n\n" }); + var buf: [4096]u8 = undefined; + const session = buf[0..try tmp.dir.realPath(testing.io, &buf)]; + @memcpy(p.session_dir[0..session.len], session); + p.session_dir_len = @intCast(session.len); + const tagexec = @intFromEnum(tree.TopFile.tagexec); + for ([_][]const u8{ "Help\n", "Find needle\n" }) |line| { + try testing.expectEqual(tree.Status.ok, th.wr(p, tagexec, line).reply.status); + const made = p.panes[p.active].?.file.?.path; + try testing.expectEqualStrings(session, std.fs.path.dirname(made).?); + } +} + test "New and Tty run at /tagexec and a column's exec, with no pane at all too" { const p = try th.withFile(testing.allocator, "x\n"); defer p.deinit(); -- cgit v1.3