diff --git a/src/buffer/Manager.zig b/src/buffer/Manager.zig index be4075f..1e6412a 100644 --- a/src/buffer/Manager.zig +++ b/src/buffer/Manager.zig @@ -241,3 +241,22 @@ pub fn buffer_from_ref(self: *Self, buffer_ref: usize) ?*Buffer { pub fn buffer_to_ref(_: *Self, buffer: *Buffer) usize { return @intFromPtr(buffer); } + +const watcher = @import("watcher"); + +pub fn watch_all_buffers(self: *Self) watcher.Error!void { + var debouncer: watcher.Debouncer = .{}; + var buffers: std.ArrayList([]const u8) = .empty; + var iter = self.buffers.keyIterator(); + while (iter.next()) |key| { + (try buffers.addOne(self.allocator)).* = key.*; + } + + var watcher_: watcher.Watcher = try .init( + self.allocator, + &debouncer, + &.{}, // dirs_to_watch.items, + ); + + try watcher_.start(); +} diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index a822b63..4abb817 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -1401,6 +1401,15 @@ const cmds = struct { } else return error.InvalidSwapTabsArgument; } pub const place_next_tab_meta: Meta = .{ .arguments = &.{ .string, .integer } }; + + pub fn enable_file_watcher(self: *Self, _: Ctx) Result { + self.buffer_manager.watch_all_buffers() catch |e| { + const logger = log.logger("watcher"); + defer logger.deinit(); + logger.print_err("watcher", "failed to watch all buffes: {s}", .{@errorName(e)}); + }; + } + pub const enable_file_watcher_meta: Meta = .{ .description = "Enable file watcher" }; }; pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result { diff --git a/src/watcher/Debouncer.zig b/src/watcher/Debouncer.zig new file mode 100644 index 0000000..614e491 --- /dev/null +++ b/src/watcher/Debouncer.zig @@ -0,0 +1,8 @@ +const std = @import("std"); + +const Debouncer = @This(); + +/// Thread-safe. To be called when a new event comes in +pub fn newEvent(d: *Debouncer) void { + _ = d; +} diff --git a/src/watcher/LinuxWatcher.zig b/src/watcher/LinuxWatcher.zig index ab06a06..8463255 100644 --- a/src/watcher/LinuxWatcher.zig +++ b/src/watcher/LinuxWatcher.zig @@ -1,8 +1,8 @@ const LinuxWatcher = @This(); const std = @import("std"); -const fatal = @import("../../../fatal.zig"); -const Debouncer = @import("../../serve.zig").Debouncer; +const Debouncer = @import("Debouncer.zig"); +const Error = @import("root.zig").Error; const Allocator = std.mem.Allocator; const log = std.log.scoped(.watcher); @@ -30,11 +30,7 @@ pub fn init( gpa: std.mem.Allocator, debouncer: *Debouncer, dir_paths: []const []const u8, -) LinuxWatcher { - errdefer |err| fatal.msg("error: unable to start the file watcher: {s}", .{ - @errorName(err), - }); - +) Error!LinuxWatcher { const notify_fd = try std.posix.inotify_init1(0); var watcher: LinuxWatcher = .{ .gpa = gpa, @@ -300,7 +296,7 @@ pub fn listen(watcher: *LinuxWatcher) !void { var event_data = buffer[0..len]; while (event_data.len > 0) { - const event: *Event = @alignCast(@ptrCast(event_data[0..event_size])); + const event: *Event = @ptrCast(@alignCast(event_data[0..event_size])); const parent = watcher.watch_fds.get(event.wd).?; event_data = event_data[event_size + event.len ..]; diff --git a/src/watcher/MacosWatcher.zig b/src/watcher/MacosWatcher.zig index 1a9f69b..7c33e92 100644 --- a/src/watcher/MacosWatcher.zig +++ b/src/watcher/MacosWatcher.zig @@ -1,8 +1,7 @@ const MacosWatcher = @This(); const std = @import("std"); -const fatal = @import("../../../fatal.zig"); -const Debouncer = @import("../../serve.zig").Debouncer; +const Debouncer = @import("Debouncer.zig"); const c = @cImport({ @cInclude("CoreServices/CoreServices.h"); @@ -31,11 +30,7 @@ pub fn start(watcher: *MacosWatcher) !void { t.detach(); } -pub fn listen(watcher: *MacosWatcher) void { - errdefer |err| switch (err) { - error.OutOfMemory => fatal.oom(), - }; - +pub fn listen(watcher: *MacosWatcher) error{ OutOfMemory, FSEventStreamError }!void { const macos_paths = try watcher.gpa.alloc( c.CFStringRef, watcher.dir_paths.len, @@ -74,9 +69,8 @@ pub fn listen(watcher: *MacosWatcher) void { c.kCFRunLoopDefaultMode, ); - if (c.FSEventStreamStart(stream) == 0) { - fatal.msg("error: macos watcher FSEventStreamStart failed", .{}); - } + if (c.FSEventStreamStart(stream) == 0) + return error.FSEventStreamError; c.CFRunLoopRun(); @@ -98,9 +92,9 @@ pub fn macosCallback( _ = eventIds; _ = eventFlags; _ = streamRef; - const watcher: *MacosWatcher = @alignCast(@ptrCast(clientCallBackInfo)); + const watcher: *MacosWatcher = @ptrCast(@alignCast(clientCallBackInfo)); - const paths: [*][*:0]u8 = @alignCast(@ptrCast(eventPaths)); + const paths: [*][*:0]u8 = @ptrCast(@alignCast(eventPaths)); for (paths[0..numEvents]) |p| { const path = std.mem.span(p); log.debug("Changed: {s}\n", .{path}); diff --git a/src/watcher/WindowsWatcher.zig b/src/watcher/WindowsWatcher.zig index 0a7fbd2..cbacc65 100644 --- a/src/watcher/WindowsWatcher.zig +++ b/src/watcher/WindowsWatcher.zig @@ -2,8 +2,7 @@ const WindowsWatcher = @This(); const std = @import("std"); const windows = std.os.windows; -const fatal = @import("../../../fatal.zig"); -const Debouncer = @import("../../serve.zig").Debouncer; +const Debouncer = @import("Debouncer.zig"); const log = std.log.scoped(.watcher); @@ -40,11 +39,7 @@ pub fn init( gpa: std.mem.Allocator, debouncer: *Debouncer, dir_paths: []const []const u8, -) WindowsWatcher { - errdefer |err| fatal.msg("error: unable to start the file watcher: {s}", .{ - @errorName(err), - }); - +) !WindowsWatcher { var watcher = WindowsWatcher{ .debouncer = debouncer, .iocp_port = windows.INVALID_HANDLE_VALUE, diff --git a/src/watcher/root.zig b/src/watcher/root.zig index 82b2ff3..2b022d0 100644 --- a/src/watcher/root.zig +++ b/src/watcher/root.zig @@ -1,8 +1,47 @@ const builtin = @import("builtin"); pub const Watcher = switch (builtin.target.os.tag) { - .linux => @import("serve/watcher/LinuxWatcher.zig"), - .macos => @import("serve/watcher/MacosWatcher.zig"), - .windows => @import("serve/watcher/WindowsWatcher.zig"), + .linux => @import("LinuxWatcher.zig"), + .macos => @import("MacosWatcher.zig"), + .windows => @import("WindowsWatcher.zig"), else => @compileError("unsupported platform"), }; + +pub const Debouncer = @import("Debouncer.zig"); + +pub const Error = error{ + OutOfMemory, + SystemResources, + Unexpected, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + LockedMemoryLimitExceeded, + ThreadQuotaExceeded, + IsDir, + WouldBlock, + AccessDenied, + ProcessNotFound, + PermissionDenied, + FileTooBig, + NoSpaceLeft, + DeviceBusy, + NoDevice, + SharingViolation, + PathAlreadyExists, + FileNotFound, + PipeBusy, + NameTooLong, + InvalidUtf8, + InvalidWtf8, + BadPathName, + NetworkNotFound, + AntivirusInterference, + SymLinkLoop, + NotDir, + FileLocksNotSupported, + FileBusy, + UserResourceLimitReached, + WatchAlreadyExists, + InvalidHandle, + QueueFailed, +};