From 53be18613d32d4c3d74f233761cc7fd2cd4f9cce Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Tue, 4 Mar 2025 10:54:19 -0600 Subject: [PATCH] config: add global configuration infra Add ability to add global configuration --- docs/comlink.3.scd | 9 +++++++++ docs/comlink.lua | 10 ++++++++++ src/app.zig | 2 ++ src/comlink.zig | 48 ++++++++++++++++++++++++++++++++++++++++++++++ src/irc.zig | 8 +++++--- src/lua.zig | 29 ++++++++++++++++++++++++++++ 6 files changed, 103 insertions(+), 3 deletions(-) diff --git a/docs/comlink.3.scd b/docs/comlink.3.scd index 8fd860f..f02504e 100644 --- a/docs/comlink.3.scd +++ b/docs/comlink.3.scd @@ -8,6 +8,8 @@ comlink - primary lua module for use in comlink configuration *local comlink = require*(_"comlink"_) +*comlink.setup*(_config_) + *local conn = comlink.connect*(_config_) *local channel = comlink.selected_channel*() @@ -25,6 +27,13 @@ comlink - primary lua module for use in comlink configuration The comlink module is the entrypoint into configuring and scripting comlink. This module provides application level API calls. +*comlink.setup* + Accepts a configuration table. This table defines the global application + configuration. The table has the following required fields: + + - *markread_on_focus*: boolean, whether to update unread indicator on + focus gain + *comlink.connect* Accepts a configuration table. This table defines the server configuration. The table has the following required fields: diff --git a/docs/comlink.lua b/docs/comlink.lua index bb33f40..8b8a207 100644 --- a/docs/comlink.lua +++ b/docs/comlink.lua @@ -5,6 +5,16 @@ ---@class comlink local comlink = {} +---@class Configuration +--- +---@field markread_on_focus boolean When true, the unread indicator will be reset when comlink +---regains focus + +---Global configuration +--- +---@param cfg Configuration Comlink global configuration +function comlink.setup(cfg) end + ---@class ConnectionConfiguration --- ---@field server string The server to connect to, eg "chat.sr.ht" diff --git a/src/app.zig b/src/app.zig index f4e2f1b..8559a9d 100644 --- a/src/app.zig +++ b/src/app.zig @@ -42,6 +42,7 @@ const State = struct { }; pub const App = struct { + config: comlink.Config, explicit_join: bool, alloc: std.mem.Allocator, /// System certificate bundle @@ -87,6 +88,7 @@ pub const App = struct { pub fn init(self: *App, gpa: std.mem.Allocator, unicode: *const vaxis.Unicode) !void { self.* = .{ .alloc = gpa, + .config = .{}, .state = .{}, .clients = std.ArrayList(*irc.Client).init(gpa), .env = try std.process.getEnvMap(gpa), diff --git a/src/comlink.zig b/src/comlink.zig index cff0ff3..e645a8f 100644 --- a/src/comlink.zig +++ b/src/comlink.zig @@ -4,6 +4,7 @@ const completer = @import("completer.zig"); const vaxis = @import("vaxis"); pub const irc = @import("irc.zig"); pub const lua = @import("lua.zig"); +const ziglua = @import("ziglua"); pub const App = app.App; pub const Completer = completer.Completer; @@ -14,6 +15,53 @@ pub const Bind = struct { command: Command, }; +pub const Config = struct { + markread_on_focus: bool = false, + + pub fn Fields() type { + const config_fields = std.meta.fieldNames(Config); + var fields: [config_fields.len]std.builtin.Type.EnumField = undefined; + + for (config_fields, 0..) |f, i| { + fields[i] = .{ + .name = f, + .value = i, + }; + } + + return @Type(.{ + .Enum = .{ + .decls = &.{}, + .tag_type = u16, + .fields = &fields, + .is_exhaustive = true, + }, + }); + } + + pub fn fieldToLuaType(field: []const u8) ziglua.LuaType { + const fields = std.meta.fields(Config); + inline for (fields) |f| { + if (std.mem.eql(u8, field, f.name)) { + switch (@typeInfo(f.type)) { + .Bool => return .boolean, + .Int, .ComptimeInt => return .number, + .Pointer => |ptr_info| { + switch (ptr_info.size) { + .Slice => { + if (ptr_info.child == u8) return .string; + }, + else => {}, + } + }, + else => return .nil, + } + } + } + return .nil; + } +}; + pub const Command = union(enum) { /// a raw irc command. Sent verbatim quote, diff --git a/src/irc.zig b/src/irc.zig index 60a045c..fac9937 100644 --- a/src/irc.zig +++ b/src/irc.zig @@ -1192,7 +1192,11 @@ pub const Channel = struct { self.scroll_to_last_read = false; } - if (self.has_unread and self.client.app.has_focus and self.messageViewIsAtBottom()) { + if (self.client.app.config.markread_on_focus and + self.has_unread and + self.client.app.has_focus and + self.messageViewIsAtBottom()) + { try self.markRead(); } @@ -1564,7 +1568,6 @@ pub const Client = struct { channels: std.ArrayList(*Channel), users: std.StringHashMap(*User), - should_close: bool = false, status: std.atomic.Value(Status), caps: Capabilities = .{}, @@ -1609,7 +1612,6 @@ pub const Client = struct { /// Closes the connection pub fn close(self: *Client) void { - self.should_close = true; if (self.status.load(.unordered) == .disconnected) return; if (self.config.tls) { self.client.close() catch {}; diff --git a/src/lua.zig b/src/lua.zig index 8d22a41..d2822c4 100644 --- a/src/lua.zig +++ b/src/lua.zig @@ -167,6 +167,7 @@ const Comlink = struct { pub fn preloader(lua: *Lua) i32 { const fns = [_]ziglua.FnReg{ .{ .name = "bind", .func = ziglua.wrap(bind) }, + .{ .name = "setup", .func = ziglua.wrap(setup) }, .{ .name = "connect", .func = ziglua.wrap(connect) }, .{ .name = "log", .func = ziglua.wrap(log) }, .{ .name = "notify", .func = ziglua.wrap(notify) }, @@ -178,6 +179,34 @@ const Comlink = struct { return 1; } + /// Sets global configuration + fn setup(lua: *Lua) i32 { + defer lua.pop(1); // [] + lua.argCheck(lua.isTable(1), 1, "expected a table"); + // [table] + const app = getApp(lua); + const fields = std.meta.fieldNames(comlink.Config); + for (fields) |field| { + defer lua.pop(1); // [table] + const lua_type = lua.getField(1, field); // [table,type] + if (lua_type == .nil) { + // The field wasn't present + continue; + } + const expected_type = comlink.Config.fieldToLuaType(field); + if (lua_type != expected_type) { + std.log.warn("unexpected type: {}, expected {}", .{ lua_type, expected_type }); + continue; + } + + const field_enum = std.meta.stringToEnum(comlink.Config.Fields(), field) orelse continue; + switch (field_enum) { + .markread_on_focus => app.config.markread_on_focus = lua.toBoolean(1), + } + } + return 0; + } + /// creates a keybind. Accepts one or two string. /// /// The first string is the key binding. The second string is the optional -- 2.51.2