From 0e0137677fc31f87d9dddcdfe5eeb4a0a80c271e Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Fri, 6 Feb 2026 13:49:13 +0100 Subject: [PATCH 1/3] fix: walk_tree file type should use cached config values --- src/Project.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Project.zig b/src/Project.zig index 4ba6bd5..4c9c8c1 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -600,12 +600,13 @@ pub fn walk_tree_entry(self: *Self, m: tp.message) OutOfMemoryError!void { tp.extract(&file_color), }) catch return)) return; const mtime = (@as(i128, @intCast(mtime_high)) << 64) | @as(i128, @intCast(mtime_low)); + const ft = file_type_config.get(file_type) catch null; self.longest_file_path = @max(self.longest_file_path, file_path.len); (try self.pending.addOne(self.allocator)).* = .{ .path = try self.allocator.dupe(u8, file_path), - .type = file_type, - .icon = file_icon, + .type = if (ft) |ft_| ft_.name else try self.allocator.dupe(u8, file_type), + .icon = if (ft) |ft_| ft_.icon orelse &.{} else try self.allocator.dupe(u8, file_icon), .color = @intCast(file_color), .mtime = mtime, }; From 3a718bf6f6e59dfe23dc4668ff7532a39ec4602d Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Fri, 6 Feb 2026 13:49:51 +0100 Subject: [PATCH 2/3] feat: allow walk_tree to follow symlinks --- src/walk_tree.zig | 54 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/src/walk_tree.zig b/src/walk_tree.zig index c88bbeb..d2c27d4 100644 --- a/src/walk_tree.zig +++ b/src/walk_tree.zig @@ -169,23 +169,14 @@ const FilteredWalker = struct { try self.name_buffer.appendSlice(self.allocator, base.name); switch (base.kind) { .directory => { - if (is_filtered_dir(base.name)) - continue; - var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { - error.NameTooLong => @panic("unexpected error.NameTooLong"), // no path sep in base.name - else => continue, - }; - { - errdefer new_dir.close(); - try self.stack.append(self.allocator, .{ - .iter = new_dir.iterateAssumeFirstIteration(), - .dirname_len = self.name_buffer.items.len, - }); - top = &self.stack.items[self.stack.items.len - 1]; - containing = &self.stack.items[self.stack.items.len - 2]; - } + _ = try self.next_directory(&base, &top, &containing); + continue; }, .file => return self.name_buffer.items, + .sym_link => if (try self.next_sym_link(&base, &top, &containing, 5)) |file| + return file + else + continue, else => continue, } } else { @@ -198,4 +189,37 @@ const FilteredWalker = struct { } return null; } + + fn next_directory(self: *FilteredWalker, base: *const std.fs.Dir.Entry, top: **StackItem, containing: **StackItem) !void { + if (is_filtered_dir(base.name)) + return; + var new_dir = top.*.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { + error.NameTooLong => @panic("unexpected error.NameTooLong"), // no path sep in base.name + else => return, + }; + { + errdefer new_dir.close(); + try self.stack.append(self.allocator, .{ + .iter = new_dir.iterateAssumeFirstIteration(), + .dirname_len = self.name_buffer.items.len, + }); + top.* = &self.stack.items[self.stack.items.len - 1]; + containing.* = &self.stack.items[self.stack.items.len - 2]; + } + return; + } + + fn next_sym_link(self: *FilteredWalker, base: *const std.fs.Dir.Entry, top: **StackItem, containing: **StackItem, stat_depth: usize) !?[]const u8 { + if (stat_depth == 0) return null; + const st = top.*.iter.dir.statFile(base.name) catch return null; + switch (st.kind) { + .directory => { + _ = try self.next_directory(base, top, containing); + return null; + }, + .file => return self.name_buffer.items, + .sym_link => return try self.next_sym_link(base, top, containing, stat_depth - 1), + else => return null, + } + } }; From c0107e32e06e5687708e922691be4d9f9f9767ca Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Fri, 6 Feb 2026 14:04:53 +0100 Subject: [PATCH 3/3] feat: add retain_symlinks option to enable writing files through links (default: true) --- src/buffer/Buffer.zig | 11 ++++++++++- src/config.zig | 2 ++ src/tui/tui.zig | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index 0f9a4fd..2b0ea6b 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -38,6 +38,8 @@ pub const BlameLine = struct { author_stamp: usize, }; +pub var retain_symlinks: bool = true; + arena: std.heap.ArenaAllocator, allocator: Allocator, external_allocator: Allocator, @@ -1599,7 +1601,14 @@ pub const StoreToFileError = error{ WriteFailed, }; -pub fn store_to_existing_file_const(self: *const Self, file_path: []const u8) StoreToFileError!void { +pub fn store_to_existing_file_const(self: *const Self, file_path_: []const u8) StoreToFileError!void { + var file_path = file_path_; + var link_buf: [std.fs.max_path_bytes]u8 = undefined; + if (retain_symlinks) blk: { + const link = cwd().readLink(file_path, &link_buf) catch break :blk; + file_path = link; + } + var atomic = blk: { var write_buffer: [4096]u8 = undefined; const stat = cwd().statFile(file_path) catch diff --git a/src/config.zig b/src/config.zig index e22f890..0ab56c4 100644 --- a/src/config.zig +++ b/src/config.zig @@ -89,6 +89,8 @@ keybind_mode: KeybindMode = .normal, dropdown_keybinds: DropdownKeybindMode = .standard, dropdown_limit: usize = 12, +retain_symlinks: bool = true, + include_files: []const u8 = "", const default_actions = [_]IdleAction{.highlight_references}; diff --git a/src/tui/tui.zig b/src/tui/tui.zig index abe8c8a..52dba2e 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -8,6 +8,7 @@ const root = @import("soft_root").root; const tracy = @import("tracy"); const builtin = @import("builtin"); const file_link = @import("file_link"); +const Buffer = @import("Buffer"); pub const renderer = @import("renderer"); const input = @import("input"); @@ -154,6 +155,8 @@ fn init(allocator: Allocator) InitError!*Self { var conf, const conf_bufs = root.read_config(@import("config"), allocator); + Buffer.retain_symlinks = conf.retain_symlinks; + if (@hasDecl(renderer, "install_crash_handler") and conf.start_debugger_on_crash) renderer.jit_debugger_enabled = true;