地圖 (Jido) is a lightweight Unix TUI file explorer designed for speed and simplicity.
1const std = @import("std");
2const vaxis = @import("vaxis");
3
4pub fn List(comptime T: type) type {
5 return struct {
6 const Self = @This();
7
8 alloc: std.mem.Allocator,
9 items: std.ArrayList(T),
10 selected: usize,
11
12 pub fn init(alloc: std.mem.Allocator) Self {
13 return Self{
14 .alloc = alloc,
15 .items = .empty,
16 .selected = 0,
17 };
18 }
19
20 pub fn deinit(self: *Self) void {
21 self.items.deinit(self.alloc);
22 }
23
24 pub fn append(self: *Self, item: T) !void {
25 try self.items.append(self.alloc, item);
26 }
27
28 pub fn clear(self: *Self) void {
29 self.items.clearAndFree(self.alloc);
30 self.selected = 0;
31 }
32
33 pub fn fromArray(self: *Self, array: []const T) !void {
34 for (array) |item| {
35 try self.append(item);
36 }
37 }
38
39 pub fn get(self: Self, index: usize) !T {
40 if (index + 1 > self.len()) {
41 return error.OutOfBounds;
42 }
43
44 return self.all()[index];
45 }
46
47 pub fn getSelected(self: *Self) !?T {
48 if (self.len() > 0) {
49 if (self.selected >= self.len()) {
50 self.selected = self.len() - 1;
51 }
52
53 return try self.get(self.selected);
54 }
55
56 return null;
57 }
58
59 pub fn all(self: Self) []T {
60 return self.items.items;
61 }
62
63 pub fn len(self: Self) usize {
64 return self.items.items.len;
65 }
66
67 pub fn next(self: *Self) void {
68 if (self.selected + 1 < self.len()) {
69 self.selected += 1;
70 }
71 }
72
73 pub fn previous(self: *Self) void {
74 if (self.selected > 0) {
75 self.selected -= 1;
76 }
77 }
78
79 pub fn selectLast(self: *Self) void {
80 self.selected = self.len() - 1;
81 }
82
83 pub fn selectFirst(self: *Self) void {
84 self.selected = 0;
85 }
86 };
87}
88
89const testing = std.testing;
90
91test "List: navigation respects bounds" {
92 var list = List(u32).init(testing.allocator);
93 defer list.deinit();
94
95 try list.append(1);
96 try list.append(2);
97 try list.append(3);
98
99 try testing.expectEqual(@as(usize, 0), list.selected);
100
101 list.next();
102 try testing.expectEqual(@as(usize, 1), list.selected);
103
104 list.next();
105 list.next();
106 // Try to go past end
107 list.next();
108 // Should stay at last
109 try testing.expectEqual(@as(usize, 2), list.selected);
110
111 list.previous();
112 try testing.expectEqual(@as(usize, 1), list.selected);
113
114 list.previous();
115 // Try to go before start
116 list.previous();
117 // Should stay at first
118 try testing.expectEqual(@as(usize, 0), list.selected);
119}
120
121test "List: getSelected handles empty list" {
122 var list = List(u32).init(testing.allocator);
123 defer list.deinit();
124
125 const result = try list.getSelected();
126 try testing.expect(result == null);
127}
128
129test "List: append and get operations" {
130 var list = List(u32).init(testing.allocator);
131 defer list.deinit();
132
133 try list.append(42);
134 try list.append(84);
135
136 try testing.expectEqual(@as(usize, 2), list.len());
137 try testing.expectEqual(@as(u32, 42), try list.get(0));
138 try testing.expectEqual(@as(u32, 84), try list.get(1));
139}
140
141test "List: selectFirst and selectLast" {
142 var list = List(u32).init(testing.allocator);
143 defer list.deinit();
144
145 try list.append(1);
146 try list.append(2);
147 try list.append(3);
148
149 list.selectLast();
150 try testing.expectEqual(@as(usize, 2), list.selected);
151
152 list.selectFirst();
153 try testing.expectEqual(@as(usize, 0), list.selected);
154}