refactor: add support for atomic rename file watcher events on linux
This commit is contained in:
parent
c46d910c87
commit
b88e98f4e2
3 changed files with 62 additions and 14 deletions
|
|
@ -805,6 +805,20 @@ pub fn file_modified(self: *Self, file_path: []const u8) void {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn file_renamed(self: *Self, from_path: []const u8, to_path: []const u8) OutOfMemoryError!void {
|
||||
for (self.files.items) |*file| {
|
||||
if (!std.mem.eql(u8, file.path, from_path)) continue;
|
||||
const new_path = try self.allocator.dupe(u8, to_path);
|
||||
self.allocator.free(file.path);
|
||||
file.path = new_path;
|
||||
file.mtime = std.time.nanoTimestamp();
|
||||
self.longest_file_path = @max(self.longest_file_path, to_path.len);
|
||||
self.sort_files_by_mtime();
|
||||
return;
|
||||
}
|
||||
return self.file_added(to_path);
|
||||
}
|
||||
|
||||
pub fn file_deleted(self: *Self, file_path: []const u8) void {
|
||||
for (self.files.items, 0..) |file, i| {
|
||||
if (!std.mem.eql(u8, file.path, file_path)) continue;
|
||||
|
|
|
|||
|
|
@ -422,12 +422,15 @@ const Process = struct {
|
|||
|
||||
var eol_mode: Buffer.EolModeTag = @intFromEnum(Buffer.EolMode.lf);
|
||||
var event_type: file_watcher.EventType = undefined;
|
||||
var from_path: []const u8 = undefined;
|
||||
|
||||
if (try cbor.match(m.buf, .{ "FW", "change", tp.extract(&path), tp.extract(&event_type) })) {
|
||||
if (try cbor.match(m.buf, .{ "FW", "rename", tp.extract(&from_path), tp.extract(&path) })) {
|
||||
self.handle_file_watch_rename(from_path, path);
|
||||
} else if (try cbor.match(m.buf, .{ "FW", "change", tp.extract(&path), tp.extract(&event_type) })) {
|
||||
self.handle_file_watch_event(path, event_type);
|
||||
} else if (try cbor.match(m.buf, .{ "walk_tree_dir", tp.extract(&project_directory), tp.extract(&path) })) {
|
||||
var abs_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const abs_path = std.fmt.bufPrint(&abs_buf, "{s}{c}{s}", .{ project_directory, std.fs.path.sep, path }) catch return;
|
||||
const abs_path = std.fmt.bufPrint(&abs_buf, "{s}{c}{s}", .{ project_directory, std.fs.path.sep, path }) catch project_directory;
|
||||
file_watcher.watch(abs_path) catch |e| self.logger.err("file_watcher.watch_dir", e);
|
||||
} else if (try cbor.match(m.buf, .{ "walk_tree_entry", tp.extract(&project_directory), tp.more })) {
|
||||
if (self.projects.get(project_directory)) |project|
|
||||
|
|
@ -555,25 +558,53 @@ const Process = struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_file_watch_event(self: *Process, abs_path: []const u8, event_type: file_watcher.EventType) void {
|
||||
std.log.debug("file_watch_event: {s} {s}", .{ @tagName(event_type), abs_path });
|
||||
fn project_for_path(self: *Process, abs_path: []const u8) ?struct { project: *Project, rel_path: []const u8 } {
|
||||
var it = self.projects.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const dir = entry.key_ptr.*;
|
||||
if (!std.mem.startsWith(u8, abs_path, dir)) continue;
|
||||
if (abs_path.len <= dir.len or abs_path[dir.len] != std.fs.path.sep) continue;
|
||||
const rel_path = abs_path[dir.len + 1 ..];
|
||||
const project = entry.value_ptr.*;
|
||||
return .{ .project = entry.value_ptr.*, .rel_path = abs_path[dir.len + 1 ..] };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn handle_file_watch_rename(self: *Process, abs_from: []const u8, abs_to: []const u8) void {
|
||||
std.log.debug("file_watch_event: rename {s} -> {s}", .{ abs_from, abs_to });
|
||||
const src = self.project_for_path(abs_from);
|
||||
const dst = self.project_for_path(abs_to);
|
||||
|
||||
if (src) |s| {
|
||||
if (dst) |d| {
|
||||
if (s.project == d.project) {
|
||||
s.project.file_renamed(s.rel_path, d.rel_path) catch |e| self.logger.err("file_watcher.file_renamed", e);
|
||||
} else {
|
||||
s.project.file_deleted(s.rel_path);
|
||||
d.project.file_added(d.rel_path) catch |e| self.logger.err("file_watcher.file_added", e);
|
||||
}
|
||||
} else {
|
||||
s.project.file_deleted(s.rel_path);
|
||||
}
|
||||
} else if (dst) |d| {
|
||||
d.project.file_added(d.rel_path) catch |e| self.logger.err("file_watcher.file_added", e);
|
||||
} else {
|
||||
self.parent.send(.{ "FW", "rename", abs_from, abs_to }) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_file_watch_event(self: *Process, abs_path: []const u8, event_type: file_watcher.EventType) void {
|
||||
std.log.debug("file_watch_event: {s} {s}", .{ @tagName(event_type), abs_path });
|
||||
if (self.project_for_path(abs_path)) |match| {
|
||||
switch (event_type) {
|
||||
.created => project.file_added(rel_path) catch |e| self.logger.err("file_watcher.file_added", e),
|
||||
.modified => project.file_modified(rel_path),
|
||||
.deleted => project.file_deleted(rel_path),
|
||||
.renamed => project.file_deleted(rel_path),
|
||||
}
|
||||
return;
|
||||
.created => match.project.file_added(match.rel_path) catch |e| self.logger.err("file_watcher.file_added", e),
|
||||
.modified => match.project.file_modified(match.rel_path),
|
||||
.deleted => match.project.file_deleted(match.rel_path),
|
||||
.renamed => match.project.file_deleted(match.rel_path),
|
||||
}
|
||||
} else {
|
||||
self.parent.send(.{ "FW", "change", abs_path, event_type }) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
fn open(self: *Process, project_directory: []const u8) (SpawnError || std.fs.Dir.OpenError)!void {
|
||||
if (self.projects.get(project_directory)) |project| {
|
||||
|
|
|
|||
|
|
@ -625,6 +625,9 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void {
|
|||
if (try m.match(.{ "FW", "change", tp.more })) // file watcher events
|
||||
return;
|
||||
|
||||
if (try m.match(.{ "FW", "rename", tp.more })) // file watcher rename events
|
||||
return;
|
||||
|
||||
return tp.unexpected(m);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue