diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-10 10:57:58 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-09-15 17:24:42 -0300 |
| commit | 6878e1c309172d624c0d7a3555f6f0217ae6770e (patch) | |
| tree | 1331379616ecee3dc4da2b735ccf27c33e3a3817 | |
| parent | bba412a3b2c83b4c6935baeaac979ead46c0a5dc (diff) | |
| download | pardes-6878e1c309172d624c0d7a3555f6f0217ae6770e.tar.gz pardes-6878e1c309172d624c0d7a3555f6f0217ae6770e.zip | |
Resolve relative restores in the dump directory and propagate LSP probe errors
| -rw-r--r-- | docs/config.md | 6 | ||||
| -rw-r--r-- | src/detached/server.zig | 2 | ||||
| -rw-r--r-- | src/dump.zig | 16 | ||||
| -rw-r--r-- | src/fs.zig | 57 | ||||
| -rw-r--r-- | src/gui/gui.zig | 2 | ||||
| -rw-r--r-- | src/macos.zig | 2 | ||||
| -rw-r--r-- | src/tty/tty.zig | 2 | ||||
| -rw-r--r-- | tools/lspprobe.zig | 4 |
8 files changed, 79 insertions, 12 deletions
diff --git a/docs/config.md b/docs/config.md index f895e599..0a6765b8 100644 --- a/docs/config.md +++ b/docs/config.md @@ -392,3 +392,9 @@ commit: a tarball, a container with no `git`, or a checkout outside version control all yield null, and the flag has to work anyway. Neither is a question asked at runtime — a binary that shelled out to `git` would describe whatever tree it was standing in rather than the one it came from. + +`Restore a.dump` first looks for the relative path in the default dump directory +(`$XDG_DATA_HOME/pardes`, or `~/.local/share/pardes`). If it is absent, Restore +uses the argument as a path as before. Absolute paths and argument-free Restore +retain their existing behavior. `Dump` still honors `$PARDES_DUMP` and otherwise +writes a timestamped file in the default directory. diff --git a/src/detached/server.zig b/src/detached/server.zig index 8b49bcb2..9a47a562 100644 --- a/src/detached/server.zig +++ b/src/detached/server.zig @@ -1163,7 +1163,7 @@ pub fn run(init: std.process.Init, opts: pardes.Options, name: []const u8) !void try session.core.pump(h); if (session.core.quit) break; if (session.core.takeRestore()) |path| restore: { - const bytes = filesystem.readFile(gpa, path) catch |err| { + const bytes = filesystem.readRestore(gpa, path) catch |err| { session.core.reportError(session.core.active, "Restore", err); break :restore; }; diff --git a/src/dump.zig b/src/dump.zig index bb692481..93bf08cc 100644 --- a/src/dump.zig +++ b/src/dump.zig @@ -15,12 +15,7 @@ pub fn outPath(buf: *[1024:0]u8) ?[:0]const u8 { if (std.c.getenv("PARDES_DUMP")) |p| return std.fmt.bufPrintSentinel(buf, "{s}", .{std.mem.span(p)}, 0) catch null; var dir_buf: [900]u8 = undefined; - const dir = if (std.c.getenv("XDG_DATA_HOME")) |x| - std.fmt.bufPrint(&dir_buf, "{s}/pardes", .{std.mem.span(x)}) catch return null - else if (std.c.getenv("HOME")) |h| - std.fmt.bufPrint(&dir_buf, "{s}/.local/share/pardes", .{std.mem.span(h)}) catch return null - else - return null; + const dir = defaultDirectory(&dir_buf) orelse return null; var dz: [901:0]u8 = undefined; @memcpy(dz[0..dir.len], dir); dz[dir.len] = 0; @@ -37,6 +32,15 @@ pub fn outPath(buf: *[1024:0]u8) ?[:0]const u8 { ds.getSecondsIntoMinute(), }, 0) catch null; } +/// The standard dump directory, without creating it or generating a filename. +pub fn defaultDirectory(buf: []u8) ?[]const u8 { + if (std.c.getenv("XDG_DATA_HOME")) |x| + return std.fmt.bufPrint(buf, "{s}/pardes", .{std.mem.span(x)}) catch null; + if (std.c.getenv("HOME")) |h| + return std.fmt.bufPrint(buf, "{s}/.local/share/pardes", .{std.mem.span(h)}) catch null; + return null; +} + pub const magic = "pardes-dump"; pub const version: u32 = 1; pub const max_panes: usize = 16; @@ -1200,6 +1200,63 @@ pub fn readFile(gpa: std.mem.Allocator, path: []const u8) ![]u8 { return readFileLimit(gpa, path, limits.max_file_bytes); } +/// Restore first tries a relative argument under the default dump directory, +/// then preserves the ordinary file lookup. Absolute paths remain verbatim. +pub fn readRestore(gpa: std.mem.Allocator, path: []const u8) ![]u8 { + var dir_buf: [4096]u8 = undefined; + const dir = @import("dump.zig").defaultDirectory(&dir_buf); + return readRestoreIn(gpa, path, dir); +} + +fn readRestoreIn(gpa: std.mem.Allocator, path: []const u8, dir: ?[]const u8) ![]u8 { + if (!std.fs.path.isAbsolute(path)) { + if (dir) |base| { + var buf: [4096]u8 = undefined; + if (std.fmt.bufPrint(&buf, "{s}/{s}", .{ base, path })) |candidate| { + if (readFile(gpa, candidate)) |bytes| return bytes else |err| switch (err) { + error.FileNotFound => {}, + else => return err, + } + } else |_| {} + } + } + return readFile(gpa, path); +} + +test "Restore prefers default directory then falls back to original path" { + if (!platform_has_fs) return error.SkipZigTest; + const gpa = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const relative = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/a.dump", .{tmp.sub_path}); + defer gpa.free(relative); + const base = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/dumps", .{tmp.sub_path}); + defer gpa.free(base); + const nested = try std.fmt.allocPrint(gpa, "dumps/.zig-cache/tmp/{s}", .{tmp.sub_path}); + defer gpa.free(nested); + try tmp.dir.createDirPath(std.testing.io, nested); + try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "dumps/a.dump", .data = "short name" }); + const short = try readRestoreIn(gpa, "a.dump", base); + defer gpa.free(short); + try std.testing.expectEqualStrings("short name", short); + try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "a.dump", .data = "original" }); + const fallback = try readRestoreIn(gpa, relative, base); + defer gpa.free(fallback); + try std.testing.expectEqualStrings("original", fallback); + const candidate = try std.fmt.allocPrint(gpa, "{s}/{s}", .{ base, relative }); + defer gpa.free(candidate); + try writeFile(candidate, "default"); + const preferred = try readRestoreIn(gpa, relative, base); + defer gpa.free(preferred); + try std.testing.expectEqualStrings("default", preferred); + const absolute = try tmp.dir.realPathFileAlloc(std.testing.io, "a.dump", gpa); + defer gpa.free(absolute); + const direct = try readRestoreIn(gpa, absolute, base); + defer gpa.free(direct); + try std.testing.expectEqualStrings("original", direct); + try std.testing.expectError(error.FileNotFound, readRestoreIn(gpa, "missing-restore-test.dump", base)); +} + fn readFileLimit(gpa: std.mem.Allocator, path: []const u8, limit: usize) ![]u8 { if (std.mem.indexOfScalar(u8, path, 0) != null) return error.OpenFailed; if (!platform_has_fs or std.mem.startsWith(u8, path, "/virtual/")) { diff --git a/src/gui/gui.zig b/src/gui/gui.zig index a7d9d167..f7b9fc7e 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -2382,7 +2382,7 @@ fn localSession( } } if (core.takeRestore()) |rp| blk: { - const bytes = filesystem.readFile(gpa, rp) catch |err| { + const bytes = filesystem.readRestore(gpa, rp) catch |err| { core.reportError(core.active, "Restore", err); break :blk; }; diff --git a/src/macos.zig b/src/macos.zig index 36c70f86..3e4edd12 100644 --- a/src/macos.zig +++ b/src/macos.zig @@ -1227,7 +1227,7 @@ export fn pardes_tick() bool { fn restoreCore(st: *State) bool { if (st.core.quit) return false; const path = st.core.takeRestore() orelse return false; - const bytes = filesystem.readFile(st.gpa, path) catch |err| { + const bytes = filesystem.readRestore(st.gpa, path) catch |err| { st.core.reportError(st.core.active, "Restore", err); return false; }; diff --git a/src/tty/tty.zig b/src/tty/tty.zig index 1f11c535..0379e59c 100644 --- a/src/tty/tty.zig +++ b/src/tty/tty.zig @@ -729,7 +729,7 @@ fn localSession( frames: while (!core.quit) { try core.pump(host); if (core.takeRestore()) |rp| blk: { - const bytes = filesystem.readFile(gpa, rp) catch |err| { + const bytes = filesystem.readRestore(gpa, rp) catch |err| { core.reportError(core.active, "Restore", err); break :blk; }; diff --git a/tools/lspprobe.zig b/tools/lspprobe.zig index 8b953cec..0b7d772c 100644 --- a/tools/lspprobe.zig +++ b/tools/lspprobe.zig @@ -145,10 +145,11 @@ pub fn main(init: std.process.Init) !void { // first (cold, server-spawning) query — the status lines on // stderr narrate what the server does with the time var out: std.Io.Writer.Allocating = .init(gpa); + defer out.deinit(); var scratch: std.heap.ArenaAllocator = .init(gpa); defer scratch.deinit(); const t0 = nowUs(); - lsp.query(gpa, scratch.allocator(), .{ + try lsp.query(gpa, scratch.allocator(), .{ .kind = kind, .path = path, .source = source, @@ -158,7 +159,6 @@ pub fn main(init: std.process.Init) !void { }, &out.writer); timings[r] = nowUs() -| t0; rows = try arena.dupe(u8, out.written()); - out.deinit(); if (first_query) { first_query = false; if (warm_s > 0) for (0..warm_s * 10) |_| { |
