diff --git a/build.zig b/build.zig
index cb8f175a4..7b66af81a 100644
--- a/build.zig
+++ b/build.zig
@@ -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(&.{
diff --git a/src/Command.zig b/src/Command.zig
index b0d804327..f28d8bb9d 100644
--- a/src/Command.zig
+++ b/src/Command.zig
@@ -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);
diff --git a/src/apprt/gtk/build/blueprint.zig b/src/apprt/gtk/build/blueprint.zig
index 1e614f972..f25e7e1f9 100644
--- a/src/apprt/gtk/build/blueprint.zig
+++ b/src/apprt/gtk/build/blueprint.zig
@@ -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.
diff --git a/src/apprt/gtk/build/gresource.zig b/src/apprt/gtk/build/gresource.zig
index 1f253fd5e..7adcd3e44 100644
--- a/src/apprt/gtk/build/gresource.zig
+++ b/src/apprt/gtk/build/gresource.zig
@@ -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(
\\
\\
@@ -157,6 +159,8 @@ pub fn main() !void {
\\
\\
);
+
+ try stdout.end();
}
/// Generate the icon resources. This works by looking up all the icons
diff --git a/src/apprt/gtk/class/config.zig b/src/apprt/gtk/class/config.zig
index 2b98c68b5..eadd3b7b8 100644
--- a/src/apprt/gtk/class/config.zig
+++ b/src/apprt/gtk/class/config.zig
@@ -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);
}
diff --git a/src/apprt/gtk/ipc/DBus.zig b/src/apprt/gtk/ipc/DBus.zig
index d14d86ce6..fa4a6723e 100644
--- a/src/apprt/gtk/ipc/DBus.zig
+++ b/src/apprt/gtk/ipc/DBus.zig
@@ -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;
}
}
diff --git a/src/apprt/gtk/ipc/new_window.zig b/src/apprt/gtk/ipc/new_window.zig
index 55e2e0e01..19c46e3aa 100644
--- a/src/apprt/gtk/ipc/new_window.zig
+++ b/src/apprt/gtk/ipc/new_window.zig
@@ -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,
diff --git a/src/benchmark/CodepointWidth.zig b/src/benchmark/CodepointWidth.zig
index 9bbc2def7..552df8d1f 100644
--- a/src/benchmark/CodepointWidth.zig
+++ b/src/benchmark/CodepointWidth.zig
@@ -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;
diff --git a/src/benchmark/GraphemeBreak.zig b/src/benchmark/GraphemeBreak.zig
index e576c71ef..a1b3380f0 100644
--- a/src/benchmark/GraphemeBreak.zig
+++ b/src/benchmark/GraphemeBreak.zig
@@ -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;
diff --git a/src/benchmark/IsSymbol.zig b/src/benchmark/IsSymbol.zig
index 97af0657a..dffa5071a 100644
--- a/src/benchmark/IsSymbol.zig
+++ b/src/benchmark/IsSymbol.zig
@@ -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) {
diff --git a/src/benchmark/TerminalParser.zig b/src/benchmark/TerminalParser.zig
index 3065c1ed6..f13b44552 100644
--- a/src/benchmark/TerminalParser.zig
+++ b/src/benchmark/TerminalParser.zig
@@ -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
diff --git a/src/benchmark/TerminalStream.zig b/src/benchmark/TerminalStream.zig
index 71ab1fdfc..ecce509f3 100644
--- a/src/benchmark/TerminalStream.zig
+++ b/src/benchmark/TerminalStream.zig
@@ -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;
};
diff --git a/src/benchmark/options.zig b/src/benchmark/options.zig
index 867be6afc..049e80f48 100644
--- a/src/benchmark/options.zig
+++ b/src/benchmark/options.zig
@@ -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, .{});
diff --git a/src/build/GhosttyFrameData.zig b/src/build/GhosttyFrameData.zig
index 7193162bd..def1dbdb3 100644
--- a/src/build/GhosttyFrameData.zig
+++ b/src/build/GhosttyFrameData.zig
@@ -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"),
diff --git a/src/cli/args.zig b/src/cli/args.zig
index c4a40acf5..a34560b78 100644
--- a/src/cli/args.zig
+++ b/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());
diff --git a/src/cli/boo.zig b/src/cli/boo.zig
index 72b282ef6..756b6d77a 100644
--- a/src/cli/boo.zig
+++ b/src/cli/boo.zig
@@ -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);
}
diff --git a/src/cli/crash_report.zig b/src/cli/crash_report.zig
index c6a383563..f0940fdab 100644
--- a/src/cli/crash_report.zig
+++ b/src/cli/crash_report.zig
@@ -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;
diff --git a/src/cli/diagnostics.zig b/src/cli/diagnostics.zig
index 2c6cb3b30..2af8bb4f8 100644
--- a/src/cli/diagnostics.zig
+++ b/src/cli/diagnostics.zig
@@ -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);
diff --git a/src/cli/edit_config.zig b/src/cli/edit_config.zig
index 116843037..f103ca4a0 100644
--- a/src/cli/edit_config.zig
+++ b/src/cli/edit_config.zig
@@ -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,
);
diff --git a/src/cli/ghostty.zig b/src/cli/ghostty.zig
index adb715d68..f6ac7d93d 100644
--- a/src/cli/ghostty.zig
+++ b/src/cli/ghostty.zig
@@ -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;
}
diff --git a/src/cli/help.zig b/src/cli/help.zig
index 0528dc1c2..a2b4dde80 100644
--- a/src/cli/help.zig
+++ b/src/cli/help.zig
@@ -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 `` is one of actions listed above.
\\
);
+ try stdout.flush();
return 0;
}
diff --git a/src/cli/list_actions.zig b/src/cli/list_actions.zig
index 6f5ce06a2..682eed251 100644
--- a/src/cli/list_actions.zig
+++ b/src/cli/list_actions.zig
@@ -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;
}
diff --git a/src/cli/list_colors.zig b/src/cli/list_colors.zig
index 63945de99..50c12a693 100644
--- a/src/cli/list_colors.zig
+++ b/src/cli/list_colors.zig
@@ -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;
}
diff --git a/src/cli/list_fonts.zig b/src/cli/list_fonts.zig
index 58246d3ad..396c4e8a6 100644
--- a/src/cli/list_fonts.zig
+++ b/src/cli/list_fonts.zig
@@ -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;
}
diff --git a/src/cli/list_keybinds.zig b/src/cli/list_keybinds.zig
index 94f445eea..a8899a4f5 100644
--- a/src/cli/list_keybinds.zig
+++ b/src/cli/list_keybinds.zig
@@ -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 };
}
diff --git a/src/cli/list_themes.zig b/src/cli/list_themes.zig
index 0c0acfe84..cc6cfaf3e 100644
--- a/src/cli/list_themes.zig
+++ b/src/cli/list_themes.zig
@@ -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();
}
diff --git a/src/cli/new_window.zig b/src/cli/new_window.zig
index 343175b4e..f3f4740d1 100644
--- a/src/cli/new_window.zig
+++ b/src/cli/new_window.zig
@@ -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;
}
}
diff --git a/src/cli/show_config.zig b/src/cli/show_config.zig
index 3f22c75c2..1b73b77c1 100644
--- a/src/cli/show_config.zig
+++ b/src/cli/show_config.zig
@@ -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;
}
diff --git a/src/cli/show_face.zig b/src/cli/show_face.zig
index e3b596bcd..9dee777b3 100644
--- a/src/cli/show_face.zig
+++ b/src/cli/show_face.zig
@@ -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,
diff --git a/src/cli/ssh-cache/DiskCache.zig b/src/cli/ssh-cache/DiskCache.zig
index db138cf37..608155dfd 100644
--- a/src/cli/ssh-cache/DiskCache.zig
+++ b/src/cli/ssh-cache/DiskCache.zig
@@ -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);
diff --git a/src/cli/ssh-cache/Entry.zig b/src/cli/ssh-cache/Entry.zig
index 3a691be80..f3403dbd4 100644
--- a/src/cli/ssh-cache/Entry.zig
+++ b/src/cli/ssh-cache/Entry.zig
@@ -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 },
diff --git a/src/cli/ssh_cache.zig b/src/cli/ssh_cache.zig
index 1099f0112..9434e9771 100644
--- a/src/cli/ssh_cache.zig
+++ b/src/cli/ssh_cache.zig
@@ -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 {
diff --git a/src/cli/validate_config.zig b/src/cli/validate_config.zig
index 114843e9a..55d861402 100644
--- a/src/cli/validate_config.zig
+++ b/src/cli/validate_config.zig
@@ -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;
}
diff --git a/src/cli/version.zig b/src/cli/version.zig
index 22608fa88..cf8e66fa6 100644
--- a/src/cli/version.zig
+++ b/src/cli/version.zig
@@ -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;
}
diff --git a/src/config/Config.zig b/src/config/Config.zig
index 8f811e9a4..caaf5feb8 100644
--- a/src/config/Config.zig
+++ b/src/config/Config.zig
@@ -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);
diff --git a/src/config/RepeatableStringMap.zig b/src/config/RepeatableStringMap.zig
index 6f143e95d..d5e634333 100644
--- a/src/config/RepeatableStringMap.zig
+++ b/src/config/RepeatableStringMap.zig
@@ -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());
}
}
diff --git a/src/config/command.zig b/src/config/command.zig
index 9efeb199e..e0cdc641b 100644
--- a/src/config/command.zig
+++ b/src/config/command.zig
@@ -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());
}
};
diff --git a/src/config/edit.zig b/src/config/edit.zig
index 38dc98169..07bb7ee5a 100644
--- a/src/config/edit.zig
+++ b/src/config/edit.zig
@@ -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(
diff --git a/src/config/formatter.zig b/src/config/formatter.zig
index a42395c19..dcf99167d 100644
--- a/src/config/formatter.zig
+++ b/src/config/formatter.zig
@@ -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());
}
}
diff --git a/src/config/io.zig b/src/config/io.zig
index 8be4be551..9d9a127e8 100644
--- a/src/config/io.zig
+++ b/src/config/io.zig
@@ -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, {});
diff --git a/src/config/path.zig b/src/config/path.zig
index 651dbdb3a..aeba69b94 100644
--- a/src/config/path.zig
+++ b/src/config/path.zig
@@ -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());
}
};
diff --git a/src/config/theme.zig b/src/config/theme.zig
index 8fa7c93dc..b1188a5c4 100644
--- a/src/config/theme.zig
+++ b/src/config/theme.zig
@@ -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,
),
}),
}
diff --git a/src/crash/sentry_envelope.zig b/src/crash/sentry_envelope.zig
index 6b675554c..08573b739 100644
--- a/src/crash/sentry_envelope.zig
+++ b/src/crash/sentry_envelope.zig
@@ -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;
-// }
-// }
diff --git a/src/datastruct/main.zig b/src/datastruct/main.zig
index 5aa68555f..14ee0e504 100644
--- a/src/datastruct/main.zig
+++ b/src/datastruct/main.zig
@@ -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());
diff --git a/src/datastruct/split_tree.zig b/src/datastruct/split_tree.zig
index 28b45ceed..eb371187c 100644
--- a/src/datastruct/split_tree.zig
+++ b/src/datastruct/split_tree.zig
@@ -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
diff --git a/src/extra/vim.zig b/src/extra/vim.zig
index 4443fd168..ff85e820b 100644
--- a/src/extra/vim.zig
+++ b/src/extra/vim.zig
@@ -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];
}
}
diff --git a/src/font/Metrics.zig b/src/font/Metrics.zig
index a0bc047c4..668b6f15f 100644
--- a/src/font/Metrics.zig
+++ b/src/font/Metrics.zig
@@ -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());
}
};
diff --git a/src/font/SharedGridSet.zig b/src/font/SharedGridSet.zig
index 813a8d6d0..4512e23cc 100644
--- a/src/font/SharedGridSet.zig
+++ b/src/font/SharedGridSet.zig
@@ -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,
diff --git a/src/font/shaper/run.zig b/src/font/shaper/run.zig
index 7bd019fd7..da3c51cee 100644
--- a/src/font/shaper/run.zig
+++ b/src/font/shaper/run.zig
@@ -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| {
diff --git a/src/global.zig b/src/global.zig
index e68ec7f74..8034fabe0 100644
--- a/src/global.zig
+++ b/src/global.zig
@@ -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,
};
diff --git a/src/helpgen.zig b/src/helpgen.zig
index 57296fe86..fe30db10c 100644
--- a/src/helpgen.zig
+++ b/src/helpgen.zig
@@ -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();
}
diff --git a/src/input/Binding.zig b/src/input/Binding.zig
index 642044067..c44fb0b09 100644
--- a/src/input/Binding.zig
+++ b/src/input/Binding.zig
@@ -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());
}
diff --git a/src/input/command.zig b/src/input/command.zig
index bf5061c12..ba55820fc 100644
--- a/src/input/command.zig
+++ b/src/input/command.zig
@@ -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;
diff --git a/src/input/function_keys.zig b/src/input/function_keys.zig
index 33a5b89c0..8c89b39bd 100644
--- a/src/input/function_keys.zig
+++ b/src/input/function_keys.zig
@@ -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] = .{
diff --git a/src/input/helpgen_actions.zig b/src/input/helpgen_actions.zig
index 1382bbe95..4210f1f91 100644
--- a/src/input/helpgen_actions.zig
+++ b/src/input/helpgen_actions.zig
@@ -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());
}
}
diff --git a/src/renderer/link.zig b/src/renderer/link.zig
index 410fb8632..9f489ed48 100644
--- a/src/renderer/link.zig
+++ b/src/renderer/link.zig
@@ -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);
}
}
}
diff --git a/src/renderer/shadertoy.zig b/src/renderer/shadertoy.zig
index 576237587..a8f62cdea 100644
--- a/src/renderer/shadertoy.zig
+++ b/src/renderer/shadertoy.zig
@@ -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);
diff --git a/src/terminal/apc.zig b/src/terminal/apc.zig
index a168da4a1..704c3fbe3 100644
--- a/src/terminal/apc.zig
+++ b/src/terminal/apc.zig
@@ -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;
};
diff --git a/src/terminal/dcs.zig b/src/terminal/dcs.zig
index 4694fc457..971ea13a0 100644
--- a/src/terminal/dcs.zig
+++ b/src/terminal/dcs.zig
@@ -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
),
diff --git a/src/terminal/kitty/graphics_command.zig b/src/terminal/kitty/graphics_command.zig
index dcb4850c9..2ce01f83a 100644
--- a/src/terminal/kitty/graphics_command.zig
+++ b/src/terminal/kitty/graphics_command.zig
@@ -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));
}
diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig
index f32b70be2..268f71601 100644
--- a/src/terminal/kitty/graphics_image.zig
+++ b/src/terminal/kitty/graphics_image.zig
@@ -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;
};
diff --git a/src/terminal/kitty/graphics_storage.zig b/src/terminal/kitty/graphics_storage.zig
index 0c3022e4a..8aef0ece5 100644
--- a/src/terminal/kitty/graphics_storage.zig
+++ b/src/terminal/kitty/graphics_storage.zig
@@ -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,
diff --git a/src/terminal/tmux.zig b/src/terminal/tmux.zig
index 1ea9f8c39..67c5a979c 100644
--- a/src/terminal/tmux.zig
+++ b/src/terminal/tmux.zig
@@ -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')).?;
diff --git a/src/terminfo/Source.zig b/src/terminfo/Source.zig
index 7692e6f54..d5c3f427a 100644
--- a/src/terminfo/Source.zig
+++ b/src/terminfo/Source.zig
@@ -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];
},
},
};
diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig
index 77fd2cc68..8c729fddc 100644
--- a/src/termio/Exec.zig
+++ b/src/termio/Exec.zig
@@ -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);
},
};
}
diff --git a/src/termio/shell_integration.zig b/src/termio/shell_integration.zig
index 90a697409..8b2648dbd 100644
--- a/src/termio/shell_integration.zig
+++ b/src/termio/shell_integration.zig
@@ -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" {