summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CHANGELOG.md13
-rw-r--r--src/main.zig50
-rw-r--r--src/pardes.zig38
3 files changed, 82 insertions, 19 deletions
diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md
index bb7e9ad5..2df1a82e 100644
--- a/src/CHANGELOG.md
+++ b/src/CHANGELOG.md
@@ -64,9 +64,16 @@
editor at all. A launch that names nothing now boots one `+Errors` pane
filling the window — acme's own vocabulary for output that came from the
program rather than from a word somebody clicked — saying `file or directory
- not found` and the argument AS TYPED. A `chdir` that fails on a directory
- that really is one is still `BadArgs`: that is a permission problem, not a
- typo, and the two want different answers.
+ not found` and the argument AS TYPED, in the directory it was typed in — an
+ output pane's directory is where a `Grep` from it walks, where its `Newtty`
+ spawns and what its `Save` prefills, so a pane rooted at `""` would have
+ pointed all three at `/`. A typo INSIDE pardes is refused by that shell in one
+ line instead: the hand-off block that keeps a pardes from stacking a second
+ full-screen UI inside a pane of the first says in its own comment that a word
+ naming nothing must not get through, and an `+Errors` boot would have made a
+ typo the one input that did. A `chdir` that fails on a directory that really
+ is one is still `BadArgs`: that is a permission problem, not a typo, and the
+ two want different answers.
- A filtered terminal costs what an unfiltered one does. `Filter`'s second
stage asked `RGB.contrast` for every cell it painted, and that call ends in
diff --git a/src/main.zig b/src/main.zig
index 92b59bc2..00eff433 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -305,8 +305,13 @@ fn nativeMain(init: std.process.Init) !void {
// rather than stacking a second full-screen UI inside one of its panes.
// The word is resolved here rather than sent raw because the outer
// instance resolves against ITS panes' directories, which are not ours.
- // A word naming nothing on disk sends nothing and falls through to the
- // classification below, which already refuses it — no second UI either way.
+ // A word naming nothing on disk is REFUSED HERE, in this shell, and does
+ // not fall through: the classification below used to refuse it too, and
+ // once it started booting an `+Errors` pane instead, a typo became the one
+ // input that stacked the second full-screen UI this whole block exists to
+ // prevent — and one with no shell pane in it, so the only way out is `Del`.
+ // The outer instance is not told either: `Look` on a word naming nothing
+ // is not something to do to somebody else's session.
//
// `--detach` is exempt for the same reason `--nested` is, arrived at from
// the other side: it stacks no UI at all. A detached session started from a
@@ -329,6 +334,17 @@ fn nativeMain(init: std.process.Init) !void {
.dir => |d| nested.sendLook(outer_pid, d, 0),
.file => |t| nested.sendLook(outer_pid, t.path, t.at.line),
.image => |t| nested.sendLook(outer_pid, t.path, 0),
+ // Nothing of that name. One line on this shell's stderr and out,
+ // which is what the paragraph above promises: the outer session is
+ // not disturbed and no UI is stacked. Said in words rather than
+ // returned as an error, because an error out of `main` is the
+ // stack trace this release stopped showing people for a typo.
+ .none => {
+ var buf: [4096]u8 = undefined;
+ const line = std.fmt.bufPrint(&buf, "pardes: file or directory not found: {s}\n", .{word}) catch "pardes: file or directory not found\n";
+ try std.Io.File.stderr().writeStreamingAll(init.io, line);
+ std.process.exit(1);
+ },
// an unreachable outer instance (an older build, a stale socket
// path) is not worth failing a launch over: run normally instead
else => false,
@@ -350,12 +366,30 @@ fn nativeMain(init: std.process.Init) !void {
.image => |t| opts.file = try arena.dupe(u8, t.path),
// Nothing of that name is there. A typo is not a reason to
// refuse to start: the session boots with one `+Errors` pane
- // naming what was asked for (pardes.zig `missing_path`). The
- // other arms stay `BadArgs` — a `.dir` here means `chdir`
- // refused a directory that IS one, which is a permission
- // problem and not a typo, and `.url`/`.pane` are targets no
- // launch can act on.
- .none => opts.missing_path = try arena.dupe(u8, a),
+ // naming what was asked for (pardes.zig `missing`).
+ //
+ // The LAUNCH DIRECTORY goes with it, and it is not decoration:
+ // an output pane's directory is where a `Grep` from it walks,
+ // where a `Newtty` spawns its shell and what a `Save` prefills.
+ // The first draft passed "" — copied from the board's boot
+ // buffer, which can afford it because that platform has no
+ // filesystem — and the pane came out at `/+Errors`, so `Grep`
+ // on the boot screen walked from the root of the filesystem.
+ //
+ // The other arms stay `BadArgs`. `.url` and `.pane` are targets
+ // no LAUNCH can act on, and `.dir` here is a directory that
+ // resolves but `chdir` refused, which is a permission problem
+ // rather than a typo. NOTE that the commonest permission case
+ // does not arrive here at all: `look.isDir` probes with
+ // `O_DIRECTORY|O_RDONLY`, so a directory you cannot read (say
+ // `/root`) fails that probe, resolves as `.file`, and dies in
+ // `file_pane.open` with `error.OpenFailed` out of `main` —
+ // still a stack trace at a human. Left as it was, because it is
+ // a different fault than the one this arm fixes.
+ .none => opts.missing = .{
+ .word = try arena.dupe(u8, a),
+ .dir = try arena.dupe(u8, std.mem.span(@as([*:0]u8, @ptrCast(cwd)))),
+ },
else => return error.BadArgs,
}
}
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" {