diff --git a/CHANGELOG.md b/CHANGELOG.md index 8353bef..105c410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## v0.9.9 (2025-04-06) - feat: Added ability to copy folders. +- fix: Scrolling command history now provides the correct values. ## v0.9.8 (2025-04-04) - fix: Ensure complete Git branch is displayed. diff --git a/PROJECT_BOARD.md b/PROJECT_BOARD.md index 0ecb4c3..98b3054 100644 --- a/PROJECT_BOARD.md +++ b/PROJECT_BOARD.md @@ -11,7 +11,6 @@ Key: - [x] File/Folder movement. - [x] Copy files. - [x] Copy folders. -- [ ] Keybind to unzip archives. - [x] Keybind to hard delete items (bypass trash). - [x] Ability to unbind keys. @@ -19,9 +18,12 @@ Key: - [x] Better error logging. There are many places errors could be caught, logged, and handled instead of crashing. + +### Bugs +- [x] Command history is skipping items on scroll. + +## Backlog - [ ] Improve image reading. Current reading can be slow which pauses users movement if they are simply scrolling past. - -### Bugs -- [ ] Command history is skipping items on scroll. +- [ ] Keybind to unzip archives. diff --git a/src/app.zig b/src/app.zig index d54cc53..fcb427b 100644 --- a/src/app.zig +++ b/src/app.zig @@ -149,10 +149,7 @@ pub fn deinit(self: *App) void { self.alloc.free(yanked.entry.name); } - self.command_history.resetSelected(); - while (self.command_history.next()) |command| { - self.alloc.free(command); - } + self.command_history.deinit(self.alloc); self.help_menu.deinit(); self.directories.deinit(); diff --git a/src/commands.zig b/src/commands.zig index 47e893f..ae337d7 100644 --- a/src/commands.zig +++ b/src/commands.zig @@ -6,41 +6,61 @@ const user_config = &@import("./config.zig").config; pub const CommandHistory = struct { const history_len = 10; - selected: usize = 0, - len: usize = 0, history: [history_len][]const u8 = undefined, + count: usize = 0, + ///Points to the oldest entry. + start: usize = 0, + cursor: ?usize = null, + + pub fn deinit(self: *CommandHistory, allocator: std.mem.Allocator) void { + for (self.history[0..self.count]) |entry| { + allocator.free(entry); + } + } + + pub fn add(self: *CommandHistory, cmd: []const u8, allocator: std.mem.Allocator) error{OutOfMemory}!void { + const index = (self.start + self.count) % history_len; - pub fn push(self: *CommandHistory, command: []const u8) ?[]const u8 { - var deleted: ?[]const u8 = null; - if (self.len == history_len) { - deleted = self.history[0]; - for (0..self.len - 1) |i| { - self.history[i] = self.history[i + 1]; - } + if (self.count < history_len) { + self.count += 1; } else { - self.len += 1; + // Overwriting the oldest entry. + allocator.free(self.history[self.start]); + self.start = (self.start + 1) % history_len; } - self.history[self.len - 1] = command; - self.selected = self.len; + self.history[index] = try allocator.dupe(u8, cmd); + self.cursor = null; + } + + pub fn previous(self: *CommandHistory) ?[]const u8 { + if (self.count == 0) return null; + + if (self.cursor == null) { + self.cursor = self.count - 1; + } else if (self.cursor.? > 0) { + self.cursor.? -= 1; + } - return deleted; + return self.getAtCursor(); } pub fn next(self: *CommandHistory) ?[]const u8 { - if (self.selected == 0) return null; - self.selected -= 1; - return self.history[self.selected]; - } + if (self.count == 0 or self.cursor == null) return null; - pub fn previous(self: *CommandHistory) ?[]const u8 { - if (self.selected + 1 == self.len) return null; - self.selected += 1; - return self.history[self.selected]; + if (self.cursor.? < self.count - 1) { + self.cursor.? += 1; + return self.getAtCursor(); + } + + self.cursor = null; + return null; } - pub fn resetSelected(self: *CommandHistory) void { - self.selected = self.len; + fn getAtCursor(self: *CommandHistory) ?[]const u8 { + if (self.cursor == null) return null; + const index = (self.start + self.cursor.?) % history_len; + return self.history[index]; } }; diff --git a/src/event_handlers.zig b/src/event_handlers.zig index 5e1585b..b565caa 100644 --- a/src/event_handlers.zig +++ b/src/event_handlers.zig @@ -104,7 +104,7 @@ pub fn handleInputEvent(app: *App, event: App.Event) !void { try app.repopulateDirectory(""); app.text_input.clearAndFree(); }, - .command => app.command_history.resetSelected(), + .command => app.command_history.cursor = null, else => {}, } @@ -127,9 +127,11 @@ pub fn handleInputEvent(app: *App, event: App.Event) !void { // Push command to history if it's not empty. if (!std.mem.eql(u8, std.mem.trim(u8, command, " "), ":")) { - if (app.command_history.push(try app.alloc.dupe(u8, command))) |deleted| { - app.alloc.free(deleted); - } + app.command_history.add(command, app.alloc) catch |err| { + const message = try std.fmt.allocPrint(app.alloc, "Failed to add command to history - {}.", .{err}); + defer app.alloc.free(message); + if (app.file_logger) |file_logger| file_logger.write(message, .err) catch {}; + }; } supported: { @@ -167,7 +169,7 @@ pub fn handleInputEvent(app: *App, event: App.Event) !void { try app.text_input.insertSliceAtCursor(":UnsupportedCommand"); } - app.command_history.resetSelected(); + app.command_history.cursor = null; }, else => {}, } @@ -179,7 +181,7 @@ pub fn handleInputEvent(app: *App, event: App.Event) !void { Key.right => app.text_input.cursorRight(), Key.up => { if (app.state == .command) { - if (app.command_history.next()) |command| { + if (app.command_history.previous()) |command| { app.text_input.clearAndFree(); app.text_input.insertSliceAtCursor(command) catch |err| { const message = try std.fmt.allocPrint(app.alloc, "Failed to get previous command history - {}.", .{err}); @@ -193,7 +195,7 @@ pub fn handleInputEvent(app: *App, event: App.Event) !void { Key.down => { if (app.state == .command) { app.text_input.clearAndFree(); - if (app.command_history.previous()) |command| { + if (app.command_history.next()) |command| { app.text_input.insertSliceAtCursor(command) catch |err| { const message = try std.fmt.allocPrint(app.alloc, "Failed to get next command history - {}.", .{err}); defer app.alloc.free(message);