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