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; | ||||
|  |  | |||
							
								
								
									
										189
									
								
								src/walk_tree.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								src/walk_tree.zig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,189 @@ | |||
| const std = @import("std"); | ||||
| const tp = @import("thespian"); | ||||
| const tracy = @import("tracy"); | ||||
| 
 | ||||
| const module_name = @typeName(@This()); | ||||
| 
 | ||||
| const SpawnError = (OutOfMemoryError || error{ThespianSpawnFailed}); | ||||
| const OutOfMemoryError = error{OutOfMemory}; | ||||
| 
 | ||||
| pub fn start(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 = .init(tree_walker.receive, self), | ||||
|                 .dir = try std.fs.cwd().openDir(self.root_path, .{ .iterate = true }), | ||||
|                 .walker = try .init(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{ | ||||
|     ".cache", | ||||
|     ".cargo", | ||||
|     ".git", | ||||
|     ".jj", | ||||
|     "node_modules", | ||||
|     ".npm", | ||||
|     ".rustup", | ||||
|     ".var", | ||||
|     ".zig-cache", | ||||
| }; | ||||
| 
 | ||||
| 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 init(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 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); | ||||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| }; | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue