feat: Project makes available modified and untracked files
Project exposes functions to query and return modified and untracked files informed by git.
This commit is contained in:
		
							parent
							
								
									9c1d1cb557
								
							
						
					
					
						commit
						52609ab198
					
				
					 1 changed files with 112 additions and 0 deletions
				
			
		
							
								
								
									
										112
									
								
								src/Project.zig
									
										
									
									
									
								
							
							
						
						
									
										112
									
								
								src/Project.zig
									
										
									
									
									
								
							|  | @ -19,8 +19,10 @@ const walk_tree = @import("walk_tree.zig"); | |||
| allocator: std.mem.Allocator, | ||||
| name: []const u8, | ||||
| files: std.ArrayListUnmanaged(File) = .empty, | ||||
| new_or_modified_files: std.ArrayListUnmanaged(FileVcsStatus) = .empty, | ||||
| pending: std.ArrayListUnmanaged(File) = .empty, | ||||
| longest_file_path: usize = 0, | ||||
| longest_new_or_modified_file_path: usize = 0, | ||||
| open_time: i64, | ||||
| language_servers: std.StringHashMap(*const LSP), | ||||
| file_language_server_name: std.StringHashMap([]const u8), | ||||
|  | @ -41,6 +43,7 @@ state: struct { | |||
|     current_branch: State = .none, | ||||
|     workspace_files: State = .none, | ||||
|     status: State = .none, | ||||
|     vcs_new_or_modified_files: State = .none, | ||||
| } = .{}, | ||||
| 
 | ||||
| status: VcsStatus = .{}, | ||||
|  | @ -66,6 +69,14 @@ const File = struct { | |||
|     visited: bool = false, | ||||
| }; | ||||
| 
 | ||||
| const FileVcsStatus = struct { | ||||
|     path: []const u8, | ||||
|     type: []const u8, | ||||
|     icon: []const u8, | ||||
|     color: u24, | ||||
|     vcs_status: u8, | ||||
| }; | ||||
| 
 | ||||
| pub const FilePos = struct { | ||||
|     row: usize = 0, | ||||
|     col: usize = 0, | ||||
|  | @ -105,6 +116,8 @@ pub fn deinit(self: *Self) void { | |||
|         self.allocator.free(p.key_ptr.*); | ||||
|         p.value_ptr.*.term(); | ||||
|     } | ||||
|     for (self.new_or_modified_files.items) |file| self.allocator.free(file.path); | ||||
|     self.new_or_modified_files.deinit(self.allocator); | ||||
|     for (self.files.items) |file| self.allocator.free(file.path); | ||||
|     self.files.deinit(self.allocator); | ||||
|     self.pending.deinit(self.allocator); | ||||
|  | @ -372,6 +385,84 @@ pub fn request_recent_files(self: *Self, from: tp.pid_ref, max: usize) ClientErr | |||
|     } | ||||
| } | ||||
| 
 | ||||
| fn simple_query_new_or_modified_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) ClientError!usize { | ||||
|     var i: usize = 0; | ||||
|     defer from.send(.{ "PRJ", "new_or_modified_files_done", self.longest_file_path, query }) catch {}; | ||||
|     for (self.new_or_modified_files.items) |file| { | ||||
|         if (file.path.len < query.len) continue; | ||||
|         if (std.mem.indexOf(u8, file.path, query)) |idx| { | ||||
|             var matches = try self.allocator.alloc(usize, query.len); | ||||
|             defer self.allocator.free(matches); | ||||
|             var n: usize = 0; | ||||
|             while (n < query.len) : (n += 1) matches[n] = idx + n; | ||||
|             from.send(.{ "PRJ", "new_or_modified_files", self.longest_new_or_modified_file_path, file.path, file.type, file.icon, file.color, file.vcs_status, matches }) catch return error.ClientFailed; | ||||
|             i += 1; | ||||
|             if (i >= max) return i; | ||||
|         } | ||||
|     } | ||||
|     return i; | ||||
| } | ||||
| 
 | ||||
