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 { pub fn buffer_to_ref(_: *Self, buffer: *Buffer) usize {
return @intFromPtr(buffer); 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; } else return error.InvalidSwapTabsArgument;
} }
pub const place_next_tab_meta: Meta = .{ .arguments = &.{ .string, .integer } }; 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 { 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 LinuxWatcher = @This();
const std = @import("std"); const std = @import("std");
const fatal = @import("../../../fatal.zig"); const Debouncer = @import("Debouncer.zig");
const Debouncer = @import("../../serve.zig").Debouncer; const Error = @import("root.zig").Error;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const log = std.log.scoped(.watcher); const log = std.log.scoped(.watcher);
@ -30,11 +30,7 @@ pub fn init(
gpa: std.mem.Allocator, gpa: std.mem.Allocator,
debouncer: *Debouncer, debouncer: *Debouncer,
dir_paths: []const []const u8, dir_paths: []const []const u8,
) LinuxWatcher { ) Error!LinuxWatcher {
errdefer |err| fatal.msg("error: unable to start the file watcher: {s}", .{
@errorName(err),
});
const notify_fd = try std.posix.inotify_init1(0); const notify_fd = try std.posix.inotify_init1(0);
var watcher: LinuxWatcher = .{ var watcher: LinuxWatcher = .{
.gpa = gpa, .gpa = gpa,
@ -300,7 +296,7 @@ pub fn listen(watcher: *LinuxWatcher) !void {
var event_data = buffer[0..len]; var event_data = buffer[0..len];
while (event_data.len > 0) { 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).?; const parent = watcher.watch_fds.get(event.wd).?;
event_data = event_data[event_size + event.len ..]; event_data = event_data[event_size + event.len ..];

View file

@ -1,8 +1,7 @@
const MacosWatcher = @This(); const MacosWatcher = @This();
const std = @import("std"); const std = @import("std");
const fatal = @import("../../../fatal.zig"); const Debouncer = @import("Debouncer.zig");
const Debouncer = @import("../../serve.zig").Debouncer;
const c = @cImport({ const c = @cImport({
@cInclude("CoreServices/CoreServices.h"); @cInclude("CoreServices/CoreServices.h");
@ -31,11 +30,7 @@ pub fn start(watcher: *MacosWatcher) !void {
t.detach(); t.detach();
} }
pub fn listen(watcher: *MacosWatcher) void { pub fn listen(watcher: *MacosWatcher) error{ OutOfMemory, FSEventStreamError }!void {
errdefer |err| switch (err) {
error.OutOfMemory => fatal.oom(),
};
const macos_paths = try watcher.gpa.alloc( const macos_paths = try watcher.gpa.alloc(
c.CFStringRef, c.CFStringRef,
watcher.dir_paths.len, watcher.dir_paths.len,
@ -74,9 +69,8 @@ pub fn listen(watcher: *MacosWatcher) void {
c.kCFRunLoopDefaultMode, c.kCFRunLoopDefaultMode,
); );
if (c.FSEventStreamStart(stream) == 0) { if (c.FSEventStreamStart(stream) == 0)
fatal.msg("error: macos watcher FSEventStreamStart failed", .{}); return error.FSEventStreamError;
}
c.CFRunLoopRun(); c.CFRunLoopRun();
@ -98,9 +92,9 @@ pub fn macosCallback(
_ = eventIds; _ = eventIds;
_ = eventFlags; _ = eventFlags;
_ = streamRef; _ = 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| { for (paths[0..numEvents]) |p| {
const path = std.mem.span(p); const path = std.mem.span(p);
log.debug("Changed: {s}\n", .{path}); log.debug("Changed: {s}\n", .{path});

View file

@ -2,8 +2,7 @@ const WindowsWatcher = @This();
const std = @import("std"); const std = @import("std");
const windows = std.os.windows; const windows = std.os.windows;
const fatal = @import("../../../fatal.zig"); const Debouncer = @import("Debouncer.zig");
const Debouncer = @import("../../serve.zig").Debouncer;
const log = std.log.scoped(.watcher); const log = std.log.scoped(.watcher);
@ -40,11 +39,7 @@ pub fn init(
gpa: std.mem.Allocator, gpa: std.mem.Allocator,
debouncer: *Debouncer, debouncer: *Debouncer,
dir_paths: []const []const u8, dir_paths: []const []const u8,
) WindowsWatcher { ) !WindowsWatcher {
errdefer |err| fatal.msg("error: unable to start the file watcher: {s}", .{
@errorName(err),
});
var watcher = WindowsWatcher{ var watcher = WindowsWatcher{
.debouncer = debouncer, .debouncer = debouncer,
.iocp_port = windows.INVALID_HANDLE_VALUE, .iocp_port = windows.INVALID_HANDLE_VALUE,

View file

@ -1,8 +1,47 @@
const builtin = @import("builtin"); const builtin = @import("builtin");
pub const Watcher = switch (builtin.target.os.tag) { pub const Watcher = switch (builtin.target.os.tag) {
.linux => @import("serve/watcher/LinuxWatcher.zig"), .linux => @import("LinuxWatcher.zig"),
.macos => @import("serve/watcher/MacosWatcher.zig"), .macos => @import("MacosWatcher.zig"),
.windows => @import("serve/watcher/WindowsWatcher.zig"), .windows => @import("WindowsWatcher.zig"),
else => @compileError("unsupported platform"), 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,
};