summaryrefslogtreecommitdiff
path: root/test/panes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 09:05:35 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commit0d04bc9214cccce42d74862b31376d49bdda3956 (patch)
treeb45286fe0847b08c76e6abd562f6e2f985322e17 /test/panes.zig
parent66c0106691e4de925011f314200e85ee265b7861 (diff)
downloadpardes-0d04bc9214cccce42d74862b31376d49bdda3956.tar.gz
pardes-0d04bc9214cccce42d74862b31376d49bdda3956.zip
Let s, S and | pressed in a tag answer for the tag's own text
A tag is a text like the body, so selecting on a regex and piping act on the text they are pressed in: a prompt records the text it answers for, the tag keeps the keyboard while it is typed, and a header's prompt goes on the active pane's band. `/` still searches the body from anywhere, as acme's Look from a tag searches the body. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'test/panes.zig')
-rw-r--r--test/panes.zig55
1 files changed, 41 insertions, 14 deletions
diff --git a/test/panes.zig b/test/panes.zig
index 9b800430..d8ed517f 100644
--- a/test/panes.zig
+++ b/test/panes.zig
@@ -293,6 +293,33 @@ const TagNavigationTests = struct {
p.update(.{ .key = .{ .cp = cp } });
}
+ test "s, S and | in a tag answer for the tag's own text; / still searches the body" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 100, .rows = 20 });
+ defer p.deinit();
+ const pane = try p.setTestFile("Some body\n");
+ try setTag(pane, " Save Del Snarf");
+ key(p, ':');
+ key(p, 'x'); // the tag's line
+ key(p, 's');
+ try std.testing.expect(pane.prompt == .search and pane.prompt_for == .tag and pane.focus == .tag);
+ p.update(.{ .key = .{ .cp = 'S', .text = "S" } });
+ key(p, Key.enter);
+ try std.testing.expect(pane.focus == .tag and pane.prompt == .none);
+ var ranges: [panes.Text.max_selections]modal.Selection = undefined;
+ const text = try pardes.edit.flatSurface(p, &pane.tag);
+ const got = pane.tag.ranges(text, 0, &ranges);
+ try std.testing.expectEqual(@as(usize, 2), got.n);
+ for (ranges[0..got.n]) |range| try std.testing.expectEqualStrings("S", text[@min(range.anchor, range.head)..@max(range.anchor, range.head)]);
+ // the body's selection is its own
+ try std.testing.expectEqual(@as(u8, 0), pane.body.nsel);
+ // `/` from the tag is the body's search, as acme's Look from a tag is
+ key(p, ',');
+ key(p, '/');
+ try std.testing.expect(pane.prompt == .search and pane.prompt_for == .body and pane.focus == .body);
+ key(p, Key.escape);
+ try std.testing.expect(pane.prompt == .none and pane.focus == .body);
+ }
+
fn window(p: *Pardes, letter: u21) void {
p.update(.{ .key = .{ .cp = 'w', .ctrl = true } });
key(p, letter);
@@ -345,7 +372,7 @@ const TagNavigationTests = struct {
try std.testing.expectEqual(@as(i32, 6), first.tag.cur_col);
key(p, Key.escape);
key(p, ':');
- p.startPrompt(first, .{ .search = config.search_marker });
+ p.startPrompt(first, &first.body, .{ .search = config.search_marker });
key(p, Key.escape);
key(p, ':');
try std.testing.expectEqual(@as(i32, 6), first.tag.cur_col);
@@ -1483,7 +1510,7 @@ test "paste into pane commands and prompts never inserts into the body" {
p.update(.{ .paste = " Custom\nCommand" });
try std.testing.expect(std.mem.indexOf(u8, pardes.tagline.curTail(pane), " Custom\nCommand") != null);
p.update(.{ .key = .{ .cp = Key.escape } });
- p.startPrompt(pane, .{ .search = config.search_marker });
+ p.startPrompt(pane, &pane.body, .{ .search = config.search_marker });
p.update(.{ .paste = "t\r\nwo" });
try std.testing.expectEqualStrings(config.search_marker ++ "two", pane.promptText().?);
try std.testing.expect(std.mem.indexOf(u8, pardes.tagline.curTail(pane), " Custom\nCommand") != null);
@@ -1806,9 +1833,9 @@ test "a prompt's entry and cancellation leave the tag, the body and its cursor a
if (case.key) |key| {
p.update(.{ .key = key });
} else if (case.save) {
- p.startPrompt(pane, .save);
+ p.startPrompt(pane, &pane.body, .save);
} else {
- p.startPrompt(pane, .{ .search = config.search_marker });
+ p.startPrompt(pane, &pane.body, .{ .search = config.search_marker });
}
// The answer is typed into the prompt's own line, never the tag.
try std.testing.expect(pane.focus == .body);
@@ -1838,8 +1865,8 @@ test "a prompt opens whatever the tag already holds" {
try setTag(pane, full);
for (0..3) |case| {
switch (case) {
- 0 => p.startPrompt(pane, .{ .search = config.search_marker }),
- 1 => p.startPrompt(pane, .save),
+ 0 => p.startPrompt(pane, &pane.body, .{ .search = config.search_marker }),
+ 1 => p.startPrompt(pane, &pane.body, .save),
else => p.update(.{ .key = .{ .cp = '|' } }),
}
try std.testing.expect(pane.prompt != .none);
@@ -1871,9 +1898,9 @@ test "a prompt owns the pipe behavior through cancellation and submission" {
pane.body.cur_col = 2;
pane.body.vsel = .{ .active = true, .row = 0, .col = 0, .explicit = true };
for (std.enums.values(modal.Normal.PipeBehavior)) |how| {
- p.startPrompt(pane, .{ .pipe = .append });
+ p.startPrompt(pane, &pane.body, .{ .pipe = .append });
p.update(.{ .key = .{ .cp = Key.escape } });
- p.startPrompt(pane, .{ .pipe = how });
+ p.startPrompt(pane, &pane.body, .{ .pipe = how });
try std.testing.expectEqual(how, pane.prompt.pipe);
p.update(.{ .key = .{ .cp = 'c', .text = "cat" } });
p.update(.{ .key = .{ .cp = Key.enter } });
@@ -1915,19 +1942,19 @@ test "prompt cancellation restores the selection before regex previews" {
}
test "a prompt that cannot be opened leaves the open one as it was" {
- const Request = @typeInfo(@TypeOf(Pardes.startPrompt)).@"fn".params[2].type.?;
+ const Request = @typeInfo(@TypeOf(Pardes.startPrompt)).@"fn".params[3].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");
- p.startPrompt(pane, .{ .search = config.search_marker });
+ p.startPrompt(pane, &pane.body, .{ .search = config.search_marker });
p.update(.{ .key = .{ .cp = 'p', .text = "prior" } });
pane.body.normal = .{ .count = 9, .prefix = .goto };
const previous_normal = pane.body.normal;
_ = p.scratch.reset(.free_all);
failing.fail_index = failing.alloc_index;
- p.startPrompt(pane, request);
+ p.startPrompt(pane, &pane.body, request);
try std.testing.expect(failing.has_induced_failure);
try std.testing.expect(pane.prompt == .search);
try std.testing.expectEqualStrings(config.search_marker ++ "prior", pane.promptText().?);
@@ -1943,7 +1970,7 @@ test "Save's prompt starts from the pane's directory" {
for ([_][]const u8{ "/dír space", "/dír space/", "" }) |dir| {
try pane.setOwnedCwd(dir);
const slash = if (dir.len == 0 or dir[dir.len - 1] != '/') "/" else "";
- p.startPrompt(pane, .save);
+ p.startPrompt(pane, &pane.body, .save);
try std.testing.expect(pane.prompt == .save);
const answer = pane.promptText().?;
try std.testing.expect(std.mem.startsWith(u8, answer, config.save_marker));
@@ -1954,7 +1981,7 @@ test "Save's prompt starts from the pane's directory" {
}
test "a prompt opens with one allocation" {
- const Request = @typeInfo(@TypeOf(Pardes.startPrompt)).@"fn".params[2].type.?;
+ const Request = @typeInfo(@TypeOf(Pardes.startPrompt)).@"fn".params[3].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 });
@@ -1962,7 +1989,7 @@ test "a prompt opens with one allocation" {
const pane = try p.setTestFile("one\n");
_ = p.scratch.reset(.free_all);
const before = failing.alloc_index;
- p.startPrompt(pane, request);
+ p.startPrompt(pane, &pane.body, request);
try std.testing.expectEqual(before + 1, failing.alloc_index);
try std.testing.expect(pane.focus == .body and pane.input.mode == .insert);
}