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, | allocator: std.mem.Allocator, | ||||||
| name: []const u8, | name: []const u8, | ||||||
| files: std.ArrayListUnmanaged(File) = .empty, | files: std.ArrayListUnmanaged(File) = .empty, | ||||||
|  | new_or_modified_files: std.ArrayListUnmanaged(FileVcsStatus) = .empty, | ||||||
| pending: std.ArrayListUnmanaged(File) = .empty, | pending: std.ArrayListUnmanaged(File) = .empty, | ||||||
| longest_file_path: usize = 0, | longest_file_path: usize = 0, | ||||||
|  | longest_new_or_modified_file_path: usize = 0, | ||||||
| open_time: i64, | open_time: i64, | ||||||
| language_servers: std.StringHashMap(*const LSP), | language_servers: std.StringHashMap(*const LSP), | ||||||
| file_language_server_name: std.StringHashMap([]const u8), | file_language_server_name: std.StringHashMap([]const u8), | ||||||
|  | @ -41,6 +43,7 @@ state: struct { | ||||||
|     current_branch: State = .none, |     current_branch: State = .none, | ||||||
|     workspace_files: State = .none, |     workspace_files: State = .none, | ||||||
|     status: State = .none, |     status: State = .none, | ||||||
|  |     vcs_new_or_modified_files: State = .none, | ||||||
| } = .{}, | } = .{}, | ||||||
| 
 | 
 | ||||||
| status: VcsStatus = .{}, | status: VcsStatus = .{}, | ||||||
|  | @ -66,6 +69,14 @@ const File = struct { | ||||||
|     visited: bool = false, |     visited: bool = false, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | const FileVcsStatus = struct { | ||||||
|  |     path: []const u8, | ||||||
|  |     type: []const u8, | ||||||
|  |     icon: []const u8, | ||||||
|  |     color: u24, | ||||||
|  |     vcs_status: u8, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| pub const FilePos = struct { | pub const FilePos = struct { | ||||||
|     row: usize = 0, |     row: usize = 0, | ||||||
|     col: usize = 0, |     col: usize = 0, | ||||||
|  | @ -105,6 +116,8 @@ pub fn deinit(self: *Self) void { | ||||||
|         self.allocator.free(p.key_ptr.*); |         self.allocator.free(p.key_ptr.*); | ||||||
|         p.value_ptr.*.term(); |         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); |     for (self.files.items) |file| self.allocator.free(file.path); | ||||||
|     self.files.deinit(self.allocator); |     self.files.deinit(self.allocator); | ||||||
|     self.pending.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 { | fn simple_query_recent_files(self: *Self, from: tp.pid_ref, max: usize, query: []const u8) ClientError!usize { | ||||||
|     var i: usize = 0; |     var i: usize = 0; | ||||||
|     defer from.send(.{ "PRJ", "recent_done", self.longest_file_path, query }) catch {}; |     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, |         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 {}; |     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 { |     git.status(@intFromPtr(self)) catch { | ||||||
|         self.state.status = .failed; |         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 { | 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 { | pub fn process_git(self: *Self, parent: tp.pid_ref, m: tp.message) (OutOfMemoryError || error{Exit})!void { | ||||||
|     var value: []const u8 = undefined; |     var value: []const u8 = undefined; | ||||||
|     var path: []const u8 = undefined; |     var path: []const u8 = undefined; | ||||||
|  |     var vcs_status: u8 = undefined; | ||||||
|     if (try m.match(.{ tp.any, tp.any, "status", tp.more })) { |     if (try m.match(.{ tp.any, tp.any, "status", tp.more })) { | ||||||
|         return self.process_status(m); |         return self.process_status(m); | ||||||
|     } else if (try m.match(.{ tp.any, tp.any, "workspace_path", tp.null_ })) { |     } 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_ })) { |     } else if (try m.match(.{ tp.any, tp.any, "workspace_files", tp.null_ })) { | ||||||
|         self.state.workspace_files = .done; |         self.state.workspace_files = .done; | ||||||
|         try self.loaded(parent); |         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 { |     } else { | ||||||
|         self.logger_git.err("git", tp.unexpected(m)); |         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