summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/detached/server.zig5
-rw-r--r--src/macos.zig5
-rw-r--r--src/ninep/ctl.zig6
-rw-r--r--src/panes.zig4
-rw-r--r--src/pardes.zig6
-rw-r--r--src/tty/tty.zig5
-rw-r--r--test/fs.py21
7 files changed, 49 insertions, 3 deletions
diff --git a/src/detached/server.zig b/src/detached/server.zig
index 7ce8a1d0..4d2d3882 100644
--- a/src/detached/server.zig
+++ b/src/detached/server.zig
@@ -496,7 +496,10 @@ pub const Session = struct {
s.ptys[pane] = .{ .fd = child.file.handle, .pid = child.pid, .cmd = .{ .watched = command and host_io.watchExit(child.pid), .fd = child.file.handle } };
setNonblock(child.file.handle);
var lbuf: [pardes.memory.limits.host_path_cap + 1]u8 = undefined;
- if (host_io.shellCwd(child.pid, &lbuf)) |wd| s.core.setCwd(pane, wd);
+ // Only a shell given no directory is asked where it is: one given a
+ // directory is named by it already (newShell), and asked now, before
+ // its chdir, it would answer this process's, a rename to undo.
+ if (cwd.len == 0) if (host_io.shellCwd(child.pid, &lbuf)) |wd| s.core.setCwd(pane, wd);
}
fn ptyWrite(ctx: ?*anyopaque, pane: u8, bytes: []const u8) void {
diff --git a/src/macos.zig b/src/macos.zig
index 75834844..9b17803b 100644
--- a/src/macos.zig
+++ b/src/macos.zig
@@ -2450,7 +2450,10 @@ fn spawnShell(ctx: ?*anyopaque, pane: u8, cwd: []const u8) void {
.cmd = .{ .watched = (if (core.panes[pane]) |pn| pn.command != null else false) and host_io.watchExit(child.pid), .fd = child.file.handle },
};
var lbuf: [1024]u8 = undefined;
- if (host_io.shellCwd(child.pid, &lbuf)) |wd| core.setCwd(pane, wd);
+ // Only a shell given no directory is asked where it is: one given a
+ // directory is named by it already (newShell), and asked now, before
+ // its chdir, it would answer this process's, a rename to undo.
+ if (cwd.len == 0) if (host_io.shellCwd(child.pid, &lbuf)) |wd| core.setCwd(pane, wd);
if (st.started) if (st.ptys[pane]) |*pt| startReader(st, pt, pane);
}
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 95d81e3b..66092451 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -1881,6 +1881,12 @@ test "a terminal told the directory it already goes by logs no rename" {
p.setCwd(term, "/tmp");
var rec: [32]u8 = undefined;
try testing.expect(!th.logHas(p, try std.fmt.bufPrint(&rec, "rename {d} ", .{p.panes[term].?.serial})));
+ // This process's directory, asked before the shell has said anything
+ // (so before its chdir): not taken.
+ const session = p.sessionDir();
+ p.setCwd(term, session);
+ try testing.expect(!th.logHas(p, try std.fmt.bufPrint(&rec, "rename {d} ", .{p.panes[term].?.serial})));
+ p.update(.{ .output = .{ .pane = @intCast(term), .bytes = "$ " } });
// Somewhere else is a rename.
p.setCwd(term, "/usr");
try testing.expect(th.logHas(p, try std.fmt.bufPrint(&rec, "rename {d} /usr", .{p.panes[term].?.serial})));
diff --git a/src/panes.zig b/src/panes.zig
index 41ba636f..f2fe5037 100644
--- a/src/panes.zig
+++ b/src/panes.zig
@@ -109,6 +109,10 @@ pub const Pane = struct {
/// Its shell could not be started (Pardes.shellFailed): a run hears
/// `error shell gone` at once.
shell_failed: bool = false,
+ /// Its shell has printed something, so it has made its chdir: until
+ /// then /proc says this process's directory, not the shell's
+ /// (Pardes.setCwd keeps the one it was started in).
+ shell_spoke: bool = false,
pending_command: Terminal.PendingCommand = .{},
/// The last command line pardes typed into this terminal (an exec, a
/// middle click, a pty/run): its first word, which Kill matches, and
diff --git a/src/pardes.zig b/src/pardes.zig
index c6e68517..24595045 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -4999,6 +4999,11 @@ pub const Pardes = struct {
else => "",
};
if (std.mem.eql(u8, cur, cwd)) return;
+ // A shell started in a directory is asked where it is before it has
+ // said anything: its chdir may not have happened yet, and the answer
+ // is this process's directory, a rename to undo. That answer, then,
+ // is not taken.
+ if (pane.isTerminal() and !pane.shell_spoke and pane.cwd == .owned and std.mem.eql(u8, cwd, p.sessionDir())) return;
pane.setOwnedCwd(cwd) catch |err| return p.reportError(id, "directory", err);
if (look.lookHoverPane(p) == id) look.cancelLookHover(p);
// A terminal is named by its directory, so /log hears it renamed; one
@@ -5560,6 +5565,7 @@ pub const Pardes = struct {
},
.output => |o| {
const pane = p.panes[o.pane] orelse return;
+ pane.shell_spoke = true;
ctlfs.events.notePtyOutput(p, o.pane, o.bytes);
panes.Terminal.feedOutput(p, pane, o.bytes);
ctlfs.pty.noteMarks(p, o.pane, pane);
diff --git a/src/tty/tty.zig b/src/tty/tty.zig
index a41e42e2..762616f5 100644
--- a/src/tty/tty.zig
+++ b/src/tty/tty.zig
@@ -1249,7 +1249,10 @@ const Shell = struct {
const command = if (s.core.panes[pane]) |pn| pn.command != null else false;
s.ptys[pane] = .{ .file = child.file, .pid = child.pid, .reader = .{ .any_future = null, .result = {} }, .cmd = .{ .watched = command and host_io.watchExit(child.pid), .fd = child.file.handle } };
var lbuf: [pardes.memory.limits.host_path_cap + 1]u8 = undefined;
- if (host_io.shellCwd(child.pid, &lbuf)) |wd| s.core.setCwd(pane, wd);
+ // Only a shell given no directory is asked where it is: one given a
+ // directory is named by it already (newShell), and asked now, before
+ // its chdir, it would answer this process's, a rename to undo.
+ if (cwd.len == 0) if (host_io.shellCwd(child.pid, &lbuf)) |wd| s.core.setCwd(pane, wd);
if (s.threads_ok) {
if (s.ptys[pane]) |*pt| {
pt.reader = s.io.concurrent(readPty, .{ s.io, s.gpa, pt.file, @as(usize, pane), s.gens[pane], s.loop }) catch pt.reader;
diff --git a/test/fs.py b/test/fs.py
index 89801a71..d2cd631c 100644
--- a/test/fs.py
+++ b/test/fs.py
@@ -494,6 +494,26 @@ def unattended_messages_expire(binary):
assert not shown(), 'an unattended message outlived MessageLinger'
+def new_terminals_named_once(binary):
+ """A terminal started outside the session's directory is named by that
+ directory from its `new` on: no rename to the session's and back."""
+ with tempfile.TemporaryDirectory(prefix='pardes-names-') as directory:
+ root = Path(directory)
+ (root / 'sub').mkdir()
+ with session(binary, root, 'names') as (client, _):
+ made = [look(client, str(root / 'sub'))]
+ before = {int(row.split()[0]) for row in client.read('/index').splitlines()}
+ client.write('/pane/1/ctl', b'Tty\n')
+ if shutil.which('fish'):
+ client.write('/pane/1/ctl', b'Tty fish\n')
+ made += [s for s in (int(row.split()[0]) for row in client.read('/index').splitlines()) if s not in before]
+ time.sleep(1.5) # their shells start and say where they are
+ log = client.read('/log').decode()
+ for serial in made:
+ assert f'\nrename {serial} ' not in '\n' + log, (serial, log)
+ assert f'\nnew {serial} {root}' in '\n' + log, (serial, log)
+
+
def python_repl_runs_one_line(binary):
"""A one-line exec sent to Python's own REPL (3.13's pyrepl, which asks
for bracketed paste) runs at once, with no second Enter: the Enter
@@ -610,6 +630,7 @@ def test(binary, quic=False):
run_file(binary)
exit_answers(binary)
unattended_messages_expire(binary)
+ new_terminals_named_once(binary)
python_repl_runs_one_line(binary)
signal_cleans_up(binary)
for options, message in [