From 264c6ca54b328db8577b1318f936789badc3528c Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 1 Dec 2024 23:24:13 +0100 Subject: [PATCH] feat: dynamically determine available keybind namespaces --- src/keybind/keybind.zig | 24 ++++++++++++++++++++++++ src/main.zig | 30 ++++++++++++++++++++++++++---- src/tui/tui.zig | 19 +++++++++++++------ 3 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/keybind/keybind.zig b/src/keybind/keybind.zig index 251e91c..7a5727f 100644 --- a/src/keybind/keybind.zig +++ b/src/keybind/keybind.zig @@ -75,6 +75,30 @@ pub const Mode = struct { const NamespaceMap = std.StringHashMapUnmanaged(Namespace); +pub fn get_namespaces(allocator: std.mem.Allocator) ![]const []const u8 { + const namespaces = try root.list_keybind_namespaces(allocator); + defer { + for (namespaces) |namespace| allocator.free(namespace); + allocator.free(namespaces); + } + var result = std.ArrayList([]const u8).init(allocator); + try result.append(try allocator.dupe(u8, "flow")); + try result.append(try allocator.dupe(u8, "emacs")); + try result.append(try allocator.dupe(u8, "vim")); + try result.append(try allocator.dupe(u8, "helix")); + for (namespaces) |namespace| { + var exists = false; + for (result.items) |existing| + if (std.mem.eql(u8, namespace, existing)) { + exists = true; + break; + }; + if (!exists) + try result.append(try allocator.dupe(u8, namespace)); + } + return result.toOwnedSlice(); +} + pub fn get_namespace() []const u8 { return current_namespace().name; } diff --git a/src/main.zig b/src/main.zig index 9bdd1dd..2ef0469 100644 --- a/src/main.zig +++ b/src/main.zig @@ -459,6 +459,20 @@ pub fn write_keybind_namespace(namespace_name: []const u8, content: []const u8) return file.writeAll(content); } +pub fn list_keybind_namespaces(allocator: std.mem.Allocator) ![]const []const u8 { + var dir = try std.fs.openDirAbsolute(try get_keybind_namespaces_directory(), .{ .iterate = true }); + defer dir.close(); + var result = std.ArrayList([]const u8).init(allocator); + var iter = dir.iterateAssumeFirstIteration(); + while (try iter.next()) |entry| { + switch (entry.kind) { + .file, .sym_link => try result.append(try allocator.dupe(u8, std.fs.path.stem(entry.name))), + else => continue, + } + } + return result.toOwnedSlice(); +} + pub fn get_config_dir() ![]const u8 { return get_app_config_dir(application_name); } @@ -631,16 +645,24 @@ pub fn get_restore_file_name() ![]const u8 { const keybind_dir = "keys"; -pub fn get_keybind_namespace_file_name(namespace_name: []const u8) ![]const u8 { +pub fn get_keybind_namespaces_directory() ![]const u8 { const local = struct { - var file_buffer: [std.posix.PATH_MAX]u8 = undefined; + var dir_buffer: [std.posix.PATH_MAX]u8 = undefined; }; const a = std.heap.c_allocator; if (std.process.getEnvVarOwned(a, "FLOW_KEYS_DIR") catch null) |dir| { defer a.free(dir); - return try std.fmt.bufPrint(&local.file_buffer, "{s}/{s}.json", .{ dir, namespace_name }); + return try std.fmt.bufPrint(&local.dir_buffer, "{s}/", .{dir}); } - return try std.fmt.bufPrint(&local.file_buffer, "{s}/{s}/{s}.json", .{ try get_app_config_dir(application_name), keybind_dir, namespace_name }); + return try std.fmt.bufPrint(&local.dir_buffer, "{s}/{s}/", .{ try get_app_config_dir(application_name), keybind_dir }); +} + +pub fn get_keybind_namespace_file_name(namespace_name: []const u8) ![]const u8 { + const dir = try get_keybind_namespaces_directory(); + const local = struct { + var file_buffer: [std.posix.PATH_MAX]u8 = undefined; + }; + return try std.fmt.bufPrint(&local.file_buffer, "{s}/{s}.json", .{ dir, namespace_name }); } fn restart() noreturn { diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 88ec008..8cd11d6 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -668,12 +668,19 @@ const cmds = struct { pub fn toggle_input_mode(self: *Self, _: Ctx) Result { var it = std.mem.splitScalar(u8, self.config.input_mode, '/'); self.config.input_mode = it.first(); - self.config.input_mode = if (std.mem.eql(u8, self.config.input_mode, "flow")) - "vim" - else if (std.mem.eql(u8, self.config.input_mode, "vim")) - "helix" - else - "flow"; + + const namespaces = keybind.get_namespaces(self.allocator) catch |e| return tp.exit_error(e, @errorReturnTrace()); + defer { + for (namespaces) |namespace| self.allocator.free(namespace); + self.allocator.free(namespaces); + } + var found = false; + self.config.input_mode = blk: for (namespaces) |namespace| { + if (found) break :blk try self.allocator.dupe(u8, namespace); + if (std.mem.eql(u8, namespace, self.config.input_mode)) + found = true; + } else try self.allocator.dupe(u8, namespaces[0]); + try self.save_config(); self.logger.print("input mode {s}", .{self.config.input_mode}); try keybind.set_namespace(self.config.input_mode);