refactor: move walk_tree to separate module
This commit is contained in:
		
							parent
							
								
									5ebd6353b3
								
							
						
					
					
						commit
						1a0d4ca7b2
					
				
					 2 changed files with 190 additions and 184 deletions
				
			
		| 
						 | 
				
			
			@ -9,6 +9,7 @@ const Buffer = @import("Buffer");
 | 
			
		|||
const builtin = @import("builtin");
 | 
			
		||||
 | 
			
		||||
const Project = @import("Project.zig");
 | 
			
		||||
const walk_tree = @import("walk_tree.zig");
 | 
			
		||||
 | 
			
		||||
pid: tp.pid_ref,
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -815,190 +816,6 @@ fn request_path_files_async(a_: std.mem.Allocator, parent_: tp.pid_ref, project_
 | 
			
		|||
    }.spawn_link(a_, parent_, project_, max_, path_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn walk_tree_async(a_: std.mem.Allocator, root_path_: []const u8) (SpawnError || std.fs.Dir.OpenError)!tp.pid {
 | 
			
		||||
    return struct {
 | 
			
		||||
        allocator: std.mem.Allocator,
 | 
			
		||||
        root_path: []const u8,
 | 
			
		||||
        parent: tp.pid,
 | 
			
		||||
        receiver: Receiver,
 | 
			
		||||
        dir: std.fs.Dir,
 | 
			
		||||
        walker: FilteredWalker,
 | 
			
		||||
 | 
			
		||||
        const tree_walker = @This();
 | 
			
		||||
        const Receiver = tp.Receiver(*tree_walker);
 | 
			
		||||
 | 
			
		||||
        fn spawn_link(allocator: std.mem.Allocator, root_path: []const u8) (SpawnError || std.fs.Dir.OpenError)!tp.pid {
 | 
			
		||||
            const self = try allocator.create(tree_walker);
 | 
			
		||||
            self.* = .{
 | 
			
		||||
                .allocator = allocator,
 | 
			
		||||
                .root_path = try allocator.dupe(u8, root_path),
 | 
			
		||||
                .parent = tp.self_pid().clone(),
 | 
			
		||||
                .receiver = Receiver.init(tree_walker.receive, self),
 | 
			
		||||
                .dir = try std.fs.cwd().openDir(self.root_path, .{ .iterate = true }),
 | 
			
		||||
                .walker = try walk_filtered(self.dir, self.allocator),
 | 
			
		||||
            };
 | 
			
		||||
            return tp.spawn_link(allocator, self, tree_walker.start, module_name ++ ".tree_walker");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn start(self: *tree_walker) tp.result {
 | 
			
		||||
            errdefer self.deinit();
 | 
			
		||||
            const frame = tracy.initZone(@src(), .{ .name = "project scan" });
 | 
			
		||||
            defer frame.deinit();
 | 
			
		||||
            tp.receive(&self.receiver);
 | 
			
		||||
            self.next() catch |e| return tp.exit_error(e, @errorReturnTrace());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn deinit(self: *tree_walker) void {
 | 
			
		||||
            self.walker.deinit();
 | 
			
		||||
            self.dir.close();
 | 
			
		||||
            self.allocator.free(self.root_path);
 | 
			
		||||
            self.parent.deinit();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn receive(self: *tree_walker, _: tp.pid_ref, m: tp.message) tp.result {
 | 
			
		||||
            errdefer self.deinit();
 | 
			
		||||
            const frame = tracy.initZone(@src(), .{ .name = "project scan" });
 | 
			
		||||
            defer frame.deinit();
 | 
			
		||||
 | 
			
		||||
            if (try m.match(.{"next"})) {
 | 
			
		||||
                self.next() catch |e| return tp.exit_error(e, @errorReturnTrace());
 | 
			
		||||
            } else if (try m.match(.{"stop"})) {
 | 
			
		||||
                return tp.exit_normal();
 | 
			
		||||
            } else {
 | 
			
		||||
                return tp.unexpected(m);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn next(self: *tree_walker) !void {
 | 
			
		||||
            if (try self.walker.next()) |path| {
 | 
			
		||||
                const stat = self.dir.statFile(path) catch return tp.self_pid().send(.{"next"});
 | 
			
		||||
                const mtime = stat.mtime;
 | 
			
		||||
                const high: i64 = @intCast(mtime >> 64);
 | 
			
		||||
                const low: i64 = @truncate(mtime);
 | 
			
		||||
                std.debug.assert(mtime == (@as(i128, @intCast(high)) << 64) | @as(i128, @intCast(low)));
 | 
			
		||||
                try self.parent.send(.{ "walk_tree_entry", self.root_path, path, high, low });
 | 
			
		||||
                return tp.self_pid().send(.{"next"});
 | 
			
		||||
            } else {
 | 
			
		||||
                self.parent.send(.{ "walk_tree_done", self.root_path }) catch {};
 | 
			
		||||
                return tp.exit_normal();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }.spawn_link(a_, root_path_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const filtered_dirs = [_][]const u8{
 | 
			
		||||
    "build",
 | 
			
		||||
    ".cache",
 | 
			
		||||
    ".cargo",
 | 
			
		||||
    ".git",
 | 
			
		||||
    ".jj",
 | 
			
		||||
    "node_modules",
 | 
			
		||||
    ".npm",
 | 
			
		||||
    ".rustup",
 | 
			
		||||
    ".var",
 | 
			
		||||
    ".zig-cache",
 | 
			
		||||
    "zig-cache",
 | 
			
		||||
    "zig-out",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
fn is_filtered_dir(dirname: []const u8) bool {
 | 
			
		||||
    for (filtered_dirs) |filter|
 | 
			
		||||
        if (std.mem.eql(u8, filter, dirname))
 | 
			
		||||
            return true;
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const FilteredWalker = struct {
 | 
			
		||||
    allocator: std.mem.Allocator,
 | 
			
		||||
    stack: std.ArrayListUnmanaged(StackItem),
 | 
			
		||||
    name_buffer: std.ArrayListUnmanaged(u8),
 | 
			
		||||
 | 
			
		||||
    const Path = []const u8;
 | 
			
		||||
 | 
			
		||||
    const StackItem = struct {
 | 
			
		||||
        iter: std.fs.Dir.Iterator,
 | 
			
		||||
        dirname_len: usize,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    pub fn next(self: *FilteredWalker) OutOfMemoryError!?Path {
 | 
			
		||||
        while (self.stack.items.len != 0) {
 | 
			
		||||
            var top = &self.stack.items[self.stack.items.len - 1];
 | 
			
		||||
            var containing = top;
 | 
			
		||||
            var dirname_len = top.dirname_len;
 | 
			
		||||
            if (top.iter.next() catch {
 | 
			
		||||
                var item_ = self.stack.pop();
 | 
			
		||||
                if (item_) |*item|
 | 
			
		||||
                    if (self.stack.items.len != 0) {
 | 
			
		||||
                        item.iter.dir.close();
 | 
			
		||||
                    };
 | 
			
		||||
                continue;
 | 
			
		||||
            }) |base| {
 | 
			
		||||
                self.name_buffer.shrinkRetainingCapacity(dirname_len);
 | 
			
		||||
                if (self.name_buffer.items.len != 0) {
 | 
			
		||||
                    try self.name_buffer.append(self.allocator, std.fs.path.sep);
 | 
			
		||||
                    dirname_len += 1;
 | 
			
		||||
                }
 | 
			
		||||
                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];
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    .file => return self.name_buffer.items,
 | 
			
		||||
                    else => continue,
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                var item_ = self.stack.pop();
 | 
			
		||||
                if (item_) |*item|
 | 
			
		||||
                    if (self.stack.items.len != 0) {
 | 
			
		||||
                        item.iter.dir.close();
 | 
			
		||||
                    };
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn deinit(self: *FilteredWalker) void {
 | 
			
		||||
        // Close any remaining directories except the initial one (which is always at index 0)
 | 
			
		||||
        if (self.stack.items.len > 1) {
 | 
			
		||||
            for (self.stack.items[1..]) |*item| {
 | 
			
		||||
                item.iter.dir.close();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        self.stack.deinit(self.allocator);
 | 
			
		||||
        self.name_buffer.deinit(self.allocator);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
fn walk_filtered(dir: std.fs.Dir, allocator: std.mem.Allocator) !FilteredWalker {
 | 
			
		||||
    var stack: std.ArrayListUnmanaged(FilteredWalker.StackItem) = .{};
 | 
			
		||||
    errdefer stack.deinit(allocator);
 | 
			
		||||
 | 
			
		||||
    try stack.append(allocator, .{
 | 
			
		||||
        .iter = dir.iterate(),
 | 
			
		||||
        .dirname_len = 0,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return .{
 | 
			
		||||
        .allocator = allocator,
 | 
			
		||||
        .stack = stack,
 | 
			
		||||
        .name_buffer = .{},
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn normalize_file_path(file_path: []const u8) []const u8 {
 | 
			
		||||
    const project = tp.env.get().str("project");
 | 
			
		||||
    if (project.len == 0) return file_path;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue