地圖 (Jido) is a lightweight Unix TUI file explorer designed for speed and simplicity.
at main 3.8 kB view raw
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}