Commit Diff


commit - 5a25ead23cee7f32c14e2dd549c3c32b1ba36d79
commit + fd4a4949b2929c0644e85af5c0e1dbef8eb89a38
blob - aa888200fc0a32f3de023c46cdda470ae438c0d2
blob + 37ae75008306305032d1674861c875bc7c8644fd
--- README.md
+++ README.md
@@ -12,7 +12,7 @@ A command-line tool for interactive shell navigation a
 
 ## Dependencies
 
-- Zig 0.15.2
+- Zig 0.16.0
 - ripgrep
 - fzf
 - bat
blob - a1d954513c27f0796620405d2f73e651d74b6b49
blob + a4b9994dc0d8b8f2550eb2485707c42db3286f04
--- config.json
+++ config.json
@@ -12,7 +12,7 @@
 		"--color=marker:#e06fad,fg+:#f0dfe5,prompt:#7a9fd4,hl+:#f0a070",
 		"--color=border:#3d2a42"
 	],
-	"fav_dirs": ["/home/miro/src"],
+	"favorites": ["~/src"],
 	"openers": [
 		{
 			"extensions": ["mp4", "mkv", "avi", "webm"],
blob - ea9b6abb41ba3e87be1033b62fc9aae49b20579e
blob + 9fd8e389b9e46e5cdb0a44cb47433891e583ca40
--- prek.toml
+++ prek.toml
@@ -4,38 +4,11 @@ rev = "v6.0.0"
 hooks = [
   { id = "trailing-whitespace" },
   { id = "end-of-file-fixer" },
-  { id = "double-quote-string-fixer" }
 ]
 
 [[repos]]
-repo = "https://github.com/biomejs/pre-commit"
-rev = "v2.4.14"
-hooks = [
-  { id = "biome-format" }
-]
-
-[[repos]]
 repo = "https://github.com/gitleaks/gitleaks"
 rev = "v8.30.1"
 hooks = [
   { id = "gitleaks" }
 ]
-
-[[repos]]
-repo = "https://github.com/astro/deadnix"
-rev = "v1.3.1"
-hooks = [
-  { id = "deadnix" }
-]
-
-[[repos]]
-repo = "local"
-hooks = [
-  {
-    id = "alejandra",
-    name = "alejandra",
-    language = "system",
-    entry = "alejandra",
-    files = '\.nix$'
-  },
-]
blob - 4c1239df39138e842c816ee72b7a464de534b9a3
blob + 4d2ce948ed673c91f5b3f0a8b15d30ecfe05723b
--- src/cfg.zig
+++ src/cfg.zig
@@ -47,18 +47,29 @@ pub fn applyFolderOverride(base: Config, cwd: []const 
 }
 
 pub fn loadConfig(allocator: std.mem.Allocator) ?std.json.Parsed(Config) {
-    const home = io.getenv("HOME") orelse return null;
-    const path = std.fs.path.join(allocator, &.{ home, ".config", "magdalena", "config.json" }) catch return null;
+    const home = io.getenv("HOME") orelse {
+        io.warn("HOME not set, skipping config load\n", .{});
+        return null;
+    };
+    const path = std.fs.path.join(allocator, &.{ home, ".config", "magdalena", "config.json" }) catch {
+        io.warn("failed to allocate config path\n", .{});
+        return null;
+    };
     defer allocator.free(path);
 
-    const content = std.Io.Dir.cwd().readFileAlloc(io.rt(), path, allocator, .unlimited) catch return null;
+    const content = std.Io.Dir.cwd().readFileAlloc(io.rt(), path, allocator, .unlimited) catch |err| {
+        if (err != error.FileNotFound) {
+            io.warn("failed to read config {s}: {}\n", .{ path, err });
+        }
+        return null;
+    };
     defer allocator.free(content);
 
     return std.json.parseFromSlice(Config, allocator, content, .{
         .ignore_unknown_fields = true,
         .allocate = .alloc_always,
     }) catch |err| {
-        std.log.err("failed to parse config: {any}", .{err});
+        io.warn("failed to parse config {s}: {}\n", .{ path, err });
         return null;
     };
 }
blob - ac9739c7a9d68808433abc92662fde17ff3a2d45
blob + e7e32675b7303c9a6a5deb82d0def49d533d80bb
--- src/cli.zig
+++ src/cli.zig
@@ -41,7 +41,6 @@ pub const CommandLineArgs = struct {
 
     exe: []const u8,
     tail: []const [:0]const u8,
-    list: []const [:0]const u8,
 
     pub fn parse(parent: std.mem.Allocator, source: std.process.Args) !CommandLineArgs {
         const arena = try parent.create(std.heap.ArenaAllocator);
@@ -62,7 +61,7 @@ pub const CommandLineArgs = struct {
         var lookup: std.StringHashMapUnmanaged([]const u8) = .{};
 
         if (items.len == 0) {
-            return .{ .exe = "", .tail = &.{}, .list = &.{}, ._arena = arena, ._lookup = lookup };
+            return .{ .exe = "", .tail = &.{}, ._arena = arena, ._lookup = lookup };
         }
 
         const exe = items[0];
@@ -96,7 +95,6 @@ pub const CommandLineArgs = struct {
         return .{
             .exe = exe,
             .tail = items[tail_start..],
-            .list = items,
             ._arena = arena,
             ._lookup = lookup,
         };
blob - 5cbedb8b32b318127fec7b9a26ca37794ea5e8cd
blob + 23f01221c0f9da9f7fa8b47c9deb04548dc0d47f
--- src/db.zig
+++ src/db.zig
@@ -153,8 +153,11 @@ pub const Db = struct {
     }
 
     fn resolvePath(self: *Db, path: []const u8) ![]const u8 {
-        return std.Io.Dir.cwd().realPathFileAlloc(io.rt(), path, self.allocator) catch {
-            return try std.fs.path.resolve(self.allocator, &[_][]const u8{path});
+        return std.Io.Dir.cwd().realPathFileAlloc(io.rt(), path, self.allocator) catch |err| {
+            if (err == error.FileNotFound) {
+                io.warn("path not found for logging: {s}\n", .{path});
+            }
+            return try std.fs.path.resolve(self.allocator, &.{ ".", path });
         };
     }
 
@@ -223,6 +226,7 @@ pub const Db = struct {
 
         var total_entries: usize = 0;
         var missing_entries: usize = 0;
+        var inaccessible_entries: usize = 0;
         var it = std.mem.splitBackwardsScalar(u8, content, '\n');
         while (it.next()) |line| {
             const trimmed = std.mem.trim(u8, line, " \r\t");
@@ -248,15 +252,17 @@ pub const Db = struct {
                     missing_entries += 1;
                     continue;
                 }
-                return err;
+                io.warn("skipping inaccessible entry {s}: {}\n", .{ p, err });
+                inaccessible_entries += 1;
+                continue;
             };
 
             try seen.put(p, {});
             try lines.append(self.allocator, trimmed);
         }
 
-        const duplicates = total_entries - lines.items.len - missing_entries;
-        io.warn("  Entries: {d} total, {d} unique, {d} missing, {d} duplicates removed\n", .{ total_entries, lines.items.len, missing_entries, duplicates });
+        const duplicates = total_entries - lines.items.len - missing_entries - inaccessible_entries;
+        io.warn("  Entries: {d} total, {d} unique, {d} missing, {d} inaccessible, {d} duplicates removed\n", .{ total_entries, lines.items.len, missing_entries, inaccessible_entries, duplicates });
 
         const write_file = try std.Io.Dir.cwd().createFile(io.rt(), path, .{ .truncate = true });
         defer write_file.close(io.rt());
blob - e07175224c5d0aa9c9fcd4fc19818ebcebaf561c
blob + 9654e0bb66dcf0077b16444c595ea288a019d167
--- src/fzf.zig
+++ src/fzf.zig
@@ -216,11 +216,15 @@ fn expandPath(allocator: std.mem.Allocator, path: []co
     var res = std.ArrayListUnmanaged(u8).empty;
     errdefer res.deinit(allocator);
 
+    const home = io.getenv("HOME") orelse return error.HomeNotFound;
+
     var i: usize = 0;
     while (i < path.len) {
-        if (std.mem.startsWith(u8, path[i..], "$HOME")) {
-            const home = io.getenv("HOME") orelse "/home/user";
+        if (path[i] == '~' and (i == 0 or path[i - 1] == std.fs.path.sep)) {
             try res.appendSlice(allocator, home);
+            i += 1;
+        } else if (std.mem.startsWith(u8, path[i..], "$HOME")) {
+            try res.appendSlice(allocator, home);
             i += 5;
         } else {
             try res.append(allocator, path[i]);
blob - c0bf997c71213570fffb7bde2052c56fab5fbbca
blob + 1939677accd3af9aeb9cd4ca98dedd28e2406993
--- src/memcached.zig
+++ src/memcached.zig
@@ -57,6 +57,8 @@ pub const Memcached = struct {
     const chunk_size = 900 * 1024;
     const chunk_prefix = "\x00chunked:";
 
+    // Large values are split into chunk keys `{key}:{i}`. Overwriting a chunked
+    // key with a smaller value leaves old chunks as orphans.
     pub fn set(self: *Memcached, key: []const u8, value: []const u8, exptime: u32) !void {
         if (value.len <= chunk_size) {
             try self.sendSet(key, value, exptime);