diff options
Diffstat (limited to 'test/panes.zig')
| -rw-r--r-- | test/panes.zig | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/test/panes.zig b/test/panes.zig index 97dc3927..a7e59923 100644 --- a/test/panes.zig +++ b/test/panes.zig @@ -518,6 +518,122 @@ const JumpSelectionTests = struct { } }; +const TagNameTintTests = struct { + const name_fg = [3]u8{ 231, 137, 43 }; + const tag_fg = [3]u8{ 111, 122, 133 }; + + fn configure(p: *Pardes) void { + var theme = p.theme().*; + theme.tag_name_fg = name_fg; + theme.tag_fg = tag_fg; + p.custom_theme = theme; + p.chrome_animation.snap(pardes.ChromeTheme.fromTheme(p.theme())); + p.settings.focus_tint = false; + p.presentation.enabled = false; + } + + fn check(p: *Pardes, id: usize, name: []const u8) !usize { + const pane = p.panes[id].?; + var arena = std.heap.ArenaAllocator.init(p.gpa); + defer arena.deinit(); + const surface = try p.render(arena.allocator()); + const text = try p.tagText(arena.allocator(), pane); + const base = std.mem.lastIndexOfScalar(u8, name, '/') orelse return error.MissingDirectory; + const rect = p.rects[id]; + const y = if (p.settings.tag_bottom) rect.y + rect.h - pardes.BOX_H else rect.y; + const left = rect.x + config.GUTTER; + var visible_name: usize = 0; + var at: usize = 0; + while (at < text.len) : (at = modal.nextGrapheme(text, at)) { + const start: i32 = @as(i32, @intCast(panes.File.rawDisplayCol(text, at))) - pane.tag_scroll; + const end = start + @as(i32, @intCast(panes.File.displayWidth(text[at..modal.nextGrapheme(text, at)]))); + var col = @max(0, start); + while (col < end and col < rect.w - config.GUTTER) : (col += 1) { + const in_name = at > base and at < name.len; + const fg = if (in_name) name_fg else tag_fg; + try std.testing.expectEqual(pardes.Color{ .rgb = fg }, surface.at(left + @as(u16, @intCast(col)), y).style.fg); + if (in_name) visible_name += 1; + } + } + return visible_name; + } + + test "tag filename tint excludes directories dirty marker and output commands" { + for ([_]bool{ false, true }) |bottom| { + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 100, .rows = 24 }); + defer p.deinit(); + const pane = try p.setTestFile("body\n"); + p.gpa.free(pane.file.?.path); + pane.file.?.path = try p.gpa.dupe(u8, "/project/็file.zig"); + pane.file.?.revision += 1; + configure(p); + defer p.custom_theme = null; + p.settings.tag_bottom = bottom; + try std.testing.expect((try check(p, 0, pane.file.?.path)) > 0); + try panes.Output.fillResults(p, 0, "/project", .search, "tint", try p.gpa.dupe(u8, "no matches\n"), null); + const id = pane.search_pane.?; + p.update(.tick); + try std.testing.expect((try check(p, id, p.panes[id].?.file.?.path)) > 0); + } + } + + test "tag filename tint follows scrolled Unicode staged names and yields to selection" { + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 38, .rows = 14 }); + defer p.deinit(); + const pane = try p.setTestFile("body\n"); + const old = pane.file.?.path.len; + configure(p); + defer p.custom_theme = null; + p.update(.{ .key = .{ .cp = ':' } }); + p.update(.{ .key = .{ .cp = 'i' } }); + pane.tag_anchor = 0; + pane.tag_col = @intCast(old - 1); + pane.tag_sel = true; + const name = "/a/very/long/directory/that/extends/past/the/tag/็e\u{301}name-with-a-long-tail.zig"; + p.update(.{ .paste = name }); + try std.testing.expectEqualStrings(name, pane.tag_name[0..pane.tag_name_len.?]); + pane.tag_col = @intCast(std.mem.indexOf(u8, name, "็").?); + pane.tag_sel = false; + try std.testing.expect((try check(p, 0, name)) > 0); + try std.testing.expect(pane.tag_scroll > 0); + pane.tag_anchor = pane.tag_col; + pane.tag_sel = true; + pane.tag_scroll += 4; // bring both cells of the selected wide glyph into view + var arena = std.heap.ArenaAllocator.init(p.gpa); + defer arena.deinit(); + const surface = try p.render(arena.allocator()); + const x = p.rects[0].x + config.GUTTER + @as(u16, @intCast(panes.File.rawDisplayCol(name, pane.tag_col) - pane.tag_scroll)); + const y = if (p.settings.tag_bottom) p.rects[0].y + p.rects[0].h - pardes.BOX_H else p.rects[0].y; + try std.testing.expectEqual(pardes.Color{ .rgb = p.theme().sel_fg }, surface.at(x, y).style.fg); + try std.testing.expectEqual(pardes.Color{ .rgb = p.theme().sel_fg }, surface.at(x + 1, y).style.fg); + } + + test "tag filename tint legacy themes retain distinct active foreground" { + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 70, .rows = 14 }); + defer p.deinit(); + const pane = try p.setTestFile("body\n"); + p.gpa.free(pane.file.?.path); + pane.file.?.path = try p.gpa.dupe(u8, "/dir/file.zig"); + configure(p); + defer p.custom_theme = null; + p.custom_theme.?.tag_name_fg = null; + const active_fg = [3]u8{ 175, 183, 194 }; + p.custom_theme.?.tag_active_fg = active_fg; + p.chrome_animation.snap(pardes.ChromeTheme.fromTheme(p.theme())); + var arena = std.heap.ArenaAllocator.init(p.gpa); + defer arena.deinit(); + for ([_]bool{ false, true }) |active| { + p.settings.focus_tint = active; + const surface = try p.render(arena.allocator()); + const x = p.rects[0].x + config.GUTTER; + const y = if (p.settings.tag_bottom) p.rects[0].y + p.rects[0].h - pardes.BOX_H else p.rects[0].y; + const fg = if (active) active_fg else tag_fg; + try std.testing.expectEqual(pardes.Color{ .rgb = fg }, surface.at(x + 1, y).style.fg); + try std.testing.expectEqual(pardes.Color{ .rgb = fg }, surface.at(x + 5, y).style.fg); + } + } +}; + test "pane filename edits stage commit and cancel without changing body or command tail" { const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 70, .rows = 12 }); defer p.deinit(); @@ -3259,6 +3375,7 @@ const TerminalTests = struct { }; test { + _ = TagNameTintTests; _ = TreeContextTests; _ = TtySelectionTests; _ = ClickCursorTests; |
