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 builtin = @import("builtin"); | ||||||
| 
 | 
 | ||||||
| const Project = @import("Project.zig"); | const Project = @import("Project.zig"); | ||||||
|  | const walk_tree = @import("walk_tree.zig"); | ||||||
| 
 | 
 | ||||||
| pid: tp.pid_ref, | 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_); |     }.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 { | pub fn normalize_file_path(file_path: []const u8) []const u8 { | ||||||
|     const project = tp.env.get().str("project"); |     const project = tp.env.get().str("project"); | ||||||
|     if (project.len == 0) return file_path; |     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