From ae8ead1e5daf9c145cd135c24780cac20dedb873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Tue, 27 May 2025 16:12:46 +0200 Subject: [PATCH] feat: requested selected item+ --- src/console/main.zig | 14 +-- src/core/action/action_handler.zig | 14 ++- src/core/provider/local.zig | 1 - src/core/tab/order.zig | 8 ++ src/core/tab/tab.zig | 131 +++++++++++++++++++---------- 5 files changed, 115 insertions(+), 53 deletions(-) create mode 100644 src/core/tab/order.zig diff --git a/src/console/main.zig b/src/console/main.zig index 1824760..0cece21 100644 --- a/src/console/main.zig +++ b/src/console/main.zig @@ -121,6 +121,9 @@ const Model = struct { { const currentTab = vm.app_common_model.appState.currentTab; if (currentTab.currentItem) |currentItem| { + const currentItemArcCopy = currentItem.retain(); + defer if (currentItemArcCopy.releaseUnwrap()) |item| item.deinit(); + currentTab.currentItems.mutex.lock(); defer currentTab.currentItems.mutex.unlock(); @@ -130,7 +133,7 @@ const Model = struct { const arc_item = item.retain(); defer if (arc_item.releaseUnwrap()) |i| i.deinit(); - if (models.FullName.eql(&arc_item.value.*.fullName, ¤tItem)) { + if (models.FullName.eql(&arc_item.value.*.fullName, ¤tItemArcCopy.value.*.fullName)) { break try std.fmt.allocPrint(ctx.arena, "{s} ", .{arc_item.value.*.displayName}); } } else ""; @@ -183,10 +186,11 @@ const Model = struct { const text_items = blk2: { const currentTab = vm.app_common_model.appState.currentTab; - const current_full_name = if (currentTab.currentItem) |currentItem| - models.FullName{ .path = try ctx.arena.dupe(u8, currentItem.path) } - else - null; + const current_full_name = if (currentTab.currentItem) |currentItem| blk: { + const arc_copy = currentItem.retain(); + defer if (arc_copy.releaseUnwrap()) |item| item.deinit(); + break :blk models.FullName{ .path = try ctx.arena.dupe(u8, currentItem.value.*.fullName.path) }; + } else null; currentTab.currentItems.mutex.lock(); defer currentTab.currentItems.mutex.unlock(); diff --git a/src/core/action/action_handler.zig b/src/core/action/action_handler.zig index 515992f..07a7365 100644 --- a/src/core/action/action_handler.zig +++ b/src/core/action/action_handler.zig @@ -11,8 +11,11 @@ pub fn handle(action: Action, appState: *AppState, arena: std.mem.Allocator) !Ac } else return error.NoCurrentLocation; }, .Enter => { - if (appState.currentTab.currentItem) |currentItem| { - const path = try arena.dupe(u8, currentItem.path); + if (appState.currentTab.currentItem) |*currentItem| { + const arc_copy = currentItem.retain(); + defer if (arc_copy.releaseUnwrap()) |item| item.deinit(); + + const path = try arena.dupe(u8, arc_copy.value.*.fullName.path); try appState.currentTab.setCurrentLocation(.{ .path = path }); return ActionResult.Handled; } else return error.NoCurrentLocation; @@ -43,7 +46,10 @@ fn selectNextIndex(currentTab: *Tab, step: SelectStep, arena: std.mem.Allocator) const index = blk: { if (currentTab.currentItem) |*currentItem| { - const index = indexOf(Arc(*models.Item), models.FullName, currentItems.items, currentItem, arcFullNameEql); + const arc_copy = currentItem.retain(); + defer if (arc_copy.releaseUnwrap()) |item| item.deinit(); + + const index = indexOf(Arc(*models.Item), models.FullName, currentItems.items, &arc_copy.value.*.fullName, arcFullNameEql); if (index) |i| { break :blk switch (step) { .Next => i +| 1, @@ -57,6 +63,8 @@ fn selectNextIndex(currentTab: *Tab, step: SelectStep, arena: std.mem.Allocator) break :blk 0; }; + if (index >= currentItems.items.len) return ActionResult.None; + const arc = currentItems.items[index]; const arc_copy = arc.retain(); defer if (arc_copy.releaseUnwrap()) |item| item.deinit(); diff --git a/src/core/provider/local.zig b/src/core/provider/local.zig index 9bb1369..ea1a4f6 100644 --- a/src/core/provider/local.zig +++ b/src/core/provider/local.zig @@ -7,7 +7,6 @@ fn loadChildren(container: *Container) void { container.childrenLoading = false; } - std.debug.print("load children {s}", .{container.item.nativePath.path}); var dir = std.fs.cwd().openDir(container.item.nativePath.path, .{ .iterate = true }) catch { // const errorContent = std.fmt.allocPrint(container.item.allocator, "Could not open directory '{s}'.", .{container.item.nativePath.path}) catch return; // container.item.errors.append(.{ .content = errorContent }) catch return; diff --git a/src/core/tab/order.zig b/src/core/tab/order.zig new file mode 100644 index 0000000..97414ac --- /dev/null +++ b/src/core/tab/order.zig @@ -0,0 +1,8 @@ +pub fn orderByDisplayName(lhs: *const models.Item, rhs: *const models.Item) std.math.Order { + if (lhs.item == .container and rhs.item == .element) return .lt; + if (lhs.item == .element and rhs.item == .container) return .gt; + return std.mem.order(u8, lhs.displayName, rhs.displayName); +} + +const std = @import("std"); +const models = @import("../models.zig"); diff --git a/src/core/tab/tab.zig b/src/core/tab/tab.zig index f20a93f..6e6117d 100644 --- a/src/core/tab/tab.zig +++ b/src/core/tab/tab.zig @@ -3,8 +3,10 @@ pub const Tab = struct { currentLocation: ?*Container, currentItems: locked(?std.ArrayList(Arc(*Item))), currentLocationChanged: Observable(?*Container), - currentItem: ?models.FullName, - currentRequestedItem: ?models.FullName, + currentItemFullName: ?models.FullName, + currentItem: ?Arc(*models.Item), + currentRequested: ?union(enum) { item: Arc(*models.Item), name: models.FullName }, + itemComparer: *const fn (item1: *const models.Item, item2: *const models.Item) std.math.Order, threadPool: *std.Thread.Pool, rootProvider: *RootProvider, @@ -19,8 +21,10 @@ pub const Tab = struct { .currentItems = .{ .data = null }, .currentLocationChanged = Observable(?*Container).init(allocator), .currentLocation = null, - .currentRequestedItem = null, + .currentRequested = null, .currentItem = null, + .currentItemFullName = null, + .itemComparer = order.orderByDisplayName, .threadPool = threadPool, .rootProvider = rootProvider, }; @@ -31,10 +35,7 @@ pub const Tab = struct { c.item.deinit(); } - if (self.currentItem) |i| { - self.allocator.free(i.path); - self.currentItem = null; - } + self.cleanCurrentItem(); const newLocationContainer = blk: { const newLocation = try self.rootProvider.getItemByFullName(newLocationFullName, &.{}, self.allocator); @@ -55,6 +56,7 @@ pub const Tab = struct { self.currentLocationChanged.notify(newLocationContainer); self.updateCurrentItem() catch { + self.currentItemFullName = null; self.currentItem = null; }; @@ -66,47 +68,80 @@ pub const Tab = struct { self.currentItems.mutex.lock(); defer self.currentItems.mutex.unlock(); - self.currentRequestedItem = models.FullName{ - .path = try self.allocator.dupe(u8, requested_selected.path), + self.cleanCurrentRequested(); + + self.currentRequested = .{ + .name = models.FullName{ + .path = try self.allocator.dupe(u8, requested_selected.path), + }, }; return self.updateCurrentItem(); } - fn updateCurrentItem(self: *Tab) !void { - const currentRequestedItem = self.currentRequestedItem orelse return; + fn cleanCurrentRequested(self: *Tab) void { + const requested = self.currentRequested orelse return; + switch (requested) { + .item => |item| { + if (item.releaseUnwrap()) |i| i.deinit(); + }, + .name => |fullName| self.allocator.free(fullName.path), + } + + self.currentRequested = null; + } + + fn updateCurrentItem(self: *Tab) !void { + const currentRequested = self.currentRequested orelse return; + + const requestedFullName = switch (currentRequested) { + .item => |*item| item.value.*.fullName, + .name => |fullName| fullName, + }; // The requested is already selected - if (self.currentItem) |currentItem| { - if (std.mem.order(u8, currentRequestedItem.path, currentItem.path) == .eq) return; + if (self.currentItemFullName) |currentItem| { + if (std.mem.order(u8, requestedFullName.path, currentItem.path) == .eq) { + return; + } } const currentItems = self.currentItems.data orelse return error.Error; if (currentItems.items.len == 0) return error.Error; // Looking for the requested - const foundIndex = for (0..currentItems.items.len) |i| { + const foundIndex = index: for (0..currentItems.items.len) |i| { const arc_item = currentItems.items[i]; const arc_copy = arc_item.retain(); defer if (arc_copy.releaseUnwrap()) |item| item.deinit(); - const comp = std.mem.order(u8, arc_copy.value.*.fullName.path, currentRequestedItem.path); - if (comp == .eq) { - break i; - } - if (comp == .lt) { - break i -| 1; + switch (currentRequested) { + .item => |*itemArc| { + const comp = self.itemComparer(itemArc.value.*, arc_copy.value.*); + if (comp == .eq) { + break :index i; + } + if (comp == .lt) { + break :index i -| 1; + } + }, + .name => |fullName| { + if (std.mem.order(u8, fullName.path, arc_copy.value.*.fullName.path) == .eq) { + break :index i; + } + }, } } else null; if (foundIndex) |i| { const arc_item = currentItems.items[i]; - const arc_copy = arc_item.retain(); - defer if (arc_copy.releaseUnwrap()) |item| item.deinit(); - self.setSelectedItemInner(models.FullName{ - .path = try self.allocator.dupe(u8, arc_copy.value.*.fullName.path), - }); + self.cleanCurrentRequested(); + self.currentRequested = .{ + .item = arc_item.retain(), + }; + + try self.setSelectedItemInner(arc_item.retain()); return; } @@ -116,21 +151,21 @@ pub const Tab = struct { // Fallback to first item { const arc_item = currentItems.items[0]; - const arc_copy = arc_item.retain(); - defer if (arc_copy.releaseUnwrap()) |item| item.deinit(); - self.setSelectedItemInner(models.FullName{ - .path = try self.allocator.dupe(u8, arc_copy.value.*.fullName.path), - }); + try self.setSelectedItemInner(arc_item.retain()); } } - fn setSelectedItemInner(self: *Tab, newSelectedItem: models.FullName) void { - if (self.currentItem) |current| { - self.allocator.free(current.path); - } + fn setSelectedItemInner(self: *Tab, newSelectedItemArc: Arc(*models.Item)) !void { + const arc = newSelectedItemArc.retain(); + defer if (arc.releaseUnwrap()) |item| item.deinit(); - self.currentItem = newSelectedItem; + self.cleanCurrentItem(); + + self.currentItem = newSelectedItemArc; + self.currentItemFullName = models.FullName{ + .path = try self.allocator.dupe(u8, arc.value.*.fullName.path), + }; } fn loadItemsWrapper(self: *Tab, location: *Container) void { @@ -177,8 +212,8 @@ pub const Tab = struct { self.currentItems.mutex.lock(); defer self.currentItems.mutex.unlock(); - const insertIndex = for (self.currentItems.data.?.items, 0..) |item, i| { - if (compareByDisplayName(resolvedItem, item.value.*)) break i; + const insertIndex = for (self.currentItems.data.?.items, 0..) |*item, i| { + if (order.orderByDisplayName(resolvedItem, item.value.*) == .lt) break i; } else null; const arc = try Arc(*Item).init(self.allocator, resolvedItem); @@ -208,17 +243,23 @@ pub const Tab = struct { currentItems.deinit(); } - if (self.currentItem) |currentItem| { - self.allocator.free(currentItem.path); - } + self.cleanCurrentItem(); + + self.cleanCurrentRequested(); self.allocator.destroy(self); } - fn compareByDisplayName(lhs: *models.Item, rhs: *models.Item) bool { - if (lhs.item == .container and rhs.item == .element) return true; - if (lhs.item == .element and rhs.item == .container) return false; - return std.mem.order(u8, lhs.displayName, rhs.displayName) == .lt; + fn cleanCurrentItem(self: *Tab) void { + if (self.currentItemFullName) |currentItem| { + self.allocator.free(currentItem.path); + self.currentItemFullName = null; + } + + if (self.currentItem) |currentItem| { + if (currentItem.releaseUnwrap()) |item| item.deinit(); + self.currentItem = null; + } } }; @@ -226,6 +267,8 @@ const std = @import("std"); const assert = std.debug.assert; const Mutex = std.Thread.Mutex; +const order = @import("./order.zig"); + const models = @import("../models.zig"); const Container = models.Container; const Item = models.Item;