Compare commits
No commits in common. "52ec22a18c26695664984cacf2c2aba7b407db72" and "32f4bcec3af3923c4d47d376e3a74bc9ff2bd766" have entirely different histories.
52ec22a18c
...
32f4bcec3a
2 changed files with 51 additions and 71 deletions
116
src/main.zig
116
src/main.zig
|
|
@ -498,8 +498,9 @@ var config_mutex: std.Thread.Mutex = .{};
|
||||||
pub fn exists_config(T: type) bool {
|
pub fn exists_config(T: type) bool {
|
||||||
config_mutex.lock();
|
config_mutex.lock();
|
||||||
defer config_mutex.unlock();
|
defer config_mutex.unlock();
|
||||||
const file_name = get_app_config_file_name(application_name, @typeName(T)) catch return false;
|
const json_file_name = get_app_config_file_name(application_name, @typeName(T)) catch return false;
|
||||||
var file = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch return false;
|
const text_file_name = json_file_name[0 .. json_file_name.len - ".json".len];
|
||||||
|
var file = std.fs.openFileAbsolute(text_file_name, .{ .mode = .read_only }) catch return false;
|
||||||
defer file.close();
|
defer file.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -519,15 +520,19 @@ pub fn read_config(T: type, allocator: std.mem.Allocator) struct { T, [][]const
|
||||||
config_mutex.lock();
|
config_mutex.lock();
|
||||||
defer config_mutex.unlock();
|
defer config_mutex.unlock();
|
||||||
var bufs: [][]const u8 = &[_][]const u8{};
|
var bufs: [][]const u8 = &[_][]const u8{};
|
||||||
const file_name = get_app_config_file_name(application_name, @typeName(T)) catch return .{ get_default(T), bufs };
|
const json_file_name = get_app_config_file_name(application_name, @typeName(T)) catch return .{ get_default(T), bufs };
|
||||||
|
const text_file_name = json_file_name[0 .. json_file_name.len - ".json".len];
|
||||||
var conf: T = get_default(T);
|
var conf: T = get_default(T);
|
||||||
_ = read_config_file(T, allocator, &conf, &bufs, file_name);
|
if (!read_config_file(T, allocator, &conf, &bufs, text_file_name)) {
|
||||||
|
_ = read_config_file(T, allocator, &conf, &bufs, json_file_name);
|
||||||
|
}
|
||||||
read_nested_include_files(T, allocator, &conf, &bufs);
|
read_nested_include_files(T, allocator, &conf, &bufs);
|
||||||
return .{ conf, bufs };
|
return .{ conf, bufs };
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if the file was found
|
// returns true if the file was found
|
||||||
fn read_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][]const u8, file_name: []const u8) bool {
|
fn read_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][]const u8, file_name: []const u8) bool {
|
||||||
|
// std.log.info("loading {s}", .{file_name});
|
||||||
const err: anyerror = blk: {
|
const err: anyerror = blk: {
|
||||||
if (std.mem.endsWith(u8, file_name, ".json")) if (read_json_config_file(T, allocator, conf, bufs, file_name)) return true else |e| break :blk e;
|
if (std.mem.endsWith(u8, file_name, ".json")) if (read_json_config_file(T, allocator, conf, bufs, file_name)) return true else |e| break :blk e;
|
||||||
if (read_text_config_file(T, allocator, conf, bufs, file_name)) return true else |e| break :blk e;
|
if (read_text_config_file(T, allocator, conf, bufs, file_name)) return true else |e| break :blk e;
|
||||||
|
|
@ -667,12 +672,16 @@ fn read_nested_include_files(T: type, allocator: std.mem.Allocator, conf: *T, bu
|
||||||
|
|
||||||
pub const ConfigWriteError = error{ CreateConfigFileFailed, WriteConfigFileFailed, WriteFailed };
|
pub const ConfigWriteError = error{ CreateConfigFileFailed, WriteConfigFileFailed, WriteFailed };
|
||||||
|
|
||||||
pub fn write_config(data: anytype, allocator: std.mem.Allocator) (ConfigDirError || ConfigWriteError)!void {
|
pub fn write_config(conf: anytype, allocator: std.mem.Allocator) (ConfigDirError || ConfigWriteError)!void {
|
||||||
const T = @TypeOf(data);
|
|
||||||
config_mutex.lock();
|
config_mutex.lock();
|
||||||
defer config_mutex.unlock();
|
defer config_mutex.unlock();
|
||||||
_ = allocator;
|
_ = allocator;
|
||||||
const file_name = try get_app_config_file_name(application_name, @typeName(T));
|
const file_name = try get_app_config_file_name(application_name, @typeName(@TypeOf(conf)));
|
||||||
|
return write_text_config_file(@TypeOf(conf), conf, file_name[0 .. file_name.len - 5]);
|
||||||
|
// return write_json_file(@TypeOf(conf), conf, allocator, try get_app_config_file_name(application_name, @typeName(@TypeOf(conf))));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_text_config_file(comptime T: type, data: T, file_name: []const u8) ConfigWriteError!void {
|
||||||
var file = std.fs.createFileAbsolute(file_name, .{ .truncate = true }) catch |e| {
|
var file = std.fs.createFileAbsolute(file_name, .{ .truncate = true }) catch |e| {
|
||||||
std.log.err("createFileAbsolute failed with {any} for: {s}", .{ e, file_name });
|
std.log.err("createFileAbsolute failed with {any} for: {s}", .{ e, file_name });
|
||||||
return error.CreateConfigFileFailed;
|
return error.CreateConfigFileFailed;
|
||||||
|
|
@ -680,20 +689,7 @@ pub fn write_config(data: anytype, allocator: std.mem.Allocator) (ConfigDirError
|
||||||
defer file.close();
|
defer file.close();
|
||||||
var buf: [4096]u8 = undefined;
|
var buf: [4096]u8 = undefined;
|
||||||
var writer = file.writer(&buf);
|
var writer = file.writer(&buf);
|
||||||
|
write_config_to_writer(T, data, &writer.interface) catch |e| {
|
||||||
try writer.interface.print(
|
|
||||||
\\# This file is written by flow when settings are changed interactively. You may
|
|
||||||
\\# edit values by hand, but not comments. Comments will be overwritten. Values
|
|
||||||
\\# configured to the default value will be automatically commented and possibly
|
|
||||||
\\# updated if the default value changes. To store values that cannot be changed
|
|
||||||
\\# by flow, put them in a new file and reference it in the include_files
|
|
||||||
\\# configuration option at the end of this file. include_files are never
|
|
||||||
\\# modified by flow.
|
|
||||||
\\
|
|
||||||
\\
|
|
||||||
, .{});
|
|
||||||
|
|
||||||
write_config_to_writer_internal(T, data, &writer.interface) catch |e| {
|
|
||||||
std.log.err("write file failed with {any} for: {s}", .{ e, file_name });
|
std.log.err("write file failed with {any} for: {s}", .{ e, file_name });
|
||||||
return error.WriteConfigFileFailed;
|
return error.WriteConfigFileFailed;
|
||||||
};
|
};
|
||||||
|
|
@ -701,24 +697,8 @@ pub fn write_config(data: anytype, allocator: std.mem.Allocator) (ConfigDirError
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_config_to_writer(comptime T: type, data: T, writer: *std.Io.Writer) std.Io.Writer.Error!void {
|
pub fn write_config_to_writer(comptime T: type, data: T, writer: *std.Io.Writer) std.Io.Writer.Error!void {
|
||||||
try writer.print(
|
|
||||||
\\# This file is generated by flow and updated when opened by an interactive
|
|
||||||
\\# command. You may edit values by hand, but not comments. Comments will be
|
|
||||||
\\# overwritten. Values configured to the default value will be automatically
|
|
||||||
\\# commented and possibly updated if the default value changes. To store
|
|
||||||
\\# values that cannot be changed by flow, put them in a new file and reference
|
|
||||||
\\# it in the include_files configuration option at the end of this file.
|
|
||||||
\\# include_files are never modified by flow.
|
|
||||||
\\
|
|
||||||
\\
|
|
||||||
, .{});
|
|
||||||
return write_config_to_writer_internal(T, data, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_config_to_writer_internal(comptime T: type, data: T, writer: *std.Io.Writer) std.Io.Writer.Error!void {
|
|
||||||
const default: T = .{};
|
const default: T = .{};
|
||||||
inline for (@typeInfo(T).@"struct".fields) |field_info| {
|
inline for (@typeInfo(T).@"struct".fields) |field_info| {
|
||||||
var is_default = false;
|
|
||||||
if (config_eql(
|
if (config_eql(
|
||||||
T,
|
T,
|
||||||
field_info.type,
|
field_info.type,
|
||||||
|
|
@ -726,39 +706,29 @@ fn write_config_to_writer_internal(comptime T: type, data: T, writer: *std.Io.Wr
|
||||||
@field(default, field_info.name),
|
@field(default, field_info.name),
|
||||||
)) {
|
)) {
|
||||||
try writer.print("# {s} ", .{field_info.name});
|
try writer.print("# {s} ", .{field_info.name});
|
||||||
is_default = true;
|
|
||||||
} else {
|
} else {
|
||||||
try writer.print("{s} ", .{field_info.name});
|
try writer.print("{s} ", .{field_info.name});
|
||||||
}
|
}
|
||||||
try write_config_value(field_info.type, @field(data, field_info.name), writer);
|
switch (field_info.type) {
|
||||||
try writer.print("\n", .{});
|
u24 => try write_color_value(@field(data, field_info.name), writer),
|
||||||
if (!is_default) {
|
?u24 => if (@field(data, field_info.name)) |value|
|
||||||
try writer.print("# default value: ", .{});
|
try write_color_value(value, writer)
|
||||||
try write_config_value(field_info.type, @field(default, field_info.name), writer);
|
else
|
||||||
try writer.print("\n", .{});
|
try writer.writeAll("null"),
|
||||||
|
else => {
|
||||||
|
var s: std.json.Stringify = .{ .writer = writer, .options = .{ .whitespace = .minified } };
|
||||||
|
try s.write(@field(data, field_info.name));
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
try writer.print("\n", .{});
|
||||||
try writer.print("# value type: ", .{});
|
try writer.print("# value type: ", .{});
|
||||||
try write_config_value_description(T, field_info.type, field_info.name, writer);
|
try write_config_value_description(T, field_info.type, writer);
|
||||||
try writer.print("\n", .{});
|
try writer.print("\n", .{});
|
||||||
try writer.print("\n", .{});
|
try writer.print("\n", .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_config_value(T: type, value: T, writer: *std.Io.Writer) !void {
|
fn write_config_value_description(T: type, field_type: type, writer: *std.Io.Writer) !void {
|
||||||
switch (T) {
|
|
||||||
u24 => try write_color_value(value, writer),
|
|
||||||
?u24 => if (value) |v|
|
|
||||||
try write_color_value(v, writer)
|
|
||||||
else
|
|
||||||
try writer.writeAll("null"),
|
|
||||||
else => {
|
|
||||||
var s: std.json.Stringify = .{ .writer = writer, .options = .{ .whitespace = .minified } };
|
|
||||||
try s.write(value);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_config_value_description(T: type, field_type: type, comptime field_name: []const u8, writer: *std.Io.Writer) !void {
|
|
||||||
switch (@typeInfo(field_type)) {
|
switch (@typeInfo(field_type)) {
|
||||||
.int => switch (field_type) {
|
.int => switch (field_type) {
|
||||||
u24 => try writer.print("24 bit hex color value in quotes", .{}),
|
u24 => try writer.print("24 bit hex color value in quotes", .{}),
|
||||||
|
|
@ -778,17 +748,14 @@ fn write_config_value_description(T: type, field_type: type, comptime field_name
|
||||||
},
|
},
|
||||||
.optional => |info| switch (@typeInfo(info.child)) {
|
.optional => |info| switch (@typeInfo(info.child)) {
|
||||||
else => {
|
else => {
|
||||||
try write_config_value_description(T, info.child, field_name, writer);
|
try write_config_value_description(T, info.child, writer);
|
||||||
try writer.print(" or null", .{});
|
try writer.print(" or null", .{});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.pointer => |info| switch (info.size) {
|
.pointer => |info| switch (info.size) {
|
||||||
.slice => if (info.child == u8) {
|
.slice => if (info.child == u8)
|
||||||
if (std.mem.eql(u8, field_name, "include_files"))
|
try writer.print("quoted string", .{})
|
||||||
try writer.print("quoted string of {c} separated paths", .{std.fs.path.delimiter})
|
else if (info.child == u16)
|
||||||
else
|
|
||||||
try writer.print("quoted string", .{});
|
|
||||||
} else if (info.child == u16)
|
|
||||||
try writer.print("quoted string (u16)", .{})
|
try writer.print("quoted string (u16)", .{})
|
||||||
else if (info.child == []const u8)
|
else if (info.child == []const u8)
|
||||||
try writer.print("list of quoted strings", .{})
|
try writer.print("list of quoted strings", .{})
|
||||||
|
|
@ -850,6 +817,19 @@ fn config_eql(config_type: type, T: type, a: T, b: T) bool {
|
||||||
unsupported_error(config_type, T);
|
unsupported_error(config_type, T);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_json_file(comptime T: type, data: T, allocator: std.mem.Allocator, file_name: []const u8) !void {
|
||||||
|
var file = try std.fs.createFileAbsolute(file_name, .{ .truncate = true });
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
var cb = std.ArrayList(u8).init(allocator);
|
||||||
|
defer cb.deinit();
|
||||||
|
try cbor.writeValue(cb.writer(), data);
|
||||||
|
|
||||||
|
var s = std.json.writeStream(file.writer(), .{ .whitespace = .indent_4 });
|
||||||
|
var iter: []const u8 = cb.items;
|
||||||
|
try cbor.JsonStream(std.fs.File).jsonWriteValue(&s, &iter);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_keybind_namespace(allocator: std.mem.Allocator, namespace_name: []const u8) ?[]const u8 {
|
pub fn read_keybind_namespace(allocator: std.mem.Allocator, namespace_name: []const u8) ?[]const u8 {
|
||||||
const file_name = get_keybind_namespace_file_name(namespace_name) catch return null;
|
const file_name = get_keybind_namespace_file_name(namespace_name) catch return null;
|
||||||
var file = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch return null;
|
var file = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch return null;
|
||||||
|
|
@ -1064,7 +1044,7 @@ fn get_app_state_dir(appname: []const u8) ![]const u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_app_config_file_name(appname: []const u8, comptime base_name: []const u8) ConfigDirError![]const u8 {
|
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);
|
return get_app_config_dir_file_name(appname, base_name ++ ".json");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_app_config_dir_file_name(appname: []const u8, comptime config_file_name: []const u8) ConfigDirError![]const u8 {
|
fn get_app_config_dir_file_name(appname: []const u8, comptime config_file_name: []const u8) ConfigDirError![]const u8 {
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,7 @@ fn open_style_config(self: *Self, Style: type) command.Result {
|
||||||
tui.reset_drag_context();
|
tui.reset_drag_context();
|
||||||
try self.create_editor();
|
try self.create_editor();
|
||||||
try command.executeName("open_scratch_buffer", command.fmt(.{
|
try command.executeName("open_scratch_buffer", command.fmt(.{
|
||||||
file_name,
|
file_name[0 .. file_name.len - ".json".len],
|
||||||
conf.written(),
|
conf.written(),
|
||||||
"conf",
|
"conf",
|
||||||
}));
|
}));
|
||||||
|
|
@ -640,13 +640,13 @@ const cmds = struct {
|
||||||
|
|
||||||
pub fn open_config(_: *Self, _: Ctx) Result {
|
pub fn open_config(_: *Self, _: Ctx) Result {
|
||||||
const file_name = try root.get_config_file_name(@import("config"));
|
const file_name = try root.get_config_file_name(@import("config"));
|
||||||
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } });
|
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name[0 .. file_name.len - 5] } });
|
||||||
}
|
}
|
||||||
pub const open_config_meta: Meta = .{ .description = "Edit configuration" };
|
pub const open_config_meta: Meta = .{ .description = "Edit configuration" };
|
||||||
|
|
||||||
pub fn open_gui_config(_: *Self, _: Ctx) Result {
|
pub fn open_gui_config(_: *Self, _: Ctx) Result {
|
||||||
const file_name = try root.get_config_file_name(@import("gui_config"));
|
const file_name = try root.get_config_file_name(@import("gui_config"));
|
||||||
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } });
|
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name[0 .. file_name.len - ".json".len] } });
|
||||||
}
|
}
|
||||||
pub const open_gui_config_meta: Meta = .{ .description = "Edit gui configuration" };
|
pub const open_gui_config_meta: Meta = .{ .description = "Edit gui configuration" };
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue