summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-27 23:42:53 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commit9584b0682b45f17aaa42157540947904d867777a (patch)
treefaac80b84c02e9611e9df9ff0e210c1d1c895a84 /test
parent95a3a1bb822e583988bfe0f312d0ef219b27a454 (diff)
downloadpardes-9584b0682b45f17aaa42157540947904d867777a.tar.gz
pardes-9584b0682b45f17aaa42157540947904d867777a.zip
Give prompts their own line to type into instead of the tag's tail
A search, pipe or Save prompt wrote its marker and answer onto the end of the tag, so the tag's cursor, its selection, its 9P writes and a prompt abandoned by a click all shared one buffer: the abandoned text stayed in the tag and a 9P write landed inside the prompt. The answer is now its own Text on the pane, typed in insert mode and shown on the notice band where it was already drawn, and the tag never holds any of it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'test')
-rw-r--r--test/panes.zig154
-rw-r--r--test/pdf.zig4
-rw-r--r--test/snapshots/find.golden1
-rw-r--r--test/snapshots/find.snap3
-rw-r--r--test/snapshots/tagbottomshort.golden2
-rw-r--r--test/snapshots/tagbottomshort.snap9
6 files changed, 45 insertions, 128 deletions
diff --git a/test/panes.zig b/test/panes.zig
index 4c969ab5..f1adafee 100644
--- a/test/panes.zig
+++ b/test/panes.zig
@@ -1353,8 +1353,9 @@ test "paste into pane commands and prompts never inserts into the body" {
try std.testing.expect(std.mem.endsWith(u8, pane.tagSlice(), " CustomCommand"));
p.update(.{ .key = .{ .cp = Key.escape } });
p.startPrompt(pane, .{ .search = config.search_marker });
- p.update(.{ .paste = "two" });
- try std.testing.expect(std.mem.endsWith(u8, pane.tagSlice(), "two"));
+ p.update(.{ .paste = "t\r\nwo" });
+ try std.testing.expectEqualStrings(config.search_marker ++ "two", pane.promptText().?);
+ try std.testing.expect(std.mem.endsWith(u8, pane.tagSlice(), " CustomCommand"));
try std.testing.expectEqualStrings("one two one\n", pane.file.?.content);
p.update(.{ .key = .{ .cp = Key.escape } });
try std.testing.expect(std.mem.endsWith(u8, pane.tagSlice(), " CustomCommand"));
@@ -1646,7 +1647,7 @@ test "terminal eviction and lower-region scrolling in one read preserve a surviv
try std.testing.expectEqualStrings("kept edit", pane.ovl.?.text);
}
-test "tag prompt entry and cancellation preserve exact text and cursor state" {
+test "a prompt's entry and cancellation leave the tag, the body and its cursor as they were" {
const Case = struct { key: ?Key = null, save: bool = false, marker: []const u8 };
const cases = [_]Case{
.{ .marker = config.search_marker },
@@ -1665,8 +1666,6 @@ test "tag prompt entry and cancellation preserve exact text and cursor state" {
@memcpy(pane.tag_tail[0..tail.len], tail);
pane.tag_tail_len = tail.len;
pane.tag_init = true;
- pane.tag_sel = true;
- pane.tag_col = 1;
pane.body.cur_row = 1;
pane.body.cur_col = 2;
pane.body.cur_pinned = true;
@@ -1677,16 +1676,16 @@ test "tag prompt entry and cancellation preserve exact text and cursor state" {
} else {
p.startPrompt(pane, .{ .search = config.search_marker });
}
- try std.testing.expect(pane.tag_edit);
- try std.testing.expect(!pane.tag_sel);
- try std.testing.expectEqual(.insert, pane.body.mode);
+ // The answer is typed into the prompt's own line, never the tag.
+ try std.testing.expect(!pane.tag_edit);
+ try std.testing.expectEqual(.insert, pane.input.mode);
+ try std.testing.expectEqualStrings(case.marker, pane.promptText().?);
+ try std.testing.expectEqual(@as(i32, @intCast(case.marker.len)), pane.input.cur_col);
+ try std.testing.expectEqualStrings(tail, pane.tag_tail[0..pane.tag_tail_len]);
try std.testing.expectEqualDeep(modal.Normal.State{}, pane.body.normal);
- try std.testing.expectEqualStrings(tail, pane.tag_tail[0..tail.len]);
- try std.testing.expectEqualStrings(case.marker, pane.tag_tail[tail.len..pane.tag_tail_len]);
- try std.testing.expectEqual(pane.file.?.path.len + pane.tag_tail_len, pane.tag_col);
p.update(.{ .key = .{ .cp = Key.escape } });
try std.testing.expect(pane.prompt == .none);
- try std.testing.expect(!pane.tag_edit);
+ try std.testing.expect(pane.input.own == null);
try std.testing.expectEqual(.normal, pane.body.mode);
try std.testing.expectEqualStrings(tail, pane.tag_tail[0..pane.tag_tail_len]);
try std.testing.expectEqual(@as(i32, 1), pane.body.cur_row);
@@ -1695,28 +1694,23 @@ test "tag prompt entry and cancellation preserve exact text and cursor state" {
}
}
-test "tag prompt capacity refusal leaves the existing tail and input state untouched" {
+test "a prompt opens whatever the tag already holds" {
const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true });
defer p.deinit();
const pane = try p.setTestFile("one\n");
@memset(&pane.tag_tail, 'x');
- pane.tag_tail_len = pane.tag_tail.len - 1;
+ pane.tag_tail_len = pane.tag_tail.len;
pane.tag_init = true;
- pane.tag_col = 17;
- pane.tag_sel = true;
for (0..3) |case| {
switch (case) {
0 => p.startPrompt(pane, .{ .search = config.search_marker }),
1 => p.startPrompt(pane, .save),
else => p.update(.{ .key = .{ .cp = '|' } }),
}
- try std.testing.expect(pane.prompt == .none);
- try std.testing.expect(!pane.tag_edit);
- try std.testing.expect(pane.tag_sel);
- try std.testing.expectEqual(.normal, pane.body.mode);
- try std.testing.expectEqual(@as(u16, 17), pane.tag_col);
- try std.testing.expectEqual(pane.tag_tail.len - 1, pane.tag_tail_len);
+ try std.testing.expect(pane.prompt != .none);
+ try std.testing.expectEqual(pane.tag_tail.len, pane.tag_tail_len);
for (pane.tag_tail) |byte| try std.testing.expectEqual(@as(u8, 'x'), byte);
+ p.update(.{ .key = .{ .cp = Key.escape } });
}
}
@@ -1735,7 +1729,7 @@ test "generated output refuses every pipe prompt before seeding the tag" {
}
}
-test "tag prompt owns the pipe behavior through cancellation and submission" {
+test "a prompt owns the pipe behavior through cancellation and submission" {
const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true });
defer p.deinit();
const pane = try p.setTestFile("one\ntwo\n");
@@ -1746,7 +1740,7 @@ test "tag prompt owns the pipe behavior through cancellation and submission" {
p.startPrompt(pane, .{ .pipe = .append });
p.update(.{ .key = .{ .cp = Key.escape } });
p.startPrompt(pane, .{ .pipe = how });
- try std.testing.expectEqual(how, pane.prompt.pipe.how);
+ try std.testing.expectEqual(how, pane.prompt.pipe);
p.update(.{ .key = .{ .cp = 'c', .text = "cat" } });
p.update(.{ .key = .{ .cp = Key.enter } });
const pending = p.pipe.wait orelse return error.MissingPipe;
@@ -1758,7 +1752,7 @@ test "tag prompt owns the pipe behavior through cancellation and submission" {
}
}
-test "tag prompt cancellation restores the selection before regex previews" {
+test "prompt cancellation restores the selection before regex previews" {
const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true });
defer p.deinit();
const pane = try p.setTestFile("one two one");
@@ -1786,133 +1780,57 @@ test "tag prompt cancellation restores the selection before regex previews" {
}
}
-test "tag prompt prefix allocation failure preserves an already seeded prompt" {
+test "a prompt that cannot be opened leaves the open one as it was" {
const Request = @typeInfo(@TypeOf(Pardes.startPrompt)).@"fn".params[2].type.?;
for ([_]Request{ .{ .search = config.search_marker }, .save, .{ .pipe = .replace }, .{ .pipe = .ignore }, .{ .pipe = .insert }, .{ .pipe = .append } }) |request| {
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});
const p = try Pardes.init(failing.allocator(), .{ .tty_only = true });
defer p.deinit();
const pane = try p.setTestFile("one\n");
- const tail = " keep /prior";
- @memcpy(pane.tag_tail[0..tail.len], tail);
- pane.tag_tail_len = tail.len;
- pane.tag_init = true;
- pane.prompt = .{ .search = 5 };
- pane.tag_edit = true;
- pane.tag_sel = true;
- pane.tag_col = 14;
- pane.tag_anchor = 7;
+ p.startPrompt(pane, .{ .search = config.search_marker });
+ p.update(.{ .key = .{ .cp = 'p', .text = "prior" } });
pane.body.normal = .{ .count = 9, .prefix = .goto };
- const previous_prompt = pane.prompt;
const previous_normal = pane.body.normal;
_ = p.scratch.reset(.free_all);
failing.fail_index = failing.alloc_index;
p.startPrompt(pane, request);
try std.testing.expect(failing.has_induced_failure);
- try std.testing.expectEqualDeep(previous_prompt, pane.prompt);
+ try std.testing.expect(pane.prompt == .search);
+ try std.testing.expectEqualStrings(config.search_marker ++ "prior", pane.promptText().?);
try std.testing.expectEqualDeep(previous_normal, pane.body.normal);
- try std.testing.expectEqualStrings(tail, pane.tag_tail[0..pane.tag_tail_len]);
- try std.testing.expect(pane.tag_edit and pane.tag_sel);
- try std.testing.expectEqual(.normal, pane.body.mode);
- try std.testing.expectEqual(@as(u16, 14), pane.tag_col);
- try std.testing.expectEqual(@as(u16, 7), pane.tag_anchor);
+ try std.testing.expect(!pane.tag_init and !pane.tag_edit);
}
}
-test "tag prompt Save requires room for its complete directory prefix" {
+test "Save's prompt starts from the pane's directory" {
const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true });
defer p.deinit();
const pane = try p.setTestFile("one\n");
for ([_][]const u8{ "/dír space", "/dír space/", "" }) |dir| {
try pane.setOwnedCwd(dir);
- const slash: usize = @intFromBool(dir.len == 0 or dir[dir.len - 1] != '/');
- const needed = config.save_marker.len + dir.len + slash;
- for ([_]usize{ 1, 0 }) |shortfall| {
- @memset(&pane.tag_tail, 'x');
- pane.tag_tail_len = pane.tag_tail.len - needed + shortfall;
- pane.tag_init = true;
- pane.tag_edit = false;
- pane.tag_sel = true;
- pane.tag_col = 17;
- const start = pane.tag_tail_len;
- p.startPrompt(pane, .save);
- if (shortfall != 0) {
- try std.testing.expect(pane.prompt == .none);
- try std.testing.expectEqual(start, pane.tag_tail_len);
- try std.testing.expect(!pane.tag_edit and pane.tag_sel);
- try std.testing.expectEqual(@as(u16, 17), pane.tag_col);
- for (pane.tag_tail) |byte| try std.testing.expectEqual(@as(u8, 'x'), byte);
- } else {
- try std.testing.expect(pane.prompt == .save);
- try std.testing.expectEqual(pane.tag_tail.len, pane.tag_tail_len);
- try std.testing.expectEqualStrings(config.save_marker, pane.tag_tail[start..][0..config.save_marker.len]);
- try std.testing.expectEqualStrings(dir, pane.tag_tail[start + config.save_marker.len ..][0..dir.len]);
- try std.testing.expectEqual(@as(u8, '/'), pane.tag_tail[pane.tag_tail_len - 1]);
- p.update(.{ .key = .{ .cp = Key.escape } });
- }
- }
+ const slash = if (dir.len == 0 or dir[dir.len - 1] != '/') "/" else "";
+ p.startPrompt(pane, .save);
+ try std.testing.expect(pane.prompt == .save);
+ const answer = pane.promptText().?;
+ try std.testing.expect(std.mem.startsWith(u8, answer, config.save_marker));
+ try std.testing.expectEqualStrings(dir, answer[config.save_marker.len..][0..dir.len]);
+ try std.testing.expectEqualStrings(slash, answer[config.save_marker.len + dir.len ..]);
+ p.update(.{ .key = .{ .cp = Key.escape } });
}
}
-test "tag prompt successful seeded entry needs one scratch allocation" {
+test "a prompt opens with one allocation" {
const Request = @typeInfo(@TypeOf(Pardes.startPrompt)).@"fn".params[2].type.?;
for ([_]Request{ .{ .search = config.search_marker }, .save, .{ .pipe = .replace }, .{ .pipe = .ignore }, .{ .pipe = .insert }, .{ .pipe = .append } }) |request| {
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});
const p = try Pardes.init(failing.allocator(), .{ .tty_only = true });
defer p.deinit();
const pane = try p.setTestFile("one\n");
- pane.tag_tail_len = 0;
- pane.tag_init = true;
_ = p.scratch.reset(.free_all);
const before = failing.alloc_index;
p.startPrompt(pane, request);
try std.testing.expectEqual(before + 1, failing.alloc_index);
- try std.testing.expect(pane.tag_edit and pane.body.mode == .insert);
- }
-}
-
-test "tag prompt failure may initialize only the default tail" {
- const Request = @typeInfo(@TypeOf(Pardes.startPrompt)).@"fn".params[2].type.?;
- for ([_]Request{ .{ .search = config.search_marker }, .save, .{ .pipe = .replace }, .{ .pipe = .ignore }, .{ .pipe = .insert }, .{ .pipe = .append } }) |request| {
- var saw_seeded_failure = false;
- var succeeded = false;
- for (0..16) |failure| {
- var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});
- const p = try Pardes.init(failing.allocator(), .{ .tty_only = true });
- defer p.deinit();
- const pane = try p.setTestFile("one\n");
- const path = try p.gpa.dupe(u8, "/" ++ "a" ** 2048 ++ ".zig");
- p.gpa.free(pane.file.?.path);
- pane.file.?.path = path;
- p.seedTail(pane);
- try std.testing.expect(pane.tag_init);
- const default_tail = pane.tag_tail;
- const default_len = pane.tag_tail_len;
- pane.tag_init = false;
- pane.tag_tail_len = 0;
- pane.tag_col = 17;
- pane.tag_sel = true;
- pane.body.normal = .{ .count = 9, .prefix = .goto };
- const previous_normal = pane.body.normal;
- _ = p.scratch.reset(.free_all);
- failing.fail_index = failing.alloc_index + failure;
- p.startPrompt(pane, request);
- if (!failing.has_induced_failure) {
- try std.testing.expect(pane.tag_edit and pane.body.mode == .insert);
- succeeded = true;
- break;
- }
- try std.testing.expect(pane.prompt == .none);
- try std.testing.expect(!pane.tag_edit and pane.tag_sel);
- try std.testing.expectEqual(.normal, pane.body.mode);
- try std.testing.expectEqualDeep(previous_normal, pane.body.normal);
- try std.testing.expectEqual(@as(u16, 17), pane.tag_col);
- if (pane.tag_init) {
- saw_seeded_failure = true;
- try std.testing.expectEqualStrings(default_tail[0..default_len], pane.tag_tail[0..pane.tag_tail_len]);
- } else try std.testing.expectEqual(@as(usize, 0), pane.tag_tail_len);
- }
- try std.testing.expect(succeeded and saw_seeded_failure);
+ try std.testing.expect(!pane.tag_edit and pane.input.mode == .insert);
}
}
diff --git a/test/pdf.zig b/test/pdf.zig
index 52fb62aa..5bc271d4 100644
--- a/test/pdf.zig
+++ b/test/pdf.zig
@@ -1113,9 +1113,9 @@ test "PDF normal adapter consumes unsupported actions and navigates page fallbac
p.update(.{ .key = .{ .cp = '!' } });
try std.testing.expect(!p.leader_on);
p.update(.{ .key = .{ .cp = '/' } });
- try std.testing.expect(pane.tag_edit);
+ try std.testing.expect(pane.prompt == .search and !pane.tag_edit);
p.update(.{ .key = .{ .cp = Key.escape } });
- try std.testing.expect(!pane.tag_edit);
+ try std.testing.expect(pane.prompt == .none);
}
test "Escape cancels PDF chrome in place and Shift-Escape leaves the pane" {
diff --git a/test/snapshots/find.golden b/test/snapshots/find.golden
index 12dac242..b6b7b322 100644
--- a/test/snapshots/find.golden
+++ b/test/snapshots/find.golden
@@ -81,7 +81,6 @@
|4:
|30: 1 gamma body Last
== snap nomatch grid=120x41 cursor=7,39
-|2: /tmp/pardes-snap/find/cwd/here.txt Save Tty Collapse Del Find /
|3: 1 Find
|18: /tmp/pardes-snap/find/cwd/+Search Save Tty Collapse Del
|19: 1
diff --git a/test/snapshots/find.snap b/test/snapshots/find.snap
index fbd585f7..fe1ecad3 100644
--- a/test/snapshots/find.snap
+++ b/test/snapshots/find.snap
@@ -64,7 +64,8 @@ key enter
stable 700 15000
snap back
# executing the WORD Find (middle-click in a body) arms the same input — the
-# enum IS the dispatch — and Esc abandons it, restoring the tag tail
+# enum IS the dispatch — and Esc abandons it. The input is its own line, so an
+# abandoned one leaves nothing behind in the tag
press middle 8 4
release middle 8 4
stable 700 10000
diff --git a/test/snapshots/tagbottomshort.golden b/test/snapshots/tagbottomshort.golden
index a5d53fe2..c11fe4ac 100644
--- a/test/snapshots/tagbottomshort.golden
+++ b/test/snapshots/tagbottomshort.golden
@@ -21,7 +21,7 @@
| New Tty Find Grep Joincol Delcol
| 1 one /ZZ
| /tmp/pardes-snap/tagbottomshort/cwd/s.txt Save Tty Collapse Del
-== snap tag-only grid=80x2 cursor=70,1
+== snap tag-only grid=80x2 cursor=79,1
|Newcol Joincol Find Grep Help Changelog Tutor Dump NextColor Debug Kill
| /tmp/pardes-snap/tagbottomshort/cwd/s.txt Save Tty Collapse Del
== snap regrown grid=80x11 cursor=79,2
diff --git a/test/snapshots/tagbottomshort.snap b/test/snapshots/tagbottomshort.snap
index 2574df36..58da6a87 100644
--- a/test/snapshots/tagbottomshort.snap
+++ b/test/snapshots/tagbottomshort.snap
@@ -10,8 +10,8 @@ wait 8000 s.txt
stable 700 20000
key space t b
stable 700 15000
-# `/` with room to spare: the prompt is cut off the tagline (grid 9) and drawn
-# on grid 8, the last body row, with the cursor.
+# `/` with room to spare: the prompt is its own line, drawn on grid 8, the last
+# body row, with the cursor; the tagline (grid 9) never holds any of it.
key /
send ZZ
stable 600 8000
@@ -27,9 +27,8 @@ resize 4 80
stable 700 10000
snap one-body
# the tagline alone. No body row, so nothing of the prompt is drawn anywhere —
-# not over the tag, not on the row above the pane. The tagline itself is
-# untouched: the marker sits after the builtins, so cutting the tail there
-# leaves every column of it exactly as `tag-only` would look unarmed.
+# not over the tag, not on the row above the pane, and its caret with it. The
+# tagline itself is untouched, exactly as `tag-only` would look unarmed.
resize 2 80
stable 700 10000
snap tag-only