| pub fn query_new_or_modified_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) ClientError!usize { | ||||
|     if (query.len < 3) | ||||
|         return self.simple_query_new_or_modified_files(from, max, query); | ||||
|     defer from.send(.{ "PRJ", "new_or_modified_files_done", self.longest_file_path, query }) catch {}; | ||||
| 
 | ||||
|     var searcher = try fuzzig.Ascii.init( | ||||
|         self.allocator, | ||||
|         4096, // haystack max size | ||||
|         4096, // needle max size | ||||
|         .{ .case_sensitive = false }, | ||||
|     ); | ||||
|     defer searcher.deinit(); | ||||
| 
 | ||||
|     const Match = struct { | ||||
|         path: []const u8, | ||||
|         type: []const u8, | ||||
|         icon: []const u8, | ||||
|         color: u24, | ||||
|         vcs_status: u8, | ||||
|         score: i32, | ||||
|         matches: []const usize, | ||||
|     }; | ||||
|     var matches: std.ArrayList(Match) = .empty; | ||||
| 
 | ||||
|     for (self.new_or_modified_files.items) |file| { | ||||
|         const match = searcher.scoreMatches(file.path, query); | ||||
|         if (match.score) |score| { | ||||
|             (try matches.addOne(self.allocator)).* = .{ | ||||
|                 .path = file.path, | ||||
|                 .type = file.type, | ||||
|                 .icon = file.icon, | ||||
|                 .color = file.color, | ||||
|                 .vcs_status = file.vcs_status, | ||||
|                 .score = score, | ||||
|                 .matches = try self.allocator.dupe(usize, match.matches), | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|     if (matches.items.len == 0) return 0; | ||||
| 
 | ||||
|     const less_fn = struct { | ||||
|         fn less_fn(_: void, lhs: Match, rhs: Match) bool { | ||||
|             return lhs.score > rhs.score; | ||||
|         } | ||||
|     }.less_fn; | ||||
|     std.mem.sort(Match, matches.items, {}, less_fn); | ||||
| 
 | ||||
|     for (matches.items[0..@min(max, matches.items.len)]) |match| | ||||
|         from.send(.{ "PRJ", "new_or_modified_files", self.longest_new_or_modified_file_path, match.path, match.type, match.icon, match.color, match.vcs_status, match.matches }) catch return error.ClientFailed; | ||||
|     return @min(max, matches.items.len); | ||||
| } | ||||
| 
 | ||||
| pub fn request_new_or_modified_files(self: *Self, from: tp.pid_ref, max: usize) ClientError!void { | ||||
|     defer from.send(.{ "PRJ", "new_or_modified_files_done", self.longest_new_or_modified_file_path, "" }) catch {}; | ||||
|     for (self.new_or_modified_files.items, 0..) |file, i| { | ||||
|         from.send(.{ "PRJ", "new_or_modified_files", self.longest_new_or_modified_file_path, file.path, file.type, file.icon, file.color, file.vcs_status }) catch return error.ClientFailed; | ||||
|         if (i >= max) return; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn simple_query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) ClientError!usize { | ||||
|     var i: usize = 0; | ||||
|     defer from.send(.{ "PRJ", "recent_done", self.longest_file_path, query }) catch {}; | ||||
|  | @ -544,6 +635,8 @@ fn loaded(self: *Self, parent: tp.pid_ref) OutOfMemoryError!void { | |||
|         std.time.milliTimestamp() - self.open_time, | ||||
|     }); | ||||
| 
 | ||||
|     self.logger.print("vcs outstanding files: {d}", .{self.new_or_modified_files.items.len}); | ||||
| 
 | ||||
|     parent.send(.{ "PRJ", "open_done", self.name, self.longest_file_path, self.files.items.len }) catch {}; | ||||
| } | ||||
| 
 | ||||
|  | @ -2080,6 +2173,11 @@ pub fn query_git(self: *Self) void { | |||
|     git.status(@intFromPtr(self)) catch { | ||||
|         self.state.status = .failed; | ||||
|     }; | ||||
|     // TODO: This needs to be invoked when there are identified changes in the fs | ||||
|     self.state.vcs_new_or_modified_files = .running; | ||||
|     git.new_or_modified_files(@intFromPtr(self)) catch { | ||||
|         self.state.vcs_new_or_modified_files = .failed; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| fn start_walker(self: *Self) void { | ||||
|  | @ -2093,6 +2191,7 @@ fn start_walker(self: *Self) void { | |||
| pub fn process_git(self: *Self, parent: tp.pid_ref, m: tp.message) (OutOfMemoryError || error{Exit})!void { | ||||
|     var value: []const u8 = undefined; | ||||
|     var path: []const u8 = undefined; | ||||
|     var vcs_status: u8 = undefined; | ||||
|     if (try m.match(.{ tp.any, tp.any, "status", tp.more })) { | ||||
|         return self.process_status(m); | ||||
|     } else if (try m.match(.{ tp.any, tp.any, "workspace_path", tp.null_ })) { | ||||
|  | @ -2131,6 +2230,19 @@ pub fn process_git(self: *Self, parent: tp.pid_ref, m: tp.message) (OutOfMemoryE | |||
|     } else if (try m.match(.{ tp.any, tp.any, "workspace_files", tp.null_ })) { | ||||
|         self.state.workspace_files = .done; | ||||
|         try self.loaded(parent); | ||||
|     } else if (try m.match(.{ tp.any, tp.any, "new_or_modified_files", tp.null_ })) { | ||||
|         self.state.vcs_new_or_modified_files = .done; | ||||
|         try self.loaded(parent); | ||||
|     } else if (try m.match(.{ tp.any, tp.any, "new_or_modified_files", tp.extract(&vcs_status), tp.extract(&path) })) { | ||||
|         self.longest_new_or_modified_file_path = @max(self.longest_new_or_modified_file_path, path.len); | ||||
|         const file_type: []const u8, const file_icon: []const u8, const file_color: u24 = guess_file_type(path); | ||||
|         (try self.new_or_modified_files.addOne(self.allocator)).* = .{ | ||||
|             .path = try self.allocator.dupe(u8, path), | ||||
|             .type = file_type, | ||||
|             .icon = file_icon, | ||||
|             .color = file_color, | ||||
|             .vcs_status = vcs_status, | ||||
|         }; | ||||
|     } else { | ||||
|         self.logger_git.err("git", tp.unexpected(m)); | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Igor Támara
						Igor Támara