refactor: get zine watcher to build in flow

This commit is contained in:
CJ van den Berg 2025-11-30 15:03:14 +01:00
parent db4e3e1936
commit b698fffd25
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
7 changed files with 90 additions and 30 deletions

View file

@ -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();
}

View file

@ -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 {

View file

@ -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;
}

View file

@ -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 ..];

View file

@ -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});

View file

@ -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,

View file

@ -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,
};