summaryrefslogtreecommitdiff
path: root/src/pardes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-03 13:30:15 -0300
committerGabriel Schneider <[email protected]>2026-09-03 13:30:15 -0300
commit3d8d4425c969d3df21915c9c14b144460a1c0086 (patch)
treef54635093525f610630a2ca850a73ecd2df7393e /src/pardes.zig
parente6c9f1726cca910c464fc807aefebc404c8e7a12 (diff)
downloadpardes-3d8d4425c969d3df21915c9c14b144460a1c0086.tar.gz
pardes-3d8d4425c969d3df21915c9c14b144460a1c0086.zip
boot: the errors pane keeps the launch directory, and a typo inside pardes stays one line
Two defects in the +Errors boot, both found by adversarial re-review. The pane was opened with `dir = ""` — copied from the board's boot buffer, which can afford it because that platform has no filesystem — so its path came out `/+Errors` and `paneDir` answered `/`. An output pane's directory is where a `Grep` from it walks, where its `Newtty` spawns a shell and what its `Save` prefills, so the boot screen rooted all three at the filesystem root, and the one word the pane prints resolved against `/` and could never be clicked. The launch directory rides in `Options.missing` beside the word now, and the test asserts the pane's path rather than only its contents. A typo INSIDE pardes stacked a second full-screen UI. The hand-off block above resolves the word and sends it to the outer instance; `.none` sent nothing and fell through, which was harmless while the classification below refused it and became the one input that stacks the UI that block exists to prevent — with no shell pane in it, so the only way out is `Del`. Its own comment said as much and was falsified by the +Errors boot. `.none` is refused in that shell now, in one line and without a stack trace, and the outer session is not told: `Look` on a word naming nothing is not something to do to somebody else's session. Also recorded, not fixed: the commonest permission case never reaches the `.dir` arm this arm's comment defends. `look.isDir` probes with O_DIRECTORY| O_RDONLY, so a directory you cannot read resolves as `.file` and dies in `file_pane.open` with `error.OpenFailed` out of `main` — still a trace at a human, and a different fault than the one fixed here. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_016Q4RATpafkwahrovHQLKRf
Diffstat (limited to 'src/pardes.zig')
-rw-r--r--src/pardes.zig38
1 files changed, 30 insertions, 8 deletions
diff --git a/src/pardes.zig b/src/pardes.zig
index 8c643be6..55e111e6 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -5842,8 +5842,9 @@ pub const Options = struct {
file: ?[]const u8 = null,
file_line: usize = 0,
/// argv NAMED SOMETHING THAT IS NOT THERE — no file, no directory, nothing
- /// `look.resolve` could make a target of. The word as the human typed it,
- /// and it boots one `+Errors` pane saying so.
+ /// `look.resolve` could make a target of. The word as the human typed it
+ /// and the directory they typed it in, and it boots one `+Errors` pane
+ /// saying so.
///
/// A launch is not a failure worth refusing. `pardes nosuchfile` used to
/// return `BadArgs` out of `main`, which std prints as `error: BadArgs`
@@ -5851,7 +5852,11 @@ pub const Options = struct {
/// typo, and it left the human with no editor at all. Outranked by `file`
/// for the same reason `file` outranks `shells`: only one of them can name
/// the boot layout, and they are never both set.
- missing_path: ?[]const u8 = null,
+ ///
+ /// `dir` is the LAUNCH DIRECTORY, and the pane needs it for the same
+ /// reasons every other pane needs one: it is where a `Grep` from that pane
+ /// walks, where its `Newtty` spawns a shell, and what its `Save` prefills.
+ missing: ?struct { word: []const u8, dir: []const u8 } = null,
tty_toggle: u21 = config.tty_toggle_default,
/// load a dump of another instance instead of spawning shells (acme -l)
load_path: ?[]const u8 = null,
@@ -6333,7 +6338,7 @@ pub const Pardes = struct {
p.ncol = 1;
p.col_n[0] = 1;
p.col_terms[0][0] = 0;
- } else if (opts.missing_path) |word| {
+ } else if (opts.missing) |missing| {
// ARGV NAMED NOTHING. `+Errors` and not the message row, because a
// launch has no pane to put a message row on yet — and because
// this is exactly what acme's `+Errors` is: output from the
@@ -6344,14 +6349,17 @@ pub const Pardes = struct {
//
// The word AS TYPED, not a resolved path: there is nothing to
// resolve, and `pardes ~/notes/tdoo.md` wants to see its own typo
- // back rather than an absolute path built around it.
+ // back rather than an absolute path built around it. The pane's
+ // DIRECTORY is the launch directory all the same — see `missing`:
+ // "" would put this pane at `/+Errors` and point every word that
+ // reads a pane's directory at the root of the filesystem.
const content = try std.fmt.allocPrint(
gpa,
"file or directory not found\n\n\t{s}\n",
- .{word},
+ .{missing.word},
);
errdefer gpa.free(content);
- _ = try output_pane.open(p, 0, "", .errors, "", content);
+ _ = try output_pane.open(p, 0, missing.dir, .errors, "", content);
p.ncol = 1;
p.col_n[0] = 1;
p.col_terms[0][0] = 0;
@@ -16454,7 +16462,11 @@ test "Esc alternates between two panes of the SAME kind" {
test "argv naming nothing boots an errors pane rather than failing the launch" {
const gpa = std.testing.allocator;
- const p = try Pardes.init(gpa, .{ .cols = 80, .rows = 24, .missing_path = "notes/tdoo.md" });
+ const p = try Pardes.init(gpa, .{
+ .cols = 80,
+ .rows = 24,
+ .missing = .{ .word = "notes/tdoo.md", .dir = "/home/pardes-test/work" },
+ });
defer p.deinit();
p.update(.{ .resize = .{ .cols = 80, .rows = 24 } });
@@ -16462,6 +16474,9 @@ test "argv naming nothing boots an errors pane rather than failing the launch" {
// shell a bare `pardes` boots: the answer is the whole screen.
try std.testing.expectEqual(@as(u8, 1), p.ncol);
try std.testing.expectEqual(@as(usize, 1), p.col_n[0]);
+ try std.testing.expectEqual(@as(usize, 0), p.col_terms[0][0]);
+ try std.testing.expectEqual(@as(usize, 0), p.active);
+ for (p.panes[1..]) |slot| try std.testing.expect(slot == null);
const pane = p.panes[0].?;
try std.testing.expect(!pane.isTerminal());
const f = pane.file.?;
@@ -16470,6 +16485,13 @@ test "argv naming nothing boots an errors pane rather than failing the launch" {
// which is the half a bare "not found" leaves out.
try std.testing.expect(std.mem.indexOf(u8, f.content, "not found") != null);
try std.testing.expect(std.mem.indexOf(u8, f.content, "notes/tdoo.md") != null);
+
+ // THE PANE'S DIRECTORY IS THE LAUNCH DIRECTORY, asserted through the path
+ // because that is what `paneDir` reads: it decides where a `Grep` from
+ // this pane walks, where its `Newtty` spawns, and what its `Save`
+ // prefills. Passing "" here put the pane at `/+Errors`, i.e. rooted every
+ // one of those at `/`.
+ try std.testing.expectEqualStrings("/home/pardes-test/work/+Errors", f.path);
}
test "Esc back into a tty leaves its view at the prompt" {