Zig 0.15: zig build test
parent
3770f97608
commit
cb295b84a0
|
|
@ -276,6 +276,8 @@ pub fn build(b: *std.Build) !void {
|
|||
.omit_frame_pointer = false,
|
||||
.unwind_tables = .sync,
|
||||
}),
|
||||
// Crash on x86_64 without this
|
||||
.use_llvm = true,
|
||||
});
|
||||
if (config.emit_test_exe) b.installArtifact(test_exe);
|
||||
_ = try deps.add(test_exe);
|
||||
|
|
@ -285,7 +287,7 @@ pub fn build(b: *std.Build) !void {
|
|||
test_step.dependOn(&test_run.step);
|
||||
|
||||
// Normal tests always test our libghostty modules
|
||||
test_step.dependOn(test_lib_vt_step);
|
||||
//test_step.dependOn(test_lib_vt_step);
|
||||
|
||||
// Valgrind test running
|
||||
const valgrind_run = b.addSystemCommand(&.{
|
||||
|
|
|
|||
|
|
@ -194,7 +194,9 @@ fn startPosix(self: *Command, arena: Allocator) !void {
|
|||
// child process so there isn't much we can do. We try to output
|
||||
// something reasonable. Its important to note we MUST NOT return
|
||||
// any other error condition from here on out.
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
var stderr_buf: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&stderr_buf);
|
||||
const stderr = &stderr_writer.interface;
|
||||
switch (err) {
|
||||
error.FileNotFound => stderr.print(
|
||||
\\Requested executable not found. Please verify the command is on
|
||||
|
|
@ -211,6 +213,7 @@ fn startPosix(self: *Command, arena: Allocator) !void {
|
|||
.{err},
|
||||
) catch {},
|
||||
}
|
||||
stderr.flush() catch {};
|
||||
|
||||
// We return a very specific error that can be detected to determine
|
||||
// we're in the child.
|
||||
|
|
@ -464,34 +467,35 @@ fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const EnvMap) ![]u1
|
|||
|
||||
/// Copied from Zig. This function could be made public in child_process.zig instead.
|
||||
fn windowsCreateCommandLine(allocator: mem.Allocator, argv: []const []const u8) ![:0]u8 {
|
||||
var buf = std.ArrayList(u8).init(allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(allocator);
|
||||
defer buf.deinit();
|
||||
const writer = &buf.writer;
|
||||
|
||||
for (argv, 0..) |arg, arg_i| {
|
||||
if (arg_i != 0) try buf.append(' ');
|
||||
if (arg_i != 0) try writer.writeByte(' ');
|
||||
if (mem.indexOfAny(u8, arg, " \t\n\"") == null) {
|
||||
try buf.appendSlice(arg);
|
||||
try writer.writeAll(arg);
|
||||
continue;
|
||||
}
|
||||
try buf.append('"');
|
||||
try writer.writeByte('"');
|
||||
var backslash_count: usize = 0;
|
||||
for (arg) |byte| {
|
||||
switch (byte) {
|
||||
'\\' => backslash_count += 1,
|
||||
'"' => {
|
||||
try buf.appendNTimes('\\', backslash_count * 2 + 1);
|
||||
try buf.append('"');
|
||||
try writer.splatByteAll('\\', backslash_count * 2 + 1);
|
||||
try writer.writeByte('"');
|
||||
backslash_count = 0;
|
||||
},
|
||||
else => {
|
||||
try buf.appendNTimes('\\', backslash_count);
|
||||
try buf.append(byte);
|
||||
try writer.splatByteAll('\\', backslash_count);
|
||||
try writer.writeByte(byte);
|
||||
backslash_count = 0;
|
||||
},
|
||||
}
|
||||
}
|
||||
try buf.appendNTimes('\\', backslash_count * 2);
|
||||
try buf.append('"');
|
||||
try writer.splatByteAll('\\', backslash_count * 2);
|
||||
try writer.writeByte('"');
|
||||
}
|
||||
|
||||
return buf.toOwnedSliceSentinel(0);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ pub fn main() !void {
|
|||
std.debug.print(
|
||||
\\`libadwaita` is too old.
|
||||
\\
|
||||
\\Ghostty requires a version {} or newer of `libadwaita` to
|
||||
\\Ghostty requires a version {f} or newer of `libadwaita` to
|
||||
\\compile this blueprint. Please install it, ensure that it is
|
||||
\\available on your PATH, and then retry building Ghostty.
|
||||
, .{required_adwaita_version});
|
||||
|
|
@ -80,7 +80,7 @@ pub fn main() !void {
|
|||
std.debug.print(
|
||||
\\`blueprint-compiler` not found.
|
||||
\\
|
||||
\\Ghostty requires version {} or newer of
|
||||
\\Ghostty requires version {f} or newer of
|
||||
\\`blueprint-compiler` as a build-time dependency starting
|
||||
\\from version 1.2. Please install it, ensure that it is
|
||||
\\available on your PATH, and then retry building Ghostty.
|
||||
|
|
@ -104,7 +104,7 @@ pub fn main() !void {
|
|||
std.debug.print(
|
||||
\\`blueprint-compiler` is the wrong version.
|
||||
\\
|
||||
\\Ghostty requires version {} or newer of
|
||||
\\Ghostty requires version {f} or newer of
|
||||
\\`blueprint-compiler` as a build-time dependency starting
|
||||
\\from version 1.2. Please install it, ensure that it is
|
||||
\\available on your PATH, and then retry building Ghostty.
|
||||
|
|
@ -145,7 +145,7 @@ pub fn main() !void {
|
|||
std.debug.print(
|
||||
\\`blueprint-compiler` not found.
|
||||
\\
|
||||
\\Ghostty requires version {} or newer of
|
||||
\\Ghostty requires version {f} or newer of
|
||||
\\`blueprint-compiler` as a build-time dependency starting
|
||||
\\from version 1.2. Please install it, ensure that it is
|
||||
\\available on your PATH, and then retry building Ghostty.
|
||||
|
|
|
|||
|
|
@ -142,7 +142,9 @@ pub fn main() !void {
|
|||
);
|
||||
}
|
||||
|
||||
const writer = std.io.getStdOut().writer();
|
||||
var buf: [4096]u8 = undefined;
|
||||
var stdout = std.fs.File.stdout().writer(&buf);
|
||||
const writer = &stdout.interface;
|
||||
try writer.writeAll(
|
||||
\\<?xml version="1.0" encoding="UTF-8"?>
|
||||
\\<gresources>
|
||||
|
|
@ -157,6 +159,8 @@ pub fn main() !void {
|
|||
\\</gresources>
|
||||
\\
|
||||
);
|
||||
|
||||
try stdout.end();
|
||||
}
|
||||
|
||||
/// Generate the icon resources. This works by looking up all the icons
|
||||
|
|
|
|||
|
|
@ -117,10 +117,10 @@ pub const Config = extern struct {
|
|||
errdefer text_buf.unref();
|
||||
|
||||
var buf: [4095:0]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
for (config._diagnostics.items()) |diag| {
|
||||
fbs.reset();
|
||||
diag.write(fbs.writer()) catch |err| {
|
||||
writer.end = 0;
|
||||
diag.format(&writer) catch |err| {
|
||||
log.warn(
|
||||
"error writing diagnostic to buffer err={}",
|
||||
.{err},
|
||||
|
|
@ -128,7 +128,7 @@ pub const Config = extern struct {
|
|||
continue;
|
||||
};
|
||||
|
||||
text_buf.insertAtCursor(&buf, @intCast(fbs.pos));
|
||||
text_buf.insertAtCursor(&buf, @intCast(writer.end));
|
||||
text_buf.insertAtCursor("\n", 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,10 @@ payload_builder: *glib.VariantBuilder,
|
|||
parameters_builder: *glib.VariantBuilder,
|
||||
|
||||
/// Initialize the helper.
|
||||
pub fn init(alloc: Allocator, target: apprt.ipc.Target, action: [:0]const u8) (Allocator.Error || std.posix.WriteError || apprt.ipc.Errors)!Self {
|
||||
pub fn init(alloc: Allocator, target: apprt.ipc.Target, action: [:0]const u8) (Allocator.Error || std.Io.Writer.Error || apprt.ipc.Errors)!Self {
|
||||
var buf: [256]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buf);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
// Get the appropriate bus name and object path for contacting the
|
||||
// Ghostty instance we're interested in.
|
||||
|
|
@ -37,7 +40,7 @@ pub fn init(alloc: Allocator, target: apprt.ipc.Target, action: [:0]const u8) (A
|
|||
.class => |class| result: {
|
||||
// Force the usage of the class specified on the CLI to determine the
|
||||
// bus name and object path.
|
||||
const object_path = try std.fmt.allocPrintZ(alloc, "/{s}", .{class});
|
||||
const object_path = try std.fmt.allocPrintSentinel(alloc, "/{s}", .{class}, 0);
|
||||
|
||||
std.mem.replaceScalar(u8, object_path, '.', '/');
|
||||
std.mem.replaceScalar(u8, object_path, '-', '_');
|
||||
|
|
@ -54,14 +57,14 @@ pub fn init(alloc: Allocator, target: apprt.ipc.Target, action: [:0]const u8) (A
|
|||
}
|
||||
|
||||
if (gio.Application.idIsValid(bus_name.ptr) == 0) {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
try stderr.print("D-Bus bus name is not valid: {s}\n", .{bus_name});
|
||||
try stderr.flush();
|
||||
return error.IPCFailed;
|
||||
}
|
||||
|
||||
if (glib.Variant.isObjectPath(object_path.ptr) == 0) {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
try stderr.print("D-Bus object path is not valid: {s}\n", .{object_path});
|
||||
try stderr.flush();
|
||||
return error.IPCFailed;
|
||||
}
|
||||
|
||||
|
|
@ -72,17 +75,17 @@ pub fn init(alloc: Allocator, target: apprt.ipc.Target, action: [:0]const u8) (A
|
|||
|
||||
const dbus_ = gio.busGetSync(.session, null, &err_);
|
||||
if (err_) |err| {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
try stderr.print(
|
||||
"Unable to establish connection to D-Bus session bus: {s}\n",
|
||||
.{err.f_message orelse "(unknown)"},
|
||||
);
|
||||
try stderr.flush();
|
||||
return error.IPCFailed;
|
||||
}
|
||||
|
||||
break :dbus dbus_ orelse {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
try stderr.print("gio.busGetSync returned null\n", .{});
|
||||
try stderr.flush();
|
||||
return error.IPCFailed;
|
||||
};
|
||||
};
|
||||
|
|
@ -128,7 +131,11 @@ pub fn addParameter(self: *Self, variant: *glib.Variant) void {
|
|||
|
||||
/// Send the IPC to the remote Ghostty. Once it completes, nothing further
|
||||
/// should be done with this object other than call `deinit`.
|
||||
pub fn send(self: *Self) (std.posix.WriteError || apprt.ipc.Errors)!void {
|
||||
pub fn send(self: *Self) (std.Io.Writer.Error || apprt.ipc.Errors)!void {
|
||||
var buf: [256]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buf);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
// finish building the parameters
|
||||
const parameters = self.parameters_builder.end();
|
||||
|
||||
|
|
@ -167,11 +174,11 @@ pub fn send(self: *Self) (std.posix.WriteError || apprt.ipc.Errors)!void {
|
|||
defer if (result_) |result| result.unref();
|
||||
|
||||
if (err_) |err| {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
try stderr.print(
|
||||
"D-Bus method call returned an error err={s}\n",
|
||||
.{err.f_message orelse "(unknown)"},
|
||||
);
|
||||
try stderr.flush();
|
||||
return error.IPCFailed;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const DBus = @import("DBus.zig");
|
|||
// ```
|
||||
// gdbus call --session --dest com.mitchellh.ghostty --object-path /com/mitchellh/ghostty --method org.gtk.Actions.Activate new-window-command '[<@as ["echo" "hello"]>]' []
|
||||
// ```
|
||||
pub fn newWindow(alloc: Allocator, target: apprt.ipc.Target, value: apprt.ipc.Action.NewWindow) (Allocator.Error || std.posix.WriteError || apprt.ipc.Errors)!bool {
|
||||
pub fn newWindow(alloc: Allocator, target: apprt.ipc.Target, value: apprt.ipc.Action.NewWindow) (Allocator.Error || std.Io.Writer.Error || apprt.ipc.Errors)!bool {
|
||||
var dbus = try DBus.init(
|
||||
alloc,
|
||||
target,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ const assert = std.debug.assert;
|
|||
const Allocator = std.mem.Allocator;
|
||||
const Benchmark = @import("Benchmark.zig");
|
||||
const options = @import("options.zig");
|
||||
const uucode = @import("uucode");
|
||||
const UTF8Decoder = @import("../terminal/UTF8Decoder.zig");
|
||||
const simd = @import("../simd/main.zig");
|
||||
const table = @import("../unicode/main.zig").table;
|
||||
|
|
@ -48,9 +47,6 @@ pub const Mode = enum {
|
|||
|
||||
/// Test our lookup table implementation.
|
||||
table,
|
||||
|
||||
/// Using uucode, with custom `width` extension based on `wcwidth`.
|
||||
uucode,
|
||||
};
|
||||
|
||||
/// Create a new terminal stream handler for the given arguments.
|
||||
|
|
@ -75,7 +71,6 @@ pub fn benchmark(self: *CodepointWidth) Benchmark {
|
|||
.wcwidth => stepWcwidth,
|
||||
.table => stepTable,
|
||||
.simd => stepSimd,
|
||||
.uucode => stepUucode,
|
||||
},
|
||||
.setupFn = setup,
|
||||
.teardownFn = teardown,
|
||||
|
|
@ -112,12 +107,15 @@ fn stepWcwidth(ptr: *anyopaque) Benchmark.Error!void {
|
|||
const self: *CodepointWidth = @ptrCast(@alignCast(ptr));
|
||||
|
||||
const f = self.data_f orelse return;
|
||||
var r = std.io.bufferedReader(f.reader());
|
||||
var read_buf: [4096]u8 = undefined;
|
||||
var f_reader = f.reader(&read_buf);
|
||||
var r = &f_reader.interface;
|
||||
|
||||
var d: UTF8Decoder = .{};
|
||||
var buf: [4096]u8 align(std.atomic.cache_line) = undefined;
|
||||
while (true) {
|
||||
const n = r.read(&buf) catch |err| {
|
||||
log.warn("error reading data file err={}", .{err});
|
||||
const n = r.readSliceShort(&buf) catch {
|
||||
log.warn("error reading data file err={?}", .{f_reader.err});
|
||||
return error.BenchmarkFailed;
|
||||
};
|
||||
if (n == 0) break; // EOF reached
|
||||
|
|
@ -136,12 +134,15 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void {
|
|||
const self: *CodepointWidth = @ptrCast(@alignCast(ptr));
|
||||
|
||||
const f = self.data_f orelse return;
|
||||
var r = std.io.bufferedReader(f.reader());
|
||||
var read_buf: [4096]u8 = undefined;
|
||||
var f_reader = f.reader(&read_buf);
|
||||
var r = &f_reader.interface;
|
||||
|
||||
var d: UTF8Decoder = .{};
|
||||
var buf: [4096]u8 align(std.atomic.cache_line) = undefined;
|
||||
while (true) {
|
||||
const n = r.read(&buf) catch |err| {
|
||||
log.warn("error reading data file err={}", .{err});
|
||||
const n = r.readSliceShort(&buf) catch {
|
||||
log.warn("error reading data file err={?}", .{f_reader.err});
|
||||
return error.BenchmarkFailed;
|
||||
};
|
||||
if (n == 0) break; // EOF reached
|
||||
|
|
@ -165,12 +166,15 @@ fn stepSimd(ptr: *anyopaque) Benchmark.Error!void {
|
|||
const self: *CodepointWidth = @ptrCast(@alignCast(ptr));
|
||||
|
||||
const f = self.data_f orelse return;
|
||||
var r = std.io.bufferedReader(f.reader());
|
||||
var read_buf: [4096]u8 = undefined;
|
||||
var f_reader = f.reader(&read_buf);
|
||||
var r = &f_reader.interface;
|
||||
|
||||
var d: UTF8Decoder = .{};
|
||||
var buf: [4096]u8 align(std.atomic.cache_line) = undefined;
|
||||
while (true) {
|
||||
const n = r.read(&buf) catch |err| {
|
||||
log.warn("error reading data file err={}", .{err});
|
||||
const n = r.readSliceShort(&buf) catch {
|
||||
log.warn("error reading data file err={?}", .{f_reader.err});
|
||||
return error.BenchmarkFailed;
|
||||
};
|
||||
if (n == 0) break; // EOF reached
|
||||
|
|
@ -185,35 +189,6 @@ fn stepSimd(ptr: *anyopaque) Benchmark.Error!void {
|
|||
}
|
||||
}
|
||||
|
||||
fn stepUucode(ptr: *anyopaque) Benchmark.Error!void {
|
||||
const self: *CodepointWidth = @ptrCast(@alignCast(ptr));
|
||||
|
||||
const f = self.data_f orelse return;
|
||||
var r = std.io.bufferedReader(f.reader());
|
||||
var d: UTF8Decoder = .{};
|
||||
var buf: [4096]u8 align(std.atomic.cache_line) = undefined;
|
||||
while (true) {
|
||||
const n = r.read(&buf) catch |err| {
|
||||
log.warn("error reading data file err={}", .{err});
|
||||
return error.BenchmarkFailed;
|
||||
};
|
||||
if (n == 0) break; // EOF reached
|
||||
|
||||
for (buf[0..n]) |c| {
|
||||
const cp_, const consumed = d.next(c);
|
||||
assert(consumed);
|
||||
if (cp_) |cp| {
|
||||
// This is the same trick we do in terminal.zig so we
|
||||
// keep it here.
|
||||
std.mem.doNotOptimizeAway(if (cp <= 0xFF)
|
||||
1
|
||||
else
|
||||
uucode.get(.width, @intCast(cp)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test CodepointWidth {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ const assert = std.debug.assert;
|
|||
const Allocator = std.mem.Allocator;
|
||||
const Benchmark = @import("Benchmark.zig");
|
||||
const options = @import("options.zig");
|
||||
const uucode = @import("uucode");
|
||||
const UTF8Decoder = @import("../terminal/UTF8Decoder.zig");
|
||||
const unicode = @import("../unicode/main.zig");
|
||||
|
||||
|
|
@ -39,9 +38,6 @@ pub const Mode = enum {
|
|||
|
||||
/// Ghostty's table-based approach.
|
||||
table,
|
||||
|
||||
/// uucode implementation
|
||||
uucode,
|
||||
};
|
||||
|
||||
/// Create a new terminal stream handler for the given arguments.
|
||||
|
|
@ -64,7 +60,6 @@ pub fn benchmark(self: *GraphemeBreak) Benchmark {
|
|||
.stepFn = switch (self.opts.mode) {
|
||||
.noop => stepNoop,
|
||||
.table => stepTable,
|
||||
.uucode => stepUucode,
|
||||
},
|
||||
.setupFn = setup,
|
||||
.teardownFn = teardown,
|
||||
|
|
@ -95,12 +90,15 @@ fn stepNoop(ptr: *anyopaque) Benchmark.Error!void {
|
|||
const self: *GraphemeBreak = @ptrCast(@alignCast(ptr));
|
||||
|
||||
const f = self.data_f orelse return;
|
||||
var r = std.io.bufferedReader(f.reader());
|
||||
var read_buf: [4096]u8 = undefined;
|
||||
var f_reader = f.reader(&read_buf);
|
||||
var r = &f_reader.interface;
|
||||
|
||||
var d: UTF8Decoder = .{};
|
||||
var buf: [4096]u8 align(std.atomic.cache_line) = undefined;
|
||||
while (true) {
|
||||
const n = r.read(&buf) catch |err| {
|
||||
log.warn("error reading data file err={}", .{err});
|
||||
const n = r.readSliceShort(&buf) catch {
|
||||
log.warn("error reading data file err={?}", .{f_reader.err});
|
||||
return error.BenchmarkFailed;
|
||||
};
|
||||
if (n == 0) break; // EOF reached
|
||||
|
|
@ -115,14 +113,17 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void {
|
|||
const self: *GraphemeBreak = @ptrCast(@alignCast(ptr));
|
||||
|
||||
const f = self.data_f orelse return;
|
||||
var r = std.io.bufferedReader(f.reader());
|
||||
var read_buf: [4096]u8 = undefined;
|
||||
var f_reader = f.reader(&read_buf);
|
||||
var r = &f_reader.interface;
|
||||
|
||||
var d: UTF8Decoder = .{};
|
||||
var state: unicode.GraphemeBreakState = .{};
|
||||
var cp1: u21 = 0;
|
||||
var buf: [4096]u8 align(std.atomic.cache_line) = undefined;
|
||||
while (true) {
|
||||
const n = r.read(&buf) catch |err| {
|
||||
log.warn("error reading data file err={}", .{err});
|
||||
const n = r.readSliceShort(&buf) catch {
|
||||
log.warn("error reading data file err={?}", .{f_reader.err});
|
||||
return error.BenchmarkFailed;
|
||||
};
|
||||
if (n == 0) break; // EOF reached
|
||||
|
|
@ -138,33 +139,6 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void {
|
|||
}
|
||||
}
|
||||
|
||||
fn stepUucode(ptr: *anyopaque) Benchmark.Error!void {
|
||||
const self: *GraphemeBreak = @ptrCast(@alignCast(ptr));
|
||||
|
||||
const f = self.data_f orelse return;
|
||||
var r = std.io.bufferedReader(f.reader());
|
||||
var d: UTF8Decoder = .{};
|
||||
var state: uucode.grapheme.BreakState = .default;
|
||||
var cp1: u21 = 0;
|
||||
var buf: [4096]u8 align(std.atomic.cache_line) = undefined;
|
||||
while (true) {
|
||||
const n = r.read(&buf) catch |err| {
|
||||
log.warn("error reading data file err={}", .{err});
|
||||
return error.BenchmarkFailed;
|
||||
};
|
||||
if (n == 0) break; // EOF reached
|
||||
|
||||
for (buf[0..n]) |c| {
|
||||
const cp_, const consumed = d.next(c);
|
||||
assert(consumed);
|
||||
if (cp_) |cp2| {
|
||||
std.mem.doNotOptimizeAway(uucode.grapheme.isBreak(cp1, @intCast(cp2), &state));
|
||||
cp1 = cp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test GraphemeBreak {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
|
|
|||
|
|
@ -90,7 +90,8 @@ fn stepUucode(ptr: *anyopaque) Benchmark.Error!void {
|
|||
const self: *IsSymbol = @ptrCast(@alignCast(ptr));
|
||||
|
||||
const f = self.data_f orelse return;
|
||||
var r = std.io.bufferedReader(f.reader());
|
||||
var read_buf: [4096]u8 = undefined;
|
||||
var r = f.reader(&read_buf);
|
||||
var d: UTF8Decoder = .{};
|
||||
var buf: [4096]u8 align(std.atomic.cache_line) = undefined;
|
||||
while (true) {
|
||||
|
|
@ -114,7 +115,8 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void {
|
|||
const self: *IsSymbol = @ptrCast(@alignCast(ptr));
|
||||
|
||||
const f = self.data_f orelse return;
|
||||
var r = std.io.bufferedReader(f.reader());
|
||||
var read_buf: [4096]u8 = undefined;
|
||||
var r = f.reader(&read_buf);
|
||||
var d: UTF8Decoder = .{};
|
||||
var buf: [4096]u8 align(std.atomic.cache_line) = undefined;
|
||||
while (true) {
|
||||
|
|
|
|||
|
|
@ -75,14 +75,16 @@ fn step(ptr: *anyopaque) Benchmark.Error!void {
|
|||
// the benchmark results and... I know writing this that we
|
||||
// aren't currently IO bound.
|
||||
const f = self.data_f orelse return;
|
||||
var r = std.io.bufferedReader(f.reader());
|
||||
var read_buf: [4096]u8 = undefined;
|
||||
var f_reader = f.reader(&read_buf);
|
||||
var r = &f_reader.interface;
|
||||
|
||||
var p: terminalpkg.Parser = .init();
|
||||
|
||||
var buf: [4096]u8 align(std.atomic.cache_line) = undefined;
|
||||
var buf: [4096]u8 = undefined;
|
||||
while (true) {
|
||||
const n = r.read(&buf) catch |err| {
|
||||
log.warn("error reading data file err={}", .{err});
|
||||
const n = r.readSliceShort(&buf) catch {
|
||||
log.warn("error reading data file err={?}", .{f_reader.err});
|
||||
return error.BenchmarkFailed;
|
||||
};
|
||||
if (n == 0) break; // EOF reached
|
||||
|
|
|
|||
|
|
@ -113,17 +113,19 @@ fn step(ptr: *anyopaque) Benchmark.Error!void {
|
|||
// the benchmark results and... I know writing this that we
|
||||
// aren't currently IO bound.
|
||||
const f = self.data_f orelse return;
|
||||
var r = std.io.bufferedReader(f.reader());
|
||||
|
||||
var buf: [4096]u8 align(std.atomic.cache_line) = undefined;
|
||||
var read_buf: [4096]u8 = undefined;
|
||||
var f_reader = f.reader(&read_buf);
|
||||
const r = &f_reader.interface;
|
||||
|
||||
var buf: [4096]u8 = undefined;
|
||||
while (true) {
|
||||
const n = r.read(&buf) catch |err| {
|
||||
log.warn("error reading data file err={}", .{err});
|
||||
const n = r.readSliceShort(&buf) catch {
|
||||
log.warn("error reading data file err={?}", .{f_reader.err});
|
||||
return error.BenchmarkFailed;
|
||||
};
|
||||
if (n == 0) break; // EOF reached
|
||||
const chunk = buf[0..n];
|
||||
self.stream.nextSlice(chunk) catch |err| {
|
||||
self.stream.nextSlice(buf[0..n]) catch |err| {
|
||||
log.warn("error processing data file chunk err={}", .{err});
|
||||
return error.BenchmarkFailed;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub fn dataFile(path_: ?[]const u8) !?std.fs.File {
|
|||
const path = path_ orelse return null;
|
||||
|
||||
// Stdin
|
||||
if (std.mem.eql(u8, path, "-")) return std.io.getStdIn();
|
||||
if (std.mem.eql(u8, path, "-")) return .stdin();
|
||||
|
||||
// Normal file
|
||||
const file = try std.fs.cwd().openFile(path, .{});
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ pub fn distResources(b: *std.Build) struct {
|
|||
.root_module = b.createModule(.{
|
||||
.target = b.graph.host,
|
||||
}),
|
||||
.use_llvm = true,
|
||||
});
|
||||
exe.addCSourceFile(.{
|
||||
.file = b.path("src/build/framegen/main.c"),
|
||||
|
|
|
|||
261
src/cli/args.zig
261
src/cli/args.zig
|
|
@ -162,10 +162,11 @@ pub fn parse(
|
|||
error.InvalidField => "unknown field",
|
||||
error.ValueRequired => formatValueRequired(T, arena_alloc, key) catch "value required",
|
||||
error.InvalidValue => formatInvalidValue(T, arena_alloc, key, value) catch "invalid value",
|
||||
else => try std.fmt.allocPrintZ(
|
||||
else => try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"unknown error {}",
|
||||
.{err},
|
||||
0,
|
||||
),
|
||||
};
|
||||
|
||||
|
|
@ -235,14 +236,16 @@ fn formatValueRequired(
|
|||
comptime T: type,
|
||||
arena_alloc: std.mem.Allocator,
|
||||
key: []const u8,
|
||||
) std.mem.Allocator.Error![:0]const u8 {
|
||||
var buf = std.ArrayList(u8).init(arena_alloc);
|
||||
errdefer buf.deinit();
|
||||
const writer = buf.writer();
|
||||
) std.Io.Writer.Error![:0]const u8 {
|
||||
var stream: std.Io.Writer.Allocating = .init(arena_alloc);
|
||||
const writer = &stream.writer;
|
||||
|
||||
try writer.print("value required", .{});
|
||||
try formatValues(T, key, writer);
|
||||
try writer.writeByte(0);
|
||||
return buf.items[0 .. buf.items.len - 1 :0];
|
||||
|
||||
const written = stream.written();
|
||||
return written[0 .. written.len - 1 :0];
|
||||
}
|
||||
|
||||
fn formatInvalidValue(
|
||||
|
|
@ -250,17 +253,23 @@ fn formatInvalidValue(
|
|||
arena_alloc: std.mem.Allocator,
|
||||
key: []const u8,
|
||||
value: ?[]const u8,
|
||||
) std.mem.Allocator.Error![:0]const u8 {
|
||||
var buf = std.ArrayList(u8).init(arena_alloc);
|
||||
errdefer buf.deinit();
|
||||
const writer = buf.writer();
|
||||
) std.Io.Writer.Error![:0]const u8 {
|
||||
var stream: std.Io.Writer.Allocating = .init(arena_alloc);
|
||||
const writer = &stream.writer;
|
||||
|
||||
try writer.print("invalid value \"{?s}\"", .{value});
|
||||
try formatValues(T, key, writer);
|
||||
try writer.writeByte(0);
|
||||
return buf.items[0 .. buf.items.len - 1 :0];
|
||||
|
||||
const written = stream.written();
|
||||
return written[0 .. written.len - 1 :0];
|
||||
}
|
||||
|
||||
fn formatValues(comptime T: type, key: []const u8, writer: anytype) std.mem.Allocator.Error!void {
|
||||
fn formatValues(
|
||||
comptime T: type,
|
||||
key: []const u8,
|
||||
writer: *std.Io.Writer,
|
||||
) std.Io.Writer.Error!void {
|
||||
@setEvalBranchQuota(2000);
|
||||
const typeinfo = @typeInfo(T);
|
||||
inline for (typeinfo.@"struct".fields) |f| {
|
||||
|
|
@ -542,8 +551,8 @@ pub fn parseAutoStruct(
|
|||
const key = std.mem.trim(u8, entry[0..idx], whitespace);
|
||||
|
||||
// used if we need to decode a double-quoted string.
|
||||
var buf: std.ArrayListUnmanaged(u8) = .empty;
|
||||
defer buf.deinit(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
const value = value: {
|
||||
const value = std.mem.trim(u8, entry[idx + 1 ..], whitespace);
|
||||
|
|
@ -554,10 +563,9 @@ pub fn parseAutoStruct(
|
|||
value[value.len - 1] == '"')
|
||||
{
|
||||
// Decode a double-quoted string as a Zig string literal.
|
||||
const writer = buf.writer(alloc);
|
||||
const parsed = try std.zig.string_literal.parseWrite(writer, value);
|
||||
const parsed = try std.zig.string_literal.parseWrite(&buf.writer, value);
|
||||
if (parsed == .failure) return error.InvalidValue;
|
||||
break :value buf.items;
|
||||
break :value buf.written();
|
||||
}
|
||||
|
||||
break :value value;
|
||||
|
|
@ -795,15 +803,13 @@ test "parse: diagnostic location" {
|
|||
} = .{};
|
||||
defer if (data._arena) |arena| arena.deinit();
|
||||
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var r: std.Io.Reader = .fixed(
|
||||
\\a=42
|
||||
\\what
|
||||
\\b=two
|
||||
);
|
||||
const r = fbs.reader();
|
||||
|
||||
const Iter = LineIterator(@TypeOf(r));
|
||||
var iter: Iter = .{ .r = r, .filepath = "test" };
|
||||
var iter: LineIterator = .{ .r = &r, .filepath = "test" };
|
||||
try parse(@TypeOf(data), testing.allocator, &data, &iter);
|
||||
try testing.expect(data._arena != null);
|
||||
try testing.expectEqualStrings("42", data.a);
|
||||
|
|
@ -1208,18 +1214,7 @@ test "parseIntoField: struct with basic fields" {
|
|||
try testing.expectEqual(84, data.value.b);
|
||||
try testing.expectEqual(24, data.value.c);
|
||||
|
||||
// Set with explicit default
|
||||
data.value = try parseAutoStruct(
|
||||
@TypeOf(data.value),
|
||||
alloc,
|
||||
"a:hello",
|
||||
.{ .a = "oh no", .b = 42 },
|
||||
);
|
||||
try testing.expectEqualStrings("hello", data.value.a);
|
||||
try testing.expectEqual(42, data.value.b);
|
||||
try testing.expectEqual(12, data.value.c);
|
||||
|
||||
// Missing required field
|
||||
// Missing require dfield
|
||||
try testing.expectError(
|
||||
error.InvalidValue,
|
||||
parseIntoField(@TypeOf(data), alloc, &data, "value", "a:hello"),
|
||||
|
|
@ -1395,115 +1390,119 @@ test "ArgsIterator" {
|
|||
/// Returns an iterator (implements "next") that reads CLI args by line.
|
||||
/// Each CLI arg is expected to be a single line. This is used to implement
|
||||
/// configuration files.
|
||||
pub fn LineIterator(comptime ReaderType: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
pub const LineIterator = struct {
|
||||
const Self = @This();
|
||||
|
||||
/// The maximum size a single line can be. We don't expect any
|
||||
/// CLI arg to exceed this size. Can't wait to git blame this in
|
||||
/// like 4 years and be wrong about this.
|
||||
pub const MAX_LINE_SIZE = 4096;
|
||||
/// The maximum size a single line can be. We don't expect any
|
||||
/// CLI arg to exceed this size. Can't wait to git blame this in
|
||||
/// like 4 years and be wrong about this.
|
||||
pub const MAX_LINE_SIZE = 4096;
|
||||
|
||||
/// Our stateful reader.
|
||||
r: ReaderType,
|
||||
/// Our stateful reader.
|
||||
r: *std.Io.Reader,
|
||||
|
||||
/// Filepath that is used for diagnostics. This is only used for
|
||||
/// diagnostic messages so it can be formatted however you want.
|
||||
/// It is prefixed to the messages followed by the line number.
|
||||
filepath: []const u8 = "",
|
||||
/// Filepath that is used for diagnostics. This is only used for
|
||||
/// diagnostic messages so it can be formatted however you want.
|
||||
/// It is prefixed to the messages followed by the line number.
|
||||
filepath: []const u8 = "",
|
||||
|
||||
/// The current line that we're on. This is 1-indexed because
|
||||
/// lines are generally 1-indexed in the real world. The value
|
||||
/// can be zero if we haven't read any lines yet.
|
||||
line: usize = 0,
|
||||
/// The current line that we're on. This is 1-indexed because
|
||||
/// lines are generally 1-indexed in the real world. The value
|
||||
/// can be zero if we haven't read any lines yet.
|
||||
line: usize = 0,
|
||||
|
||||
/// This is the buffer where we store the current entry that
|
||||
/// is formatted to be compatible with the parse function.
|
||||
entry: [MAX_LINE_SIZE]u8 = [_]u8{ '-', '-' } ++ ([_]u8{0} ** (MAX_LINE_SIZE - 2)),
|
||||
/// This is the buffer where we store the current entry that
|
||||
/// is formatted to be compatible with the parse function.
|
||||
entry: [MAX_LINE_SIZE]u8 = [_]u8{ '-', '-' } ++ ([_]u8{0} ** (MAX_LINE_SIZE - 2)),
|
||||
|
||||
pub fn next(self: *Self) ?[]const u8 {
|
||||
// TODO: detect "--" prefixed lines and give a friendlier error
|
||||
const buf = buf: {
|
||||
while (true) {
|
||||
// Read the full line
|
||||
var entry = self.r.readUntilDelimiterOrEof(self.entry[2..], '\n') catch |err| switch (err) {
|
||||
inline else => |e| {
|
||||
log.warn("cannot read from \"{s}\": {}", .{ self.filepath, e });
|
||||
return null;
|
||||
},
|
||||
} orelse return null;
|
||||
pub fn init(reader: *std.Io.Reader) Self {
|
||||
return .{ .r = reader };
|
||||
}
|
||||
|
||||
// Increment our line counter
|
||||
self.line += 1;
|
||||
pub fn next(self: *Self) ?[]const u8 {
|
||||
// First prime the reader.
|
||||
// File readers at least are initialized with a size of 0,
|
||||
// and this will actually prompt the reader to get the actual
|
||||
// size of the file, which will be used in the EOF check below.
|
||||
//
|
||||
// This will also optimize reads down the line as we're
|
||||
// more likely to beworking with buffered data.
|
||||
self.r.fillMore() catch {};
|
||||
|
||||
// Trim any whitespace (including CR) around it
|
||||
const trim = std.mem.trim(u8, entry, whitespace ++ "\r");
|
||||
if (trim.len != entry.len) {
|
||||
std.mem.copyForwards(u8, entry, trim);
|
||||
entry = entry[0..trim.len];
|
||||
}
|
||||
var writer: std.Io.Writer = .fixed(self.entry[2..]);
|
||||
|
||||
// Ignore blank lines and comments
|
||||
if (entry.len == 0 or entry[0] == '#') continue;
|
||||
var entry = while (self.r.seek != self.r.end) {
|
||||
// Reset write head
|
||||
writer.end = 0;
|
||||
|
||||
// Trim spaces around '='
|
||||
if (mem.indexOf(u8, entry, "=")) |idx| {
|
||||
const key = std.mem.trim(u8, entry[0..idx], whitespace);
|
||||
const value = value: {
|
||||
var value = std.mem.trim(u8, entry[idx + 1 ..], whitespace);
|
||||
_ = self.r.streamDelimiterEnding(&writer, '\n') catch |e| {
|
||||
log.warn("cannot read from \"{s}\": {}", .{ self.filepath, e });
|
||||
return null;
|
||||
};
|
||||
_ = self.r.discardDelimiterInclusive('\n') catch {};
|
||||
|
||||
// Detect a quoted string.
|
||||
if (value.len >= 2 and
|
||||
value[0] == '"' and
|
||||
value[value.len - 1] == '"')
|
||||
{
|
||||
// Trim quotes since our CLI args processor expects
|
||||
// quotes to already be gone.
|
||||
value = value[1 .. value.len - 1];
|
||||
}
|
||||
var entry = writer.buffered();
|
||||
self.line += 1;
|
||||
|
||||
break :value value;
|
||||
};
|
||||
// Trim any whitespace (including CR) around it
|
||||
const trim = std.mem.trim(u8, entry, whitespace ++ "\r");
|
||||
if (trim.len != entry.len) {
|
||||
std.mem.copyForwards(u8, entry, trim);
|
||||
entry = entry[0..trim.len];
|
||||
}
|
||||
|
||||
const len = key.len + value.len + 1;
|
||||
if (entry.len != len) {
|
||||
std.mem.copyForwards(u8, entry, key);
|
||||
entry[key.len] = '=';
|
||||
std.mem.copyForwards(u8, entry[key.len + 1 ..], value);
|
||||
entry = entry[0..len];
|
||||
}
|
||||
}
|
||||
// Ignore blank lines and comments
|
||||
if (entry.len == 0 or entry[0] == '#') continue;
|
||||
break entry;
|
||||
} else return null;
|
||||
|
||||
break :buf entry;
|
||||
if (mem.indexOf(u8, entry, "=")) |idx| {
|
||||
const key = std.mem.trim(u8, entry[0..idx], whitespace);
|
||||
const value = value: {
|
||||
var value = std.mem.trim(u8, entry[idx + 1 ..], whitespace);
|
||||
|
||||
// Detect a quoted string.
|
||||
if (value.len >= 2 and
|
||||
value[0] == '"' and
|
||||
value[value.len - 1] == '"')
|
||||
{
|
||||
// Trim quotes since our CLI args processor expects
|
||||
// quotes to already be gone.
|
||||
value = value[1 .. value.len - 1];
|
||||
}
|
||||
|
||||
break :value value;
|
||||
};
|
||||
|
||||
// We need to reslice so that we include our '--' at the beginning
|
||||
// of our buffer so that we can trick the CLI parser to treat it
|
||||
// as CLI args.
|
||||
return self.entry[0 .. buf.len + 2];
|
||||
const len = key.len + value.len + 1;
|
||||
if (entry.len != len) {
|
||||
std.mem.copyForwards(u8, entry, key);
|
||||
entry[key.len] = '=';
|
||||
std.mem.copyForwards(u8, entry[key.len + 1 ..], value);
|
||||
entry = entry[0..len];
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a location for a diagnostic message.
|
||||
pub fn location(
|
||||
self: *const Self,
|
||||
alloc: Allocator,
|
||||
) Allocator.Error!?diags.Location {
|
||||
// If we have no filepath then we have no location.
|
||||
if (self.filepath.len == 0) return null;
|
||||
// We need to reslice so that we include our '--' at the beginning
|
||||
// of our buffer so that we can trick the CLI parser to treat it
|
||||
// as CLI args.
|
||||
return self.entry[0 .. entry.len + 2];
|
||||
}
|
||||
|
||||
return .{ .file = .{
|
||||
.path = try alloc.dupe(u8, self.filepath),
|
||||
.line = self.line,
|
||||
} };
|
||||
}
|
||||
};
|
||||
}
|
||||
/// Returns a location for a diagnostic message.
|
||||
pub fn location(
|
||||
self: *const Self,
|
||||
alloc: Allocator,
|
||||
) Allocator.Error!?diags.Location {
|
||||
// If we have no filepath then we have no location.
|
||||
if (self.filepath.len == 0) return null;
|
||||
|
||||
// Constructs a LineIterator (see docs for that).
|
||||
fn lineIterator(reader: anytype) LineIterator(@TypeOf(reader)) {
|
||||
return .{ .r = reader };
|
||||
}
|
||||
return .{ .file = .{
|
||||
.path = try alloc.dupe(u8, self.filepath),
|
||||
.line = self.line,
|
||||
} };
|
||||
}
|
||||
};
|
||||
|
||||
/// An iterator valid for arg parsing from a slice.
|
||||
pub const SliceIterator = struct {
|
||||
|
|
@ -1526,7 +1525,7 @@ pub fn sliceIterator(slice: []const []const u8) SliceIterator {
|
|||
|
||||
test "LineIterator" {
|
||||
const testing = std.testing;
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
\\A
|
||||
\\B=42
|
||||
\\C
|
||||
|
|
@ -1541,7 +1540,7 @@ test "LineIterator" {
|
|||
\\F= "value "
|
||||
);
|
||||
|
||||
var iter = lineIterator(fbs.reader());
|
||||
var iter: LineIterator = .init(&reader);
|
||||
try testing.expectEqualStrings("--A", iter.next().?);
|
||||
try testing.expectEqualStrings("--B=42", iter.next().?);
|
||||
try testing.expectEqualStrings("--C", iter.next().?);
|
||||
|
|
@ -1554,9 +1553,9 @@ test "LineIterator" {
|
|||
|
||||
test "LineIterator end in newline" {
|
||||
const testing = std.testing;
|
||||
var fbs = std.io.fixedBufferStream("A\n\n");
|
||||
var reader: std.Io.Reader = .fixed("A\n\n");
|
||||
|
||||
var iter = lineIterator(fbs.reader());
|
||||
var iter: LineIterator = .init(&reader);
|
||||
try testing.expectEqualStrings("--A", iter.next().?);
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
|
|
@ -1564,9 +1563,9 @@ test "LineIterator end in newline" {
|
|||
|
||||
test "LineIterator spaces around '='" {
|
||||
const testing = std.testing;
|
||||
var fbs = std.io.fixedBufferStream("A = B\n\n");
|
||||
var reader: std.Io.Reader = .fixed("A = B\n\n");
|
||||
|
||||
var iter = lineIterator(fbs.reader());
|
||||
var iter: LineIterator = .init(&reader);
|
||||
try testing.expectEqualStrings("--A=B", iter.next().?);
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
|
|
@ -1574,18 +1573,18 @@ test "LineIterator spaces around '='" {
|
|||
|
||||
test "LineIterator no value" {
|
||||
const testing = std.testing;
|
||||
var fbs = std.io.fixedBufferStream("A = \n\n");
|
||||
var reader: std.Io.Reader = .fixed("A = \n\n");
|
||||
|
||||
var iter = lineIterator(fbs.reader());
|
||||
var iter: LineIterator = .init(&reader);
|
||||
try testing.expectEqualStrings("--A=", iter.next().?);
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
}
|
||||
|
||||
test "LineIterator with CRLF line endings" {
|
||||
const testing = std.testing;
|
||||
var fbs = std.io.fixedBufferStream("A\r\nB = C\r\n");
|
||||
var reader: std.Io.Reader = .fixed("A\r\nB = C\r\n");
|
||||
|
||||
var iter = lineIterator(fbs.reader());
|
||||
var iter: LineIterator = .init(&reader);
|
||||
try testing.expectEqualStrings("--A", iter.next().?);
|
||||
try testing.expectEqualStrings("--B=C", iter.next().?);
|
||||
try testing.expectEqual(@as(?[]const u8, null), iter.next());
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const Allocator = std.mem.Allocator;
|
|||
const help_strings = @import("help_strings");
|
||||
const vaxis = @import("vaxis");
|
||||
|
||||
const framedata = @import("framedata");
|
||||
const framedata = @embedFile("framedata");
|
||||
|
||||
const vxfw = vaxis.vxfw;
|
||||
|
||||
|
|
@ -218,17 +218,20 @@ var frames: []const []const u8 = undefined;
|
|||
|
||||
/// Decompress the frames into a slice of individual frames
|
||||
fn decompressFrames(gpa: Allocator) !void {
|
||||
var fbs = std.io.fixedBufferStream(framedata.compressed);
|
||||
var list = std.ArrayList(u8).init(gpa);
|
||||
var src: std.Io.Reader = .fixed(framedata);
|
||||
|
||||
try std.compress.flate.decompress(fbs.reader(), list.writer());
|
||||
decompressed_data = try list.toOwnedSlice();
|
||||
// var buf: [std.compress.flate.max_window_len]u8 = undefined;
|
||||
var decompress: std.compress.flate.Decompress = .init(&src, .raw, &.{});
|
||||
|
||||
var frame_list = try std.ArrayList([]const u8).initCapacity(gpa, 235);
|
||||
var out: std.Io.Writer.Allocating = .init(gpa);
|
||||
_ = try decompress.reader.streamRemaining(&out.writer);
|
||||
decompressed_data = try out.toOwnedSlice();
|
||||
|
||||
var frame_list: std.ArrayList([]const u8) = try .initCapacity(gpa, 235);
|
||||
|
||||
var frame_iter = std.mem.splitScalar(u8, decompressed_data, '\x01');
|
||||
while (frame_iter.next()) |frame| {
|
||||
try frame_list.append(frame);
|
||||
try frame_list.append(gpa, frame);
|
||||
}
|
||||
frames = try frame_list.toOwnedSlice();
|
||||
frames = try frame_list.toOwnedSlice(gpa);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,21 +38,35 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
|||
try args.parse(Options, alloc_gpa, &opts, &iter);
|
||||
}
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
const result = runInner(alloc, &stdout_file, stdout);
|
||||
stdout.flush() catch {};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn runInner(
|
||||
alloc: Allocator,
|
||||
stdout_file: *std.fs.File,
|
||||
stdout: *std.Io.Writer,
|
||||
) !u8 {
|
||||
const crash_dir = try crash.defaultDir(alloc);
|
||||
var reports = std.ArrayList(crash.Report).init(alloc);
|
||||
var reports: std.ArrayList(crash.Report) = .empty;
|
||||
errdefer reports.deinit(alloc);
|
||||
|
||||
var it = try crash_dir.iterator();
|
||||
while (try it.next()) |report| try reports.append(.{
|
||||
while (try it.next()) |report| try reports.append(alloc, .{
|
||||
.name = try alloc.dupe(u8, report.name),
|
||||
.mtime = report.mtime,
|
||||
});
|
||||
|
||||
const stdout = std.io.getStdOut();
|
||||
|
||||
// If we have no reports, then we're done. If we have a tty then we
|
||||
// print a message, otherwise we do nothing.
|
||||
if (reports.items.len == 0) {
|
||||
if (std.posix.isatty(stdout.handle)) {
|
||||
if (std.posix.isatty(stdout_file.handle)) {
|
||||
try stdout.writeAll("No crash reports! 👻\n");
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -60,16 +74,15 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
|||
|
||||
std.mem.sort(crash.Report, reports.items, {}, lt);
|
||||
|
||||
const writer = stdout.writer();
|
||||
for (reports.items) |report| {
|
||||
var buf: [128]u8 = undefined;
|
||||
const now = std.time.nanoTimestamp();
|
||||
const diff = now - report.mtime;
|
||||
const since = if (diff <= 0) "now" else s: {
|
||||
const d = Config.Duration{ .duration = @intCast(diff) };
|
||||
break :s try std.fmt.bufPrint(&buf, "{s} ago", .{d.round(std.time.ns_per_s)});
|
||||
break :s try std.fmt.bufPrint(&buf, "{f} ago", .{d.round(std.time.ns_per_s)});
|
||||
};
|
||||
try writer.print("{s} ({s})\n", .{ report.name, since });
|
||||
try stdout.print("{s} ({s})\n", .{ report.name, since });
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ pub const Diagnostic = struct {
|
|||
message: [:0]const u8,
|
||||
|
||||
/// Write the full user-friendly diagnostic message to the writer.
|
||||
pub fn write(self: *const Diagnostic, writer: anytype) !void {
|
||||
pub fn format(self: *const Diagnostic, writer: *std.Io.Writer) !void {
|
||||
switch (self.location) {
|
||||
.none => {},
|
||||
.cli => |index| try writer.print("cli:{}:", .{index}),
|
||||
|
|
@ -157,11 +157,14 @@ pub const DiagnosticList = struct {
|
|||
errdefer _ = self.list.pop();
|
||||
|
||||
if (comptime precompute_enabled) {
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
defer buf.deinit();
|
||||
try diag.write(buf.writer());
|
||||
var stream: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer stream.deinit();
|
||||
diag.format(&stream.writer) catch |err| switch (err) {
|
||||
// WriteFailed in this instance can only mean an OOM
|
||||
error.WriteFailed => return error.OutOfMemory,
|
||||
};
|
||||
|
||||
const owned: [:0]const u8 = try buf.toOwnedSliceSentinel(0);
|
||||
const owned: [:0]const u8 = try stream.toOwnedSliceSentinel(0);
|
||||
errdefer alloc.free(owned);
|
||||
|
||||
try self.precompute.messages.append(alloc, owned);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,9 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
// not using `exec` anymore and because this command isn't performance
|
||||
// critical where setting up the defer cleanup is a problem.
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
var opts: Options = .{};
|
||||
defer opts.deinit();
|
||||
|
|
@ -58,6 +60,13 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
const result = runInner(alloc, stderr);
|
||||
// Flushing *shouldn't* fail but...
|
||||
stderr.flush() catch {};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 {
|
||||
// We load the configuration once because that will write our
|
||||
// default configuration files to disk. We don't use the config.
|
||||
var config = try Config.load(alloc);
|
||||
|
|
@ -133,23 +142,13 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
// so this is not a big deal.
|
||||
comptime assert(builtin.link_libc);
|
||||
|
||||
var buf: std.ArrayListUnmanaged(u8) = .empty;
|
||||
errdefer buf.deinit(alloc);
|
||||
|
||||
const writer = buf.writer(alloc);
|
||||
var shellescape: internal_os.ShellEscapeWriter(std.ArrayListUnmanaged(u8).Writer) = .init(writer);
|
||||
var shellescapewriter = shellescape.writer();
|
||||
|
||||
try writer.writeAll(editor);
|
||||
try writer.writeByte(' ');
|
||||
try shellescapewriter.writeAll(path);
|
||||
|
||||
const command = try buf.toOwnedSliceSentinel(alloc, 0);
|
||||
defer alloc.free(command);
|
||||
|
||||
const editorZ = try alloc.dupeZ(u8, editor);
|
||||
defer alloc.free(editorZ);
|
||||
const pathZ = try alloc.dupeZ(u8, path);
|
||||
defer alloc.free(pathZ);
|
||||
const err = std.posix.execvpeZ(
|
||||
"sh",
|
||||
&.{ "sh", "-c", command },
|
||||
editorZ,
|
||||
&.{ editorZ, pathZ },
|
||||
std.c.environ,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -107,12 +107,18 @@ pub const Action = enum {
|
|||
// for all commands by just changing this one place.
|
||||
|
||||
if (std.mem.eql(u8, field.name, @tagName(self))) {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
const text = @field(help_strings.Action, field.name) ++ "\n";
|
||||
stdout.writeAll(text) catch |write_err| {
|
||||
std.log.warn("failed to write help text: {}\n", .{write_err});
|
||||
break :err 1;
|
||||
};
|
||||
stdout.flush() catch |flush_err| {
|
||||
std.log.warn("failed to flush help text: {}\n", .{flush_err});
|
||||
break :err 1;
|
||||
};
|
||||
|
||||
break :err 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
var buffer: [2048]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
try stdout.writeAll(
|
||||
\\Usage: ghostty [+action] [options]
|
||||
\\
|
||||
|
|
@ -70,6 +72,7 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
\\where `<action>` is one of actions listed above.
|
||||
\\
|
||||
);
|
||||
try stdout.flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,8 +37,15 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
try helpgen_actions.generate(stdout, .plaintext, opts.docs, std.heap.page_allocator);
|
||||
var stdout: std.fs.File = .stdout();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
try helpgen_actions.generate(
|
||||
&stdout_writer.interface,
|
||||
.plaintext,
|
||||
opts.docs,
|
||||
std.heap.page_allocator,
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,11 +39,9 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut();
|
||||
|
||||
var keys = std.ArrayList([]const u8).init(alloc);
|
||||
defer keys.deinit();
|
||||
for (x11_color.map.keys()) |key| try keys.append(key);
|
||||
var keys: std.ArrayList([]const u8) = .empty;
|
||||
defer keys.deinit(alloc);
|
||||
for (x11_color.map.keys()) |key| try keys.append(alloc, key);
|
||||
|
||||
std.mem.sortUnstable([]const u8, keys.items, {}, struct {
|
||||
fn lessThan(_: void, lhs: []const u8, rhs: []const u8) bool {
|
||||
|
|
@ -52,12 +50,15 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
}.lessThan);
|
||||
|
||||
// Despite being under the posix namespace, this also works on Windows as of zig 0.13.0
|
||||
var stdout: std.fs.File = .stdout();
|
||||
if (tui.can_pretty_print and !opts.plain and std.posix.isatty(stdout.handle)) {
|
||||
var arena = std.heap.ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
return prettyPrint(arena.allocator(), keys.items);
|
||||
} else {
|
||||
const writer = stdout.writer();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
const writer = &stdout_writer.interface;
|
||||
for (keys.items) |name| {
|
||||
const rgb = x11_color.map.get(name).?;
|
||||
try writer.print("{s} = #{x:0>2}{x:0>2}{x:0>2}\n", .{
|
||||
|
|
@ -74,19 +75,17 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
|
||||
fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 {
|
||||
// Set up vaxis
|
||||
var tty = try vaxis.Tty.init();
|
||||
var buf: [1024]u8 = undefined;
|
||||
var tty = try vaxis.Tty.init(&buf);
|
||||
defer tty.deinit();
|
||||
var vx = try vaxis.init(alloc, .{});
|
||||
defer vx.deinit(alloc, tty.anyWriter());
|
||||
defer vx.deinit(alloc, tty.writer());
|
||||
|
||||
// We know we are ghostty, so let's enable mode 2027. Vaxis normally does this but you need an
|
||||
// event loop to auto-enable it.
|
||||
vx.caps.unicode = .unicode;
|
||||
try tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_set);
|
||||
defer tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_reset) catch {};
|
||||
|
||||
var buf_writer = tty.bufferedWriter();
|
||||
const writer = buf_writer.writer().any();
|
||||
try tty.writer().writeAll(vaxis.ctlseqs.unicode_set);
|
||||
defer tty.writer().writeAll(vaxis.ctlseqs.unicode_reset) catch {};
|
||||
|
||||
const winsize: vaxis.Winsize = switch (builtin.os.tag) {
|
||||
// We use some default, it doesn't really matter for what
|
||||
|
|
@ -100,7 +99,7 @@ fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 {
|
|||
|
||||
else => try vaxis.Tty.getWinsize(tty.fd),
|
||||
};
|
||||
try vx.resize(alloc, tty.anyWriter(), winsize);
|
||||
try vx.resize(alloc, tty.writer(), winsize);
|
||||
|
||||
const win = vx.window();
|
||||
|
||||
|
|
@ -203,11 +202,8 @@ fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 {
|
|||
}
|
||||
|
||||
// output the data
|
||||
try vx.prettyPrint(writer);
|
||||
try vx.prettyPrint(tty.writer());
|
||||
}
|
||||
|
||||
// be sure to flush!
|
||||
try buf_writer.flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,9 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
|||
|
||||
// Its possible to build Ghostty without font discovery!
|
||||
if (comptime font.Discover == void) {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
try stderr.print(
|
||||
\\Ghostty was built without a font discovery mechanism. This is a compile-time
|
||||
\\option. Please review how Ghostty was built from source, contact the
|
||||
|
|
@ -85,15 +87,18 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
|||
,
|
||||
.{},
|
||||
);
|
||||
try stderr.flush();
|
||||
return 1;
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
var buffer: [2048]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
// We'll be putting our fonts into a list categorized by family
|
||||
// so it is easier to read the output.
|
||||
var families = std.ArrayList([]const u8).init(alloc);
|
||||
var map = std.StringHashMap(std.ArrayListUnmanaged([]const u8)).init(alloc);
|
||||
var families: std.ArrayList([]const u8) = .empty;
|
||||
var map: std.StringHashMap(std.ArrayListUnmanaged([]const u8)) = .init(alloc);
|
||||
|
||||
// Look up all available fonts
|
||||
var disco = font.Discover.init();
|
||||
|
|
@ -123,7 +128,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
|||
|
||||
const gop = try map.getOrPut(family);
|
||||
if (!gop.found_existing) {
|
||||
try families.append(family);
|
||||
try families.append(alloc, family);
|
||||
gop.value_ptr.* = .{};
|
||||
}
|
||||
try gop.value_ptr.append(alloc, full_name);
|
||||
|
|
@ -155,5 +160,6 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
|||
try stdout.print("\n", .{});
|
||||
}
|
||||
|
||||
try stdout.flush();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,27 +64,38 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
var config = if (opts.default) try Config.default(alloc) else try Config.load(alloc);
|
||||
defer config.deinit();
|
||||
|
||||
const stdout = std.io.getStdOut();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
const stdout: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
const writer = &stdout_writer.interface;
|
||||
|
||||
// Despite being under the posix namespace, this also works on Windows as of zig 0.13.0
|
||||
if (tui.can_pretty_print and !opts.plain and std.posix.isatty(stdout.handle)) {
|
||||
if (tui.can_pretty_print and !opts.plain and stdout.isTty()) {
|
||||
var arena = std.heap.ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
return prettyPrint(arena.allocator(), config.keybind);
|
||||
} else {
|
||||
try config.keybind.formatEntryDocs(
|
||||
configpkg.entryFormatter("keybind", stdout.writer()),
|
||||
configpkg.entryFormatter("keybind", writer),
|
||||
opts.docs,
|
||||
);
|
||||
}
|
||||
|
||||
// Don't forget to flush!
|
||||
try writer.flush();
|
||||
return 0;
|
||||
}
|
||||
|
||||
const TriggerList = std.SinglyLinkedList(Binding.Trigger);
|
||||
const TriggerNode = struct {
|
||||
data: Binding.Trigger,
|
||||
node: std.SinglyLinkedList.Node = .{},
|
||||
|
||||
pub fn get(node: *std.SinglyLinkedList.Node) *TriggerNode {
|
||||
return @fieldParentPtr("node", node);
|
||||
}
|
||||
};
|
||||
|
||||
const ChordBinding = struct {
|
||||
triggers: TriggerList,
|
||||
triggers: std.SinglyLinkedList,
|
||||
action: Binding.Action,
|
||||
|
||||
// Order keybinds based on various properties
|
||||
|
|
@ -109,7 +120,8 @@ const ChordBinding = struct {
|
|||
const lhs_count: usize = blk: {
|
||||
var count: usize = 0;
|
||||
var maybe_trigger = lhs.triggers.first;
|
||||
while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) {
|
||||
while (maybe_trigger) |node| : (maybe_trigger = node.next) {
|
||||
const trigger: *TriggerNode = .get(node);
|
||||
if (trigger.data.mods.super) count += 1;
|
||||
if (trigger.data.mods.ctrl) count += 1;
|
||||
if (trigger.data.mods.shift) count += 1;
|
||||
|
|
@ -120,7 +132,8 @@ const ChordBinding = struct {
|
|||
const rhs_count: usize = blk: {
|
||||
var count: usize = 0;
|
||||
var maybe_trigger = rhs.triggers.first;
|
||||
while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) {
|
||||
while (maybe_trigger) |node| : (maybe_trigger = node.next) {
|
||||
const trigger: *TriggerNode = .get(node);
|
||||
if (trigger.data.mods.super) count += 1;
|
||||
if (trigger.data.mods.ctrl) count += 1;
|
||||
if (trigger.data.mods.shift) count += 1;
|
||||
|
|
@ -137,8 +150,8 @@ const ChordBinding = struct {
|
|||
var l_trigger = lhs.triggers.first;
|
||||
var r_trigger = rhs.triggers.first;
|
||||
while (l_trigger != null and r_trigger != null) {
|
||||
const l_int = l_trigger.?.data.mods.int();
|
||||
const r_int = r_trigger.?.data.mods.int();
|
||||
const l_int = TriggerNode.get(l_trigger.?).data.mods.int();
|
||||
const r_int = TriggerNode.get(r_trigger.?).data.mods.int();
|
||||
|
||||
if (l_int != r_int) {
|
||||
return l_int > r_int;
|
||||
|
|
@ -154,13 +167,13 @@ const ChordBinding = struct {
|
|||
|
||||
while (l_trigger != null and r_trigger != null) {
|
||||
const lhs_key: c_int = blk: {
|
||||
switch (l_trigger.?.data.key) {
|
||||
switch (TriggerNode.get(l_trigger.?).data.key) {
|
||||
.physical => |key| break :blk @intFromEnum(key),
|
||||
.unicode => |key| break :blk @intCast(key),
|
||||
}
|
||||
};
|
||||
const rhs_key: c_int = blk: {
|
||||
switch (r_trigger.?.data.key) {
|
||||
switch (TriggerNode.get(r_trigger.?).data.key) {
|
||||
.physical => |key| break :blk @intFromEnum(key),
|
||||
.unicode => |key| break :blk @intCast(key),
|
||||
}
|
||||
|
|
@ -186,19 +199,18 @@ const ChordBinding = struct {
|
|||
|
||||
fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
||||
// Set up vaxis
|
||||
var tty = try vaxis.Tty.init();
|
||||
var buf: [1024]u8 = undefined;
|
||||
var tty = try vaxis.Tty.init(&buf);
|
||||
defer tty.deinit();
|
||||
var vx = try vaxis.init(alloc, .{});
|
||||
defer vx.deinit(alloc, tty.anyWriter());
|
||||
const writer = tty.writer();
|
||||
defer vx.deinit(alloc, writer);
|
||||
|
||||
// We know we are ghostty, so let's enable mode 2027. Vaxis normally does this but you need an
|
||||
// event loop to auto-enable it.
|
||||
vx.caps.unicode = .unicode;
|
||||
try tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_set);
|
||||
defer tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_reset) catch {};
|
||||
|
||||
var buf_writer = tty.bufferedWriter();
|
||||
const writer = buf_writer.writer().any();
|
||||
try writer.writeAll(vaxis.ctlseqs.unicode_set);
|
||||
defer writer.writeAll(vaxis.ctlseqs.unicode_reset) catch {};
|
||||
|
||||
const winsize: vaxis.Winsize = switch (builtin.os.tag) {
|
||||
// We use some default, it doesn't really matter for what
|
||||
|
|
@ -212,7 +224,7 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
|||
|
||||
else => try vaxis.Tty.getWinsize(tty.fd),
|
||||
};
|
||||
try vx.resize(alloc, tty.anyWriter(), winsize);
|
||||
try vx.resize(alloc, writer, winsize);
|
||||
|
||||
const win = vx.window();
|
||||
|
||||
|
|
@ -234,7 +246,9 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
|||
|
||||
var result: vaxis.Window.PrintResult = .{ .col = 0, .row = 0, .overflow = false };
|
||||
var maybe_trigger = bind.triggers.first;
|
||||
while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) {
|
||||
while (maybe_trigger) |node| : (maybe_trigger = node.next) {
|
||||
const trigger: *TriggerNode = .get(node);
|
||||
|
||||
if (trigger.data.mods.super) {
|
||||
result = win.printSegment(.{ .text = "super", .style = super_style }, .{ .col_offset = result.col });
|
||||
result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col });
|
||||
|
|
@ -252,18 +266,18 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
|||
result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col });
|
||||
}
|
||||
const key = switch (trigger.data.key) {
|
||||
.physical => |k| try std.fmt.allocPrint(alloc, "{s}", .{@tagName(k)}),
|
||||
.physical => |k| try std.fmt.allocPrint(alloc, "{t}", .{k}),
|
||||
.unicode => |c| try std.fmt.allocPrint(alloc, "{u}", .{c}),
|
||||
};
|
||||
result = win.printSegment(.{ .text = key }, .{ .col_offset = result.col });
|
||||
|
||||
// Print a separator between chorded keys
|
||||
if (trigger.next != null) {
|
||||
if (trigger.node.next != null) {
|
||||
result = win.printSegment(.{ .text = " > ", .style = .{ .bold = true, .fg = .{ .index = 6 } } }, .{ .col_offset = result.col });
|
||||
}
|
||||
}
|
||||
|
||||
const action = try std.fmt.allocPrint(alloc, "{}", .{bind.action});
|
||||
const action = try std.fmt.allocPrint(alloc, "{f}", .{bind.action});
|
||||
// If our action has an argument, we print the argument in a different color
|
||||
if (std.mem.indexOfScalar(u8, action, ':')) |idx| {
|
||||
_ = win.print(&.{
|
||||
|
|
@ -276,29 +290,33 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 {
|
|||
}
|
||||
try vx.prettyPrint(writer);
|
||||
}
|
||||
try buf_writer.flush();
|
||||
try writer.flush();
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn iterateBindings(alloc: Allocator, iter: anytype, win: *const vaxis.Window) !struct { []ChordBinding, u16 } {
|
||||
fn iterateBindings(
|
||||
alloc: Allocator,
|
||||
iter: anytype,
|
||||
win: *const vaxis.Window,
|
||||
) !struct { []ChordBinding, u16 } {
|
||||
var widest_chord: u16 = 0;
|
||||
var bindings = std.ArrayList(ChordBinding).init(alloc);
|
||||
var bindings: std.ArrayList(ChordBinding) = .empty;
|
||||
while (iter.next()) |bind| {
|
||||
const width = blk: {
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
const t = bind.key_ptr.*;
|
||||
|
||||
if (t.mods.super) try std.fmt.format(buf.writer(), "super + ", .{});
|
||||
if (t.mods.ctrl) try std.fmt.format(buf.writer(), "ctrl + ", .{});
|
||||
if (t.mods.alt) try std.fmt.format(buf.writer(), "alt + ", .{});
|
||||
if (t.mods.shift) try std.fmt.format(buf.writer(), "shift + ", .{});
|
||||
if (t.mods.super) try buf.writer.print("super + ", .{});
|
||||
if (t.mods.ctrl) try buf.writer.print("ctrl + ", .{});
|
||||
if (t.mods.alt) try buf.writer.print("alt + ", .{});
|
||||
if (t.mods.shift) try buf.writer.print("shift + ", .{});
|
||||
|
||||
switch (t.key) {
|
||||
.physical => |k| try std.fmt.format(buf.writer(), "{s}", .{@tagName(k)}),
|
||||
.unicode => |c| try std.fmt.format(buf.writer(), "{u}", .{c}),
|
||||
.physical => |k| try buf.writer.print("{t}", .{k}),
|
||||
.unicode => |c| try buf.writer.print("{u}", .{c}),
|
||||
}
|
||||
|
||||
break :blk win.gwidth(buf.items);
|
||||
break :blk win.gwidth(buf.written());
|
||||
};
|
||||
|
||||
switch (bind.value_ptr.*) {
|
||||
|
|
@ -310,28 +328,28 @@ fn iterateBindings(alloc: Allocator, iter: anytype, win: *const vaxis.Window) !s
|
|||
|
||||
// Prepend the current keybind onto the list of sub-binds
|
||||
for (sub_bindings) |*nb| {
|
||||
const prepend_node = try alloc.create(TriggerList.Node);
|
||||
prepend_node.* = TriggerList.Node{ .data = bind.key_ptr.* };
|
||||
nb.triggers.prepend(prepend_node);
|
||||
const prepend_node = try alloc.create(TriggerNode);
|
||||
prepend_node.* = .{ .data = bind.key_ptr.* };
|
||||
nb.triggers.prepend(&prepend_node.node);
|
||||
}
|
||||
|
||||
// Add the longest sub-bind width to the current bind width along with a padding
|
||||
// of 5 for the ' > ' spacer
|
||||
widest_chord = @max(widest_chord, width + max_width + 5);
|
||||
try bindings.appendSlice(sub_bindings);
|
||||
try bindings.appendSlice(alloc, sub_bindings);
|
||||
},
|
||||
.leaf => |leaf| {
|
||||
const node = try alloc.create(TriggerList.Node);
|
||||
node.* = TriggerList.Node{ .data = bind.key_ptr.* };
|
||||
const triggers = TriggerList{
|
||||
.first = node,
|
||||
};
|
||||
const node = try alloc.create(TriggerNode);
|
||||
node.* = .{ .data = bind.key_ptr.* };
|
||||
|
||||
widest_chord = @max(widest_chord, width);
|
||||
try bindings.append(.{ .triggers = triggers, .action = leaf.action });
|
||||
try bindings.append(alloc, .{
|
||||
.triggers = .{ .first = &node.node },
|
||||
.action = leaf.action,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return .{ try bindings.toOwnedSlice(), widest_chord };
|
||||
return .{ try bindings.toOwnedSlice(alloc), widest_chord };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,9 +57,12 @@ const ThemeListElement = struct {
|
|||
.host = .{ .raw = "" },
|
||||
.path = .{ .raw = self.path },
|
||||
};
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
errdefer buf.deinit();
|
||||
try uri.writeToStream(.{ .scheme = true, .authority = true, .path = true }, buf.writer());
|
||||
try uri.writeToStream(
|
||||
&buf.writer,
|
||||
.{ .scheme = true, .authority = true, .path = true },
|
||||
);
|
||||
return buf.toOwnedSlice();
|
||||
}
|
||||
};
|
||||
|
|
@ -114,8 +117,14 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
|||
var arena = std.heap.ArenaAllocator.init(gpa_alloc);
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
var stdout_buf: [4096]u8 = undefined;
|
||||
var stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&stdout_buf);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
var stderr_buf: [4096]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&stderr_buf);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
const resources_dir = global_state.resources_dir.app();
|
||||
if (resources_dir == null)
|
||||
|
|
@ -124,9 +133,9 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
|||
|
||||
var count: usize = 0;
|
||||
|
||||
var themes = std.ArrayList(ThemeListElement).init(alloc);
|
||||
var themes: std.ArrayList(ThemeListElement) = .empty;
|
||||
|
||||
var it = themepkg.LocationIterator{ .arena_alloc = arena.allocator() };
|
||||
var it: themepkg.LocationIterator = .{ .arena_alloc = arena.allocator() };
|
||||
|
||||
while (try it.next()) |loc| {
|
||||
var dir = std.fs.cwd().openDir(loc.dir, .{ .iterate = true }) catch |err| switch (err) {
|
||||
|
|
@ -148,7 +157,7 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
|||
count += 1;
|
||||
|
||||
const path = try std.fs.path.join(alloc, &.{ loc.dir, entry.name });
|
||||
try themes.append(.{
|
||||
try themes.append(alloc, .{
|
||||
.path = path,
|
||||
.location = loc.location,
|
||||
.theme = try alloc.dupe(u8, entry.name),
|
||||
|
|
@ -166,18 +175,20 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
|||
|
||||
std.mem.sortUnstable(ThemeListElement, themes.items, {}, ThemeListElement.lessThan);
|
||||
|
||||
if (tui.can_pretty_print and !opts.plain and std.posix.isatty(std.io.getStdOut().handle)) {
|
||||
if (tui.can_pretty_print and !opts.plain and stdout_file.isTty()) {
|
||||
try preview(gpa_alloc, themes.items, opts.color);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (themes.items) |theme| {
|
||||
if (opts.path)
|
||||
try stdout.print("{s} ({s}) {s}\n", .{ theme.theme, @tagName(theme.location), theme.path })
|
||||
try stdout.print("{s} ({t}) {s}\n", .{ theme.theme, theme.location, theme.path })
|
||||
else
|
||||
try stdout.print("{s} ({s})\n", .{ theme.theme, @tagName(theme.location) });
|
||||
try stdout.print("{s} ({t})\n", .{ theme.theme, theme.location });
|
||||
}
|
||||
|
||||
// Don't forget to flush!
|
||||
try stdout.flush();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -209,23 +220,28 @@ const Preview = struct {
|
|||
text_input: vaxis.widgets.TextInput,
|
||||
theme_filter: ColorScheme,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, themes: []ThemeListElement, theme_filter: ColorScheme) !*Preview {
|
||||
pub fn init(
|
||||
allocator: std.mem.Allocator,
|
||||
themes: []ThemeListElement,
|
||||
theme_filter: ColorScheme,
|
||||
buf: []u8,
|
||||
) !*Preview {
|
||||
const self = try allocator.create(Preview);
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.should_quit = false,
|
||||
.tty = try vaxis.Tty.init(),
|
||||
.tty = try .init(buf),
|
||||
.vx = try vaxis.init(allocator, .{}),
|
||||
.mouse = null,
|
||||
.themes = themes,
|
||||
.filtered = try std.ArrayList(usize).initCapacity(allocator, themes.len),
|
||||
.filtered = try .initCapacity(allocator, themes.len),
|
||||
.current = 0,
|
||||
.window = 0,
|
||||
.hex = false,
|
||||
.mode = .normal,
|
||||
.color_scheme = .light,
|
||||
.text_input = vaxis.widgets.TextInput.init(allocator, &self.vx.unicode),
|
||||
.text_input = .init(allocator, &self.vx.unicode),
|
||||
.theme_filter = theme_filter,
|
||||
};
|
||||
|
||||
|
|
@ -236,9 +252,9 @@ const Preview = struct {
|
|||
|
||||
pub fn deinit(self: *Preview) void {
|
||||
const allocator = self.allocator;
|
||||
self.filtered.deinit();
|
||||
self.filtered.deinit(allocator);
|
||||
self.text_input.deinit();
|
||||
self.vx.deinit(allocator, self.tty.anyWriter());
|
||||
self.vx.deinit(allocator, self.tty.writer());
|
||||
self.tty.deinit();
|
||||
allocator.destroy(self);
|
||||
}
|
||||
|
|
@ -251,12 +267,14 @@ const Preview = struct {
|
|||
try loop.init();
|
||||
try loop.start();
|
||||
|
||||
try self.vx.enterAltScreen(self.tty.anyWriter());
|
||||
try self.vx.setTitle(self.tty.anyWriter(), "👻 Ghostty Theme Preview 👻");
|
||||
try self.vx.queryTerminal(self.tty.anyWriter(), 1 * std.time.ns_per_s);
|
||||
try self.vx.setMouseMode(self.tty.anyWriter(), true);
|
||||
const writer = self.tty.writer();
|
||||
|
||||
try self.vx.enterAltScreen(writer);
|
||||
try self.vx.setTitle(writer, "👻 Ghostty Theme Preview 👻");
|
||||
try self.vx.queryTerminal(writer, 1 * std.time.ns_per_s);
|
||||
try self.vx.setMouseMode(writer, true);
|
||||
if (self.vx.caps.color_scheme_updates)
|
||||
try self.vx.subscribeToColorSchemeUpdates(self.tty.anyWriter());
|
||||
try self.vx.subscribeToColorSchemeUpdates(writer);
|
||||
|
||||
while (!self.should_quit) {
|
||||
var arena = std.heap.ArenaAllocator.init(self.allocator);
|
||||
|
|
@ -269,9 +287,8 @@ const Preview = struct {
|
|||
}
|
||||
try self.draw(alloc);
|
||||
|
||||
var buffered = self.tty.bufferedWriter();
|
||||
try self.vx.render(buffered.writer().any());
|
||||
try buffered.flush();
|
||||
try self.vx.render(writer);
|
||||
try writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,11 +325,11 @@ const Preview = struct {
|
|||
const string = try std.ascii.allocLowerString(self.allocator, buffer);
|
||||
defer self.allocator.free(string);
|
||||
|
||||
var tokens = std.ArrayList([]const u8).init(self.allocator);
|
||||
defer tokens.deinit();
|
||||
var tokens: std.ArrayList([]const u8) = .empty;
|
||||
defer tokens.deinit(self.allocator);
|
||||
|
||||
var it = std.mem.tokenizeScalar(u8, string, ' ');
|
||||
while (it.next()) |token| try tokens.append(token);
|
||||
while (it.next()) |token| try tokens.append(self.allocator, token);
|
||||
|
||||
for (self.themes, 0..) |*theme, i| {
|
||||
try theme_config.loadFile(theme_config._arena.?.allocator(), theme.path);
|
||||
|
|
@ -322,13 +339,13 @@ const Preview = struct {
|
|||
.to_lower = true,
|
||||
.plain = true,
|
||||
});
|
||||
if (theme.rank != null) try self.filtered.append(i);
|
||||
if (theme.rank != null) try self.filtered.append(self.allocator, i);
|
||||
}
|
||||
} else {
|
||||
for (self.themes, 0..) |*theme, i| {
|
||||
try theme_config.loadFile(theme_config._arena.?.allocator(), theme.path);
|
||||
if (shouldIncludeTheme(self.theme_filter, theme_config)) {
|
||||
try self.filtered.append(i);
|
||||
try self.filtered.append(self.allocator, i);
|
||||
theme.rank = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -421,13 +438,13 @@ const Preview = struct {
|
|||
self.hex = false;
|
||||
if (key.matches('c', .{}))
|
||||
try self.vx.copyToSystemClipboard(
|
||||
self.tty.anyWriter(),
|
||||
self.tty.writer(),
|
||||
self.themes[self.filtered.items[self.current]].theme,
|
||||
alloc,
|
||||
)
|
||||
else if (key.matches('c', .{ .shift = true }))
|
||||
try self.vx.copyToSystemClipboard(
|
||||
self.tty.anyWriter(),
|
||||
self.tty.writer(),
|
||||
self.themes[self.filtered.items[self.current]].path,
|
||||
alloc,
|
||||
);
|
||||
|
|
@ -471,7 +488,7 @@ const Preview = struct {
|
|||
},
|
||||
.color_scheme => |color_scheme| self.color_scheme = color_scheme,
|
||||
.mouse => |mouse| self.mouse = mouse,
|
||||
.winsize => |ws| try self.vx.resize(self.allocator, self.tty.anyWriter(), ws),
|
||||
.winsize => |ws| try self.vx.resize(self.allocator, self.tty.writer(), ws),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1044,14 +1061,14 @@ const Preview = struct {
|
|||
);
|
||||
}
|
||||
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
for (config._diagnostics.items(), 0..) |diag, captured_i| {
|
||||
const i: u16 = @intCast(captured_i);
|
||||
try diag.write(buf.writer());
|
||||
try diag.format(&buf.writer);
|
||||
_ = child.printSegment(
|
||||
.{
|
||||
.text = buf.items,
|
||||
.text = buf.written(),
|
||||
.style = self.ui_err(),
|
||||
},
|
||||
.{
|
||||
|
|
@ -1319,7 +1336,7 @@ const Preview = struct {
|
|||
.{ .text = "const ", .style = color5 },
|
||||
.{ .text = "stdout ", .style = standard },
|
||||
.{ .text = "=", .style = color5 },
|
||||
.{ .text = " std.io.getStdOut().writer();", .style = standard },
|
||||
.{ .text = " std.Io.getStdOut().writer();", .style = standard },
|
||||
},
|
||||
.{
|
||||
.row_offset = 7,
|
||||
|
|
@ -1651,7 +1668,13 @@ fn color(config: Config, palette: usize) vaxis.Color {
|
|||
const lorem_ipsum = @embedFile("lorem_ipsum.txt");
|
||||
|
||||
fn preview(allocator: std.mem.Allocator, themes: []ThemeListElement, theme_filter: ColorScheme) !void {
|
||||
var app = try Preview.init(allocator, themes, theme_filter);
|
||||
var buf: [4096]u8 = undefined;
|
||||
var app = try Preview.init(
|
||||
allocator,
|
||||
themes,
|
||||
theme_filter,
|
||||
&buf,
|
||||
);
|
||||
defer app.deinit();
|
||||
try app.run();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ pub const Options = struct {
|
|||
// If it's not `-e` continue with the standard argument parsning.
|
||||
if (!std.mem.eql(u8, arg, "-e")) return true;
|
||||
|
||||
var arguments: std.ArrayListUnmanaged([:0]const u8) = .empty;
|
||||
var arguments: std.ArrayList([:0]const u8) = .empty;
|
||||
errdefer {
|
||||
for (arguments.items) |argument| alloc.free(argument);
|
||||
arguments.deinit(alloc);
|
||||
|
|
@ -99,12 +99,21 @@ pub const Options = struct {
|
|||
pub fn run(alloc: Allocator) !u8 {
|
||||
var iter = try args.argsIterator(alloc);
|
||||
defer iter.deinit();
|
||||
return try runArgs(alloc, &iter);
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stderr().writer(&buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
const result = runArgs(alloc, &iter, stderr);
|
||||
stderr.flush() catch {};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
fn runArgs(
|
||||
alloc_gpa: Allocator,
|
||||
argsIter: anytype,
|
||||
stderr: *std.Io.Writer,
|
||||
) !u8 {
|
||||
var opts: Options = .{};
|
||||
defer opts.deinit();
|
||||
|
||||
|
|
@ -126,9 +135,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
|||
inner: inline for (@typeInfo(Options).@"struct".fields) |field| {
|
||||
if (field.name[0] == '_') continue :inner;
|
||||
if (std.mem.eql(u8, field.name, diagnostic.key)) {
|
||||
try stderr.writeAll("config error: ");
|
||||
try diagnostic.write(stderr);
|
||||
try stderr.writeAll("\n");
|
||||
try stderr.print("config error: {f}\n", .{diagnostic});
|
||||
exit = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,10 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
|
||||
// For some reason `std.fmt.format` isn't working here but it works in
|
||||
// tests so we just do configfmt.format.
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
try configfmt.format("", .{}, stdout);
|
||||
var stdout: std.fs.File = .stdout();
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var stdout_writer = stdout.writer(&buffer);
|
||||
try configfmt.format(&stdout_writer.interface);
|
||||
try stdout_writer.end();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,13 +64,32 @@ pub const Options = struct {
|
|||
pub fn run(alloc: Allocator) !u8 {
|
||||
var iter = try args.argsIterator(alloc);
|
||||
defer iter.deinit();
|
||||
return try runArgs(alloc, &iter);
|
||||
|
||||
var stdout_buffer: [1024]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
var stderr_buffer: [1024]u8 = undefined;
|
||||
var stderr_writer = std.fs.File.stdout().writer(&stderr_buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
const result = runArgs(
|
||||
alloc,
|
||||
&iter,
|
||||
stdout,
|
||||
stderr,
|
||||
);
|
||||
stdout.flush() catch {};
|
||||
stderr.flush() catch {};
|
||||
return result;
|
||||
}
|
||||
|
||||
fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
fn runArgs(
|
||||
alloc_gpa: Allocator,
|
||||
argsIter: anytype,
|
||||
stdout: *std.Io.Writer,
|
||||
stderr: *std.Io.Writer,
|
||||
) !u8 {
|
||||
// Its possible to build Ghostty without font discovery!
|
||||
if (comptime font.Discover == void) {
|
||||
try stderr.print(
|
||||
|
|
@ -104,9 +123,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
|||
inner: inline for (@typeInfo(Options).@"struct".fields) |field| {
|
||||
if (field.name[0] == '_') continue :inner;
|
||||
if (std.mem.eql(u8, field.name, diagnostic.key)) {
|
||||
try stderr.writeAll("config error: ");
|
||||
try diagnostic.write(stderr);
|
||||
try stderr.writeAll("\n");
|
||||
try stderr.print("config error: {f}\n", .{diagnostic});
|
||||
exit = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -138,9 +155,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
|||
if (field.name[0] == '_') continue :inner;
|
||||
if (std.mem.eql(u8, field.name, diagnostic.key) and (diagnostic.location == .none or diagnostic.location == .cli)) continue :outer;
|
||||
}
|
||||
try stderr.writeAll("config error: ");
|
||||
try diagnostic.write(stderr);
|
||||
try stderr.writeAll("\n");
|
||||
try stderr.print("config error: {f}\n", .{diagnostic});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -189,8 +204,8 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
|||
|
||||
fn lookup(
|
||||
alloc: std.mem.Allocator,
|
||||
stdout: anytype,
|
||||
stderr: anytype,
|
||||
stdout: *std.Io.Writer,
|
||||
stderr: *std.Io.Writer,
|
||||
font_grid: *font.SharedGrid,
|
||||
style: font.Style,
|
||||
presentation: ?font.Presentation,
|
||||
|
|
|
|||
|
|
@ -57,8 +57,6 @@ pub fn clear(self: DiskCache) !void {
|
|||
|
||||
pub const AddResult = enum { added, updated };
|
||||
|
||||
pub const AddError = std.fs.Dir.MakeError || std.fs.File.OpenError || std.fs.File.LockError || std.fs.File.ReadError || std.fs.File.WriteError || std.posix.RealPathError || std.posix.RenameError || Allocator.Error || error{ HostnameIsInvalid, CacheIsLocked };
|
||||
|
||||
/// Add or update a hostname entry in the cache.
|
||||
/// Returns AddResult.added for new entries or AddResult.updated for existing ones.
|
||||
/// The cache file is created if it doesn't exist with secure permissions (0600).
|
||||
|
|
@ -66,7 +64,7 @@ pub fn add(
|
|||
self: DiskCache,
|
||||
alloc: Allocator,
|
||||
hostname: []const u8,
|
||||
) AddError!AddResult {
|
||||
) !AddResult {
|
||||
if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid;
|
||||
|
||||
// Create cache directory if needed
|
||||
|
|
@ -130,15 +128,13 @@ pub fn add(
|
|||
return result;
|
||||
}
|
||||
|
||||
pub const RemoveError = std.fs.Dir.OpenError || std.fs.File.OpenError || std.fs.File.ReadError || std.fs.File.WriteError || std.posix.RealPathError || std.posix.RenameError || Allocator.Error || error{ HostnameIsInvalid, CacheIsLocked };
|
||||
|
||||
/// Remove a hostname entry from the cache.
|
||||
/// No error is returned if the hostname doesn't exist or the cache file is missing.
|
||||
pub fn remove(
|
||||
self: DiskCache,
|
||||
alloc: Allocator,
|
||||
hostname: []const u8,
|
||||
) RemoveError!void {
|
||||
) !void {
|
||||
if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid;
|
||||
|
||||
// Open our file
|
||||
|
|
@ -199,7 +195,7 @@ pub fn contains(
|
|||
return entries.contains(hostname);
|
||||
}
|
||||
|
||||
fn fixupPermissions(file: std.fs.File) !void {
|
||||
fn fixupPermissions(file: std.fs.File) (std.fs.File.StatError || std.fs.File.ChmodError)!void {
|
||||
// Windows does not support chmod
|
||||
if (comptime builtin.os.tag == .windows) return;
|
||||
|
||||
|
|
@ -211,14 +207,12 @@ fn fixupPermissions(file: std.fs.File) !void {
|
|||
}
|
||||
}
|
||||
|
||||
pub const WriteCacheFileError = std.fs.Dir.OpenError || std.fs.File.OpenError || std.fs.File.WriteError || std.fs.Dir.RealPathAllocError || std.posix.RealPathError || std.posix.RenameError || error{FileTooBig};
|
||||
|
||||
fn writeCacheFile(
|
||||
self: DiskCache,
|
||||
alloc: Allocator,
|
||||
entries: std.StringHashMap(Entry),
|
||||
expire_days: ?u32,
|
||||
) WriteCacheFileError!void {
|
||||
) !void {
|
||||
var td: TempDir = try .init();
|
||||
defer td.deinit();
|
||||
|
||||
|
|
@ -227,14 +221,18 @@ fn writeCacheFile(
|
|||
const tmp_path = try td.dir.realpathAlloc(alloc, "ssh-cache");
|
||||
defer alloc.free(tmp_path);
|
||||
|
||||
const writer = tmp_file.writer();
|
||||
var buf: [1024]u8 = undefined;
|
||||
var writer = tmp_file.writer(&buf);
|
||||
var iter = entries.iterator();
|
||||
while (iter.next()) |kv| {
|
||||
// Only write non-expired entries
|
||||
if (kv.value_ptr.isExpired(expire_days)) continue;
|
||||
try kv.value_ptr.format(writer);
|
||||
try kv.value_ptr.format(&writer.interface);
|
||||
}
|
||||
|
||||
// Don't forget to flush!!
|
||||
try writer.interface.flush();
|
||||
|
||||
// Atomic replace
|
||||
try std.fs.renameAbsolute(tmp_path, self.path);
|
||||
}
|
||||
|
|
@ -278,8 +276,12 @@ pub fn deinitEntries(
|
|||
fn readEntries(
|
||||
alloc: Allocator,
|
||||
file: std.fs.File,
|
||||
) (std.fs.File.ReadError || Allocator.Error || error{FileTooBig})!std.StringHashMap(Entry) {
|
||||
const content = try file.readToEndAlloc(alloc, MAX_CACHE_SIZE);
|
||||
) !std.StringHashMap(Entry) {
|
||||
var reader = file.reader(&.{});
|
||||
const content = try reader.interface.allocRemaining(
|
||||
alloc,
|
||||
.limited(MAX_CACHE_SIZE),
|
||||
);
|
||||
defer alloc.free(content);
|
||||
|
||||
var entries = std.StringHashMap(Entry).init(alloc);
|
||||
|
|
@ -403,10 +405,12 @@ test "disk cache clear" {
|
|||
// Create our path
|
||||
var td: TempDir = try .init();
|
||||
defer td.deinit();
|
||||
var buf: [4096]u8 = undefined;
|
||||
{
|
||||
var file = try td.dir.createFile("cache", .{});
|
||||
defer file.close();
|
||||
try file.writer().writeAll("HELLO!");
|
||||
var file_writer = file.writer(&buf);
|
||||
try file_writer.interface.writeAll("HELLO!");
|
||||
}
|
||||
const path = try td.dir.realpathAlloc(alloc, "cache");
|
||||
defer alloc.free(path);
|
||||
|
|
@ -429,10 +433,14 @@ test "disk cache operations" {
|
|||
// Create our path
|
||||
var td: TempDir = try .init();
|
||||
defer td.deinit();
|
||||
var buf: [4096]u8 = undefined;
|
||||
{
|
||||
var file = try td.dir.createFile("cache", .{});
|
||||
defer file.close();
|
||||
try file.writer().writeAll("HELLO!");
|
||||
var file_writer = file.writer(&buf);
|
||||
const writer = &file_writer.interface;
|
||||
try writer.writeAll("HELLO!");
|
||||
try writer.flush();
|
||||
}
|
||||
const path = try td.dir.realpathAlloc(alloc, "cache");
|
||||
defer alloc.free(path);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ pub fn parse(line: []const u8) ?Entry {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn format(self: Entry, writer: anytype) !void {
|
||||
pub fn format(self: Entry, writer: *std.Io.Writer) !void {
|
||||
try writer.print(
|
||||
"{s}|{d}|{s}\n",
|
||||
.{ self.hostname, self.timestamp, self.terminfo_version },
|
||||
|
|
|
|||
|
|
@ -61,9 +61,30 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
|||
try args.parse(Options, alloc_gpa, &opts, &iter);
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
var stdout_buffer: [1024]u8 = undefined;
|
||||
var stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&stdout_buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
|
||||
var stderr_buffer: [1024]u8 = undefined;
|
||||
var stderr_file: std.fs.File = .stderr();
|
||||
var stderr_writer = stderr_file.writer(&stderr_buffer);
|
||||
const stderr = &stderr_writer.interface;
|
||||
|
||||
const result = runInner(alloc, opts, stdout, stderr);
|
||||
|
||||
// Flushing *shouldn't* fail but...
|
||||
stdout.flush() catch {};
|
||||
stderr.flush() catch {};
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn runInner(
|
||||
alloc: Allocator,
|
||||
opts: Options,
|
||||
stdout: *std.Io.Writer,
|
||||
stderr: *std.Io.Writer,
|
||||
) !u8 {
|
||||
// Setup our disk cache to the standard location
|
||||
const cache_path = try DiskCache.defaultPath(alloc, "ghostty");
|
||||
const cache: DiskCache = .{ .path = cache_path };
|
||||
|
|
@ -165,7 +186,7 @@ pub fn run(alloc_gpa: Allocator) !u8 {
|
|||
fn listEntries(
|
||||
alloc: Allocator,
|
||||
entries: *const std.StringHashMap(Entry),
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
if (entries.count() == 0) {
|
||||
try writer.print("No hosts in cache.\n", .{});
|
||||
|
|
@ -173,12 +194,12 @@ fn listEntries(
|
|||
}
|
||||
|
||||
// Sort entries by hostname for consistent output
|
||||
var items = std.ArrayList(Entry).init(alloc);
|
||||
defer items.deinit();
|
||||
var items: std.ArrayList(Entry) = .empty;
|
||||
defer items.deinit(alloc);
|
||||
|
||||
var iter = entries.iterator();
|
||||
while (iter.next()) |kv| {
|
||||
try items.append(kv.value_ptr.*);
|
||||
try items.append(alloc, kv.value_ptr.*);
|
||||
}
|
||||
|
||||
std.mem.sort(Entry, items.items, {}, struct {
|
||||
|
|
|
|||
|
|
@ -40,8 +40,19 @@ pub fn run(alloc: std.mem.Allocator) !u8 {
|
|||
try args.parse(Options, alloc, &opts, &iter);
|
||||
}
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var stdout_writer = std.fs.File.stdout().writer(&buffer);
|
||||
const stdout = &stdout_writer.interface;
|
||||
const result = runInner(alloc, opts, stdout);
|
||||
try stdout_writer.end();
|
||||
return result;
|
||||
}
|
||||
|
||||
fn runInner(
|
||||
alloc: std.mem.Allocator,
|
||||
opts: Options,
|
||||
stdout: *std.Io.Writer,
|
||||
) !u8 {
|
||||
var cfg = try Config.default(alloc);
|
||||
defer cfg.deinit();
|
||||
|
||||
|
|
@ -58,15 +69,9 @@ pub fn run(alloc: std.mem.Allocator) !u8 {
|
|||
try cfg.finalize();
|
||||
|
||||
if (cfg._diagnostics.items().len > 0) {
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
for (cfg._diagnostics.items()) |diag| {
|
||||
try diag.write(buf.writer());
|
||||
try stdout.print("{s}\n", .{buf.items});
|
||||
buf.clearRetainingCapacity();
|
||||
try stdout.print("{f}\n", .{diag});
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,12 @@ pub const Options = struct {};
|
|||
/// The `version` command is used to display information about Ghostty. Recognized as
|
||||
/// either `+version` or `--version`.
|
||||
pub fn run(alloc: Allocator) !u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const tty = std.io.getStdOut().isTty();
|
||||
var buffer: [1024]u8 = undefined;
|
||||
const stdout_file: std.fs.File = .stdout();
|
||||
var stdout_writer = stdout_file.writer(&buffer);
|
||||
|
||||
const stdout = &stdout_writer.interface;
|
||||
const tty = stdout_file.isTty();
|
||||
|
||||
if (tty) if (build_config.version.build) |commit_hash| {
|
||||
try stdout.print(
|
||||
|
|
@ -29,7 +33,7 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
|
||||
try stdout.print("Version\n", .{});
|
||||
try stdout.print(" - version: {s}\n", .{build_config.version_string});
|
||||
try stdout.print(" - channel: {s}\n", .{@tagName(build_config.release_channel)});
|
||||
try stdout.print(" - channel: {t}\n", .{build_config.release_channel});
|
||||
|
||||
try stdout.print("Build Config\n", .{});
|
||||
try stdout.print(" - Zig version : {s}\n", .{builtin.zig_version_string});
|
||||
|
|
@ -37,20 +41,20 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
try stdout.print(" - app runtime : {}\n", .{build_config.app_runtime});
|
||||
try stdout.print(" - font engine : {}\n", .{build_config.font_backend});
|
||||
try stdout.print(" - renderer : {}\n", .{renderer.Renderer});
|
||||
try stdout.print(" - libxev : {s}\n", .{@tagName(xev.backend)});
|
||||
try stdout.print(" - libxev : {t}\n", .{xev.backend});
|
||||
if (comptime build_config.app_runtime == .gtk) {
|
||||
if (comptime builtin.os.tag == .linux) {
|
||||
const kernel_info = internal_os.getKernelInfo(alloc);
|
||||
defer if (kernel_info) |k| alloc.free(k);
|
||||
try stdout.print(" - kernel version: {s}\n", .{kernel_info orelse "Kernel information unavailable"});
|
||||
}
|
||||
try stdout.print(" - desktop env : {s}\n", .{@tagName(internal_os.desktopEnvironment())});
|
||||
try stdout.print(" - desktop env : {t}\n", .{internal_os.desktopEnvironment()});
|
||||
try stdout.print(" - GTK version :\n", .{});
|
||||
try stdout.print(" build : {}\n", .{gtk_version.comptime_version});
|
||||
try stdout.print(" runtime : {}\n", .{gtk_version.getRuntimeVersion()});
|
||||
try stdout.print(" build : {f}\n", .{gtk_version.comptime_version});
|
||||
try stdout.print(" runtime : {f}\n", .{gtk_version.getRuntimeVersion()});
|
||||
try stdout.print(" - libadwaita : enabled\n", .{});
|
||||
try stdout.print(" build : {}\n", .{adw_version.comptime_version});
|
||||
try stdout.print(" runtime : {}\n", .{adw_version.getRuntimeVersion()});
|
||||
try stdout.print(" build : {f}\n", .{adw_version.comptime_version});
|
||||
try stdout.print(" runtime : {f}\n", .{adw_version.getRuntimeVersion()});
|
||||
if (comptime build_options.x11) {
|
||||
try stdout.print(" - libX11 : enabled\n", .{});
|
||||
} else {
|
||||
|
|
@ -65,5 +69,8 @@ pub fn run(alloc: Allocator) !u8 {
|
|||
try stdout.print(" - libwayland : disabled\n", .{});
|
||||
}
|
||||
}
|
||||
|
||||
// Don't forget to flush!
|
||||
try stdout.flush();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3417,10 +3417,10 @@ pub fn loadFile(self: *Config, alloc: Allocator, path: []const u8) !void {
|
|||
defer file.close();
|
||||
|
||||
std.log.info("reading configuration file path={s}", .{path});
|
||||
var buf_reader = std.io.bufferedReader(file.reader());
|
||||
const reader = buf_reader.reader();
|
||||
const Iter = cli.args.LineIterator(@TypeOf(reader));
|
||||
var iter: Iter = .{ .r = reader, .filepath = path };
|
||||
var buf: [2048]u8 = undefined;
|
||||
var file_reader = file.reader(&buf);
|
||||
const reader = &file_reader.interface;
|
||||
var iter: cli.args.LineIterator = .{ .r = reader, .filepath = path };
|
||||
try self.loadIter(alloc, &iter);
|
||||
try self.expandPaths(std.fs.path.dirname(path).?);
|
||||
}
|
||||
|
|
@ -3457,8 +3457,10 @@ fn writeConfigTemplate(path: []const u8) !void {
|
|||
}
|
||||
const file = try std.fs.createFileAbsolute(path, .{});
|
||||
defer file.close();
|
||||
try std.fmt.format(
|
||||
file.writer(),
|
||||
var buf: [4096]u8 = undefined;
|
||||
var file_writer = file.writer(&buf);
|
||||
const writer = &file_writer.interface;
|
||||
try writer.print(
|
||||
@embedFile("./config-template"),
|
||||
.{ .path = path },
|
||||
);
|
||||
|
|
@ -3628,17 +3630,17 @@ pub fn loadCliArgs(self: *Config, alloc_gpa: Allocator) !void {
|
|||
|
||||
// Next, take all remaining args and use that to build up
|
||||
// a command to execute.
|
||||
var builder = std.ArrayList([:0]const u8).init(arena_alloc);
|
||||
errdefer builder.deinit();
|
||||
var builder: std.ArrayList([:0]const u8) = .empty;
|
||||
errdefer builder.deinit(arena_alloc);
|
||||
for (args) |arg_raw| {
|
||||
const arg = std.mem.sliceTo(arg_raw, 0);
|
||||
const copy = try arena_alloc.dupeZ(u8, arg);
|
||||
try self._replay_steps.append(arena_alloc, .{ .arg = copy });
|
||||
try builder.append(copy);
|
||||
try builder.append(arena_alloc, copy);
|
||||
}
|
||||
|
||||
self.@"_xdg-terminal-exec" = true;
|
||||
self.@"initial-command" = .{ .direct = try builder.toOwnedSlice() };
|
||||
self.@"initial-command" = .{ .direct = try builder.toOwnedSlice(arena_alloc) };
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -3710,13 +3712,13 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void {
|
|||
// PRIOR to the "-e" in our replay steps, since everything
|
||||
// after "-e" becomes an "initial-command". To do this, we
|
||||
// dupe the values if we find it.
|
||||
var replay_suffix = std.ArrayList(Replay.Step).init(alloc_gpa);
|
||||
defer replay_suffix.deinit();
|
||||
var replay_suffix: std.ArrayList(Replay.Step) = .empty;
|
||||
defer replay_suffix.deinit(alloc_gpa);
|
||||
for (self._replay_steps.items, 0..) |step, i| if (step == .@"-e") {
|
||||
// We don't need to clone the steps because they should
|
||||
// all be allocated in our arena and we're keeping our
|
||||
// arena.
|
||||
try replay_suffix.appendSlice(self._replay_steps.items[i..]);
|
||||
try replay_suffix.appendSlice(alloc_gpa, self._replay_steps.items[i..]);
|
||||
|
||||
// Remove our old values. Again, don't need to free any
|
||||
// memory here because its all part of our arena.
|
||||
|
|
@ -3744,10 +3746,11 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void {
|
|||
// We must only load a unique file once
|
||||
if (try loaded.fetchPut(path, {}) != null) {
|
||||
const diag: cli.Diagnostic = .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"config-file {s}: cycle detected",
|
||||
.{path},
|
||||
0,
|
||||
),
|
||||
};
|
||||
|
||||
|
|
@ -3759,10 +3762,11 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void {
|
|||
var file = std.fs.openFileAbsolute(path, .{}) catch |err| {
|
||||
if (err != error.FileNotFound or !optional) {
|
||||
const diag: cli.Diagnostic = .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"error opening config-file {s}: {}",
|
||||
.{ path, err },
|
||||
0,
|
||||
),
|
||||
};
|
||||
|
||||
|
|
@ -3778,10 +3782,11 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void {
|
|||
.file => {},
|
||||
else => |kind| {
|
||||
const diag: cli.Diagnostic = .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"config-file {s}: not reading because file type is {s}",
|
||||
.{ path, @tagName(kind) },
|
||||
0,
|
||||
),
|
||||
};
|
||||
|
||||
|
|
@ -3792,10 +3797,10 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void {
|
|||
}
|
||||
|
||||
log.info("loading config-file path={s}", .{path});
|
||||
var buf_reader = std.io.bufferedReader(file.reader());
|
||||
const reader = buf_reader.reader();
|
||||
const Iter = cli.args.LineIterator(@TypeOf(reader));
|
||||
var iter: Iter = .{ .r = reader, .filepath = path };
|
||||
var buf: [2048]u8 = undefined;
|
||||
var file_reader = file.reader(&buf);
|
||||
const reader = &file_reader.interface;
|
||||
var iter: cli.args.LineIterator = .{ .r = reader, .filepath = path };
|
||||
try self.loadIter(alloc_gpa, &iter);
|
||||
try self.expandPaths(std.fs.path.dirname(path).?);
|
||||
}
|
||||
|
|
@ -3944,10 +3949,10 @@ fn loadTheme(self: *Config, theme: Theme) !void {
|
|||
errdefer new_config.deinit();
|
||||
|
||||
// Load our theme
|
||||
var buf_reader = std.io.bufferedReader(file.reader());
|
||||
const reader = buf_reader.reader();
|
||||
const Iter = cli.args.LineIterator(@TypeOf(reader));
|
||||
var iter: Iter = .{ .r = reader, .filepath = path };
|
||||
var buf: [2048]u8 = undefined;
|
||||
var file_reader = file.reader(&buf);
|
||||
const reader = &file_reader.interface;
|
||||
var iter: cli.args.LineIterator = .{ .r = reader, .filepath = path };
|
||||
try new_config.loadIter(alloc_gpa, &iter);
|
||||
|
||||
// Setup our replay to be conditional.
|
||||
|
|
@ -4190,7 +4195,7 @@ pub fn finalize(self: *Config) !void {
|
|||
if (self.@"quit-after-last-window-closed-delay") |duration| {
|
||||
if (duration.duration < 5 * std.time.ns_per_s) {
|
||||
log.warn(
|
||||
"quit-after-last-window-closed-delay is set to a very short value ({}), which might cause problems",
|
||||
"quit-after-last-window-closed-delay is set to a very short value ({f}), which might cause problems",
|
||||
.{duration},
|
||||
);
|
||||
}
|
||||
|
|
@ -4221,22 +4226,23 @@ pub fn parseManuallyHook(
|
|||
|
||||
// Build up the command. We don't clean this up because we take
|
||||
// ownership in our allocator.
|
||||
var command: std.ArrayList([:0]const u8) = .init(alloc);
|
||||
errdefer command.deinit();
|
||||
var command: std.ArrayList([:0]const u8) = .empty;
|
||||
errdefer command.deinit(alloc);
|
||||
|
||||
while (iter.next()) |param| {
|
||||
const copy = try alloc.dupeZ(u8, param);
|
||||
try self._replay_steps.append(alloc, .{ .arg = copy });
|
||||
try command.append(copy);
|
||||
try command.append(alloc, copy);
|
||||
}
|
||||
|
||||
if (command.items.len == 0) {
|
||||
try self._diagnostics.append(alloc, .{
|
||||
.location = try cli.Location.fromIter(iter, alloc),
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
alloc,
|
||||
"missing command after {s}",
|
||||
.{arg},
|
||||
0,
|
||||
),
|
||||
});
|
||||
|
||||
|
|
@ -4371,10 +4377,11 @@ pub fn addDiagnosticFmt(
|
|||
) Allocator.Error!void {
|
||||
const alloc = self._arena.?.allocator();
|
||||
try self._diagnostics.append(alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
alloc,
|
||||
fmt,
|
||||
args,
|
||||
0,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
|
@ -4892,7 +4899,7 @@ pub const Color = struct {
|
|||
}
|
||||
|
||||
/// Used by Formatter
|
||||
pub fn formatEntry(self: Color, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: Color, formatter: formatterpkg.EntryFormatter) !void {
|
||||
var buf: [128]u8 = undefined;
|
||||
try formatter.formatEntry(
|
||||
[]const u8,
|
||||
|
|
@ -4959,12 +4966,12 @@ pub const Color = struct {
|
|||
|
||||
test "formatConfig" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var color: Color = .{ .r = 10, .g = 11, .b = 12 };
|
||||
try color.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = #0a0b0c\n", buf.items);
|
||||
try color.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = #0a0b0c\n", buf.written());
|
||||
}
|
||||
|
||||
test "parseCLI with whitespace" {
|
||||
|
|
@ -4995,7 +5002,7 @@ pub const TerminalColor = union(enum) {
|
|||
}
|
||||
|
||||
/// Used by Formatter
|
||||
pub fn formatEntry(self: TerminalColor, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: TerminalColor, formatter: formatterpkg.EntryFormatter) !void {
|
||||
switch (self) {
|
||||
.color => try self.color.formatEntry(formatter),
|
||||
|
||||
|
|
@ -5030,12 +5037,12 @@ pub const TerminalColor = union(enum) {
|
|||
|
||||
test "formatConfig" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var sc: TerminalColor = .@"cell-foreground";
|
||||
try sc.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try testing.expectEqualSlices(u8, "a = cell-foreground\n", buf.items);
|
||||
try sc.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try testing.expectEqualSlices(u8, "a = cell-foreground\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -5051,7 +5058,7 @@ pub const BoldColor = union(enum) {
|
|||
}
|
||||
|
||||
/// Used by Formatter
|
||||
pub fn formatEntry(self: BoldColor, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: BoldColor, formatter: formatterpkg.EntryFormatter) !void {
|
||||
switch (self) {
|
||||
.color => try self.color.formatEntry(formatter),
|
||||
.bright => try formatter.formatEntry(
|
||||
|
|
@ -5082,12 +5089,12 @@ pub const BoldColor = union(enum) {
|
|||
|
||||
test "formatConfig" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var sc: BoldColor = .bright;
|
||||
try sc.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try testing.expectEqualSlices(u8, "a = bright\n", buf.items);
|
||||
try sc.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try testing.expectEqualSlices(u8, "a = bright\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -5174,8 +5181,7 @@ pub const ColorList = struct {
|
|||
// Build up the value of our config. Our buffer size should be
|
||||
// sized to contain all possible maximum values.
|
||||
var buf: [1024]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
var writer = fbs.writer();
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
for (self.colors.items, 0..) |color, i| {
|
||||
var color_buf: [128]u8 = undefined;
|
||||
const color_str = try color.formatBuf(&color_buf);
|
||||
|
|
@ -5185,7 +5191,7 @@ pub const ColorList = struct {
|
|||
|
||||
try formatter.formatEntry(
|
||||
[]const u8,
|
||||
fbs.getWritten(),
|
||||
writer.buffered(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -5214,7 +5220,7 @@ pub const ColorList = struct {
|
|||
|
||||
test "format" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -5223,8 +5229,8 @@ pub const ColorList = struct {
|
|||
|
||||
var p: Self = .{};
|
||||
try p.parseCLI(alloc, "black,white");
|
||||
try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = #000000,#ffffff\n", buf.items);
|
||||
try p.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = #000000,#ffffff\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -5285,7 +5291,7 @@ pub const Palette = struct {
|
|||
}
|
||||
|
||||
/// Used by Formatter
|
||||
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void {
|
||||
var buf: [128]u8 = undefined;
|
||||
for (0.., self.value) |k, v| {
|
||||
try formatter.formatEntry(
|
||||
|
|
@ -5340,12 +5346,12 @@ pub const Palette = struct {
|
|||
|
||||
test "formatConfig" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var list: Self = .{};
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = 0=#1d1f21\n", buf.items[0..14]);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = 0=#1d1f21\n", buf.written()[0..14]);
|
||||
}
|
||||
|
||||
test "parseCLI with whitespace" {
|
||||
|
|
@ -5439,7 +5445,7 @@ pub const RepeatableString = struct {
|
|||
}
|
||||
|
||||
/// Used by Formatter
|
||||
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void {
|
||||
// If no items, we want to render an empty field.
|
||||
if (self.list.items.len == 0) {
|
||||
try formatter.formatEntry(void, {});
|
||||
|
|
@ -5486,17 +5492,17 @@ pub const RepeatableString = struct {
|
|||
|
||||
test "formatConfig empty" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var list: Self = .{};
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = \n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = \n", buf.written());
|
||||
}
|
||||
|
||||
test "formatConfig single item" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -5505,13 +5511,13 @@ pub const RepeatableString = struct {
|
|||
|
||||
var list: Self = .{};
|
||||
try list.parseCLI(alloc, "A");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = A\n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = A\n", buf.written());
|
||||
}
|
||||
|
||||
test "formatConfig multiple items" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -5521,8 +5527,8 @@ pub const RepeatableString = struct {
|
|||
var list: Self = .{};
|
||||
try list.parseCLI(alloc, "A");
|
||||
try list.parseCLI(alloc, "B");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = A\na = B\n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = A\na = B\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -5638,7 +5644,7 @@ pub const RepeatableFontVariation = struct {
|
|||
|
||||
test "formatConfig single" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -5647,8 +5653,8 @@ pub const RepeatableFontVariation = struct {
|
|||
|
||||
var list: Self = .{};
|
||||
try list.parseCLI(alloc, "wght = 200");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = wght=200\n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = wght=200\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -6449,7 +6455,7 @@ pub const Keybinds = struct {
|
|||
}
|
||||
|
||||
/// Like formatEntry but has an option to include docs.
|
||||
pub fn formatEntryDocs(self: Keybinds, formatter: anytype, docs: bool) !void {
|
||||
pub fn formatEntryDocs(self: Keybinds, formatter: formatterpkg.EntryFormatter, docs: bool) !void {
|
||||
if (self.set.bindings.size == 0) {
|
||||
try formatter.formatEntry(void, {});
|
||||
return;
|
||||
|
|
@ -6478,14 +6484,14 @@ pub const Keybinds = struct {
|
|||
}
|
||||
}
|
||||
|
||||
var buffer_stream = std.io.fixedBufferStream(&buf);
|
||||
std.fmt.format(buffer_stream.writer(), "{}", .{k}) catch return error.OutOfMemory;
|
||||
try v.formatEntries(&buffer_stream, formatter);
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
writer.print("{f}", .{k}) catch return error.OutOfMemory;
|
||||
try v.formatEntries(&writer, formatter);
|
||||
}
|
||||
}
|
||||
|
||||
/// Used by Formatter
|
||||
pub fn formatEntry(self: Keybinds, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: Keybinds, formatter: formatterpkg.EntryFormatter) !void {
|
||||
try self.formatEntryDocs(formatter, false);
|
||||
}
|
||||
|
||||
|
|
@ -6502,7 +6508,7 @@ pub const Keybinds = struct {
|
|||
|
||||
test "formatConfig single" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -6511,14 +6517,14 @@ pub const Keybinds = struct {
|
|||
|
||||
var list: Keybinds = .{};
|
||||
try list.parseCLI(alloc, "shift+a=csi:hello");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = shift+a=csi:hello\n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = shift+a=csi:hello\n", buf.written());
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/ghostty-org/ghostty/issues/2734
|
||||
test "formatConfig multiple items" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -6528,7 +6534,7 @@ pub const Keybinds = struct {
|
|||
var list: Keybinds = .{};
|
||||
try list.parseCLI(alloc, "ctrl+z>1=goto_tab:1");
|
||||
try list.parseCLI(alloc, "ctrl+z>2=goto_tab:2");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("keybind", buf.writer()));
|
||||
try list.formatEntry(formatterpkg.entryFormatter("keybind", &buf.writer));
|
||||
|
||||
// Note they turn into translated keys because they match
|
||||
// their ASCII mapping.
|
||||
|
|
@ -6537,12 +6543,12 @@ pub const Keybinds = struct {
|
|||
\\keybind = ctrl+z>1=goto_tab:1
|
||||
\\
|
||||
;
|
||||
try std.testing.expectEqualStrings(want, buf.items);
|
||||
try std.testing.expectEqualStrings(want, buf.written());
|
||||
}
|
||||
|
||||
test "formatConfig multiple items nested" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -6554,7 +6560,7 @@ pub const Keybinds = struct {
|
|||
try list.parseCLI(alloc, "ctrl+a>ctrl+b>w=close_window");
|
||||
try list.parseCLI(alloc, "ctrl+a>ctrl+c>t=new_tab");
|
||||
try list.parseCLI(alloc, "ctrl+b>ctrl+d>a=previous_tab");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
|
||||
// NB: This does not currently retain the order of the keybinds.
|
||||
const want =
|
||||
|
|
@ -6564,7 +6570,7 @@ pub const Keybinds = struct {
|
|||
\\a = ctrl+b>ctrl+d>a=previous_tab
|
||||
\\
|
||||
;
|
||||
try std.testing.expectEqualStrings(want, buf.items);
|
||||
try std.testing.expectEqualStrings(want, buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -6790,7 +6796,7 @@ pub const RepeatableCodepointMap = struct {
|
|||
|
||||
test "formatConfig single" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -6799,13 +6805,13 @@ pub const RepeatableCodepointMap = struct {
|
|||
|
||||
var list: Self = .{};
|
||||
try list.parseCLI(alloc, "U+ABCD=Comic Sans");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = U+ABCD=Comic Sans\n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = U+ABCD=Comic Sans\n", buf.written());
|
||||
}
|
||||
|
||||
test "formatConfig range" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -6814,13 +6820,13 @@ pub const RepeatableCodepointMap = struct {
|
|||
|
||||
var list: Self = .{};
|
||||
try list.parseCLI(alloc, "U+0001 - U+0005=Verdana");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = U+0001-U+0005=Verdana\n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = U+0001-U+0005=Verdana\n", buf.written());
|
||||
}
|
||||
|
||||
test "formatConfig multiple" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -6829,12 +6835,12 @@ pub const RepeatableCodepointMap = struct {
|
|||
|
||||
var list: Self = .{};
|
||||
try list.parseCLI(alloc, "U+0006-U+0009, U+ABCD=Courier");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8,
|
||||
\\a = U+0006-U+0009=Courier
|
||||
\\a = U+ABCD=Courier
|
||||
\\
|
||||
, buf.items);
|
||||
, buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -6886,7 +6892,7 @@ pub const FontStyle = union(enum) {
|
|||
}
|
||||
|
||||
/// Used by Formatter
|
||||
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void {
|
||||
switch (self) {
|
||||
.default, .false => try formatter.formatEntry(
|
||||
[]const u8,
|
||||
|
|
@ -6918,7 +6924,7 @@ pub const FontStyle = union(enum) {
|
|||
|
||||
test "formatConfig default" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -6927,13 +6933,13 @@ pub const FontStyle = union(enum) {
|
|||
|
||||
var p: Self = .{ .default = {} };
|
||||
try p.parseCLI(alloc, "default");
|
||||
try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = default\n", buf.items);
|
||||
try p.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = default\n", buf.written());
|
||||
}
|
||||
|
||||
test "formatConfig false" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -6942,13 +6948,13 @@ pub const FontStyle = union(enum) {
|
|||
|
||||
var p: Self = .{ .default = {} };
|
||||
try p.parseCLI(alloc, "false");
|
||||
try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = false\n", buf.items);
|
||||
try p.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = false\n", buf.written());
|
||||
}
|
||||
|
||||
test "formatConfig named" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -6957,8 +6963,8 @@ pub const FontStyle = union(enum) {
|
|||
|
||||
var p: Self = .{ .default = {} };
|
||||
try p.parseCLI(alloc, "bold");
|
||||
try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = bold\n", buf.items);
|
||||
try p.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = bold\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -7018,7 +7024,7 @@ pub const RepeatableLink = struct {
|
|||
}
|
||||
|
||||
/// Used by Formatter
|
||||
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void {
|
||||
// This currently can't be set so we don't format anything.
|
||||
_ = self;
|
||||
_ = formatter;
|
||||
|
|
@ -7128,7 +7134,10 @@ pub const RepeatableCommand = struct {
|
|||
}
|
||||
|
||||
/// Used by Formatter
|
||||
pub fn formatEntry(self: RepeatableCommand, formatter: anytype) !void {
|
||||
pub fn formatEntry(
|
||||
self: RepeatableCommand,
|
||||
formatter: formatterpkg.EntryFormatter,
|
||||
) !void {
|
||||
if (self.value.items.len == 0) {
|
||||
try formatter.formatEntry(void, {});
|
||||
return;
|
||||
|
|
@ -7136,22 +7145,23 @@ pub const RepeatableCommand = struct {
|
|||
|
||||
for (self.value.items) |item| {
|
||||
var buf: [4096]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
var writer = fbs.writer();
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
|
||||
writer.writeAll("title:\"") catch return error.OutOfMemory;
|
||||
std.zig.stringEscape(item.title, "", .{}, writer) catch return error.OutOfMemory;
|
||||
writer.writeAll("\"") catch return error.OutOfMemory;
|
||||
writer.print(
|
||||
"title:\"{f}\"",
|
||||
.{std.zig.fmtString(item.title)},
|
||||
) catch return error.OutOfMemory;
|
||||
|
||||
if (item.description.len > 0) {
|
||||
writer.writeAll(",description:\"") catch return error.OutOfMemory;
|
||||
std.zig.stringEscape(item.description, "", .{}, writer) catch return error.OutOfMemory;
|
||||
writer.writeAll("\"") catch return error.OutOfMemory;
|
||||
writer.print(
|
||||
",description:\"{f}\"",
|
||||
.{std.zig.fmtString(item.description)},
|
||||
) catch return error.OutOfMemory;
|
||||
}
|
||||
|
||||
writer.print(",action:\"{}\"", .{item.action}) catch return error.OutOfMemory;
|
||||
writer.print(",action:\"{f}\"", .{item.action}) catch return error.OutOfMemory;
|
||||
|
||||
try formatter.formatEntry([]const u8, fbs.getWritten());
|
||||
try formatter.formatEntry([]const u8, writer.buffered());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7197,17 +7207,17 @@ pub const RepeatableCommand = struct {
|
|||
|
||||
test "RepeatableCommand formatConfig empty" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var list: RepeatableCommand = .{};
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = \n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = \n", buf.written());
|
||||
}
|
||||
|
||||
test "RepeatableCommand formatConfig single item" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -7216,13 +7226,13 @@ pub const RepeatableCommand = struct {
|
|||
|
||||
var list: RepeatableCommand = .{};
|
||||
try list.parseCLI(alloc, "title:Bobr, action:text:Bober");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = title:\"Bobr\",action:\"text:Bober\"\n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = title:\"Bobr\",action:\"text:Bober\"\n", buf.written());
|
||||
}
|
||||
|
||||
test "RepeatableCommand formatConfig multiple items" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -7232,14 +7242,12 @@ pub const RepeatableCommand = struct {
|
|||
var list: RepeatableCommand = .{};
|
||||
try list.parseCLI(alloc, "title:Bobr, action:text:kurwa");
|
||||
try list.parseCLI(alloc, "title:Ja, description: pierdole, action:text:jakie bydle");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = title:\"Bobr\",action:\"text:kurwa\"\na = title:\"Ja\",description:\"pierdole\",action:\"text:jakie bydle\"\n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = title:\"Bobr\",action:\"text:kurwa\"\na = title:\"Ja\",description:\"pierdole\",action:\"text:jakie bydle\"\n", buf.written());
|
||||
}
|
||||
|
||||
test "RepeatableCommand parseCLI commas" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
|
@ -7455,14 +7463,14 @@ pub const MouseScrollMultiplier = struct {
|
|||
}
|
||||
|
||||
/// Used by Formatter
|
||||
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||
var buf: [32]u8 = undefined;
|
||||
const formatted = std.fmt.bufPrint(
|
||||
&buf,
|
||||
pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void {
|
||||
var buf: [4096]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
writer.print(
|
||||
"precision:{d},discrete:{d}",
|
||||
.{ self.precision, self.discrete },
|
||||
) catch return error.OutOfMemory;
|
||||
try formatter.formatEntry([]const u8, formatted);
|
||||
try formatter.formatEntry([]const u8, writer.buffered());
|
||||
}
|
||||
|
||||
test "parse" {
|
||||
|
|
@ -7505,12 +7513,12 @@ pub const MouseScrollMultiplier = struct {
|
|||
|
||||
test "format entry MouseScrollMultiplier" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var args: Self = .{ .precision = 1.5, .discrete = 2.5 };
|
||||
try args.formatEntry(formatterpkg.entryFormatter("mouse-scroll-multiplier", buf.writer()));
|
||||
try testing.expectEqualSlices(u8, "mouse-scroll-multiplier = precision:1.5,discrete:2.5\n", buf.items);
|
||||
try args.formatEntry(formatterpkg.entryFormatter("mouse-scroll-multiplier", &buf.writer));
|
||||
try testing.expectEqualSlices(u8, "mouse-scroll-multiplier = precision:1.5,discrete:2.5\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -7627,7 +7635,7 @@ pub const QuickTerminalSize = struct {
|
|||
return error.MissingUnit;
|
||||
}
|
||||
|
||||
fn format(self: Size, writer: anytype) !void {
|
||||
fn format(self: Size, writer: *std.Io.Writer) !void {
|
||||
switch (self) {
|
||||
.percentage => |v| try writer.print("{d}%", .{v}),
|
||||
.pixels => |v| try writer.print("{}px", .{v}),
|
||||
|
|
@ -7745,20 +7753,19 @@ pub const QuickTerminalSize = struct {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn formatEntry(self: QuickTerminalSize, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: QuickTerminalSize, formatter: formatterpkg.EntryFormatter) !void {
|
||||
const primary = self.primary orelse return;
|
||||
|
||||
var buf: [4096]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
const writer = fbs.writer();
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
|
||||
primary.format(writer) catch return error.OutOfMemory;
|
||||
primary.format(&writer) catch return error.OutOfMemory;
|
||||
if (self.secondary) |secondary| {
|
||||
writer.writeByte(',') catch return error.OutOfMemory;
|
||||
secondary.format(writer) catch return error.OutOfMemory;
|
||||
secondary.format(&writer) catch return error.OutOfMemory;
|
||||
}
|
||||
|
||||
try formatter.formatEntry([]const u8, fbs.getWritten());
|
||||
try formatter.formatEntry([]const u8, writer.buffered());
|
||||
}
|
||||
|
||||
test "parse QuickTerminalSize" {
|
||||
|
|
@ -8318,15 +8325,17 @@ pub const Duration = struct {
|
|||
return if (value) |v| .{ .duration = v } else error.ValueRequired;
|
||||
}
|
||||
|
||||
pub fn formatEntry(self: Duration, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: Duration, formatter: formatterpkg.EntryFormatter) !void {
|
||||
var buf: [64]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
const writer = fbs.writer();
|
||||
try self.format("", .{}, writer);
|
||||
try formatter.formatEntry([]const u8, fbs.getWritten());
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
try self.format(&writer);
|
||||
try formatter.formatEntry([]const u8, writer.buffered());
|
||||
}
|
||||
|
||||
pub fn format(self: Duration, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||
pub fn format(
|
||||
self: Duration,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
var value = self.duration;
|
||||
var i: usize = 0;
|
||||
for (units) |unit| {
|
||||
|
|
@ -8393,7 +8402,7 @@ pub const WindowPadding = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void {
|
||||
var buf: [128]u8 = undefined;
|
||||
if (self.top_left == self.bottom_right) {
|
||||
try formatter.formatEntry(
|
||||
|
|
@ -8555,7 +8564,7 @@ test "test format" {
|
|||
inline for (Duration.units) |unit| {
|
||||
const d: Duration = .{ .duration = unit.factor };
|
||||
var actual_buf: [16]u8 = undefined;
|
||||
const actual = try std.fmt.bufPrint(&actual_buf, "{}", .{d});
|
||||
const actual = try std.fmt.bufPrint(&actual_buf, "{f}", .{d});
|
||||
var expected_buf: [16]u8 = undefined;
|
||||
const expected = if (!std.mem.eql(u8, unit.name, "us"))
|
||||
try std.fmt.bufPrint(&expected_buf, "1{s}", .{unit.name})
|
||||
|
|
@ -8566,12 +8575,12 @@ test "test format" {
|
|||
}
|
||||
|
||||
test "test entryFormatter" {
|
||||
var buf = std.ArrayList(u8).init(std.testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(std.testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var p: Duration = .{ .duration = std.math.maxInt(u64) };
|
||||
try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualStrings("a = 584y 49w 23h 34m 33s 709ms 551µs 615ns\n", buf.items);
|
||||
try p.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualStrings("a = 584y 49w 23h 34m 33s 709ms 551µs 615ns\n", buf.written());
|
||||
}
|
||||
|
||||
const TestIterator = struct {
|
||||
|
|
@ -8681,15 +8690,20 @@ test "clone can then change conditional state" {
|
|||
// Setup our test theme
|
||||
var td = try internal_os.TempDir.init();
|
||||
defer td.deinit();
|
||||
var buf: [4096]u8 = undefined;
|
||||
{
|
||||
var file = try td.dir.createFile("theme_light", .{});
|
||||
defer file.close();
|
||||
try file.writer().writeAll(@embedFile("testdata/theme_light"));
|
||||
var writer = file.writer(&buf);
|
||||
try writer.interface.writeAll(@embedFile("testdata/theme_light"));
|
||||
try writer.end();
|
||||
}
|
||||
{
|
||||
var file = try td.dir.createFile("theme_dark", .{});
|
||||
defer file.close();
|
||||
try file.writer().writeAll(@embedFile("testdata/theme_dark"));
|
||||
var writer = file.writer(&buf);
|
||||
try writer.interface.writeAll(@embedFile("testdata/theme_dark"));
|
||||
try writer.end();
|
||||
}
|
||||
var light_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const light = try td.dir.realpath("theme_light", &light_buf);
|
||||
|
|
@ -8815,10 +8829,13 @@ test "theme loading" {
|
|||
// Setup our test theme
|
||||
var td = try internal_os.TempDir.init();
|
||||
defer td.deinit();
|
||||
var buf: [4096]u8 = undefined;
|
||||
{
|
||||
var file = try td.dir.createFile("theme", .{});
|
||||
defer file.close();
|
||||
try file.writer().writeAll(@embedFile("testdata/theme_simple"));
|
||||
var writer = file.writer(&buf);
|
||||
try writer.interface.writeAll(@embedFile("testdata/theme_simple"));
|
||||
try writer.end();
|
||||
}
|
||||
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const path = try td.dir.realpath("theme", &path_buf);
|
||||
|
|
@ -8851,10 +8868,13 @@ test "theme loading preserves conditional state" {
|
|||
// Setup our test theme
|
||||
var td = try internal_os.TempDir.init();
|
||||
defer td.deinit();
|
||||
var buf: [4096]u8 = undefined;
|
||||
{
|
||||
var file = try td.dir.createFile("theme", .{});
|
||||
defer file.close();
|
||||
try file.writer().writeAll(@embedFile("testdata/theme_simple"));
|
||||
var writer = file.writer(&buf);
|
||||
try writer.interface.writeAll(@embedFile("testdata/theme_simple"));
|
||||
try writer.end();
|
||||
}
|
||||
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const path = try td.dir.realpath("theme", &path_buf);
|
||||
|
|
@ -8881,10 +8901,13 @@ test "theme priority is lower than config" {
|
|||
// Setup our test theme
|
||||
var td = try internal_os.TempDir.init();
|
||||
defer td.deinit();
|
||||
var buf: [4096]u8 = undefined;
|
||||
{
|
||||
var file = try td.dir.createFile("theme", .{});
|
||||
defer file.close();
|
||||
try file.writer().writeAll(@embedFile("testdata/theme_simple"));
|
||||
var writer = file.writer(&buf);
|
||||
try writer.interface.writeAll(@embedFile("testdata/theme_simple"));
|
||||
try writer.end();
|
||||
}
|
||||
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const path = try td.dir.realpath("theme", &path_buf);
|
||||
|
|
@ -8915,15 +8938,20 @@ test "theme loading correct light/dark" {
|
|||
// Setup our test theme
|
||||
var td = try internal_os.TempDir.init();
|
||||
defer td.deinit();
|
||||
var buf: [4096]u8 = undefined;
|
||||
{
|
||||
var file = try td.dir.createFile("theme_light", .{});
|
||||
defer file.close();
|
||||
try file.writer().writeAll(@embedFile("testdata/theme_light"));
|
||||
var writer = file.writer(&buf);
|
||||
try writer.interface.writeAll(@embedFile("testdata/theme_light"));
|
||||
try writer.end();
|
||||
}
|
||||
{
|
||||
var file = try td.dir.createFile("theme_dark", .{});
|
||||
defer file.close();
|
||||
try file.writer().writeAll(@embedFile("testdata/theme_dark"));
|
||||
var writer = file.writer(&buf);
|
||||
try writer.interface.writeAll(@embedFile("testdata/theme_dark"));
|
||||
try writer.end();
|
||||
}
|
||||
var light_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const light = try td.dir.realpath("theme_light", &light_buf);
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ pub fn equal(self: RepeatableStringMap, other: RepeatableStringMap) bool {
|
|||
}
|
||||
|
||||
/// Used by formatter
|
||||
pub fn formatEntry(self: RepeatableStringMap, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: RepeatableStringMap, formatter: formatterpkg.EntryFormatter) !void {
|
||||
// If no items, we want to render an empty field.
|
||||
if (self.map.count() == 0) {
|
||||
try formatter.formatEntry(void, {});
|
||||
|
|
@ -146,12 +146,12 @@ test "RepeatableStringMap: parseCLI" {
|
|||
|
||||
test "RepeatableStringMap: formatConfig empty" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var list: RepeatableStringMap = .{};
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = \n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = \n", buf.written());
|
||||
}
|
||||
|
||||
test "RepeatableStringMap: formatConfig single item" {
|
||||
|
|
@ -162,20 +162,20 @@ test "RepeatableStringMap: formatConfig single item" {
|
|||
const alloc = arena.allocator();
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
var map: RepeatableStringMap = .{};
|
||||
try map.parseCLI(alloc, "A=B");
|
||||
try map.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = A=B\n", buf.items);
|
||||
try map.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = A=B\n", buf.written());
|
||||
}
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
var map: RepeatableStringMap = .{};
|
||||
try map.parseCLI(alloc, " A = B ");
|
||||
try map.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = A=B\n", buf.items);
|
||||
try map.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = A=B\n", buf.written());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -187,12 +187,12 @@ test "RepeatableStringMap: formatConfig multiple items" {
|
|||
const alloc = arena.allocator();
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
var list: RepeatableStringMap = .{};
|
||||
try list.parseCLI(alloc, "A=B");
|
||||
try list.parseCLI(alloc, "B = C");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = A=B\na = B=C\n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = A=B\na = B=C\n", buf.written());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,21 +166,20 @@ pub const Command = union(enum) {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void {
|
||||
switch (self) {
|
||||
.shell => |v| try formatter.formatEntry([]const u8, v),
|
||||
|
||||
.direct => |v| {
|
||||
var buf: [4096]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
const writer = fbs.writer();
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
writer.writeAll("direct:") catch return error.OutOfMemory;
|
||||
for (v) |arg| {
|
||||
writer.writeAll(arg) catch return error.OutOfMemory;
|
||||
writer.writeByte(' ') catch return error.OutOfMemory;
|
||||
}
|
||||
|
||||
const written = fbs.getWritten();
|
||||
const written = writer.buffered();
|
||||
try formatter.formatEntry(
|
||||
[]const u8,
|
||||
written[0..@intCast(written.len - 1)],
|
||||
|
|
@ -292,13 +291,13 @@ pub const Command = union(enum) {
|
|||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
var v: Self = undefined;
|
||||
try v.parseCLI(alloc, "echo hello");
|
||||
try v.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = echo hello\n", buf.items);
|
||||
try v.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = echo hello\n", buf.written());
|
||||
}
|
||||
|
||||
test "Command: formatConfig direct" {
|
||||
|
|
@ -307,13 +306,13 @@ pub const Command = union(enum) {
|
|||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
var v: Self = undefined;
|
||||
try v.parseCLI(alloc, "direct: echo hello");
|
||||
try v.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = direct:echo hello\n", buf.items);
|
||||
try v.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = direct:echo hello\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@ fn configPath(alloc_arena: Allocator) ![]const u8 {
|
|||
/// Returns a const list of possible paths the main config file could be
|
||||
/// in for the current OS.
|
||||
fn configPathCandidates(alloc_arena: Allocator) ![]const []const u8 {
|
||||
var paths = try std.ArrayList([]const u8).initCapacity(alloc_arena, 2);
|
||||
errdefer paths.deinit();
|
||||
var paths: std.ArrayList([]const u8) = try .initCapacity(alloc_arena, 2);
|
||||
errdefer paths.deinit(alloc_arena);
|
||||
|
||||
if (comptime builtin.os.tag == .macos) {
|
||||
paths.appendAssumeCapacity(try internal_os.macos.appSupportDir(
|
||||
|
|
|
|||
|
|
@ -8,38 +8,36 @@ const Key = @import("key.zig").Key;
|
|||
/// Returns a single entry formatter for the given field name and writer.
|
||||
pub fn entryFormatter(
|
||||
name: []const u8,
|
||||
writer: anytype,
|
||||
) EntryFormatter(@TypeOf(writer)) {
|
||||
writer: *std.Io.Writer,
|
||||
) EntryFormatter {
|
||||
return .{ .name = name, .writer = writer };
|
||||
}
|
||||
|
||||
/// The entry formatter type for a given writer.
|
||||
pub fn EntryFormatter(comptime WriterType: type) type {
|
||||
return struct {
|
||||
name: []const u8,
|
||||
writer: WriterType,
|
||||
pub const EntryFormatter = struct {
|
||||
name: []const u8,
|
||||
writer: *std.Io.Writer,
|
||||
|
||||
pub fn formatEntry(
|
||||
self: @This(),
|
||||
comptime T: type,
|
||||
value: T,
|
||||
) !void {
|
||||
return formatter.formatEntry(
|
||||
T,
|
||||
self.name,
|
||||
value,
|
||||
self.writer,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
pub fn formatEntry(
|
||||
self: @This(),
|
||||
comptime T: type,
|
||||
value: T,
|
||||
) !void {
|
||||
return formatter.formatEntry(
|
||||
T,
|
||||
self.name,
|
||||
value,
|
||||
self.writer,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/// Format a single type with the given name and value.
|
||||
pub fn formatEntry(
|
||||
comptime T: type,
|
||||
name: []const u8,
|
||||
value: T,
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
switch (@typeInfo(T)) {
|
||||
.bool, .int => {
|
||||
|
|
@ -53,7 +51,7 @@ pub fn formatEntry(
|
|||
},
|
||||
|
||||
.@"enum" => {
|
||||
try writer.print("{s} = {s}\n", .{ name, @tagName(value) });
|
||||
try writer.print("{s} = {t}\n", .{ name, value });
|
||||
return;
|
||||
},
|
||||
|
||||
|
|
@ -143,19 +141,14 @@ pub const FileFormatter = struct {
|
|||
/// Implements std.fmt so it can be used directly with std.fmt.
|
||||
pub fn format(
|
||||
self: FileFormatter,
|
||||
comptime layout: []const u8,
|
||||
opts: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
writer: *std.Io.Writer,
|
||||
) std.Io.Writer.Error!void {
|
||||
@setEvalBranchQuota(10_000);
|
||||
|
||||
_ = layout;
|
||||
_ = opts;
|
||||
|
||||
// If we're change-tracking then we need the default config to
|
||||
// compare against.
|
||||
var default: ?Config = if (self.changed)
|
||||
try .default(self.alloc)
|
||||
Config.default(self.alloc) catch return error.WriteFailed
|
||||
else
|
||||
null;
|
||||
defer if (default) |*v| v.deinit();
|
||||
|
|
@ -179,12 +172,12 @@ pub const FileFormatter = struct {
|
|||
}
|
||||
}
|
||||
|
||||
try formatEntry(
|
||||
formatEntry(
|
||||
field.type,
|
||||
field.name,
|
||||
value,
|
||||
writer,
|
||||
);
|
||||
) catch return error.WriteFailed;
|
||||
|
||||
if (do_docs) try writer.print("\n", .{});
|
||||
}
|
||||
|
|
@ -198,7 +191,7 @@ test "format default config" {
|
|||
var cfg = try Config.default(alloc);
|
||||
defer cfg.deinit();
|
||||
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
// We just make sure this works without errors. We aren't asserting output.
|
||||
|
|
@ -206,9 +199,9 @@ test "format default config" {
|
|||
.alloc = alloc,
|
||||
.config = &cfg,
|
||||
};
|
||||
try std.fmt.format(buf.writer(), "{}", .{fmt});
|
||||
try fmt.format(&buf.writer);
|
||||
|
||||
//std.log.warn("{s}", .{buf.items});
|
||||
//std.log.warn("{s}", .{buf.written()});
|
||||
}
|
||||
|
||||
test "format default config changed" {
|
||||
|
|
@ -218,7 +211,7 @@ test "format default config changed" {
|
|||
defer cfg.deinit();
|
||||
cfg.@"font-size" = 42;
|
||||
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
// We just make sure this works without errors. We aren't asserting output.
|
||||
|
|
@ -227,26 +220,26 @@ test "format default config changed" {
|
|||
.config = &cfg,
|
||||
.changed = true,
|
||||
};
|
||||
try std.fmt.format(buf.writer(), "{}", .{fmt});
|
||||
try fmt.format(&buf.writer);
|
||||
|
||||
//std.log.warn("{s}", .{buf.items});
|
||||
//std.log.warn("{s}", .{buf.written()});
|
||||
}
|
||||
|
||||
test "formatEntry bool" {
|
||||
const testing = std.testing;
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
try formatEntry(bool, "a", true, buf.writer());
|
||||
try testing.expectEqualStrings("a = true\n", buf.items);
|
||||
try formatEntry(bool, "a", true, &buf.writer);
|
||||
try testing.expectEqualStrings("a = true\n", buf.written());
|
||||
}
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
try formatEntry(bool, "a", false, buf.writer());
|
||||
try testing.expectEqualStrings("a = false\n", buf.items);
|
||||
try formatEntry(bool, "a", false, &buf.writer);
|
||||
try testing.expectEqualStrings("a = false\n", buf.written());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,10 +247,10 @@ test "formatEntry int" {
|
|||
const testing = std.testing;
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
try formatEntry(u8, "a", 123, buf.writer());
|
||||
try testing.expectEqualStrings("a = 123\n", buf.items);
|
||||
try formatEntry(u8, "a", 123, &buf.writer);
|
||||
try testing.expectEqualStrings("a = 123\n", buf.written());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -265,10 +258,10 @@ test "formatEntry float" {
|
|||
const testing = std.testing;
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
try formatEntry(f64, "a", 0.7, buf.writer());
|
||||
try testing.expectEqualStrings("a = 0.7\n", buf.items);
|
||||
try formatEntry(f64, "a", 0.7, &buf.writer);
|
||||
try testing.expectEqualStrings("a = 0.7\n", buf.written());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -277,10 +270,10 @@ test "formatEntry enum" {
|
|||
const Enum = enum { one, two, three };
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
try formatEntry(Enum, "a", .two, buf.writer());
|
||||
try testing.expectEqualStrings("a = two\n", buf.items);
|
||||
try formatEntry(Enum, "a", .two, &buf.writer);
|
||||
try testing.expectEqualStrings("a = two\n", buf.written());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -288,10 +281,10 @@ test "formatEntry void" {
|
|||
const testing = std.testing;
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
try formatEntry(void, "a", {}, buf.writer());
|
||||
try testing.expectEqualStrings("a = \n", buf.items);
|
||||
try formatEntry(void, "a", {}, &buf.writer);
|
||||
try testing.expectEqualStrings("a = \n", buf.written());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -299,17 +292,17 @@ test "formatEntry optional" {
|
|||
const testing = std.testing;
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
try formatEntry(?bool, "a", null, buf.writer());
|
||||
try testing.expectEqualStrings("a = \n", buf.items);
|
||||
try formatEntry(?bool, "a", null, &buf.writer);
|
||||
try testing.expectEqualStrings("a = \n", buf.written());
|
||||
}
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
try formatEntry(?bool, "a", false, buf.writer());
|
||||
try testing.expectEqualStrings("a = false\n", buf.items);
|
||||
try formatEntry(?bool, "a", false, &buf.writer);
|
||||
try testing.expectEqualStrings("a = false\n", buf.written());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -317,10 +310,10 @@ test "formatEntry string" {
|
|||
const testing = std.testing;
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
try formatEntry([]const u8, "a", "hello", buf.writer());
|
||||
try testing.expectEqualStrings("a = hello\n", buf.items);
|
||||
try formatEntry([]const u8, "a", "hello", &buf.writer);
|
||||
try testing.expectEqualStrings("a = hello\n", buf.written());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -332,9 +325,9 @@ test "formatEntry packed struct" {
|
|||
};
|
||||
|
||||
{
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
try formatEntry(Value, "a", .{}, buf.writer());
|
||||
try testing.expectEqualStrings("a = one,no-two\n", buf.items);
|
||||
try formatEntry(Value, "a", .{}, &buf.writer);
|
||||
try testing.expectEqualStrings("a = one,no-two\n", buf.written());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,10 +94,9 @@ pub const ReadableIO = union(enum) {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void {
|
||||
var buf: [4096]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&buf);
|
||||
const writer = fbs.writer();
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
switch (self) {
|
||||
inline else => |v, tag| {
|
||||
writer.writeAll(@tagName(tag)) catch return error.OutOfMemory;
|
||||
|
|
@ -106,10 +105,9 @@ pub const ReadableIO = union(enum) {
|
|||
},
|
||||
}
|
||||
|
||||
const written = fbs.getWritten();
|
||||
try formatter.formatEntry(
|
||||
[]const u8,
|
||||
written,
|
||||
writer.buffered(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -144,13 +142,13 @@ pub const ReadableIO = union(enum) {
|
|||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var buf = std.ArrayList(u8).init(alloc);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
|
||||
var v: Self = undefined;
|
||||
try v.parseCLI(alloc, "raw:foo");
|
||||
try v.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = raw:foo\n", buf.items);
|
||||
try v.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = raw:foo\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -222,7 +220,7 @@ pub const RepeatableReadableIO = struct {
|
|||
/// Used by Formatter
|
||||
pub fn formatEntry(
|
||||
self: Self,
|
||||
formatter: anytype,
|
||||
formatter: formatterpkg.EntryFormatter,
|
||||
) !void {
|
||||
if (self.list.items.len == 0) {
|
||||
try formatter.formatEntry(void, {});
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ pub const Path = union(enum) {
|
|||
}
|
||||
|
||||
/// Used by formatter.
|
||||
pub fn formatEntry(self: *const Path, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: *const Path, formatter: formatterpkg.EntryFormatter) !void {
|
||||
var buf: [std.fs.max_path_bytes + 1]u8 = undefined;
|
||||
const value = switch (self.*) {
|
||||
.optional => |path| std.fmt.bufPrint(
|
||||
|
|
@ -154,10 +154,11 @@ pub const Path = union(enum) {
|
|||
&buf,
|
||||
) catch |err| {
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"error expanding home directory for path {s}: {}",
|
||||
.{ path, err },
|
||||
0,
|
||||
),
|
||||
});
|
||||
|
||||
|
|
@ -194,10 +195,11 @@ pub const Path = union(enum) {
|
|||
}
|
||||
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"error resolving file path {s}: {}",
|
||||
.{ path, err },
|
||||
0,
|
||||
),
|
||||
});
|
||||
|
||||
|
|
@ -306,7 +308,7 @@ pub const Path = union(enum) {
|
|||
|
||||
test "formatConfig single item" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -315,13 +317,13 @@ pub const Path = union(enum) {
|
|||
|
||||
var item: Path = undefined;
|
||||
try item.parseCLI(alloc, "A");
|
||||
try item.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = A\n", buf.items);
|
||||
try item.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = A\n", buf.written());
|
||||
}
|
||||
|
||||
test "formatConfig multiple items" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -331,8 +333,8 @@ pub const Path = union(enum) {
|
|||
var item: Path = undefined;
|
||||
try item.parseCLI(alloc, "A");
|
||||
try item.parseCLI(alloc, "?B");
|
||||
try item.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = ?B\n", buf.items);
|
||||
try item.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = ?B\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -382,7 +384,7 @@ pub const RepeatablePath = struct {
|
|||
}
|
||||
|
||||
/// Used by Formatter
|
||||
pub fn formatEntry(self: RepeatablePath, formatter: anytype) !void {
|
||||
pub fn formatEntry(self: RepeatablePath, formatter: formatterpkg.EntryFormatter) !void {
|
||||
if (self.value.items.len == 0) {
|
||||
try formatter.formatEntry(void, {});
|
||||
return;
|
||||
|
|
@ -453,17 +455,17 @@ pub const RepeatablePath = struct {
|
|||
|
||||
test "formatConfig empty" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var list: RepeatablePath = .{};
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = \n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = \n", buf.written());
|
||||
}
|
||||
|
||||
test "formatConfig single item" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -472,13 +474,13 @@ pub const RepeatablePath = struct {
|
|||
|
||||
var list: RepeatablePath = .{};
|
||||
try list.parseCLI(alloc, "A");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = A\n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = A\n", buf.written());
|
||||
}
|
||||
|
||||
test "formatConfig multiple items" {
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
|
|
@ -488,7 +490,7 @@ pub const RepeatablePath = struct {
|
|||
var list: RepeatablePath = .{};
|
||||
try list.parseCLI(alloc, "A");
|
||||
try list.parseCLI(alloc, "?B");
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = A\na = ?B\n", buf.items);
|
||||
try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = A\na = ?B\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -125,10 +125,11 @@ pub fn open(
|
|||
) orelse return null;
|
||||
const stat = file.stat() catch |err| {
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"not reading theme from \"{s}\": {}",
|
||||
.{ theme, err },
|
||||
0,
|
||||
),
|
||||
});
|
||||
return null;
|
||||
|
|
@ -137,10 +138,11 @@ pub fn open(
|
|||
.file => {},
|
||||
else => {
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"not reading theme from \"{s}\": it is a {s}",
|
||||
.{ theme, @tagName(stat.kind) },
|
||||
0,
|
||||
),
|
||||
});
|
||||
return null;
|
||||
|
|
@ -152,10 +154,11 @@ pub fn open(
|
|||
const basename = std.fs.path.basename(theme);
|
||||
if (!std.mem.eql(u8, theme, basename)) {
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"theme \"{s}\" cannot include path separators unless it is an absolute path",
|
||||
.{theme},
|
||||
0,
|
||||
),
|
||||
});
|
||||
return null;
|
||||
|
|
@ -170,10 +173,11 @@ pub fn open(
|
|||
if (cwd.openFile(path, .{})) |file| {
|
||||
const stat = file.stat() catch |err| {
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"not reading theme from \"{s}\": {}",
|
||||
.{ theme, err },
|
||||
0,
|
||||
),
|
||||
});
|
||||
return null;
|
||||
|
|
@ -182,10 +186,11 @@ pub fn open(
|
|||
.file => {},
|
||||
else => {
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"not reading theme from \"{s}\": it is a {s}",
|
||||
.{ theme, @tagName(stat.kind) },
|
||||
0,
|
||||
),
|
||||
});
|
||||
return null;
|
||||
|
|
@ -202,10 +207,11 @@ pub fn open(
|
|||
// Anything else is an error we log and give up on.
|
||||
else => {
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"failed to load theme \"{s}\" from the file \"{s}\": {}",
|
||||
.{ theme, path, err },
|
||||
0,
|
||||
),
|
||||
});
|
||||
|
||||
|
|
@ -222,10 +228,11 @@ pub fn open(
|
|||
while (try it.next()) |loc| {
|
||||
const path = try std.fs.path.join(arena_alloc, &.{ loc.dir, theme });
|
||||
try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"theme \"{s}\" not found, tried path \"{s}\"",
|
||||
.{ theme, path },
|
||||
0,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
|
@ -249,17 +256,19 @@ pub fn openAbsolute(
|
|||
return std.fs.openFileAbsolute(theme, .{}) catch |err| {
|
||||
switch (err) {
|
||||
error.FileNotFound => try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"failed to load theme from the path \"{s}\"",
|
||||
.{theme},
|
||||
0,
|
||||
),
|
||||
}),
|
||||
else => try diags.append(arena_alloc, .{
|
||||
.message = try std.fmt.allocPrintZ(
|
||||
.message = try std.fmt.allocPrintSentinel(
|
||||
arena_alloc,
|
||||
"failed to load theme from the path \"{s}\": {}",
|
||||
.{ theme, err },
|
||||
0,
|
||||
),
|
||||
}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ pub const Envelope = struct {
|
|||
headers: std.json.ObjectMap,
|
||||
|
||||
/// The items in the envelope in the order they're encoded.
|
||||
items: std.ArrayListUnmanaged(Item),
|
||||
items: std.ArrayList(Item),
|
||||
|
||||
/// Parse an envelope from a reader.
|
||||
///
|
||||
|
|
@ -37,7 +37,7 @@ pub const Envelope = struct {
|
|||
/// parsing in our use case is not a hot path.
|
||||
pub fn parse(
|
||||
alloc_gpa: Allocator,
|
||||
reader: anytype,
|
||||
reader: *std.Io.Reader,
|
||||
) !Envelope {
|
||||
// We use an arena allocator to read from reader. We pair this
|
||||
// with `alloc_if_needed` when parsing json to allow the json
|
||||
|
|
@ -62,23 +62,24 @@ pub const Envelope = struct {
|
|||
|
||||
fn parseHeader(
|
||||
alloc: Allocator,
|
||||
reader: anytype,
|
||||
reader: *std.Io.Reader,
|
||||
) !std.json.ObjectMap {
|
||||
var buf: std.ArrayListUnmanaged(u8) = .{};
|
||||
reader.streamUntilDelimiter(
|
||||
buf.writer(alloc),
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
_ = try reader.streamDelimiterLimit(
|
||||
&buf.writer,
|
||||
'\n',
|
||||
1024 * 1024, // 1MB, arbitrary choice
|
||||
) catch |err| switch (err) {
|
||||
// Envelope can be header-only.
|
||||
.limited(1024 * 1024), // 1MB, arbitrary choice
|
||||
);
|
||||
_ = reader.discardDelimiterInclusive('\n') catch |err| switch (err) {
|
||||
// It's okay if there isn't a trailing newline
|
||||
error.EndOfStream => {},
|
||||
else => |v| return v,
|
||||
else => return err,
|
||||
};
|
||||
|
||||
const value = try std.json.parseFromSliceLeaky(
|
||||
std.json.Value,
|
||||
alloc,
|
||||
buf.items,
|
||||
buf.written(),
|
||||
.{ .allocate = .alloc_if_needed },
|
||||
);
|
||||
|
||||
|
|
@ -90,9 +91,9 @@ pub const Envelope = struct {
|
|||
|
||||
fn parseItems(
|
||||
alloc: Allocator,
|
||||
reader: anytype,
|
||||
) !std.ArrayListUnmanaged(Item) {
|
||||
var items: std.ArrayListUnmanaged(Item) = .{};
|
||||
reader: *std.Io.Reader,
|
||||
) !std.ArrayList(Item) {
|
||||
var items: std.ArrayList(Item) = .{};
|
||||
errdefer items.deinit(alloc);
|
||||
while (try parseOneItem(alloc, reader)) |item| {
|
||||
try items.append(alloc, item);
|
||||
|
|
@ -103,22 +104,27 @@ pub const Envelope = struct {
|
|||
|
||||
fn parseOneItem(
|
||||
alloc: Allocator,
|
||||
reader: anytype,
|
||||
reader: *std.Io.Reader,
|
||||
) !?Item {
|
||||
// Get the next item which must start with a header.
|
||||
var buf: std.ArrayListUnmanaged(u8) = .{};
|
||||
reader.streamUntilDelimiter(
|
||||
buf.writer(alloc),
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
_ = reader.streamDelimiterLimit(
|
||||
&buf.writer,
|
||||
'\n',
|
||||
1024 * 1024, // 1MB, arbitrary choice
|
||||
.limited(1024 * 1024), // 1MB, arbitrary choice
|
||||
) catch |err| switch (err) {
|
||||
error.EndOfStream => return null,
|
||||
else => |v| return v,
|
||||
error.StreamTooLong => return null,
|
||||
else => return err,
|
||||
};
|
||||
_ = reader.discardDelimiterInclusive('\n') catch |err| switch (err) {
|
||||
// It's okay if there isn't a trailing newline
|
||||
error.EndOfStream => {},
|
||||
else => return err,
|
||||
};
|
||||
|
||||
// Parse the header JSON
|
||||
const headers: std.json.ObjectMap = headers: {
|
||||
const line = std.mem.trim(u8, buf.items, " \t");
|
||||
const line = std.mem.trim(u8, buf.written(), " \t");
|
||||
if (line.len == 0) return null;
|
||||
|
||||
const value = try std.json.parseFromSliceLeaky(
|
||||
|
|
@ -156,18 +162,16 @@ pub const Envelope = struct {
|
|||
// Get the payload
|
||||
const payload: []const u8 = if (len_) |len| payload: {
|
||||
// The payload length is specified so read the exact length.
|
||||
var payload = std.ArrayList(u8).init(alloc);
|
||||
var payload: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer payload.deinit();
|
||||
for (0..len) |_| {
|
||||
const byte = reader.readByte() catch |err| switch (err) {
|
||||
error.EndOfStream => return error.EnvelopeItemPayloadTooShort,
|
||||
else => return err,
|
||||
};
|
||||
try payload.append(byte);
|
||||
}
|
||||
|
||||
reader.streamExact(&payload.writer, len) catch |err| switch (err) {
|
||||
error.EndOfStream => return error.EnvelopeItemPayloadTooShort,
|
||||
else => return err,
|
||||
};
|
||||
|
||||
// The next byte must be a newline.
|
||||
if (reader.readByte()) |byte| {
|
||||
if (reader.takeByte()) |byte| {
|
||||
if (byte != '\n') return error.EnvelopeItemPayloadNoNewline;
|
||||
} else |err| switch (err) {
|
||||
error.EndOfStream => {},
|
||||
|
|
@ -177,16 +181,20 @@ pub const Envelope = struct {
|
|||
break :payload try payload.toOwnedSlice();
|
||||
} else payload: {
|
||||
// The payload is the next line ending in `\n`. It is required.
|
||||
var payload = std.ArrayList(u8).init(alloc);
|
||||
defer payload.deinit();
|
||||
reader.streamUntilDelimiter(
|
||||
payload.writer(),
|
||||
var payload: std.Io.Writer.Allocating = .init(alloc);
|
||||
_ = reader.streamDelimiterLimit(
|
||||
&payload.writer,
|
||||
'\n',
|
||||
1024 * 1024 * 50, // 50MB, arbitrary choice
|
||||
.limited(1024 * 1024), // 50MB, arbitrary choice
|
||||
) catch |err| switch (err) {
|
||||
error.EndOfStream => return error.EnvelopeItemPayloadTooShort,
|
||||
error.StreamTooLong => return error.EnvelopeItemPayloadTooShort,
|
||||
else => |v| return v,
|
||||
};
|
||||
_ = reader.discardDelimiterInclusive('\n') catch |err| switch (err) {
|
||||
// It's okay if there isn't a trailing newline
|
||||
error.EndOfStream => {},
|
||||
else => return err,
|
||||
};
|
||||
break :payload try payload.toOwnedSlice();
|
||||
};
|
||||
|
||||
|
|
@ -212,15 +220,13 @@ pub const Envelope = struct {
|
|||
/// therefore may allocate.
|
||||
pub fn serialize(
|
||||
self: *Envelope,
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
// Header line first
|
||||
try std.json.stringify(
|
||||
try writer.print("{f}\n", .{std.json.fmt(
|
||||
std.json.Value{ .object = self.headers },
|
||||
json_opts,
|
||||
writer,
|
||||
);
|
||||
try writer.writeByte('\n');
|
||||
)});
|
||||
|
||||
// Write each item
|
||||
const alloc = self.allocator();
|
||||
|
|
@ -230,13 +236,13 @@ pub const Envelope = struct {
|
|||
const encoded = try item.encode(alloc);
|
||||
assert(item.* == .encoded);
|
||||
|
||||
try std.json.stringify(
|
||||
std.json.Value{ .object = encoded.headers },
|
||||
json_opts,
|
||||
writer,
|
||||
);
|
||||
try writer.writeByte('\n');
|
||||
try writer.writeAll(encoded.payload);
|
||||
try writer.print("{f}\n{s}", .{
|
||||
std.json.fmt(
|
||||
std.json.Value{ .object = encoded.headers },
|
||||
json_opts,
|
||||
),
|
||||
encoded.payload,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -425,7 +431,7 @@ pub const Attachment = struct {
|
|||
pub const ObjectMapUnmanaged = std.StringArrayHashMapUnmanaged(std.json.Value);
|
||||
|
||||
/// The options we must use for serialization.
|
||||
const json_opts: std.json.StringifyOptions = .{
|
||||
const json_opts: std.json.Stringify.Options = .{
|
||||
// This is the default but I want to be explicit because its
|
||||
// VERY important for the correctness of the envelope. This is
|
||||
// the only whitespace type in std.json that doesn't emit newlines.
|
||||
|
|
@ -437,10 +443,10 @@ test "Envelope parse" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
\\{}
|
||||
);
|
||||
var v = try Envelope.parse(alloc, fbs.reader());
|
||||
var v = try Envelope.parse(alloc, &reader);
|
||||
defer v.deinit();
|
||||
}
|
||||
|
||||
|
|
@ -448,12 +454,12 @@ test "Envelope parse session" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
\\{}
|
||||
\\{"type":"session","length":218}
|
||||
\\{"init":true,"sid":"c148cc2f-5f9f-4231-575c-2e85504d6434","status":"abnormal","errors":0,"started":"2024-08-29T02:38:57.607016Z","duration":0.000343,"attrs":{"release":"0.1.0-HEAD+d37b7d09","environment":"production"}}
|
||||
);
|
||||
var v = try Envelope.parse(alloc, fbs.reader());
|
||||
var v = try Envelope.parse(alloc, &reader);
|
||||
defer v.deinit();
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), v.items.items.len);
|
||||
|
|
@ -464,14 +470,14 @@ test "Envelope parse multiple" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
\\{}
|
||||
\\{"type":"session","length":218}
|
||||
\\{"init":true,"sid":"c148cc2f-5f9f-4231-575c-2e85504d6434","status":"abnormal","errors":0,"started":"2024-08-29T02:38:57.607016Z","duration":0.000343,"attrs":{"release":"0.1.0-HEAD+d37b7d09","environment":"production"}}
|
||||
\\{"type":"attachment","length":4,"filename":"test.txt"}
|
||||
\\ABCD
|
||||
);
|
||||
var v = try Envelope.parse(alloc, fbs.reader());
|
||||
var v = try Envelope.parse(alloc, &reader);
|
||||
defer v.deinit();
|
||||
|
||||
try testing.expectEqual(@as(usize, 2), v.items.items.len);
|
||||
|
|
@ -483,14 +489,14 @@ test "Envelope parse multiple no length" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
\\{}
|
||||
\\{"type":"session"}
|
||||
\\{}
|
||||
\\{"type":"attachment","length":4,"filename":"test.txt"}
|
||||
\\ABCD
|
||||
);
|
||||
var v = try Envelope.parse(alloc, fbs.reader());
|
||||
var v = try Envelope.parse(alloc, &reader);
|
||||
defer v.deinit();
|
||||
|
||||
try testing.expectEqual(@as(usize, 2), v.items.items.len);
|
||||
|
|
@ -502,13 +508,13 @@ test "Envelope parse end in new line" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
\\{}
|
||||
\\{"type":"session","length":218}
|
||||
\\{"init":true,"sid":"c148cc2f-5f9f-4231-575c-2e85504d6434","status":"abnormal","errors":0,"started":"2024-08-29T02:38:57.607016Z","duration":0.000343,"attrs":{"release":"0.1.0-HEAD+d37b7d09","environment":"production"}}
|
||||
\\
|
||||
);
|
||||
var v = try Envelope.parse(alloc, fbs.reader());
|
||||
var v = try Envelope.parse(alloc, &reader);
|
||||
defer v.deinit();
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), v.items.items.len);
|
||||
|
|
@ -519,12 +525,12 @@ test "Envelope parse attachment" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
\\{}
|
||||
\\{"type":"attachment","length":4,"filename":"test.txt"}
|
||||
\\ABCD
|
||||
);
|
||||
var v = try Envelope.parse(alloc, fbs.reader());
|
||||
var v = try Envelope.parse(alloc, &reader);
|
||||
defer v.deinit();
|
||||
|
||||
try testing.expectEqual(@as(usize, 1), v.items.items.len);
|
||||
|
|
@ -537,14 +543,14 @@ test "Envelope parse attachment" {
|
|||
|
||||
// Serialization test
|
||||
{
|
||||
var output = std.ArrayList(u8).init(alloc);
|
||||
var output: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer output.deinit();
|
||||
try v.serialize(output.writer());
|
||||
try v.serialize(&output.writer);
|
||||
try testing.expectEqualStrings(
|
||||
\\{}
|
||||
\\{"type":"attachment","length":4,"filename":"test.txt"}
|
||||
\\ABCD
|
||||
, std.mem.trim(u8, output.items, "\n"));
|
||||
, std.mem.trim(u8, output.written(), "\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -552,76 +558,40 @@ test "Envelope serialize empty" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
\\{}
|
||||
);
|
||||
var v = try Envelope.parse(alloc, fbs.reader());
|
||||
var v = try Envelope.parse(alloc, &reader);
|
||||
defer v.deinit();
|
||||
|
||||
var output = std.ArrayList(u8).init(alloc);
|
||||
var output: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer output.deinit();
|
||||
try v.serialize(output.writer());
|
||||
try v.serialize(&output.writer);
|
||||
|
||||
try testing.expectEqualStrings(
|
||||
\\{}
|
||||
, std.mem.trim(u8, output.items, "\n"));
|
||||
, std.mem.trim(u8, output.written(), "\n"));
|
||||
}
|
||||
|
||||
test "Envelope serialize session" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var fbs = std.io.fixedBufferStream(
|
||||
var reader: std.Io.Reader = .fixed(
|
||||
\\{}
|
||||
\\{"type":"session","length":218}
|
||||
\\{"init":true,"sid":"c148cc2f-5f9f-4231-575c-2e85504d6434","status":"abnormal","errors":0,"started":"2024-08-29T02:38:57.607016Z","duration":0.000343,"attrs":{"release":"0.1.0-HEAD+d37b7d09","environment":"production"}}
|
||||
);
|
||||
var v = try Envelope.parse(alloc, fbs.reader());
|
||||
var v = try Envelope.parse(alloc, &reader);
|
||||
defer v.deinit();
|
||||
|
||||
var output = std.ArrayList(u8).init(alloc);
|
||||
var output: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer output.deinit();
|
||||
try v.serialize(output.writer());
|
||||
try v.serialize(&output.writer);
|
||||
|
||||
try testing.expectEqualStrings(
|
||||
\\{}
|
||||
\\{"type":"session","length":218}
|
||||
\\{"init":true,"sid":"c148cc2f-5f9f-4231-575c-2e85504d6434","status":"abnormal","errors":0,"started":"2024-08-29T02:38:57.607016Z","duration":0.000343,"attrs":{"release":"0.1.0-HEAD+d37b7d09","environment":"production"}}
|
||||
, std.mem.trim(u8, output.items, "\n"));
|
||||
, std.mem.trim(u8, output.written(), "\n"));
|
||||
}
|
||||
|
||||
// // Uncomment this test if you want to extract a minidump file from an
|
||||
// // existing envelope. This is useful for getting new test contents.
|
||||
// test "Envelope extract mdmp" {
|
||||
// const testing = std.testing;
|
||||
// const alloc = testing.allocator;
|
||||
//
|
||||
// var fbs = std.io.fixedBufferStream(@embedFile("in.crash"));
|
||||
// var v = try Envelope.parse(alloc, fbs.reader());
|
||||
// defer v.deinit();
|
||||
//
|
||||
// try testing.expect(v.items.items.len > 0);
|
||||
// for (v.items.items, 0..) |*item, i| {
|
||||
// if (item.encoded.type != .attachment) {
|
||||
// log.warn("ignoring item type={} i={}", .{ item.encoded.type, i });
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// try item.decode(v.allocator());
|
||||
// const attach = item.attachment;
|
||||
// const attach_type = attach.type orelse {
|
||||
// log.warn("attachment missing type i={}", .{i});
|
||||
// continue;
|
||||
// };
|
||||
// if (!std.mem.eql(u8, attach_type, "event.minidump")) {
|
||||
// log.warn("ignoring attachment type={s} i={}", .{ attach_type, i });
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// log.warn("found minidump i={}", .{i});
|
||||
// var f = try std.fs.cwd().createFile("out.mdmp", .{});
|
||||
// defer f.close();
|
||||
// try f.writer().writeAll(attach.payload);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ pub const CacheTable = cache_table.CacheTable;
|
|||
pub const CircBuf = circ_buf.CircBuf;
|
||||
pub const IntrusiveDoublyLinkedList = intrusive_linked_list.DoublyLinkedList;
|
||||
pub const SegmentedPool = segmented_pool.SegmentedPool;
|
||||
//pub const SplitTree = split_tree.SplitTree;
|
||||
pub const SplitTree = split_tree.SplitTree;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
|
|
|
|||
|
|
@ -1023,45 +1023,33 @@ pub fn SplitTree(comptime V: type) type {
|
|||
}
|
||||
|
||||
/// Format the tree in a human-readable format. By default this will
|
||||
/// output a diagram followed by a textual representation. This can
|
||||
/// be controlled via the formatting string:
|
||||
///
|
||||
/// - `diagram` - Output a diagram of the split tree only.
|
||||
/// - `text` - Output a textual representation of the split tree only.
|
||||
/// - Empty - Output both a diagram and a textual representation.
|
||||
///
|
||||
/// output a diagram followed by a textual representation.
|
||||
pub fn format(
|
||||
self: *const Self,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
_ = options;
|
||||
|
||||
if (self.nodes.len == 0) {
|
||||
try writer.writeAll("empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, fmt, "diagram")) {
|
||||
self.formatDiagram(writer) catch
|
||||
try writer.writeAll("failed to draw split tree diagram");
|
||||
} else if (std.mem.eql(u8, fmt, "text")) {
|
||||
try self.formatText(writer, .root, 0);
|
||||
} else if (fmt.len == 0) {
|
||||
self.formatDiagram(writer) catch {};
|
||||
try self.formatText(writer, .root, 0);
|
||||
} else {
|
||||
return error.InvalidFormat;
|
||||
}
|
||||
self.formatDiagram(writer) catch {};
|
||||
try self.formatText(writer);
|
||||
}
|
||||
|
||||
fn formatText(
|
||||
self: *const Self,
|
||||
writer: anytype,
|
||||
pub fn formatText(self: Self, writer: *std.Io.Writer) std.Io.Writer.Error!void {
|
||||
if (self.nodes.len == 0) {
|
||||
try writer.writeAll("empty");
|
||||
return;
|
||||
}
|
||||
try self.formatTextInner(writer, .root, 0);
|
||||
}
|
||||
|
||||
fn formatTextInner(
|
||||
self: Self,
|
||||
writer: *std.Io.Writer,
|
||||
current: Node.Handle,
|
||||
depth: usize,
|
||||
) !void {
|
||||
) std.Io.Writer.Error!void {
|
||||
for (0..depth) |_| try writer.writeAll(" ");
|
||||
|
||||
if (self.zoomed) |zoomed| if (zoomed == current) {
|
||||
|
|
@ -1075,20 +1063,25 @@ pub fn SplitTree(comptime V: type) type {
|
|||
try writer.print("leaf: {d}\n", .{current}),
|
||||
|
||||
.split => |s| {
|
||||
try writer.print("split (layout: {s}, ratio: {d:.2})\n", .{
|
||||
@tagName(s.layout),
|
||||
try writer.print("split (layout: {t}, ratio: {d:.2})\n", .{
|
||||
s.layout,
|
||||
s.ratio,
|
||||
});
|
||||
try self.formatText(writer, s.left, depth + 1);
|
||||
try self.formatText(writer, s.right, depth + 1);
|
||||
try self.formatTextInner(writer, s.left, depth + 1);
|
||||
try self.formatTextInner(writer, s.right, depth + 1);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn formatDiagram(
|
||||
self: *const Self,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
pub fn formatDiagram(
|
||||
self: Self,
|
||||
writer: *std.Io.Writer,
|
||||
) std.Io.Writer.Error!void {
|
||||
if (self.nodes.len == 0) {
|
||||
try writer.writeAll("empty");
|
||||
return;
|
||||
}
|
||||
|
||||
// Use our arena's GPA to allocate some intermediate memory.
|
||||
// Requiring allocation for formatting is nasty but this is really
|
||||
// only used for debugging and testing and shouldn't hit OOM
|
||||
|
|
@ -1099,7 +1092,7 @@ pub fn SplitTree(comptime V: type) type {
|
|||
|
||||
// Get our spatial representation.
|
||||
const sp = spatial: {
|
||||
const sp = try self.spatial(alloc);
|
||||
const sp = self.spatial(alloc) catch return error.WriteFailed;
|
||||
|
||||
// Scale our spatial representation to have minimum width/height 1.
|
||||
var min_w: f16 = 1;
|
||||
|
|
@ -1111,7 +1104,7 @@ pub fn SplitTree(comptime V: type) type {
|
|||
|
||||
const ratio_w: f16 = 1 / min_w;
|
||||
const ratio_h: f16 = 1 / min_h;
|
||||
const slots = try alloc.dupe(Spatial.Slot, sp.slots);
|
||||
const slots = alloc.dupe(Spatial.Slot, sp.slots) catch return error.WriteFailed;
|
||||
for (slots) |*slot| {
|
||||
slot.x *= ratio_w;
|
||||
slot.y *= ratio_h;
|
||||
|
|
@ -1168,9 +1161,9 @@ pub fn SplitTree(comptime V: type) type {
|
|||
width *= cell_width;
|
||||
height *= cell_height;
|
||||
|
||||
const rows = try alloc.alloc([]u8, height);
|
||||
const rows = alloc.alloc([]u8, height) catch return error.WriteFailed;
|
||||
for (0..rows.len) |y| {
|
||||
rows[y] = try alloc.alloc(u8, width + 1);
|
||||
rows[y] = alloc.alloc(u8, width + 1) catch return error.WriteFailed;
|
||||
@memset(rows[y], ' ');
|
||||
rows[y][width] = '\n';
|
||||
}
|
||||
|
|
@ -1223,7 +1216,7 @@ pub fn SplitTree(comptime V: type) type {
|
|||
const label: []const u8 = if (@hasDecl(View, "splitTreeLabel"))
|
||||
node.leaf.splitTreeLabel()
|
||||
else
|
||||
try std.fmt.bufPrint(&buf, "{d}", .{handle});
|
||||
std.fmt.bufPrint(&buf, "{d}", .{handle}) catch return error.WriteFailed;
|
||||
|
||||
// Draw the handle in the center
|
||||
const x_mid = width / 2 + x;
|
||||
|
|
@ -1231,7 +1224,7 @@ pub fn SplitTree(comptime V: type) type {
|
|||
const label_width = label.len;
|
||||
const label_start = x_mid - label_width / 2;
|
||||
const row = grid[y_mid][label_start..];
|
||||
_ = try std.fmt.bufPrint(row, "{s}", .{label});
|
||||
_ = std.fmt.bufPrint(row, "{s}", .{label}) catch return error.WriteFailed;
|
||||
}
|
||||
|
||||
// Output every row
|
||||
|
|
@ -1339,7 +1332,7 @@ test "SplitTree: empty tree" {
|
|||
var t: TestTree = .empty;
|
||||
defer t.deinit();
|
||||
|
||||
const str = try std.fmt.allocPrint(alloc, "{}", .{t});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{t});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\empty
|
||||
|
|
@ -1353,7 +1346,7 @@ test "SplitTree: single node" {
|
|||
var t: TestTree = try .init(alloc, &v);
|
||||
defer t.deinit();
|
||||
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{t});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(t, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---+
|
||||
|
|
@ -1383,7 +1376,7 @@ test "SplitTree: split horizontal" {
|
|||
defer t3.deinit();
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{}", .{t3});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{t3});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---++---+
|
||||
|
|
@ -1415,7 +1408,7 @@ test "SplitTree: split horizontal" {
|
|||
defer t4.deinit();
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{}", .{t4});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{t4});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+--------++---++---+
|
||||
|
|
@ -1449,7 +1442,7 @@ test "SplitTree: split horizontal" {
|
|||
defer t5.deinit();
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{}", .{t5});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{t5});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(
|
||||
\\+------------------++--------++---++---+
|
||||
|
|
@ -1547,7 +1540,7 @@ test "SplitTree: split vertical" {
|
|||
);
|
||||
defer t3.deinit();
|
||||
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{t3});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(t3, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---+
|
||||
|
|
@ -1583,7 +1576,7 @@ test "SplitTree: split horizontal with zero ratio" {
|
|||
const split = splitAB;
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---+
|
||||
|
|
@ -1617,7 +1610,7 @@ test "SplitTree: split vertical with zero ratio" {
|
|||
const split = splitAB;
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---+
|
||||
|
|
@ -1651,7 +1644,7 @@ test "SplitTree: split horizontal with full width" {
|
|||
const split = splitAB;
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---+
|
||||
|
|
@ -1685,7 +1678,7 @@ test "SplitTree: split vertical with full width" {
|
|||
const split = splitAB;
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---+
|
||||
|
|
@ -1727,7 +1720,7 @@ test "SplitTree: remove leaf" {
|
|||
);
|
||||
defer t4.deinit();
|
||||
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{t4});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(t4, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---+
|
||||
|
|
@ -1772,7 +1765,7 @@ test "SplitTree: split twice, remove intermediary" {
|
|||
defer split2.deinit();
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split2});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split2, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---++---+
|
||||
|
|
@ -1798,7 +1791,7 @@ test "SplitTree: split twice, remove intermediary" {
|
|||
defer split3.deinit();
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split3});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split3, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---+
|
||||
|
|
@ -1883,7 +1876,7 @@ test "SplitTree: spatial goto" {
|
|||
const split = splitBD;
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---++---+
|
||||
|
|
@ -1943,7 +1936,7 @@ test "SplitTree: spatial goto" {
|
|||
defer equal.deinit();
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{equal});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(equal, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---++---+
|
||||
|
|
@ -1979,7 +1972,7 @@ test "SplitTree: resize" {
|
|||
defer split.deinit();
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+---++---+
|
||||
|
|
@ -2005,7 +1998,7 @@ test "SplitTree: resize" {
|
|||
0.25,
|
||||
);
|
||||
defer resized.deinit();
|
||||
const str = try std.fmt.allocPrint(alloc, "{diagram}", .{resized});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(resized, .formatDiagram)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\+-------------++---+
|
||||
|
|
@ -2026,7 +2019,7 @@ test "SplitTree: clone empty tree" {
|
|||
defer t2.deinit();
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{}", .{t2});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{t2});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\empty
|
||||
|
|
@ -2064,7 +2057,7 @@ test "SplitTree: zoom" {
|
|||
});
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{text}", .{split});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatText)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\split (layout: horizontal, ratio: 0.50)
|
||||
|
|
@ -2079,7 +2072,7 @@ test "SplitTree: zoom" {
|
|||
defer clone.deinit();
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{text}", .{clone});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(clone, .formatText)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\split (layout: horizontal, ratio: 0.50)
|
||||
|
|
@ -2122,7 +2115,7 @@ test "SplitTree: split resets zoom" {
|
|||
defer split.deinit();
|
||||
|
||||
{
|
||||
const str = try std.fmt.allocPrint(alloc, "{text}", .{split});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatText)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\split (layout: horizontal, ratio: 0.50)
|
||||
|
|
@ -2178,7 +2171,7 @@ test "SplitTree: remove and zoom" {
|
|||
defer removed.deinit();
|
||||
try testing.expect(removed.zoomed == null);
|
||||
|
||||
const str = try std.fmt.allocPrint(alloc, "{text}", .{removed});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(removed, .formatText)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\leaf: B
|
||||
|
|
@ -2201,7 +2194,7 @@ test "SplitTree: remove and zoom" {
|
|||
);
|
||||
defer removed.deinit();
|
||||
|
||||
const str = try std.fmt.allocPrint(alloc, "{text}", .{removed});
|
||||
const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(removed, .formatText)});
|
||||
defer alloc.free(str);
|
||||
try testing.expectEqualStrings(str,
|
||||
\\(zoomed) leaf: A
|
||||
|
|
|
|||
|
|
@ -59,14 +59,15 @@ pub const compiler =
|
|||
/// Generates the syntax file at comptime.
|
||||
fn comptimeGenSyntax() []const u8 {
|
||||
comptime {
|
||||
var counting_writer = std.io.countingWriter(std.io.null_writer);
|
||||
try writeSyntax(&counting_writer.writer());
|
||||
@setEvalBranchQuota(50000);
|
||||
var counter: std.Io.Writer.Discarding = .init(&.{});
|
||||
try writeSyntax(&counter.writer);
|
||||
|
||||
var buf: [counting_writer.bytes_written]u8 = undefined;
|
||||
var stream = std.io.fixedBufferStream(&buf);
|
||||
try writeSyntax(stream.writer());
|
||||
var buf: [counter.count]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
try writeSyntax(&writer);
|
||||
const final = buf;
|
||||
return final[0..stream.getWritten().len];
|
||||
return final[0..writer.end];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -471,23 +471,23 @@ pub const Modifier = union(enum) {
|
|||
test "formatConfig percent" {
|
||||
const configpkg = @import("../config.zig");
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
const p = try parseCLI("24%");
|
||||
try p.formatEntry(configpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = 24%\n", buf.items);
|
||||
try p.formatEntry(configpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = 24%\n", buf.written());
|
||||
}
|
||||
|
||||
test "formatConfig absolute" {
|
||||
const configpkg = @import("../config.zig");
|
||||
const testing = std.testing;
|
||||
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||
var buf: std.Io.Writer.Allocating = .init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
const p = try parseCLI("-30");
|
||||
try p.formatEntry(configpkg.entryFormatter("a", buf.writer()));
|
||||
try std.testing.expectEqualSlices(u8, "a = -30\n", buf.items);
|
||||
try p.formatEntry(configpkg.entryFormatter("a", &buf.writer));
|
||||
try std.testing.expectEqualSlices(u8, "a = -30\n", buf.written());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -596,10 +596,10 @@ pub const Key = struct {
|
|||
// from DerivedConfig below.
|
||||
var config = try DerivedConfig.init(alloc, config_src);
|
||||
|
||||
var descriptors = std.ArrayList(discovery.Descriptor).init(alloc);
|
||||
defer descriptors.deinit();
|
||||
var descriptors: std.ArrayList(discovery.Descriptor) = .empty;
|
||||
defer descriptors.deinit(alloc);
|
||||
for (config.@"font-family".list.items) |family| {
|
||||
try descriptors.append(.{
|
||||
try descriptors.append(alloc, .{
|
||||
.family = family,
|
||||
.style = config.@"font-style".nameValue(),
|
||||
.size = font_size.points,
|
||||
|
|
@ -617,7 +617,7 @@ pub const Key = struct {
|
|||
// italic.
|
||||
for (config.@"font-family-bold".list.items) |family| {
|
||||
const style = config.@"font-style-bold".nameValue();
|
||||
try descriptors.append(.{
|
||||
try descriptors.append(alloc, .{
|
||||
.family = family,
|
||||
.style = style,
|
||||
.size = font_size.points,
|
||||
|
|
@ -627,7 +627,7 @@ pub const Key = struct {
|
|||
}
|
||||
for (config.@"font-family-italic".list.items) |family| {
|
||||
const style = config.@"font-style-italic".nameValue();
|
||||
try descriptors.append(.{
|
||||
try descriptors.append(alloc, .{
|
||||
.family = family,
|
||||
.style = style,
|
||||
.size = font_size.points,
|
||||
|
|
@ -637,7 +637,7 @@ pub const Key = struct {
|
|||
}
|
||||
for (config.@"font-family-bold-italic".list.items) |family| {
|
||||
const style = config.@"font-style-bold-italic".nameValue();
|
||||
try descriptors.append(.{
|
||||
try descriptors.append(alloc, .{
|
||||
.family = family,
|
||||
.style = style,
|
||||
.size = font_size.points,
|
||||
|
|
@ -681,7 +681,7 @@ pub const Key = struct {
|
|||
|
||||
return .{
|
||||
.arena = arena,
|
||||
.descriptors = try descriptors.toOwnedSlice(),
|
||||
.descriptors = try descriptors.toOwnedSlice(alloc),
|
||||
.style_offsets = .{
|
||||
regular_offset,
|
||||
bold_offset,
|
||||
|
|
|
|||
|
|
@ -356,8 +356,8 @@ pub const RunIterator = struct {
|
|||
// If this is a grapheme, we need to find a font that supports
|
||||
// all of the codepoints in the grapheme.
|
||||
const cps = self.opts.row.grapheme(cell) orelse return primary;
|
||||
var candidates = try std.ArrayList(font.Collection.Index).initCapacity(alloc, cps.len + 1);
|
||||
defer candidates.deinit();
|
||||
var candidates: std.ArrayList(font.Collection.Index) = try .initCapacity(alloc, cps.len + 1);
|
||||
defer candidates.deinit(alloc);
|
||||
candidates.appendAssumeCapacity(primary);
|
||||
|
||||
for (cps) |cp| {
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ pub const GlobalState = struct {
|
|||
std.log.info("dependency fontconfig={d}", .{fontconfig.version()});
|
||||
}
|
||||
std.log.info("renderer={}", .{renderer.Renderer});
|
||||
std.log.info("libxev default backend={s}", .{@tagName(xev.backend)});
|
||||
std.log.info("libxev default backend={t}", .{xev.backend});
|
||||
|
||||
// As early as possible, initialize our resource limits.
|
||||
self.rlimits = .init();
|
||||
|
|
@ -206,7 +206,7 @@ pub const GlobalState = struct {
|
|||
|
||||
var sa: p.Sigaction = .{
|
||||
.handler = .{ .handler = p.SIG.IGN },
|
||||
.mask = p.empty_sigset,
|
||||
.mask = p.sigemptyset(),
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,19 +11,22 @@ pub fn main() !void {
|
|||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
try stdout.writeAll(
|
||||
var buf: [4096]u8 = undefined;
|
||||
var stdout = std.fs.File.stdout().writer(&buf);
|
||||
const writer = &stdout.interface;
|
||||
try writer.writeAll(
|
||||
\\// THIS FILE IS AUTO GENERATED
|
||||
\\
|
||||
\\
|
||||
);
|
||||
|
||||
try genConfig(alloc, stdout);
|
||||
try genActions(alloc, stdout);
|
||||
try genKeybindActions(alloc, stdout);
|
||||
try genConfig(alloc, writer);
|
||||
try genActions(alloc, writer);
|
||||
try genKeybindActions(alloc, writer);
|
||||
try stdout.end();
|
||||
}
|
||||
|
||||
fn genConfig(alloc: std.mem.Allocator, writer: anytype) !void {
|
||||
fn genConfig(alloc: std.mem.Allocator, writer: *std.Io.Writer) !void {
|
||||
var ast = try std.zig.Ast.parse(alloc, @embedFile("config/Config.zig"), .zig);
|
||||
defer ast.deinit(alloc);
|
||||
|
||||
|
|
@ -44,7 +47,7 @@ fn genConfig(alloc: std.mem.Allocator, writer: anytype) !void {
|
|||
|
||||
fn genConfigField(
|
||||
alloc: std.mem.Allocator,
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
ast: std.zig.Ast,
|
||||
comptime field: []const u8,
|
||||
) !void {
|
||||
|
|
@ -69,7 +72,7 @@ fn genConfigField(
|
|||
}
|
||||
}
|
||||
|
||||
fn genActions(alloc: std.mem.Allocator, writer: anytype) !void {
|
||||
fn genActions(alloc: std.mem.Allocator, writer: *std.Io.Writer) !void {
|
||||
try writer.writeAll(
|
||||
\\
|
||||
\\/// Actions help
|
||||
|
|
@ -115,7 +118,7 @@ fn genActions(alloc: std.mem.Allocator, writer: anytype) !void {
|
|||
try writer.writeAll("};\n");
|
||||
}
|
||||
|
||||
fn genKeybindActions(alloc: std.mem.Allocator, writer: anytype) !void {
|
||||
fn genKeybindActions(alloc: std.mem.Allocator, writer: *std.Io.Writer) !void {
|
||||
var ast = try std.zig.Ast.parse(alloc, @embedFile("input/Binding.zig"), .zig);
|
||||
defer ast.deinit(alloc);
|
||||
|
||||
|
|
@ -149,24 +152,24 @@ fn extractDocComments(
|
|||
} else unreachable;
|
||||
|
||||
// Go through and build up the lines.
|
||||
var lines = std.ArrayList([]const u8).init(alloc);
|
||||
defer lines.deinit();
|
||||
var lines: std.ArrayList([]const u8) = .empty;
|
||||
defer lines.deinit(alloc);
|
||||
for (start_idx..index + 1) |i| {
|
||||
const token = tokens[i];
|
||||
if (token != .doc_comment) break;
|
||||
try lines.append(ast.tokenSlice(@intCast(i))[3..]);
|
||||
try lines.append(alloc, ast.tokenSlice(@intCast(i))[3..]);
|
||||
}
|
||||
|
||||
// Convert the lines to a multiline string.
|
||||
var buffer = std.ArrayList(u8).init(alloc);
|
||||
const writer = buffer.writer();
|
||||
var buffer: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buffer.deinit();
|
||||
const prefix = findCommonPrefix(lines);
|
||||
for (lines.items) |line| {
|
||||
try writer.writeAll(" \\\\");
|
||||
try writer.writeAll(line[@min(prefix, line.len)..]);
|
||||
try writer.writeAll("\n");
|
||||
try buffer.writer.writeAll(" \\\\");
|
||||
try buffer.writer.writeAll(line[@min(prefix, line.len)..]);
|
||||
try buffer.writer.writeAll("\n");
|
||||
}
|
||||
try writer.writeAll(";\n");
|
||||
try buffer.writer.writeAll(";\n");
|
||||
|
||||
return buffer.toOwnedSlice();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ const Allocator = std.mem.Allocator;
|
|||
const assert = std.debug.assert;
|
||||
const build_config = @import("../build_config.zig");
|
||||
const uucode = @import("uucode");
|
||||
const EntryFormatter = @import("../config/formatter.zig").EntryFormatter;
|
||||
const key = @import("key.zig");
|
||||
const KeyEvent = key.KeyEvent;
|
||||
|
||||
|
|
@ -1184,13 +1185,8 @@ pub const Action = union(enum) {
|
|||
/// action back into the format used by parse.
|
||||
pub fn format(
|
||||
self: Action,
|
||||
comptime layout: []const u8,
|
||||
opts: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
_ = layout;
|
||||
_ = opts;
|
||||
|
||||
switch (self) {
|
||||
inline else => |value| {
|
||||
// All actions start with the tag.
|
||||
|
|
@ -1206,16 +1202,16 @@ pub const Action = union(enum) {
|
|||
}
|
||||
|
||||
fn formatValue(
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
value: anytype,
|
||||
) !void {
|
||||
const Value = @TypeOf(value);
|
||||
const value_info = @typeInfo(Value);
|
||||
switch (Value) {
|
||||
void => {},
|
||||
[]const u8 => try std.zig.stringEscape(value, "", .{}, writer),
|
||||
[]const u8 => try std.zig.stringEscape(value, writer),
|
||||
else => switch (value_info) {
|
||||
.@"enum" => try writer.print("{s}", .{@tagName(value)}),
|
||||
.@"enum" => try writer.print("{t}", .{value}),
|
||||
.float => try writer.print("{d}", .{value}),
|
||||
.int => try writer.print("{d}", .{value}),
|
||||
.@"struct" => |info| if (!info.is_tuple) {
|
||||
|
|
@ -1648,13 +1644,8 @@ pub const Trigger = struct {
|
|||
/// Format implementation for fmt package.
|
||||
pub fn format(
|
||||
self: Trigger,
|
||||
comptime layout: []const u8,
|
||||
opts: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
_ = layout;
|
||||
_ = opts;
|
||||
|
||||
// Modifiers first
|
||||
if (self.mods.super) try writer.writeAll("super+");
|
||||
if (self.mods.ctrl) try writer.writeAll("ctrl+");
|
||||
|
|
@ -1663,7 +1654,7 @@ pub const Trigger = struct {
|
|||
|
||||
// Key
|
||||
switch (self.key) {
|
||||
.physical => |k| try writer.print("{s}", .{@tagName(k)}),
|
||||
.physical => |k| try writer.print("{t}", .{k}),
|
||||
.unicode => |c| try writer.print("{u}", .{c}),
|
||||
}
|
||||
}
|
||||
|
|
@ -1721,13 +1712,8 @@ pub const Set = struct {
|
|||
/// action back into the format used by parse.
|
||||
pub fn format(
|
||||
self: Value,
|
||||
comptime layout: []const u8,
|
||||
opts: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
) !void {
|
||||
_ = layout;
|
||||
_ = opts;
|
||||
|
||||
switch (self) {
|
||||
.leader => |set| {
|
||||
// the leader key was already printed.
|
||||
|
|
@ -1758,26 +1744,34 @@ pub const Set = struct {
|
|||
/// that is shared between calls to nested levels of the set.
|
||||
/// For example, 'a>b>c=x' and 'a>b>d=y' will re-use the 'a>b' written
|
||||
/// to the buffer before flushing it to the formatter with 'c=x' and 'd=y'.
|
||||
pub fn formatEntries(self: Value, buffer_stream: anytype, formatter: anytype) !void {
|
||||
pub fn formatEntries(
|
||||
self: Value,
|
||||
buffer: *std.Io.Writer,
|
||||
formatter: EntryFormatter,
|
||||
) !void {
|
||||
switch (self) {
|
||||
.leader => |set| {
|
||||
// We'll rewind to this position after each sub-entry,
|
||||
// sharing the prefix between siblings.
|
||||
const pos = try buffer_stream.getPos();
|
||||
const pos = buffer.end;
|
||||
|
||||
var iter = set.bindings.iterator();
|
||||
while (iter.next()) |binding| {
|
||||
buffer_stream.seekTo(pos) catch unreachable; // can't fail
|
||||
std.fmt.format(buffer_stream.writer(), ">{s}", .{binding.key_ptr.*}) catch return error.OutOfMemory;
|
||||
try binding.value_ptr.*.formatEntries(buffer_stream, formatter);
|
||||
// I'm not exactly if this is safe for any arbitrary
|
||||
// writer since the Writer interface does not have any
|
||||
// rewind functions, but for our use case of a
|
||||
// fixed-size buffer writer this should work just fine.
|
||||
buffer.end = pos;
|
||||
buffer.print(">{f}", .{binding.key_ptr.*}) catch return error.OutOfMemory;
|
||||
try binding.value_ptr.*.formatEntries(buffer, formatter);
|
||||
}
|
||||
},
|
||||
|
||||
.leaf => |leaf| {
|
||||
// When we get to the leaf, the buffer_stream contains
|
||||
// the full sequence of keys needed to reach this action.
|
||||
std.fmt.format(buffer_stream.writer(), "={s}", .{leaf.action}) catch return error.OutOfMemory;
|
||||
try formatter.formatEntry([]const u8, buffer_stream.getWritten());
|
||||
buffer.print("={f}", .{leaf.action}) catch return error.OutOfMemory;
|
||||
try formatter.formatEntry([]const u8, buffer.buffer[0..buffer.end]);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -3234,11 +3228,8 @@ test "action: format" {
|
|||
|
||||
const a: Action = .{ .text = "👻" };
|
||||
|
||||
var buf: std.ArrayListUnmanaged(u8) = .empty;
|
||||
defer buf.deinit(alloc);
|
||||
|
||||
const writer = buf.writer(alloc);
|
||||
try a.format("", .{}, writer);
|
||||
|
||||
try testing.expectEqualStrings("text:\\xf0\\x9f\\x91\\xbb", buf.items);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
try a.format(&buf.writer);
|
||||
try testing.expectEqualStrings("text:\\xf0\\x9f\\x91\\xbb", buf.written());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ pub const Command = struct {
|
|||
|
||||
return .{
|
||||
.action_key = @tagName(self.action),
|
||||
.action = std.fmt.comptimePrint("{s}", .{self.action}),
|
||||
.action = std.fmt.comptimePrint("{t}", .{self.action}),
|
||||
.title = self.title,
|
||||
.description = self.description,
|
||||
};
|
||||
|
|
@ -94,6 +94,7 @@ pub const defaults: []const Command = defaults: {
|
|||
|
||||
/// Defaults in C-compatible form.
|
||||
pub const defaultsC: []const Command.C = defaults: {
|
||||
@setEvalBranchQuota(100_000);
|
||||
var result: [defaults.len]Command.C = undefined;
|
||||
for (defaults, 0..) |cmd, i| result[i] = cmd.comptimeCval();
|
||||
const final = result;
|
||||
|
|
|
|||
|
|
@ -278,6 +278,7 @@ fn pcStyle(comptime fmt: []const u8) []Entry {
|
|||
// The comptime {} wrapper is superfluous but it prevents us from
|
||||
// accidentally running this function at runtime.
|
||||
comptime {
|
||||
@setEvalBranchQuota(500_000);
|
||||
var entries: [modifiers.len]Entry = undefined;
|
||||
for (modifiers, 2.., 0..) |mods, code, i| {
|
||||
entries[i] = .{
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ pub const Format = enum {
|
|||
/// Markdown formatted output
|
||||
markdown,
|
||||
|
||||
fn formatFieldName(self: Format, writer: anytype, field_name: []const u8) !void {
|
||||
fn formatFieldName(self: Format, writer: *std.Io.Writer, field_name: []const u8) !void {
|
||||
switch (self) {
|
||||
.plaintext => {
|
||||
try writer.writeAll(field_name);
|
||||
|
|
@ -27,16 +27,16 @@ pub const Format = enum {
|
|||
}
|
||||
}
|
||||
|
||||
fn formatDocLine(self: Format, writer: anytype, line: []const u8) !void {
|
||||
fn formatDocLine(self: Format, writer: *std.Io.Writer, line: []const u8) !void {
|
||||
switch (self) {
|
||||
.plaintext => {
|
||||
try writer.appendSlice(" ");
|
||||
try writer.appendSlice(line);
|
||||
try writer.appendSlice("\n");
|
||||
try writer.writeAll(" ");
|
||||
try writer.writeAll(line);
|
||||
try writer.writeAll("\n");
|
||||
},
|
||||
.markdown => {
|
||||
try writer.appendSlice(line);
|
||||
try writer.appendSlice("\n");
|
||||
try writer.writeAll(line);
|
||||
try writer.writeAll("\n");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ pub const Format = enum {
|
|||
|
||||
/// Generate keybind actions documentation with the specified format
|
||||
pub fn generate(
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
format: Format,
|
||||
show_docs: bool,
|
||||
page_allocator: std.mem.Allocator,
|
||||
|
|
@ -70,8 +70,8 @@ pub fn generate(
|
|||
try writer.writeAll(header);
|
||||
}
|
||||
|
||||
var buffer = std.ArrayList(u8).init(page_allocator);
|
||||
defer buffer.deinit();
|
||||
var stream: std.Io.Writer.Allocating = .init(page_allocator);
|
||||
defer stream.deinit();
|
||||
|
||||
const fields = @typeInfo(KeybindAction).@"union".fields;
|
||||
inline for (fields) |field| {
|
||||
|
|
@ -79,10 +79,9 @@ pub fn generate(
|
|||
|
||||
// Write previously stored doc comment below all related actions
|
||||
if (show_docs and @hasDecl(help_strings.KeybindAction, field.name)) {
|
||||
try writer.writeAll(buffer.items);
|
||||
try writer.writeAll(stream.written());
|
||||
try writer.writeAll("\n");
|
||||
|
||||
buffer.clearRetainingCapacity();
|
||||
stream.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
if (show_docs) {
|
||||
|
|
@ -101,13 +100,13 @@ pub fn generate(
|
|||
while (iter.next()) |s| {
|
||||
// If it is the last line and empty, then skip it.
|
||||
if (iter.peek() == null and s.len == 0) continue;
|
||||
try format.formatDocLine(&buffer, s);
|
||||
try format.formatDocLine(&stream.writer, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write any remaining buffered documentation
|
||||
if (buffer.items.len > 0) {
|
||||
try writer.writeAll(buffer.items);
|
||||
if (stream.written().len > 0) {
|
||||
try writer.writeAll(stream.written());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,19 +34,19 @@ pub const Set = struct {
|
|||
alloc: Allocator,
|
||||
config: []const inputpkg.Link,
|
||||
) !Set {
|
||||
var links = std.ArrayList(Link).init(alloc);
|
||||
defer links.deinit();
|
||||
var links: std.ArrayList(Link) = .empty;
|
||||
defer links.deinit(alloc);
|
||||
|
||||
for (config) |link| {
|
||||
var regex = try link.oniRegex();
|
||||
errdefer regex.deinit();
|
||||
try links.append(.{
|
||||
try links.append(alloc, .{
|
||||
.regex = regex,
|
||||
.highlight = link.highlight,
|
||||
});
|
||||
}
|
||||
|
||||
return .{ .links = try links.toOwnedSlice() };
|
||||
return .{ .links = try links.toOwnedSlice(alloc) };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Set, alloc: Allocator) void {
|
||||
|
|
@ -77,8 +77,8 @@ pub const Set = struct {
|
|||
// as selections which contain the start and end points of
|
||||
// the match. There is no way to map these back to the link
|
||||
// configuration right now because we don't need to.
|
||||
var matches = std.ArrayList(terminal.Selection).init(alloc);
|
||||
defer matches.deinit();
|
||||
var matches: std.ArrayList(terminal.Selection) = .empty;
|
||||
defer matches.deinit(alloc);
|
||||
|
||||
// If our mouse is over an OSC8 link, then we can skip the regex
|
||||
// matches below since OSC8 takes priority.
|
||||
|
|
@ -101,7 +101,7 @@ pub const Set = struct {
|
|||
);
|
||||
}
|
||||
|
||||
return .{ .matches = try matches.toOwnedSlice() };
|
||||
return .{ .matches = try matches.toOwnedSlice(alloc) };
|
||||
}
|
||||
|
||||
fn matchSetFromOSC8(
|
||||
|
|
@ -112,8 +112,6 @@ pub const Set = struct {
|
|||
mouse_pin: terminal.Pin,
|
||||
mouse_mods: inputpkg.Mods,
|
||||
) !void {
|
||||
_ = alloc;
|
||||
|
||||
// If the right mods aren't pressed, then we can't match.
|
||||
if (!mouse_mods.equal(inputpkg.ctrlOrSuper(.{}))) return;
|
||||
|
||||
|
|
@ -135,6 +133,7 @@ pub const Set = struct {
|
|||
if (link.id == .implicit) {
|
||||
const uri = link.uri.offset.ptr(page.memory)[0..link.uri.len];
|
||||
return try self.matchSetFromOSC8Implicit(
|
||||
alloc,
|
||||
matches,
|
||||
mouse_pin,
|
||||
uri,
|
||||
|
|
@ -154,7 +153,7 @@ pub const Set = struct {
|
|||
// building our matching selection.
|
||||
if (!row.hyperlink) {
|
||||
if (current) |sel| {
|
||||
try matches.append(sel);
|
||||
try matches.append(alloc, sel);
|
||||
current = null;
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +190,7 @@ pub const Set = struct {
|
|||
|
||||
// No match, if we have a current selection then complete it.
|
||||
if (current) |sel| {
|
||||
try matches.append(sel);
|
||||
try matches.append(alloc, sel);
|
||||
current = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -203,6 +202,7 @@ pub const Set = struct {
|
|||
/// around the mouse pin.
|
||||
fn matchSetFromOSC8Implicit(
|
||||
self: *const Set,
|
||||
alloc: Allocator,
|
||||
matches: *std.ArrayList(terminal.Selection),
|
||||
mouse_pin: terminal.Pin,
|
||||
uri: []const u8,
|
||||
|
|
@ -264,7 +264,7 @@ pub const Set = struct {
|
|||
sel.endPtr().* = cell_pin;
|
||||
}
|
||||
|
||||
try matches.append(sel);
|
||||
try matches.append(alloc, sel);
|
||||
}
|
||||
|
||||
/// Fills matches with the matches from regex link matches.
|
||||
|
|
@ -334,7 +334,7 @@ pub const Set = struct {
|
|||
=> if (!sel.contains(screen, mouse_pin)) continue,
|
||||
}
|
||||
|
||||
try matches.append(sel);
|
||||
try matches.append(alloc, sel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ pub fn loadFromFile(
|
|||
/// mainImage function and don't define any of the uniforms. This function
|
||||
/// will convert the ShaderToy shader into a valid GLSL shader that can be
|
||||
/// compiled and linked.
|
||||
pub fn glslFromShader(writer: anytype, src: []const u8) !void {
|
||||
pub fn glslFromShader(writer: *std.Io.Writer, src: []const u8) !void {
|
||||
const prefix = @embedFile("shaders/shadertoy_prefix.glsl");
|
||||
try writer.writeAll(prefix);
|
||||
try writer.writeAll("\n\n");
|
||||
|
|
@ -138,7 +138,7 @@ pub fn glslFromShader(writer: anytype, src: []const u8) !void {
|
|||
|
||||
/// Convert a GLSL shader into SPIR-V assembly.
|
||||
pub fn spirvFromGlsl(
|
||||
writer: anytype,
|
||||
writer: *std.Io.Writer,
|
||||
errlog: ?*SpirvLog,
|
||||
src: [:0]const u8,
|
||||
) !void {
|
||||
|
|
@ -331,10 +331,10 @@ fn spvCross(
|
|||
|
||||
/// Convert ShaderToy shader to null-terminated glsl for testing.
|
||||
fn testGlslZ(alloc: Allocator, src: []const u8) ![:0]const u8 {
|
||||
var list = std.ArrayList(u8).init(alloc);
|
||||
defer list.deinit();
|
||||
try glslFromShader(list.writer(), src);
|
||||
return try list.toOwnedSliceSentinel(0);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
try glslFromShader(&buf.writer, src);
|
||||
return try buf.toOwnedSliceSentinel(0);
|
||||
}
|
||||
|
||||
test "spirv" {
|
||||
|
|
@ -345,9 +345,8 @@ test "spirv" {
|
|||
defer alloc.free(src);
|
||||
|
||||
var buf: [4096 * 4]u8 = undefined;
|
||||
var buf_stream = std.io.fixedBufferStream(&buf);
|
||||
const writer = buf_stream.writer();
|
||||
try spirvFromGlsl(writer, null, src);
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
try spirvFromGlsl(&writer, null, src);
|
||||
}
|
||||
|
||||
test "spirv invalid" {
|
||||
|
|
@ -358,12 +357,11 @@ test "spirv invalid" {
|
|||
defer alloc.free(src);
|
||||
|
||||
var buf: [4096 * 4]u8 = undefined;
|
||||
var buf_stream = std.io.fixedBufferStream(&buf);
|
||||
const writer = buf_stream.writer();
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
|
||||
var errlog: SpirvLog = .{ .alloc = alloc };
|
||||
defer errlog.deinit();
|
||||
try testing.expectError(error.GlslangFailed, spirvFromGlsl(writer, &errlog, src));
|
||||
try testing.expectError(error.GlslangFailed, spirvFromGlsl(&writer, &errlog, src));
|
||||
try testing.expect(errlog.info.len > 0);
|
||||
}
|
||||
|
||||
|
|
@ -374,9 +372,14 @@ test "shadertoy to msl" {
|
|||
const src = try testGlslZ(alloc, test_crt);
|
||||
defer alloc.free(src);
|
||||
|
||||
var spvlist = std.ArrayListAligned(u8, @alignOf(u32)).init(alloc);
|
||||
defer spvlist.deinit();
|
||||
try spirvFromGlsl(spvlist.writer(), null, src);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
try spirvFromGlsl(&buf.writer, null, src);
|
||||
|
||||
// TODO: Replace this with an aligned version of Writer.Allocating
|
||||
var spvlist: std.ArrayListAligned(u8, .of(u32)) = .empty;
|
||||
defer spvlist.deinit(alloc);
|
||||
try spvlist.appendSlice(alloc, buf.written());
|
||||
|
||||
const msl = try mslFromSpv(alloc, spvlist.items);
|
||||
defer alloc.free(msl);
|
||||
|
|
@ -389,9 +392,14 @@ test "shadertoy to glsl" {
|
|||
const src = try testGlslZ(alloc, test_crt);
|
||||
defer alloc.free(src);
|
||||
|
||||
var spvlist = std.ArrayListAligned(u8, @alignOf(u32)).init(alloc);
|
||||
defer spvlist.deinit();
|
||||
try spirvFromGlsl(spvlist.writer(), null, src);
|
||||
var buf: std.Io.Writer.Allocating = .init(alloc);
|
||||
defer buf.deinit();
|
||||
try spirvFromGlsl(&buf.writer, null, src);
|
||||
|
||||
// TODO: Replace this with an aligned version of Writer.Allocating
|
||||
var spvlist: std.ArrayListAligned(u8, .of(u32)) = .empty;
|
||||
defer spvlist.deinit(alloc);
|
||||
try spvlist.appendSlice(alloc, buf.written());
|
||||
|
||||
const glsl = try glslFromSpv(alloc, spvlist.items);
|
||||
defer alloc.free(glsl);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,9 @@ pub const Handler = struct {
|
|||
.kitty => |*p| kitty: {
|
||||
if (comptime !build_options.kitty_graphics) unreachable;
|
||||
|
||||
const command = p.complete() catch |err| {
|
||||
// Use the same allocator that was used to create the parser.
|
||||
const alloc = p.arena.child_allocator;
|
||||
const command = p.complete(alloc) catch |err| {
|
||||
log.warn("kitty graphics protocol error: {}", .{err});
|
||||
break :kitty null;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ pub const Handler = struct {
|
|||
.state = .{
|
||||
.tmux = .{
|
||||
.max_bytes = self.max_bytes,
|
||||
.buffer = try std.ArrayList(u8).initCapacity(
|
||||
.buffer = try .initCapacity(
|
||||
alloc,
|
||||
128, // Arbitrary choice to limit initial reallocs
|
||||
),
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ pub const Parser = struct {
|
|||
errdefer arena.deinit();
|
||||
var result: Parser = .{
|
||||
.arena = arena,
|
||||
.data = std.ArrayList(u8).init(alloc),
|
||||
.data = .empty,
|
||||
.kv = .{},
|
||||
.kv_temp_len = 0,
|
||||
.kv_current = 0,
|
||||
|
|
@ -77,8 +77,8 @@ pub const Parser = struct {
|
|||
|
||||
pub fn deinit(self: *Parser) void {
|
||||
// We don't free the hash map because its in the arena
|
||||
self.data.deinit(self.arena.child_allocator);
|
||||
self.arena.deinit();
|
||||
self.data.deinit();
|
||||
}
|
||||
|
||||
/// Parse a complete command string.
|
||||
|
|
@ -86,7 +86,7 @@ pub const Parser = struct {
|
|||
var parser = init(alloc);
|
||||
defer parser.deinit();
|
||||
for (data) |c| try parser.feed(c);
|
||||
return try parser.complete();
|
||||
return try parser.complete(alloc);
|
||||
}
|
||||
|
||||
/// Feed a single byte to the parser.
|
||||
|
|
@ -136,7 +136,7 @@ pub const Parser = struct {
|
|||
else => {},
|
||||
},
|
||||
|
||||
.data => try self.data.append(c),
|
||||
.data => try self.data.append(self.arena.child_allocator, c),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ pub const Parser = struct {
|
|||
///
|
||||
/// The allocator given will be used for the long-lived data
|
||||
/// of the final command.
|
||||
pub fn complete(self: *Parser) !Command {
|
||||
pub fn complete(self: *Parser, alloc: Allocator) !Command {
|
||||
switch (self.state) {
|
||||
// We can't ever end in the control key state and be valid.
|
||||
// This means the command looked something like "a=1,b"
|
||||
|
|
@ -194,14 +194,14 @@ pub const Parser = struct {
|
|||
return .{
|
||||
.control = control,
|
||||
.quiet = quiet,
|
||||
.data = try self.decodeData(),
|
||||
.data = try self.decodeData(alloc),
|
||||
};
|
||||
}
|
||||
|
||||
/// Decodes the payload data from base64 and returns it as a slice.
|
||||
/// This function will destroy the contents of self.data, it should
|
||||
/// only be used once we are done collecting payload bytes.
|
||||
fn decodeData(self: *Parser) ![]const u8 {
|
||||
fn decodeData(self: *Parser, alloc: Allocator) ![]const u8 {
|
||||
if (self.data.items.len == 0) {
|
||||
return "";
|
||||
}
|
||||
|
|
@ -225,7 +225,7 @@ pub const Parser = struct {
|
|||
// Remove the extra bytes.
|
||||
self.data.items.len = decoded.len;
|
||||
|
||||
return try self.data.toOwnedSlice();
|
||||
return try self.data.toOwnedSlice(alloc);
|
||||
}
|
||||
|
||||
fn accumulateValue(self: *Parser, c: u8, overflow_state: State) !void {
|
||||
|
|
@ -969,7 +969,7 @@ test "transmission command" {
|
|||
|
||||
const input = "f=24,s=10,v=20";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .transmit);
|
||||
|
|
@ -987,7 +987,7 @@ test "transmission ignores 'm' if medium is not direct" {
|
|||
|
||||
const input = "a=t,t=t,m=1";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .transmit);
|
||||
|
|
@ -1004,7 +1004,7 @@ test "transmission respects 'm' if medium is direct" {
|
|||
|
||||
const input = "a=t,t=d,m=1";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .transmit);
|
||||
|
|
@ -1021,7 +1021,7 @@ test "query command" {
|
|||
|
||||
const input = "i=31,s=1,v=1,a=q,t=d,f=24;QUFBQQ";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .query);
|
||||
|
|
@ -1041,7 +1041,7 @@ test "display command" {
|
|||
|
||||
const input = "a=p,U=1,i=31,c=80,r=120";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .display);
|
||||
|
|
@ -1059,7 +1059,7 @@ test "delete command" {
|
|||
|
||||
const input = "a=d,d=p,x=3,y=4";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .delete);
|
||||
|
|
@ -1079,7 +1079,7 @@ test "no control data" {
|
|||
|
||||
const input = ";QUFBQQ";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .transmit);
|
||||
|
|
@ -1094,7 +1094,7 @@ test "ignore unknown keys (long)" {
|
|||
|
||||
const input = "f=24,s=10,v=20,hello=world";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .transmit);
|
||||
|
|
@ -1112,7 +1112,7 @@ test "ignore very long values" {
|
|||
|
||||
const input = "f=24,s=10,v=2000000000000000000000000000000000000000";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .transmit);
|
||||
|
|
@ -1130,7 +1130,7 @@ test "ensure very large negative values don't get skipped" {
|
|||
|
||||
const input = "a=p,i=1,z=-2000000000";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .display);
|
||||
|
|
@ -1147,7 +1147,7 @@ test "ensure proper overflow error for u32" {
|
|||
|
||||
const input = "a=p,i=10000000000";
|
||||
for (input) |c| try p.feed(c);
|
||||
try testing.expectError(error.Overflow, p.complete());
|
||||
try testing.expectError(error.Overflow, p.complete(alloc));
|
||||
}
|
||||
|
||||
test "ensure proper overflow error for i32" {
|
||||
|
|
@ -1158,7 +1158,7 @@ test "ensure proper overflow error for i32" {
|
|||
|
||||
const input = "a=p,i=1,z=-9999999999";
|
||||
for (input) |c| try p.feed(c);
|
||||
try testing.expectError(error.Overflow, p.complete());
|
||||
try testing.expectError(error.Overflow, p.complete(alloc));
|
||||
}
|
||||
|
||||
test "all i32 values" {
|
||||
|
|
@ -1171,7 +1171,7 @@ test "all i32 values" {
|
|||
defer p.deinit();
|
||||
const input = "a=p,i=1,z=-1";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .display);
|
||||
|
|
@ -1186,7 +1186,7 @@ test "all i32 values" {
|
|||
defer p.deinit();
|
||||
const input = "a=p,i=1,H=-1";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .display);
|
||||
|
|
@ -1201,7 +1201,7 @@ test "all i32 values" {
|
|||
defer p.deinit();
|
||||
const input = "a=p,i=1,V=-1";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .display);
|
||||
|
|
@ -1259,7 +1259,7 @@ test "delete range command 1" {
|
|||
|
||||
const input = "a=d,d=r,x=3,y=4";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .delete);
|
||||
|
|
@ -1279,7 +1279,7 @@ test "delete range command 2" {
|
|||
|
||||
const input = "a=d,d=R,x=5,y=11";
|
||||
for (input) |c| try p.feed(c);
|
||||
const command = try p.complete();
|
||||
const command = try p.complete(alloc);
|
||||
defer command.deinit(alloc);
|
||||
|
||||
try testing.expect(command.control == .delete);
|
||||
|
|
@ -1299,7 +1299,7 @@ test "delete range command 3" {
|
|||
|
||||
const input = "a=d,d=R,x=5,y=4";
|
||||
for (input) |c| try p.feed(c);
|
||||
try testing.expectError(error.InvalidFormat, p.complete());
|
||||
try testing.expectError(error.InvalidFormat, p.complete(alloc));
|
||||
}
|
||||
|
||||
test "delete range command 4" {
|
||||
|
|
@ -1310,7 +1310,7 @@ test "delete range command 4" {
|
|||
|
||||
const input = "a=d,d=R,x=5";
|
||||
for (input) |c| try p.feed(c);
|
||||
try testing.expectError(error.InvalidFormat, p.complete());
|
||||
try testing.expectError(error.InvalidFormat, p.complete(alloc));
|
||||
}
|
||||
|
||||
test "delete range command 5" {
|
||||
|
|
@ -1321,5 +1321,5 @@ test "delete range command 5" {
|
|||
|
||||
const input = "a=d,d=R,y=5";
|
||||
for (input) |c| try p.feed(c);
|
||||
try testing.expectError(error.InvalidFormat, p.complete());
|
||||
try testing.expectError(error.InvalidFormat, p.complete(alloc));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,15 +259,16 @@ pub const LoadingImage = struct {
|
|||
};
|
||||
}
|
||||
|
||||
var buf_reader = std.io.bufferedReader(file.reader());
|
||||
const reader = buf_reader.reader();
|
||||
var buf: [4096]u8 = undefined;
|
||||
var buf_reader = file.reader(&buf);
|
||||
const reader = &buf_reader.interface;
|
||||
|
||||
// Read the file
|
||||
var managed = std.ArrayList(u8).init(alloc);
|
||||
errdefer managed.deinit();
|
||||
var managed: std.ArrayList(u8) = .empty;
|
||||
errdefer managed.deinit(alloc);
|
||||
const size: usize = if (t.size > 0) @min(t.size, max_size) else max_size;
|
||||
reader.readAllArrayList(&managed, size) catch |err| {
|
||||
log.warn("failed to read temporary file: {}", .{err});
|
||||
reader.appendRemaining(alloc, &managed, .limited(size)) catch {
|
||||
log.warn("failed to read temporary file: {?}", .{buf_reader.err});
|
||||
return error.InvalidData;
|
||||
};
|
||||
|
||||
|
|
@ -402,14 +403,15 @@ pub const LoadingImage = struct {
|
|||
|
||||
fn decompressZlib(self: *LoadingImage, alloc: Allocator) !void {
|
||||
// Open our zlib stream
|
||||
var fbs = std.io.fixedBufferStream(self.data.items);
|
||||
var stream = std.compress.zlib.decompressor(fbs.reader());
|
||||
var buf: [std.compress.flate.max_window_len]u8 = undefined;
|
||||
var reader: std.Io.Reader = .fixed(self.data.items);
|
||||
var stream: std.compress.flate.Decompress = .init(&reader, .zlib, &buf);
|
||||
|
||||
// Write it to an array list
|
||||
var list = std.ArrayList(u8).init(alloc);
|
||||
errdefer list.deinit();
|
||||
stream.reader().readAllArrayList(&list, max_size) catch |err| {
|
||||
log.warn("failed to read decompressed data: {}", .{err});
|
||||
var list: std.ArrayList(u8) = .empty;
|
||||
errdefer list.deinit(alloc);
|
||||
stream.reader.appendRemaining(alloc, &list, .limited(max_size)) catch {
|
||||
log.warn("failed to read decompressed data: {?}", .{stream.err});
|
||||
return error.DecompressionFailed;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -526,8 +526,8 @@ pub const ImageStorage = struct {
|
|||
used: bool,
|
||||
};
|
||||
|
||||
var candidates = std.ArrayList(Candidate).init(alloc);
|
||||
defer candidates.deinit();
|
||||
var candidates: std.ArrayList(Candidate) = .empty;
|
||||
defer candidates.deinit(alloc);
|
||||
|
||||
var it = self.images.iterator();
|
||||
while (it.next()) |kv| {
|
||||
|
|
@ -548,7 +548,7 @@ pub const ImageStorage = struct {
|
|||
break :used false;
|
||||
};
|
||||
|
||||
try candidates.append(.{
|
||||
try candidates.append(alloc, .{
|
||||
.id = img.id,
|
||||
.time = img.transmit_time,
|
||||
.used = used,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ pub const Client = struct {
|
|||
state: State = .idle,
|
||||
|
||||
/// The buffer used to store in-progress notifications, output, etc.
|
||||
buffer: std.ArrayList(u8),
|
||||
buffer: std.Io.Writer.Allocating,
|
||||
|
||||
/// The maximum size in bytes of the buffer. This is used to limit
|
||||
/// memory usage. If the buffer exceeds this size, the client will
|
||||
|
|
@ -49,7 +49,7 @@ pub const Client = struct {
|
|||
|
||||
// Handle a byte of input.
|
||||
pub fn put(self: *Client, byte: u8) !?Notification {
|
||||
if (self.buffer.items.len >= self.max_bytes) {
|
||||
if (self.buffer.written().len >= self.max_bytes) {
|
||||
self.broken();
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
|
|
@ -81,18 +81,19 @@ pub const Client = struct {
|
|||
// If we're in a block then we accumulate until we see a newline
|
||||
// and then we check to see if that line ended the block.
|
||||
.block => if (byte == '\n') {
|
||||
const written = self.buffer.written();
|
||||
const idx = if (std.mem.lastIndexOfScalar(
|
||||
u8,
|
||||
self.buffer.items,
|
||||
written,
|
||||
'\n',
|
||||
)) |v| v + 1 else 0;
|
||||
const line = self.buffer.items[idx..];
|
||||
const line = written[idx..];
|
||||
|
||||
if (std.mem.startsWith(u8, line, "%end") or
|
||||
std.mem.startsWith(u8, line, "%error"))
|
||||
{
|
||||
const err = std.mem.startsWith(u8, line, "%error");
|
||||
const output = std.mem.trimRight(u8, self.buffer.items[0..idx], "\r\n");
|
||||
const output = std.mem.trimRight(u8, written[0..idx], "\r\n");
|
||||
|
||||
// If it is an error then log it.
|
||||
if (err) log.warn("tmux control mode error={s}", .{output});
|
||||
|
|
@ -107,7 +108,7 @@ pub const Client = struct {
|
|||
},
|
||||
}
|
||||
|
||||
try self.buffer.append(byte);
|
||||
try self.buffer.writer.writeByte(byte);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -116,7 +117,7 @@ pub const Client = struct {
|
|||
assert(self.state == .notification);
|
||||
|
||||
const line = line: {
|
||||
var line = self.buffer.items;
|
||||
var line = self.buffer.written();
|
||||
if (line[line.len - 1] == '\r') line = line[0 .. line.len - 1];
|
||||
break :line line;
|
||||
};
|
||||
|
|
@ -274,7 +275,7 @@ pub const Client = struct {
|
|||
// Mark the tmux state as broken.
|
||||
fn broken(self: *Client) void {
|
||||
self.state = .broken;
|
||||
self.buffer.clearAndFree();
|
||||
self.buffer.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -313,7 +314,7 @@ test "tmux begin/end empty" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Client = .{ .buffer = std.ArrayList(u8).init(alloc) };
|
||||
var c: Client = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%begin 1578922740 269 1\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%end 1578922740 269 1") |byte| try testing.expect(try c.put(byte) == null);
|
||||
|
|
@ -326,7 +327,7 @@ test "tmux begin/error empty" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Client = .{ .buffer = std.ArrayList(u8).init(alloc) };
|
||||
var c: Client = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%begin 1578922740 269 1\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("%error 1578922740 269 1") |byte| try testing.expect(try c.put(byte) == null);
|
||||
|
|
@ -339,7 +340,7 @@ test "tmux begin/end data" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Client = .{ .buffer = std.ArrayList(u8).init(alloc) };
|
||||
var c: Client = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%begin 1578922740 269 1\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
for ("hello\nworld\n") |byte| try testing.expect(try c.put(byte) == null);
|
||||
|
|
@ -353,7 +354,7 @@ test "tmux output" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Client = .{ .buffer = std.ArrayList(u8).init(alloc) };
|
||||
var c: Client = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%output %42 foo bar baz") |byte| try testing.expect(try c.put(byte) == null);
|
||||
const n = (try c.put('\n')).?;
|
||||
|
|
@ -366,7 +367,7 @@ test "tmux session-changed" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Client = .{ .buffer = std.ArrayList(u8).init(alloc) };
|
||||
var c: Client = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%session-changed $42 foo") |byte| try testing.expect(try c.put(byte) == null);
|
||||
const n = (try c.put('\n')).?;
|
||||
|
|
@ -379,7 +380,7 @@ test "tmux sessions-changed" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Client = .{ .buffer = std.ArrayList(u8).init(alloc) };
|
||||
var c: Client = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%sessions-changed") |byte| try testing.expect(try c.put(byte) == null);
|
||||
const n = (try c.put('\n')).?;
|
||||
|
|
@ -390,7 +391,7 @@ test "tmux sessions-changed carriage return" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Client = .{ .buffer = std.ArrayList(u8).init(alloc) };
|
||||
var c: Client = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%sessions-changed\r") |byte| try testing.expect(try c.put(byte) == null);
|
||||
const n = (try c.put('\n')).?;
|
||||
|
|
@ -401,7 +402,7 @@ test "tmux window-add" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Client = .{ .buffer = std.ArrayList(u8).init(alloc) };
|
||||
var c: Client = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%window-add @14") |byte| try testing.expect(try c.put(byte) == null);
|
||||
const n = (try c.put('\n')).?;
|
||||
|
|
@ -413,7 +414,7 @@ test "tmux window-renamed" {
|
|||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var c: Client = .{ .buffer = std.ArrayList(u8).init(alloc) };
|
||||
var c: Client = .{ .buffer = .init(alloc) };
|
||||
defer c.deinit();
|
||||
for ("%window-renamed @42 bar") |byte| try testing.expect(try c.put(byte) == null);
|
||||
const n = (try c.put('\n')).?;
|
||||
|
|
|
|||
|
|
@ -115,9 +115,10 @@ pub fn xtgettcapMap(comptime self: Source) std.StaticStringMap([]const u8) {
|
|||
},
|
||||
.numeric => |v| numeric: {
|
||||
var buf: [10]u8 = undefined;
|
||||
const num_len = std.fmt.formatIntBuf(&buf, v, 10, .upper, .{});
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
writer.printInt(v, 10, .upper, .{}) catch unreachable;
|
||||
const final = buf;
|
||||
break :numeric final[0..num_len];
|
||||
break :numeric final[0..writer.end];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1518,7 +1518,7 @@ fn execCommand(
|
|||
|
||||
.shell => |v| shell: {
|
||||
var args: std.ArrayList([:0]const u8) = try .initCapacity(alloc, 4);
|
||||
defer args.deinit();
|
||||
defer args.deinit(alloc);
|
||||
|
||||
if (comptime builtin.os.tag == .windows) {
|
||||
// We run our shell wrapped in `cmd.exe` so that we don't have
|
||||
|
|
@ -1539,21 +1539,21 @@ fn execCommand(
|
|||
"cmd.exe",
|
||||
});
|
||||
|
||||
try args.append(cmd);
|
||||
try args.append("/C");
|
||||
try args.append(alloc, cmd);
|
||||
try args.append(alloc, "/C");
|
||||
} else {
|
||||
// We run our shell wrapped in `/bin/sh` so that we don't have
|
||||
// to parse the command line ourselves if it has arguments.
|
||||
// Additionally, some environments (NixOS, I found) use /bin/sh
|
||||
// to setup some environment variables that are important to
|
||||
// have set.
|
||||
try args.append("/bin/sh");
|
||||
if (internal_os.isFlatpak()) try args.append("-l");
|
||||
try args.append("-c");
|
||||
try args.append(alloc, "/bin/sh");
|
||||
if (internal_os.isFlatpak()) try args.append(alloc, "-l");
|
||||
try args.append(alloc, "-c");
|
||||
}
|
||||
|
||||
try args.append(v);
|
||||
break :shell try args.toOwnedSlice();
|
||||
try args.append(alloc, v);
|
||||
break :shell try args.toOwnedSlice(alloc);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,7 +175,9 @@ pub fn setupFeatures(
|
|||
inline for (fields) |field| n += field.name.len;
|
||||
break :capacity n;
|
||||
};
|
||||
var buffer = try std.BoundedArray(u8, capacity).init(0);
|
||||
|
||||
var buf: [capacity]u8 = undefined;
|
||||
var writer: std.Io.Writer = .fixed(&buf);
|
||||
|
||||
// Sort the fields so that the output is deterministic. This is
|
||||
// done at comptime so it has no runtime cost
|
||||
|
|
@ -197,13 +199,13 @@ pub fn setupFeatures(
|
|||
|
||||
inline for (fields_sorted) |name| {
|
||||
if (@field(features, name)) {
|
||||
if (buffer.len > 0) try buffer.append(',');
|
||||
try buffer.appendSlice(name);
|
||||
if (writer.end > 0) try writer.writeByte(',');
|
||||
try writer.writeAll(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer.len > 0) {
|
||||
try env.put("GHOSTTY_SHELL_FEATURES", buffer.slice());
|
||||
if (writer.end > 0) {
|
||||
try env.put("GHOSTTY_SHELL_FEATURES", buf[0..writer.end]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -257,8 +259,8 @@ fn setupBash(
|
|||
resource_dir: []const u8,
|
||||
env: *EnvMap,
|
||||
) !?config.Command {
|
||||
var args = try std.ArrayList([:0]const u8).initCapacity(alloc, 3);
|
||||
defer args.deinit();
|
||||
var args: std.ArrayList([:0]const u8) = try .initCapacity(alloc, 3);
|
||||
defer args.deinit(alloc);
|
||||
|
||||
// Iterator that yields each argument in the original command line.
|
||||
// This will allocate once proportionate to the command line length.
|
||||
|
|
@ -267,21 +269,22 @@ fn setupBash(
|
|||
|
||||
// Start accumulating arguments with the executable and initial flags.
|
||||
if (iter.next()) |exe| {
|
||||
try args.append(try alloc.dupeZ(u8, exe));
|
||||
try args.append(alloc, try alloc.dupeZ(u8, exe));
|
||||
} else return null;
|
||||
try args.append("--posix");
|
||||
try args.append(alloc, "--posix");
|
||||
|
||||
// On macOS, we request a login shell to match that platform's norms.
|
||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||
try args.append("--login");
|
||||
try args.append(alloc, "--login");
|
||||
}
|
||||
|
||||
// Stores the list of intercepted command line flags that will be passed
|
||||
// to our shell integration script: --norc --noprofile
|
||||
// We always include at least "1" so the script can differentiate between
|
||||
// being manually sourced or automatically injected (from here).
|
||||
var inject = try std.BoundedArray(u8, 32).init(0);
|
||||
try inject.appendSlice("1");
|
||||
var buf: [32]u8 = undefined;
|
||||
var inject: std.Io.Writer = .fixed(&buf);
|
||||
try inject.writeAll("1");
|
||||
|
||||
// Walk through the rest of the given arguments. If we see an option that
|
||||
// would require complex or unsupported integration behavior, we bail out
|
||||
|
|
@ -296,9 +299,9 @@ fn setupBash(
|
|||
if (std.mem.eql(u8, arg, "--posix")) {
|
||||
return null;
|
||||
} else if (std.mem.eql(u8, arg, "--norc")) {
|
||||
try inject.appendSlice(" --norc");
|
||||
try inject.writeAll(" --norc");
|
||||
} else if (std.mem.eql(u8, arg, "--noprofile")) {
|
||||
try inject.appendSlice(" --noprofile");
|
||||
try inject.writeAll(" --noprofile");
|
||||
} else if (std.mem.eql(u8, arg, "--rcfile") or std.mem.eql(u8, arg, "--init-file")) {
|
||||
rcfile = iter.next();
|
||||
} else if (arg.len > 1 and arg[0] == '-' and arg[1] != '-') {
|
||||
|
|
@ -306,20 +309,20 @@ fn setupBash(
|
|||
if (std.mem.indexOfScalar(u8, arg, 'c') != null) {
|
||||
return null;
|
||||
}
|
||||
try args.append(try alloc.dupeZ(u8, arg));
|
||||
try args.append(alloc, try alloc.dupeZ(u8, arg));
|
||||
} else if (std.mem.eql(u8, arg, "-") or std.mem.eql(u8, arg, "--")) {
|
||||
// All remaining arguments should be passed directly to the shell
|
||||
// command. We shouldn't perform any further option processing.
|
||||
try args.append(try alloc.dupeZ(u8, arg));
|
||||
try args.append(alloc, try alloc.dupeZ(u8, arg));
|
||||
while (iter.next()) |remaining_arg| {
|
||||
try args.append(try alloc.dupeZ(u8, remaining_arg));
|
||||
try args.append(alloc, try alloc.dupeZ(u8, remaining_arg));
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
try args.append(try alloc.dupeZ(u8, arg));
|
||||
try args.append(alloc, try alloc.dupeZ(u8, arg));
|
||||
}
|
||||
}
|
||||
try env.put("GHOSTTY_BASH_INJECT", inject.slice());
|
||||
try env.put("GHOSTTY_BASH_INJECT", buf[0..inject.end]);
|
||||
if (rcfile) |v| {
|
||||
try env.put("GHOSTTY_BASH_RCFILE", v);
|
||||
}
|
||||
|
|
@ -356,7 +359,7 @@ fn setupBash(
|
|||
|
||||
// Since we built up a command line, we don't need to wrap it in
|
||||
// ANOTHER shell anymore and can do a direct command.
|
||||
return .{ .direct = try args.toOwnedSlice() };
|
||||
return .{ .direct = try args.toOwnedSlice(alloc) };
|
||||
}
|
||||
|
||||
test "bash" {
|
||||
|
|
|
|||
Loading…
Reference in New Issue