From d178c8c2e175373892642a1898a9746df0544cf4 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 11 Feb 2026 13:05:43 +0100 Subject: [PATCH] fix: add config option to enable following symlinks to directories (default: false) --- src/Project.zig | 4 +++- src/config.zig | 1 + src/tui/tui.zig | 1 + src/walk_tree.zig | 21 +++++++++++++++------ 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Project.zig b/src/Project.zig index 4c9c8c1..3f49c7c 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -2701,7 +2701,9 @@ pub fn query_git(self: *Self) void { fn start_walker(self: *Self) void { self.state.walk_tree = .running; - self.walker = walk_tree.start(self.allocator, self.name, walk_tree_entry_callback, walk_tree_done_callback) catch blk: { + self.walker = walk_tree.start(self.allocator, self.name, walk_tree_entry_callback, walk_tree_done_callback, .{ + .follow_directory_symlinks = tp.env.get().is("follow_directory_symlinks"), + }) catch blk: { self.state.walk_tree = .failed; break :blk null; }; diff --git a/src/config.zig b/src/config.zig index 0ab56c4..bfee975 100644 --- a/src/config.zig +++ b/src/config.zig @@ -90,6 +90,7 @@ dropdown_keybinds: DropdownKeybindMode = .standard, dropdown_limit: usize = 12, retain_symlinks: bool = true, +follow_directory_symlinks: bool = false, include_files: []const u8 = "", diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 52dba2e..51a79a0 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -174,6 +174,7 @@ fn init(allocator: Allocator) InitError!*Self { const frame_clock = try tp.metronome.init(frame_time); tp.env.get().set("lsp_verbose", conf.lsp_output == .verbose); + tp.env.get().set("follow_directory_symlinks", conf.follow_directory_symlinks); var self = try allocator.create(Self); // don't destroy diff --git a/src/walk_tree.zig b/src/walk_tree.zig index d2c27d4..d43e298 100644 --- a/src/walk_tree.zig +++ b/src/walk_tree.zig @@ -10,7 +10,11 @@ const OutOfMemoryError = error{OutOfMemory}; pub const EntryCallBack = *const fn (parent: tp.pid_ref, root_path: []const u8, path: []const u8, mtime_high: i64, mtime_low: i64) error{Exit}!void; pub const DoneCallBack = *const fn (parent: tp.pid_ref, root_path: []const u8) error{Exit}!void; -pub fn start(a_: std.mem.Allocator, root_path_: []const u8, entry_handler: EntryCallBack, done_handler: DoneCallBack) (SpawnError || std.fs.Dir.OpenError)!tp.pid { +pub const Options = struct { + follow_directory_symlinks: bool = false, +}; + +pub fn start(a_: std.mem.Allocator, root_path_: []const u8, entry_handler: EntryCallBack, done_handler: DoneCallBack, options: Options) (SpawnError || std.fs.Dir.OpenError)!tp.pid { return struct { allocator: std.mem.Allocator, root_path: []const u8, @@ -20,11 +24,12 @@ pub fn start(a_: std.mem.Allocator, root_path_: []const u8, entry_handler: Entry walker: FilteredWalker, entry_handler: EntryCallBack, done_handler: DoneCallBack, + options: Options, const tree_walker = @This(); const Receiver = tp.Receiver(*tree_walker); - fn spawn_link(allocator: std.mem.Allocator, root_path: []const u8, entry_handler_: EntryCallBack, done_handler_: DoneCallBack) (SpawnError || std.fs.Dir.OpenError)!tp.pid { + fn spawn_link(allocator: std.mem.Allocator, root_path: []const u8, entry_handler_: EntryCallBack, done_handler_: DoneCallBack, options_: Options) (SpawnError || std.fs.Dir.OpenError)!tp.pid { const self = try allocator.create(tree_walker); errdefer allocator.destroy(self); self.* = .{ @@ -33,9 +38,10 @@ pub fn start(a_: std.mem.Allocator, root_path_: []const u8, entry_handler: Entry .parent = tp.self_pid().clone(), .receiver = .init(tree_walker.receive, self), .dir = try std.fs.cwd().openDir(self.root_path, .{ .iterate = true }), - .walker = try .init(self.dir, self.allocator), + .walker = try .init(self.dir, self.allocator, options_), .entry_handler = entry_handler_, .done_handler = done_handler_, + .options = options_, }; return tp.spawn_link(allocator, self, tree_walker.start, module_name ++ ".tree_walker"); } @@ -86,7 +92,7 @@ pub fn start(a_: std.mem.Allocator, root_path_: []const u8, entry_handler: Entry return tp.exit_normal(); } } - }.spawn_link(a_, root_path_, entry_handler, done_handler); + }.spawn_link(a_, root_path_, entry_handler, done_handler, options); } const filtered_dirs = [_][]const u8{ @@ -113,6 +119,7 @@ const FilteredWalker = struct { allocator: std.mem.Allocator, stack: std.ArrayListUnmanaged(StackItem), name_buffer: std.ArrayListUnmanaged(u8), + options: Options, const Path = []const u8; @@ -121,7 +128,7 @@ const FilteredWalker = struct { dirname_len: usize, }; - pub fn init(dir: std.fs.Dir, allocator: std.mem.Allocator) !FilteredWalker { + pub fn init(dir: std.fs.Dir, allocator: std.mem.Allocator, options: Options) !FilteredWalker { var stack: std.ArrayListUnmanaged(FilteredWalker.StackItem) = .{}; errdefer stack.deinit(allocator); @@ -134,6 +141,7 @@ const FilteredWalker = struct { .allocator = allocator, .stack = stack, .name_buffer = .{}, + .options = options, }; } @@ -214,7 +222,8 @@ const FilteredWalker = struct { const st = top.*.iter.dir.statFile(base.name) catch return null; switch (st.kind) { .directory => { - _ = try self.next_directory(base, top, containing); + if (self.options.follow_directory_symlinks) + _ = try self.next_directory(base, top, containing); return null; }, .file => return self.name_buffer.items,