地圖 (Jido) is a lightweight Unix TUI file explorer designed for speed and simplicity.

feat: add log.txt to config dir. this acts as a log file to provide more information for errors that the notification system cannot.

+10
src/app.zig
··· 6 6 const config = &@import("./config.zig").config; 7 7 const List = @import("./list.zig").List; 8 8 const Directories = @import("./directories.zig"); 9 + const FileLogger = @import("./file_logger.zig"); 9 10 const CircStack = @import("./circ_stack.zig").CircularStack; 10 11 const zuid = @import("zuid"); 11 12 const vaxis = @import("vaxis"); ··· 54 55 55 56 directories: Directories, 56 57 notification: Notification, 58 + // Assigned in main after config parsing. 59 + file_logger: FileLogger = undefined, 57 60 58 61 text_input: vaxis.widgets.TextInput, 59 62 text_input_buf: [std.fs.max_path_bytes]u8 = undefined, ··· 107 110 self.text_input.deinit(); 108 111 self.vx.deinit(self.alloc, self.tty.anyWriter()); 109 112 self.tty.deinit(); 113 + self.file_logger.deinit(); 110 114 } 111 115 112 116 pub fn run(self: *App) !void { ··· 140 144 } else |err| switch (err) { 141 145 error.SyntaxError => { 142 146 try self.notification.writeErr(.ConfigSyntaxError); 147 + }, 148 + error.InvalidCharacter => { 149 + try self.notification.writeErr(.InvalidKeybind); 150 + }, 151 + error.DuplicateKeybind => { 152 + try self.notification.writeErr(.DuplicateKeybinds); 143 153 }, 144 154 else => { 145 155 try self.notification.writeErr(.ConfigUnknownError);
+13
src/config.zig
··· 2 2 const builtin = @import("builtin"); 3 3 const environment = @import("./environment.zig"); 4 4 const vaxis = @import("vaxis"); 5 + const FileLogger = @import("file_logger.zig"); 5 6 const Notification = @import("./notification.zig"); 6 7 7 8 const CONFIG_NAME = "config.json"; ··· 103 104 104 105 // Check duplicate keybinds 105 106 { 107 + var file_logger = try FileLogger.init(alloc); 108 + defer file_logger.deinit(); 109 + 106 110 var key_map = std.AutoHashMap(u21, []const u8).init(alloc); 107 111 defer { 108 112 var it = key_map.iterator(); ··· 117 121 118 122 const res = try key_map.getOrPut(codepoint); 119 123 if (res.found_existing) { 124 + const message = try std.fmt.allocPrint( 125 + alloc, 126 + "'{s}' and '{s}' have the same keybind: '{d}'", 127 + .{ res.value_ptr.*, field.name, codepoint }, 128 + ); 129 + defer alloc.free(message); 130 + 131 + file_logger.write(message, .err) catch {}; 132 + 120 133 return error.DuplicateKeybind; 121 134 } 122 135 res.value_ptr.* = try alloc.dupe(u8, field.name);
+60
src/file_logger.zig
··· 1 + const std = @import("std"); 2 + const environment = @import("environment.zig"); 3 + const config = &@import("./config.zig").config; 4 + 5 + pub const LOG_PATH = "log.txt"; 6 + 7 + const LogLevel = enum { 8 + err, 9 + info, 10 + warn, 11 + 12 + pub fn toString(level: LogLevel) []const u8 { 13 + return switch (level) { 14 + .err => "ERROR", 15 + .info => "INFO", 16 + .warn => "WARN", 17 + }; 18 + } 19 + }; 20 + 21 + const FileLogger = @This(); 22 + 23 + alloc: std.mem.Allocator, 24 + dir: std.fs.Dir, 25 + 26 + pub fn init(alloc: std.mem.Allocator) !FileLogger { 27 + const file_logger = FileLogger{ 28 + .alloc = alloc, 29 + .dir = try config.configDir() orelse return error.UnableToFindConfigDir, 30 + }; 31 + 32 + if (!environment.fileExists(file_logger.dir, LOG_PATH)) { 33 + _ = try file_logger.dir.createFile(LOG_PATH, .{}); 34 + } 35 + 36 + return file_logger; 37 + } 38 + 39 + pub fn deinit(self: FileLogger) void { 40 + var dir = self.dir; 41 + dir.close(); 42 + } 43 + 44 + pub fn write(self: FileLogger, msg: []const u8, level: LogLevel) !void { 45 + var log = try self.dir.openFile(LOG_PATH, .{ .mode = .write_only }); 46 + defer log.close(); 47 + 48 + const message = try std.fmt.allocPrint( 49 + self.alloc, 50 + "({d}) {s}: {s}\n", 51 + .{ std.time.timestamp(), LogLevel.toString(level), msg }, 52 + ); 53 + defer self.alloc.free(message); 54 + 55 + if (try log.tryLock(std.fs.File.Lock.shared)) { 56 + defer log.unlock(); 57 + try log.seekFromEnd(0); 58 + try log.writeAll(message); 59 + } 60 + }
+3
src/main.zig
··· 1 1 const std = @import("std"); 2 2 const builtin = @import("builtin"); 3 3 const App = @import("app.zig"); 4 + const FileLogger = @import("file_logger.zig"); 4 5 const vaxis = @import("vaxis"); 5 6 const config = &@import("./config.zig").config; 6 7 ··· 40 41 try app.notification.writeErr(.ConfigUnknownError); 41 42 }, 42 43 }; 44 + 45 + app.file_logger = try FileLogger.init(alloc); 43 46 44 47 try app.run(); 45 48 }