feat: add support for loading customized flow themes

This commit is contained in:
CJ van den Berg 2025-04-25 10:02:01 +02:00
parent 8f66e855ed
commit e8dbff76a1
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
2 changed files with 64 additions and 14 deletions

View file

@ -1,5 +1,7 @@
const std = @import("std");
const cbor = @import("cbor");
const Theme = @import("theme");
const themes = @import("themes");
const builtin = @import("builtin");
const application_name = "flow";
@ -193,8 +195,6 @@ fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 {
return config_dir;
}
const theme_dir = "themes";
fn get_app_config_file_name(appname: []const u8, comptime base_name: []const u8) ConfigDirError![]const u8 {
return get_app_config_dir_file_name(appname, base_name ++ ".json");
}
@ -205,3 +205,60 @@ fn get_app_config_dir_file_name(appname: []const u8, comptime config_file_name:
};
return std.fmt.bufPrint(&local.config_file_buffer, "{s}/{s}", .{ try get_app_config_dir(appname), config_file_name });
}
const theme_dir = "themes";
fn get_theme_directory() ![]const u8 {
const local = struct {
var dir_buffer: [std.posix.PATH_MAX]u8 = undefined;
};
const a = std.heap.c_allocator;
if (std.process.getEnvVarOwned(a, "FLOW_THEMES_DIR") catch null) |dir| {
defer a.free(dir);
return try std.fmt.bufPrint(&local.dir_buffer, "{s}", .{dir});
}
return try std.fmt.bufPrint(&local.dir_buffer, "{s}/{s}", .{ try get_app_config_dir(application_name), theme_dir });
}
pub fn get_theme_file_name(theme_name: []const u8) ![]const u8 {
const dir = try get_theme_directory();
const local = struct {
var file_buffer: [std.posix.PATH_MAX]u8 = undefined;
};
return try std.fmt.bufPrint(&local.file_buffer, "{s}/{s}.json", .{ dir, theme_name });
}
fn read_theme(allocator: std.mem.Allocator, theme_name: []const u8) ?[]const u8 {
const file_name = get_theme_file_name(theme_name) catch return null;
var file = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch return null;
defer file.close();
return file.readToEndAlloc(allocator, 64 * 1024) catch null;
}
fn load_theme_file(allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Theme) {
return load_theme_file_internal(allocator, theme_name) catch |e| {
std.log.err("loaded theme from file failed: {}", .{e});
return e;
};
}
fn load_theme_file_internal(allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Theme) {
_ = std.json.Scanner;
const json_str = read_theme(allocator, theme_name) orelse return null;
defer allocator.free(json_str);
return try std.json.parseFromSlice(Theme, allocator, json_str, .{ .allocate = .alloc_always });
}
pub fn get_theme_by_name(allocator: std.mem.Allocator, name: []const u8) ?struct { Theme, ?std.json.Parsed(Theme) } {
if (load_theme_file(allocator, name) catch null) |parsed_theme| {
std.log.info("loaded theme from file: {s}", .{name});
return .{ parsed_theme.value, parsed_theme };
}
std.log.info("loading theme: {s}", .{name});
for (themes.themes) |theme_| {
if (std.mem.eql(u8, theme_.name, name))
return .{ theme_, null };
}
return null;
}

View file

@ -94,14 +94,15 @@ pub fn main() !void {
}
if (highlight_line_end < highlight_line_start) {
try std.io.getStdErr().writer().print("invalid range\n", .{});
std.log.err("invalid range", .{});
std.process.exit(1);
}
const theme = get_theme_by_name(theme_name) orelse {
try std.io.getStdErr().writer().print("theme \"{s}\" not found\n", .{theme_name});
const theme, const parsed_theme = config_loader.get_theme_by_name(a, theme_name) orelse {
std.log.err("theme \"{s}\" not found", .{theme_name});
std.process.exit(1);
};
_ = parsed_theme;
const set_style: StyleFn = if (res.args.html != 0) set_html_style else set_ansi_style;
const unset_style: StyleFn = if (res.args.html != 0) unset_html_style else unset_ansi_style;
@ -174,7 +175,7 @@ fn get_parser(a: std.mem.Allocator, content: []const u8, file_path: []const u8,
}
fn unknown_file_type(name: []const u8) noreturn {
std.io.getStdErr().writer().print("unknown file type \'{s}\'\n", .{name}) catch {};
std.log.err("unknown file type \'{s}\'\n", .{name});
std.process.exit(1);
}
@ -391,14 +392,6 @@ pub const fallbacks: []const FallBack = &[_]FallBack{
.{ .ts = "field", .tm = "variable" },
};
fn get_theme_by_name(name: []const u8) ?Theme {
for (themes.themes) |theme| {
if (std.mem.eql(u8, theme.name, name))
return theme;
}
return null;
}
fn list_themes(writer: Writer) !void {
var max_name_len: usize = 0;
for (themes.themes) |theme|