summaryrefslogtreecommitdiff
path: root/test/output.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-11 12:38:56 -0300
committerGabriel Schneider <[email protected]>2026-09-15 17:24:42 -0300
commit682e237df7e8b22f820d14a4adee58e6e2f84268 (patch)
tree5d888cb46f1a49e89fc55e2a4c232d3af0265b1d /test/output.zig
parent6878e1c309172d624c0d7a3555f6f0217ae6770e (diff)
downloadpardes-682e237df7e8b22f820d14a4adee58e6e2f84268.tar.gz
pardes-682e237df7e8b22f820d14a4adee58e6e2f84268.zip
trunk: resume before the Reload experiment
Empty marker on the last pre-Reload change. Keep the Reload experiment on reload (3801914), its first change on reload-start (200a1fc), and the unfinished performance investigation on reload-perf-wip.
Diffstat (limited to 'test/output.zig')
-rw-r--r--test/output.zig87
1 files changed, 85 insertions, 2 deletions
diff --git a/test/output.zig b/test/output.zig
index 577b8e73..fd4252e1 100644
--- a/test/output.zig
+++ b/test/output.zig
@@ -11,6 +11,49 @@ const platform = pardes.platform;
const font_picker = pardes.font_picker;
const fonts = if (font_picker) pardes.fonts else struct {};
+test "search tint survives wrapping and theme changes while selections remain authoritative" {
+ const text = "nested/path/a.txt:1:5-7 let abc = 1;\n";
+ for ([_]u16{ 30, 80 }) |width| {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = width, .rows = 12 });
+ defer p.deinit();
+ const pane = try p.setTestFile(text);
+ pane.file.?.output = .{ .from = .search };
+ var frame = std.heap.ArenaAllocator.init(std.testing.allocator);
+ defer frame.deinit();
+ for ([_]bool{ false, true }) |bottom| {
+ p.settings.tag_bottom = bottom;
+ for ([_]bool{ true, false, true }) |colors| {
+ p.settings.colors = colors;
+ const surface = try p.render(frame.allocator());
+ var marked: usize = 0;
+ for (surface.cells) |cell| {
+ if (std.meta.eql(cell.style.bg, pardes.Color{ .rgb = p.theme().search_bg.? })) marked += 1;
+ }
+ try std.testing.expectEqual(@as(usize, if (colors) 3 else 0), marked);
+ _ = frame.reset(.retain_capacity);
+ }
+ }
+ // The body changes immediately even when dark chrome is still fading.
+ try std.testing.expect(p.executeBuiltinLine(0, "Theme daybreak"));
+ const light = try p.render(frame.allocator());
+ var light_matches: usize = 0;
+ for (light.cells) |cell| {
+ if (!std.meta.eql(cell.style.bg, pardes.Color{ .rgb = p.theme().search_bg.? })) continue;
+ try std.testing.expectEqualDeep(pardes.Color{ .rgb = p.theme().search_fg.? }, cell.style.fg);
+ light_matches += 1;
+ }
+ try std.testing.expectEqual(@as(usize, 3), light_matches);
+ pane.mode = .normal;
+ pane.cur_row = 0;
+ pane.cur_col = @intCast(std.mem.indexOf(u8, text, "abc").? + 2);
+ pane.vsel = .{ .active = true, .row = 0, .col = pane.cur_col - 2, .explicit = true };
+ const selected = try p.render(frame.allocator());
+ for (selected.cells) |cell|
+ try std.testing.expect(!std.meta.eql(cell.style.bg, pardes.Color{ .rgb = p.theme().search_bg.? }));
+ try std.testing.expectEqualStrings(text, pane.file.?.content);
+ }
+}
+
test "location results sort filenames and numeric positions while remapping their anchor" {
const before = "z.zig:1:1 z\nb.zig:10:2-3 ten\nb.zig:2:10-12 two-b\nb.zig:2:2-3 two-a\na.zig:99:1 a\nb.zig:2:2-3 duplicate\n";
const after = "a.zig:99:1 a\nb.zig:2:2-3 two-a\nb.zig:2:2-3 duplicate\nb.zig:2:10-12 two-b\nb.zig:10:2-3 ten\nz.zig:1:1 z\n";
@@ -326,6 +369,8 @@ test "Mini syntax colors survive toggles themes and rendering without source acc
const id = p.active;
const mini = p.panes[id].?;
const keyword = std.mem.indexOfScalar(u8, mini.file.?.mini.?.colors, @intFromEnum(pardes.syntax.Syn.keyword)) orelse return error.MissingKeywordColor;
+ // Direct pane construction bypasses the host update's layout sync.
+ p.update(.{ .resize = .{ .cols = 80, .rows = 24 } });
const position = modal.positionAt(mini.file.?.content, keyword);
const column = panes.File.displayWidth(modal.lineSlice(mini.file.?.content, position.row)[0..position.col]);
try tmp.dir.deleteFile(std.testing.io, "demo.zig");
@@ -391,7 +436,7 @@ test "Config prints the startup path and refreshes its one output" {
const out = p.panes[opened].?.file.?;
for ([_][]const u8{
"Startup config: " ++ path ++ "\n",
- "Theme: helix\n",
+ "Theme: orchard\n",
"Shell requested (new panes): " ++ config.default_shell ++ " (default)\n",
"Shell effective (last spawn): (none)\n",
"Shell pending: on\n",
@@ -691,6 +736,44 @@ test "N exactly reverses n across output panes and the ring seam" {
try std.testing.expect(saw_first and saw_second);
}
+test "ThemeSel groups native themes first and skips headings in both directions" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 100, .rows = 40 });
+ defer p.deinit();
+ const source = try p.setTestFile("notes\n");
+ p.setThemeIndex(pardes.themes.len - 1);
+ const original_theme = p.settings.theme;
+ try std.testing.expect(p.executeBuiltinLine(0, "ThemeSel"));
+ const tid = source.search_pane orelse return error.MissingThemeList;
+ const list = p.panes[tid].?;
+ const content = list.file.?.content;
+ try std.testing.expectEqual(@as(usize, 0), p.active);
+ try std.testing.expectEqual(@as(i32, 0), list.cur_row);
+ try std.testing.expectEqual(@as(usize, 0), list.file.?.scroll);
+ try std.testing.expectEqualStrings("# Pardes themes", modal.lineSlice(content, 0));
+ try std.testing.expectEqualStrings("# Legacy and imported themes", modal.lineSlice(content, pardes.native_theme_count + 2));
+ try std.testing.expectEqual(@as(usize, pardes.themes.len + 3), std.mem.count(u8, content, "\n"));
+ for (pardes.themes, 0..) |theme, i| {
+ const row = i + 1 + @as(usize, if (i >= pardes.native_theme_count) 2 else 0);
+ const line = modal.lineSlice(content, row);
+ try std.testing.expect(std.mem.startsWith(u8, line, "Theme "));
+ try std.testing.expectEqualStrings(theme.name, line["Theme ".len..]);
+ }
+ // Opening an imported theme's selector must still begin with the natives.
+ // Walk across the section break without selecting or executing a heading.
+ for (pardes.themes[0 .. pardes.native_theme_count + 1]) |theme| {
+ p.update(.{ .key = .{ .cp = 'n' } });
+ try std.testing.expectEqual(tid, p.active);
+ const selected = selectedOutputText(list) orelse return error.MissingThemeSelection;
+ try std.testing.expectEqualStrings(theme.name, selected["Theme ".len..]);
+ try std.testing.expectEqual(original_theme, p.settings.theme);
+ }
+ p.update(.{ .key = .{ .cp = 'N' } });
+ const previous = selectedOutputText(list) orelse return error.MissingThemeSelection;
+ try std.testing.expectEqualStrings(pardes.themes[pardes.native_theme_count - 1].name, previous["Theme ".len..]);
+ p.update(.{ .key = .{ .cp = Key.tab } });
+ try std.testing.expectEqual(pardes.native_theme_count - 1, p.settings.theme);
+}
+
test "n/N selects command outputs by whole row" {
if (platform == .web) return;
var cwd_buf: [4096]u8 = undefined;
@@ -705,7 +788,7 @@ test "n/N selects command outputs by whole row" {
const themes = p.panes[tid].?;
try std.testing.expect(panes.Output.fileTraits(themes.file.?.output).commands);
p.update(.{ .key = .{ .cp = 'n' } });
- const row0 = std.mem.trimEnd(u8, modal.lineSlice(themes.file.?.content, 0), " \t\r");
+ const row0 = std.mem.trimEnd(u8, modal.lineSlice(themes.file.?.content, 1), " \t\r");
try std.testing.expectEqualStrings(row0, selectedOutputText(themes) orelse "");
const theme_before = p.settings.theme;
p.update(.{ .key = .{ .cp = 'n' } });