diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-03 13:30:15 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-09-03 13:30:15 -0300 |
| commit | 3d8d4425c969d3df21915c9c14b144460a1c0086 (patch) | |
| tree | f54635093525f610630a2ca850a73ecd2df7393e /src/pardes.zig | |
| parent | e6c9f1726cca910c464fc807aefebc404c8e7a12 (diff) | |
| download | pardes-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.zig | 38 |
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" { |
