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