From 41e93e1f8827a8d50ff8266f9107b0ae624c2b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Sun, 22 Jun 2025 06:31:52 +0200 Subject: [PATCH] feat: action map refactor, bugfix --- src/console/ActionMap.zig | 96 ++++++++++++++++++++++++++++++++++ src/console/action_map.zig | 21 -------- src/console/main.zig | 6 ++- src/core/provider/local.zig | 17 ++++++ src/core/provider/provider.zig | 4 ++ src/core/tab/tab.zig | 7 +-- 6 files changed, 125 insertions(+), 26 deletions(-) create mode 100644 src/console/ActionMap.zig delete mode 100644 src/console/action_map.zig diff --git a/src/console/ActionMap.zig b/src/console/ActionMap.zig new file mode 100644 index 0000000..5bdec82 --- /dev/null +++ b/src/console/ActionMap.zig @@ -0,0 +1,96 @@ +key_action_map: []KeyActionMap, +previous_keys: std.ArrayList(vaxis.Key), +_private: Private, + +const Self = @This(); + +const Private = struct { + key_action_map: std.ArrayList(KeyActionMap), + max_action_key_length: u8, +}; + +pub fn init(allocator: std.mem.Allocator) !Self { + var key_action_map = std.ArrayList(KeyActionMap).init(allocator); + try init_key_action_map(&key_action_map); + + var max_action_key_length: u8 = 0; + for (key_action_map.items) |item| { + if (item.keys.len > max_action_key_length) { + max_action_key_length = @intCast(item.keys.len); + } + } + + return .{ + .key_action_map = key_action_map.items, + .previous_keys = std.ArrayList(vaxis.Key).init(allocator), + ._private = .{ + .key_action_map = key_action_map, + .max_action_key_length = max_action_key_length, + }, + }; +} + +pub fn deinit(self: *Self) void { + self._private.key_action_map.deinit(); +} + +fn init_key_action_map(map_list: *std.ArrayList(KeyActionMap)) !void { + try map_list.appendSlice(&.{ + .{ + .keys = &[1]KeyMatch{.{ .code_point = vaxis.Key.left, .modifiers = .{} }}, + .action = Action.GoUp, + }, + .{ + .keys = &[1]KeyMatch{.{ .code_point = vaxis.Key.right, .modifiers = .{} }}, + .action = Action.Enter, + }, + .{ + .keys = &[1]KeyMatch{.{ .code_point = vaxis.Key.up, .modifiers = .{} }}, + .action = Action.SelectPrevious, + }, + .{ + .keys = &[1]KeyMatch{.{ .code_point = vaxis.Key.down, .modifiers = .{} }}, + .action = Action.SelectNext, + }, + }); +} + +pub fn handle_key(self: *Self, key: vaxis.Key, appState: *AppState, arena: std.mem.Allocator) !ActionResult { + try self.previous_keys.append(key); + + const action_result = action_result: for (self.key_action_map) |action_map| { + if (action_map.keys.len != self.previous_keys.items.len) continue; + + const matches = blk: for (action_map.keys, self.previous_keys.items) |action_key, previous_key| { + if (!previous_key.matches(action_key.code_point, action_key.modifiers)) break :blk false; + } else true; + + if (!matches) continue; + + self.previous_keys.clearRetainingCapacity(); + break :action_result try handle_action(action_map.action, appState, arena); + } else ActionResult.None; + + if (self.previous_keys.items.len >= self._private.max_action_key_length) { + self.previous_keys.clearRetainingCapacity(); + } + + return action_result; +} + +const KeyMatch = struct { + code_point: u21, + modifiers: vaxis.Key.Modifiers, +}; +const KeyActionMap = struct { + keys: []const KeyMatch, + action: Action, +}; + +const std = @import("std"); +const vaxis = @import("vaxis"); +const ActionResult = @import("../core/action/action.zig").ActionResult; +const handle_action = @import("../core/action/action_handler.zig").handle; +const Action = @import("../core/action/action.zig").Action; +const AppState = @import("../core/app_state.zig").AppState; +const RootProvider = @import("../core/provider/root.zig").RootProvider; diff --git a/src/console/action_map.zig b/src/console/action_map.zig deleted file mode 100644 index 604cb77..0000000 --- a/src/console/action_map.zig +++ /dev/null @@ -1,21 +0,0 @@ -pub fn handle_key(key: vaxis.Key, appState: *AppState, arena: std.mem.Allocator) !ActionResult { - if (key.matches(vaxis.Key.left, .{})) { - return try handle_action(Action.GoUp, appState, arena); - } else if (key.matches(vaxis.Key.right, .{})) { - return try handle_action(Action.Enter, appState, arena); - } else if (key.matches(vaxis.Key.up, .{})) { - return try handle_action(Action.SelectPrevious, appState, arena); - } else if (key.matches(vaxis.Key.down, .{})) { - return try handle_action(Action.SelectNext, appState, arena); - } - - return ActionResult.None; -} - -const std = @import("std"); -const vaxis = @import("vaxis"); -const ActionResult = @import("../core/action/action.zig").ActionResult; -const handle_action = @import("../core/action/action_handler.zig").handle; -const Action = @import("../core/action/action.zig").Action; -const AppState = @import("../core/app_state.zig").AppState; -const RootProvider = @import("../core/provider/root.zig").RootProvider; diff --git a/src/console/main.zig b/src/console/main.zig index 29f7466..9dcb801 100644 --- a/src/console/main.zig +++ b/src/console/main.zig @@ -7,6 +7,7 @@ const Model = struct { current_children_view: *vxfw.ListView, app_common_model: *AppCommonModel, root_provider: *RootProvider, + action_map: ActionMap, /// Helper function to return a vxfw.Widget struct pub fn widget(self: *Model) vxfw.Widget { @@ -54,7 +55,7 @@ const Model = struct { var arena = std.heap.ArenaAllocator.init(vm.allocator); defer arena.deinit(); - const result = handle_key(key, &vm.app_common_model.appState, arena.allocator()) catch null; + const result = vm.action_map.handle_key(key, &vm.app_common_model.appState, arena.allocator()) catch null; if (result) |r| { if (r == ActionResult.Handled) { ctx.consumeAndRedraw(); @@ -542,6 +543,7 @@ pub fn main() !void { .parent_items_view = &parent_items_list, .current_children_view = ¤t_children_view, .root_provider = &rootProvider, + .action_map = try ActionMap.init(allocator), }; model.app_common_model.usage_number.data += 1; @@ -572,7 +574,7 @@ const Tab = @import("../core/tab/tab.zig").Tab; const locked = @import("../core/sync.zig").locked; const AppCommonModel = @import("../app_common/Model.zig"); const AppState = @import("../core/app_state.zig").AppState; -const handle_key = @import("./action_map.zig").handle_key; +const ActionMap = @import("./ActionMap.zig"); const Arc = @import("zigrc").Arc; const Observer = @import("../core/observable.zig").Observer; const draw_context = @import("draw_context.zig"); diff --git a/src/core/provider/local.zig b/src/core/provider/local.zig index f47b13c..7a867da 100644 --- a/src/core/provider/local.zig +++ b/src/core/provider/local.zig @@ -65,6 +65,22 @@ pub fn getFullNameByNativePath(allocator: std.mem.Allocator, nativePath: NativeP } pub fn getNativePathByFullName(allocator: std.mem.Allocator, fullName: FullName) ![]u8 { + std.debug.assert(!std.mem.endsWith(u8, fullName.path, "/")); + + if (fullName.path.len < LocalProviderId.len or + !std.mem.eql(u8, fullName.path[0..LocalProviderId.len], LocalProviderId)) + { + return ProviderError.WrongProvider; + } + + //NOTE: their might be an other edge case where the fullname is local/ that is not handled by this + if (fullName.path.len == LocalProviderId.len) { + if (native_os == .linux) + return try std.fmt.allocPrint(allocator, "/", .{}) + else + return ProviderError.WrongProvider; + } + const fullNameWithoutId = fullName.path[LocalProviderId.len + 1 ..]; var native_path = try std.mem.replaceOwned(u8, allocator, fullNameWithoutId, "/", std.fs.path.sep_str); @@ -217,6 +233,7 @@ const models = @import("../models.zig"); const Provider = @import("provider.zig").Provider; const ProviderVTable = @import("provider.zig").VTable; const GetItemsError = @import("provider.zig").GetItemsError; +const ProviderError = @import("provider.zig").ProviderError; const InitContext = @import("provider.zig").InitContext; const FullName = models.FullName; diff --git a/src/core/provider/provider.zig b/src/core/provider/provider.zig index 26ad882..a3758de 100644 --- a/src/core/provider/provider.zig +++ b/src/core/provider/provider.zig @@ -18,6 +18,10 @@ pub const GetItemsError = error{ OutOfMemory, NotExists, NotSupported, +} || ProviderError; + +pub const ProviderError = error{ + WrongProvider, }; pub const Provider = struct { diff --git a/src/core/tab/tab.zig b/src/core/tab/tab.zig index 8626fb0..f22dca3 100644 --- a/src/core/tab/tab.zig +++ b/src/core/tab/tab.zig @@ -39,6 +39,7 @@ pub const Tab = struct { pub fn setCurrentLocation(self: *Tab, newLocationFullName: models.FullName) !void { if (self.currentLocation) |c| { c.item.deinit(); + self.currentLocation = null; } self.cleanCurrentItem(); @@ -201,7 +202,7 @@ pub const Tab = struct { }; const resolvedParent = location.item.provider.getItemByFullName(parentFullName, &.{}, allocator) catch |e| { - std.debug.print("error {} {s}\r\n", .{ e, parentFullName.path }); + std.debug.print("error {s}:{} {} {s}\r\n", .{ @src().file, @src().line, e, parentFullName.path }); return; }; const parentContainer = switch (resolvedParent.item) { @@ -229,7 +230,7 @@ pub const Tab = struct { const allocator = arena.allocator(); const resolvedChild = newSelectedItemArc.value.*.provider.getItemByFullName(newSelectedItemArc.value.*.fullName, &.{}, allocator) catch |e| { - std.debug.print("error {} {s}\r\n", .{ e, newSelectedItemArc.value.*.fullName.path }); + std.debug.print("error {s}:{} {} {s}\r\n", .{ @src().file, @src().line, e, newSelectedItemArc.value.*.fullName.path }); return; }; const childContainer = switch (resolvedChild.item) { @@ -362,7 +363,7 @@ fn loadItems( for (location.children.data.items[processed_number..]) |item_fullname| { const resolvedItem = location.item.provider.getItemByFullName(item_fullname, &.{}, allocator) catch |e| { //TODO: save error to container errors - std.debug.print("error {} {s}\r\n", .{ e, item_fullname.path }); + std.debug.print("error {s}:{} {} {s}\r\n", .{ @src().file, @src().line, e, item_fullname.path }); continue; };