summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMitchell Hashimoto <m@mitchellh.com>2025-10-03 07:12:12 -0700
committerGitHub <noreply@github.com>2025-10-03 07:12:12 -0700
commite0cf528576f998aea67c27fbc2384f920ac8381c (patch)
tree6173dcaf3958df4237f511cced34d7328d30523f /src
parentdfa2354fecb82c3408c659aa55159e11cd10db9a (diff)
parent569fe9238959943a6d3ff39d54ffec10b31cae0c (diff)
Zig 0.15 (#9004)
Replaces #8372 Before merge I'm going to squash this and give @pluiedev coauthor, since I took a lot of her work. I just have to go through this myself to make sure I learn all the changes in Zig 0.15, but as I got things, I copy and pasted her work in. My work is probably less thorough and there are places we can convert deprecated things, probably, but this results in green CI. ## Benchmarks It looks like there are some speed regressions in isolated places. I'm not sure if this is noise or not, I'm going to keep running some tests. If someone can check macOS that'd be helpful (my vtebench is down on macOS atm), cc @qwerasd205 if interested. On an x86_64 system: ![output](https://github.com/user-attachments/assets/25395c18-2560-45c8-9fd3-d524bc5cbfb8)
Diffstat (limited to 'src')
-rw-r--r--src/Command.zig26
-rw-r--r--src/Surface.zig18
-rw-r--r--src/apprt/action.zig6
-rw-r--r--src/apprt/embedded.zig6
-rw-r--r--src/apprt/gtk/adw_version.zig2
-rw-r--r--src/apprt/gtk/build/blueprint.zig8
-rw-r--r--src/apprt/gtk/build/gresource.zig12
-rw-r--r--src/apprt/gtk/cgroup.zig12
-rw-r--r--src/apprt/gtk/class.zig2
-rw-r--r--src/apprt/gtk/class/application.zig8
-rw-r--r--src/apprt/gtk/class/command_palette.zig5
-rw-r--r--src/apprt/gtk/class/config.zig8
-rw-r--r--src/apprt/gtk/class/global_shortcuts.zig6
-rw-r--r--src/apprt/gtk/class/resize_overlay.zig4
-rw-r--r--src/apprt/gtk/class/split_tree.zig2
-rw-r--r--src/apprt/gtk/class/surface.zig58
-rw-r--r--src/apprt/gtk/class/surface_title_dialog.zig2
-rw-r--r--src/apprt/gtk/class/tab.zig15
-rw-r--r--src/apprt/gtk/gtk_version.zig2
-rw-r--r--src/apprt/gtk/ipc/DBus.zig23
-rw-r--r--src/apprt/gtk/ipc/new_window.zig2
-rw-r--r--src/apprt/gtk/key.zig23
-rw-r--r--src/benchmark/CodepointWidth.zig61
-rw-r--r--src/benchmark/GraphemeBreak.zig50
-rw-r--r--src/benchmark/IsSymbol.zig6
-rw-r--r--src/benchmark/TerminalParser.zig10
-rw-r--r--src/benchmark/TerminalStream.zig14
-rw-r--r--src/benchmark/options.zig2
-rw-r--r--src/build/Config.zig2
-rw-r--r--src/build/GhosttyBench.zig8
-rw-r--r--src/build/GhosttyDist.zig14
-rw-r--r--src/build/GhosttyDocs.zig10
-rw-r--r--src/build/GhosttyExe.zig2
-rw-r--r--src/build/GhosttyFrameData.zig5
-rw-r--r--src/build/GhosttyI18n.zig8
-rw-r--r--src/build/GhosttyLib.zig12
-rw-r--r--src/build/GhosttyLibVt.zig3
-rw-r--r--src/build/GhosttyResources.zig67
-rw-r--r--src/build/GhosttyWebdata.zig22
-rw-r--r--src/build/MetallibStep.zig2
-rw-r--r--src/build/SharedDeps.zig44
-rw-r--r--src/build/UnicodeTables.zig6
-rw-r--r--src/build/docker/debian/Dockerfile4
-rw-r--r--src/build/mdgen/main_ghostty_1.zig7
-rw-r--r--src/build/mdgen/main_ghostty_5.zig15
-rw-r--r--src/build/mdgen/mdgen.zig8
-rw-r--r--src/build/webgen/main_actions.zig6
-rw-r--r--src/build/webgen/main_commands.zig8
-rw-r--r--src/build/webgen/main_config.zig10
-rw-r--r--src/cli/args.zig275
-rw-r--r--src/cli/boo.zig19
-rw-r--r--src/cli/crash_report.zig29
-rw-r--r--src/cli/diagnostics.zig13
-rw-r--r--src/cli/edit_config.zig33
-rw-r--r--src/cli/ghostty.zig8
-rw-r--r--src/cli/help.zig5
-rw-r--r--src/cli/list_actions.zig11
-rw-r--r--src/cli/list_colors.zig32
-rw-r--r--src/cli/list_fonts.zig16
-rw-r--r--src/cli/list_keybinds.zig110
-rw-r--r--src/cli/list_themes.zig97
-rw-r--r--src/cli/new_window.zig23
-rw-r--r--src/cli/show_config.zig7
-rw-r--r--src/cli/show_face.zig41
-rw-r--r--src/cli/ssh-cache/DiskCache.zig40
-rw-r--r--src/cli/ssh-cache/Entry.zig2
-rw-r--r--src/cli/ssh_cache.zig33
-rw-r--r--src/cli/validate_config.zig21
-rw-r--r--src/cli/version.zig25
-rw-r--r--src/config/Config.zig336
-rw-r--r--src/config/RepeatableStringMap.zig26
-rw-r--r--src/config/command.zig19
-rw-r--r--src/config/edit.zig4
-rw-r--r--src/config/formatter.zig131
-rw-r--r--src/config/io.zig16
-rw-r--r--src/config/path.zig40
-rw-r--r--src/config/theme.zig27
-rw-r--r--src/crash/sentry.zig9
-rw-r--r--src/crash/sentry_envelope.zig192
-rw-r--r--src/datastruct/lru.zig70
-rw-r--r--src/datastruct/split_tree.zig119
-rw-r--r--src/extra/bash.zig14
-rw-r--r--src/extra/fish.zig14
-rw-r--r--src/extra/vim.zig15
-rw-r--r--src/extra/zsh.zig14
-rw-r--r--src/font/Atlas.zig2
-rw-r--r--src/font/Collection.zig9
-rw-r--r--src/font/Metrics.zig12
-rw-r--r--src/font/SharedGridSet.zig14
-rw-r--r--src/font/opentype/sfnt.zig6
-rw-r--r--src/font/shaper/feature.zig4
-rw-r--r--src/font/shaper/run.zig4
-rw-r--r--src/global.zig4
-rw-r--r--src/helpgen.zig39
-rw-r--r--src/input/Binding.zig63
-rw-r--r--src/input/command.zig3
-rw-r--r--src/input/function_keys.zig1
-rw-r--r--src/input/helpgen_actions.zig31
-rw-r--r--src/inspector/Inspector.zig3
-rw-r--r--src/inspector/termio.zig24
-rw-r--r--src/main_build_data.zig4
-rw-r--r--src/main_ghostty.zig13
-rw-r--r--src/os/cgroup.zig26
-rw-r--r--src/os/shell.zig151
-rw-r--r--src/pty.zig2
-rw-r--r--src/renderer/link.zig26
-rw-r--r--src/renderer/shadertoy.zig94
-rw-r--r--src/synthetic/cli.zig7
-rw-r--r--src/synthetic/cli/Ascii.zig12
-rw-r--r--src/synthetic/cli/Osc.zig12
-rw-r--r--src/synthetic/cli/Utf8.zig12
-rw-r--r--src/terminal/PageList.zig21
-rw-r--r--src/terminal/Parser.zig34
-rw-r--r--src/terminal/Screen.zig84
-rw-r--r--src/terminal/Terminal.zig11
-rw-r--r--src/terminal/apc.zig4
-rw-r--r--src/terminal/bitmap_allocator.zig6
-rw-r--r--src/terminal/dcs.zig33
-rw-r--r--src/terminal/hash_map.zig22
-rw-r--r--src/terminal/kitty/color.zig11
-rw-r--r--src/terminal/kitty/graphics_command.zig82
-rw-r--r--src/terminal/kitty/graphics_image.zig26
-rw-r--r--src/terminal/kitty/graphics_storage.zig6
-rw-r--r--src/terminal/osc.zig20
-rw-r--r--src/terminal/page.zig56
-rw-r--r--src/terminal/ref_counted_set.zig4
-rw-r--r--src/terminal/search.zig142
-rw-r--r--src/terminal/stream.zig286
-rw-r--r--src/terminal/style.zig4
-rw-r--r--src/terminal/tmux.zig35
-rw-r--r--src/terminfo/Source.zig13
-rw-r--r--src/terminfo/ghostty.zig6
-rw-r--r--src/termio/Exec.zig62
-rw-r--r--src/termio/shell_integration.zig43
-rw-r--r--src/termio/stream_handler.zig47
-rw-r--r--src/unicode/Properties.zig8
-rw-r--r--src/unicode/lut.zig36
-rw-r--r--src/unicode/props_uucode.zig6
-rw-r--r--src/unicode/symbols_uucode.zig6
139 files changed, 2265 insertions, 1969 deletions
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/Surface.zig b/src/Surface.zig
index 3b4bf872f..403a628d2 100644
--- a/src/Surface.zig
+++ b/src/Surface.zig
@@ -305,19 +305,19 @@ const DerivedConfig = struct {
// Build all of our links
const links = links: {
- var links = std.ArrayList(Link).init(alloc);
- defer links.deinit();
+ var links: std.ArrayList(Link) = .empty;
+ defer links.deinit(alloc);
for (config.link.links.items) |link| {
var regex = try link.oniRegex();
errdefer regex.deinit();
- try links.append(.{
+ try links.append(alloc, .{
.regex = regex,
.action = link.action,
.highlight = link.highlight,
});
}
- break :links try links.toOwnedSlice();
+ break :links try links.toOwnedSlice(alloc);
};
errdefer {
for (links) |*link| link.regex.deinit();
@@ -1009,7 +1009,7 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
self.command_timer = null;
const duration: Duration = .{ .duration = end.since(start) };
- log.debug("command took {}", .{duration});
+ log.debug("command took {f}", .{duration});
_ = self.rt_app.performAction(
.{ .surface = self },
@@ -2493,7 +2493,7 @@ fn maybeHandleBinding(
self.keyboard.bindings = null;
// Attempt to perform the action
- log.debug("key event binding flags={} action={}", .{
+ log.debug("key event binding flags={} action={f}", .{
leaf.flags,
action,
});
@@ -5119,7 +5119,9 @@ fn writeScreenFile(
defer file.close();
// Screen.dumpString writes byte-by-byte, so buffer it
- var buf_writer = std.io.bufferedWriter(file.writer());
+ var buf: [4096]u8 = undefined;
+ var file_writer = file.writer(&buf);
+ var buf_writer = &file_writer.interface;
// Write the scrollback contents. This requires a lock.
{
@@ -5169,7 +5171,7 @@ fn writeScreenFile(
const br = sel.bottomRight(&self.io.terminal.screen);
try self.io.terminal.screen.dumpString(
- buf_writer.writer(),
+ buf_writer,
.{
.tl = tl,
.br = br,
diff --git a/src/apprt/action.zig b/src/apprt/action.zig
index b356ff32f..14a8165f2 100644
--- a/src/apprt/action.zig
+++ b/src/apprt/action.zig
@@ -578,7 +578,7 @@ pub const SetTitle = struct {
value: @This(),
comptime _: []const u8,
_: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
try writer.print("{s}{{ {s} }}", .{ @typeName(@This()), value.title });
}
@@ -602,7 +602,7 @@ pub const Pwd = struct {
value: @This(),
comptime _: []const u8,
_: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
try writer.print("{s}{{ {s} }}", .{ @typeName(@This()), value.pwd });
}
@@ -630,7 +630,7 @@ pub const DesktopNotification = struct {
value: @This(),
comptime _: []const u8,
_: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
try writer.print("{s}{{ title: {s}, body: {s} }}", .{
@typeName(@This()),
diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig
index 08d8291ef..617557995 100644
--- a/src/apprt/embedded.zig
+++ b/src/apprt/embedded.zig
@@ -266,8 +266,8 @@ pub const App = struct {
// embedded apprt.
self.performPreAction(target, action, value);
- log.debug("dispatching action target={s} action={} value={}", .{
- @tagName(target),
+ log.debug("dispatching action target={t} action={} value={any}", .{
+ target,
action,
value,
});
@@ -1910,7 +1910,7 @@ pub const CAPI = struct {
};
return ptr.core_surface.performBindingAction(action) catch |err| {
- log.err("error performing binding action action={} err={}", .{ action, err });
+ log.err("error performing binding action action={f} err={}", .{ action, err });
return false;
};
}
diff --git a/src/apprt/gtk/adw_version.zig b/src/apprt/gtk/adw_version.zig
index 7ce88f585..6f7be52da 100644
--- a/src/apprt/gtk/adw_version.zig
+++ b/src/apprt/gtk/adw_version.zig
@@ -27,7 +27,7 @@ pub fn getRuntimeVersion() std.SemanticVersion {
}
pub fn logVersion() void {
- log.info("libadwaita version build={} runtime={}", .{
+ log.info("libadwaita version build={f} runtime={f}", .{
comptime_version,
getRuntimeVersion(),
});
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..fabd5763e 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(
\\<?xml version="1.0" encoding="UTF-8"?>
\\<gresources>
@@ -157,12 +159,14 @@ pub fn main() !void {
\\</gresources>
\\
);
+
+ try stdout.end();
}
/// Generate the icon resources. This works by looking up all the icons
/// specified by `icon_sizes` in `images/icons/`. They are asserted to exist
/// by trying to access the file.
-fn genIcons(writer: anytype) !void {
+fn genIcons(writer: *std.Io.Writer) !void {
try writer.print(
\\ <gresource prefix="{s}/icons">
\\
@@ -204,7 +208,7 @@ fn genIcons(writer: anytype) !void {
}
/// Generate the resources at the root prefix.
-fn genRoot(writer: anytype) !void {
+fn genRoot(writer: *std.Io.Writer) !void {
try writer.print(
\\ <gresource prefix="{s}">
\\
@@ -236,7 +240,7 @@ fn genRoot(writer: anytype) !void {
/// assuming these will be
fn genUi(
alloc: Allocator,
- writer: anytype,
+ writer: *std.Io.Writer,
files: *const std.ArrayListUnmanaged([]const u8),
) !void {
try writer.print(
diff --git a/src/apprt/gtk/cgroup.zig b/src/apprt/gtk/cgroup.zig
index 23c4d545e..697126798 100644
--- a/src/apprt/gtk/cgroup.zig
+++ b/src/apprt/gtk/cgroup.zig
@@ -50,7 +50,7 @@ pub fn init(
) orelse "";
if (!std.mem.eql(u8, original, current)) break :transient current;
alloc.free(current);
- std.time.sleep(25 * std.time.ns_per_ms);
+ std.Thread.sleep(25 * std.time.ns_per_ms);
};
errdefer alloc.free(transient);
log.info("transient scope created cgroup={s}", .{transient});
@@ -101,21 +101,21 @@ fn enableControllers(alloc: Allocator, cgroup: []const u8) !void {
defer alloc.free(raw);
// Build our string builder for enabling all controllers
- var builder = std.ArrayList(u8).init(alloc);
+ var builder: std.Io.Writer.Allocating = .init(alloc);
defer builder.deinit();
// Controllers are space-separated
var it = std.mem.splitScalar(u8, raw, ' ');
while (it.next()) |controller| {
- try builder.append('+');
- try builder.appendSlice(controller);
- if (it.rest().len > 0) try builder.append(' ');
+ try builder.writer.writeByte('+');
+ try builder.writer.writeAll(controller);
+ if (it.rest().len > 0) try builder.writer.writeByte(' ');
}
// Enable them all
try internal_os.cgroup.configureControllers(
cgroup,
- builder.items,
+ builder.written(),
);
}
diff --git a/src/apprt/gtk/class.zig b/src/apprt/gtk/class.zig
index 4b46f8365..942666cf4 100644
--- a/src/apprt/gtk/class.zig
+++ b/src/apprt/gtk/class.zig
@@ -282,7 +282,7 @@ pub fn Common(
fn setter(self: *Self, value: ?[:0]const u8) void {
const priv = private(self);
if (@field(priv, name)) |v| {
- glib.free(@constCast(@ptrCast(v)));
+ glib.free(@ptrCast(@constCast(v)));
}
// We don't need to copy this because it was already
diff --git a/src/apprt/gtk/class/application.zig b/src/apprt/gtk/class/application.zig
index 90c72681d..af56130d3 100644
--- a/src/apprt/gtk/class/application.zig
+++ b/src/apprt/gtk/class/application.zig
@@ -1044,7 +1044,9 @@ pub const Application = extern struct {
defer file.close();
log.info("loading gtk-custom-css path={s}", .{path});
- const contents = try file.reader().readAllAlloc(
+ var buf: [4096]u8 = undefined;
+ var reader = file.reader(&buf);
+ const contents = try reader.interface.readAlloc(
alloc,
5 * 1024 * 1024, // 5MB,
);
@@ -1115,8 +1117,8 @@ pub const Application = extern struct {
// This should really never, never happen. Its not critical enough
// to actually crash, but this is a bug somewhere. An accelerator
// for a trigger can't possibly be more than 1024 bytes.
- error.NoSpaceLeft => {
- log.warn("accelerator somehow longer than 1024 bytes: {}", .{trigger});
+ error.WriteFailed => {
+ log.warn("accelerator somehow longer than 1024 bytes: {f}", .{trigger});
return;
},
};
diff --git a/src/apprt/gtk/class/command_palette.zig b/src/apprt/gtk/class/command_palette.zig
index 8b7bb328c..6da49115e 100644
--- a/src/apprt/gtk/class/command_palette.zig
+++ b/src/apprt/gtk/class/command_palette.zig
@@ -485,10 +485,11 @@ const Command = extern struct {
const command = priv.command orelse return null;
- priv.action_key = std.fmt.allocPrintZ(
+ priv.action_key = std.fmt.allocPrintSentinel(
priv.arena.allocator(),
- "{}",
+ "{f}",
.{command.action},
+ 0,
) catch null;
return priv.action_key;
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/class/global_shortcuts.zig b/src/apprt/gtk/class/global_shortcuts.zig
index 18280cfe9..9c67be7c1 100644
--- a/src/apprt/gtk/class/global_shortcuts.zig
+++ b/src/apprt/gtk/class/global_shortcuts.zig
@@ -188,9 +188,9 @@ pub const GlobalShortcuts = extern struct {
// If there isn't space to translate the trigger, then our
// buffer might be too small (but 1024 is insane!). In any case
// we don't want to stop registering globals.
- error.NoSpaceLeft => {
+ error.WriteFailed => {
log.warn(
- "buffer too small to translate trigger, ignoring={}",
+ "buffer too small to translate trigger, ignoring={f}",
.{entry.key_ptr.*},
);
continue;
@@ -257,7 +257,7 @@ pub const GlobalShortcuts = extern struct {
const trigger = entry.key_ptr.*.ptr;
const action = std.fmt.bufPrintZ(
&action_buf,
- "{}",
+ "{f}",
.{entry.value_ptr.*},
) catch continue;
diff --git a/src/apprt/gtk/class/resize_overlay.zig b/src/apprt/gtk/class/resize_overlay.zig
index 9bb9a0a7c..f6e0c1442 100644
--- a/src/apprt/gtk/class/resize_overlay.zig
+++ b/src/apprt/gtk/class/resize_overlay.zig
@@ -172,7 +172,7 @@ pub const ResizeOverlay = extern struct {
/// overlay if it is currently hidden; you must call schedule.
pub fn setLabel(self: *Self, label: ?[:0]const u8) void {
const priv = self.private();
- if (priv.label_text) |v| glib.free(@constCast(@ptrCast(v)));
+ if (priv.label_text) |v| glib.free(@ptrCast(@constCast(v)));
priv.label_text = null;
if (label) |v| priv.label_text = glib.ext.dupeZ(u8, v);
self.as(gobject.Object).notifyByPspec(properties.label.impl.param_spec);
@@ -285,7 +285,7 @@ pub const ResizeOverlay = extern struct {
fn finalize(self: *Self) callconv(.c) void {
const priv = self.private();
if (priv.label_text) |v| {
- glib.free(@constCast(@ptrCast(v)));
+ glib.free(@ptrCast(@constCast(v)));
priv.label_text = null;
}
diff --git a/src/apprt/gtk/class/split_tree.zig b/src/apprt/gtk/class/split_tree.zig
index 977a7eab2..a498ca5dc 100644
--- a/src/apprt/gtk/class/split_tree.zig
+++ b/src/apprt/gtk/class/split_tree.zig
@@ -268,7 +268,7 @@ pub const SplitTree = extern struct {
);
defer new_tree.deinit();
log.debug(
- "new split at={} direction={} old_tree={} new_tree={}",
+ "new split at={} direction={} old_tree={f} new_tree={f}",
.{ handle, direction, old_tree, &new_tree },
);
diff --git a/src/apprt/gtk/class/surface.zig b/src/apprt/gtk/class/surface.zig
index 401e542e4..5ca964fe3 100644
--- a/src/apprt/gtk/class/surface.zig
+++ b/src/apprt/gtk/class/surface.zig
@@ -1375,11 +1375,11 @@ pub const Surface = extern struct {
defer arena.deinit();
const alloc = arena.allocator();
- var env_to_remove = std.ArrayList([]const u8).init(alloc);
- var env_to_update = std.ArrayList(struct {
+ var env_to_remove: std.ArrayList([]const u8) = .empty;
+ var env_to_update: std.ArrayList(struct {
key: []const u8,
value: []const u8,
- }).init(alloc);
+ }) = .empty;
var it = env_map.iterator();
while (it.next()) |entry| {
@@ -1392,13 +1392,11 @@ pub const Surface = extern struct {
// Any env var starting with SNAP must be removed
if (std.mem.startsWith(u8, key, "SNAP_")) {
- try env_to_remove.append(key);
+ try env_to_remove.append(alloc, key);
continue;
}
- var filtered_paths = std.ArrayList([]const u8).init(alloc);
- defer filtered_paths.deinit();
-
+ var filtered_paths: std.ArrayList([]const u8) = .empty;
var modified = false;
var paths = std.mem.splitAny(u8, value, ":");
while (paths.next()) |path| {
@@ -1411,15 +1409,15 @@ pub const Surface = extern struct {
break;
}
};
- if (include) try filtered_paths.append(path);
+ if (include) try filtered_paths.append(alloc, path);
}
if (modified) {
if (filtered_paths.items.len > 0) {
const new_value = try std.mem.join(alloc, ":", filtered_paths.items);
- try env_to_update.append(.{ .key = key, .value = new_value });
+ try env_to_update.append(alloc, .{ .key = key, .value = new_value });
} else {
- try env_to_remove.append(key);
+ try env_to_remove.append(alloc, key);
}
}
}
@@ -1626,7 +1624,7 @@ pub const Surface = extern struct {
priv.core_surface = null;
}
if (priv.mouse_hover_url) |v| {
- glib.free(@constCast(@ptrCast(v)));
+ glib.free(@ptrCast(@constCast(v)));
priv.mouse_hover_url = null;
}
if (priv.default_size) |v| {
@@ -1642,15 +1640,15 @@ pub const Surface = extern struct {
priv.min_size = null;
}
if (priv.pwd) |v| {
- glib.free(@constCast(@ptrCast(v)));
+ glib.free(@ptrCast(@constCast(v)));
priv.pwd = null;
}
if (priv.title) |v| {
- glib.free(@constCast(@ptrCast(v)));
+ glib.free(@ptrCast(@constCast(v)));
priv.title = null;
}
if (priv.title_override) |v| {
- glib.free(@constCast(@ptrCast(v)));
+ glib.free(@ptrCast(@constCast(v)));
priv.title_override = null;
}
self.clearCgroup();
@@ -1674,7 +1672,7 @@ pub const Surface = extern struct {
/// title. For manually set titles see `setTitleOverride`.
pub fn setTitle(self: *Self, title: ?[:0]const u8) void {
const priv = self.private();
- if (priv.title) |v| glib.free(@constCast(@ptrCast(v)));
+ if (priv.title) |v| glib.free(@ptrCast(@constCast(v)));
priv.title = null;
if (title) |v| priv.title = glib.ext.dupeZ(u8, v);
self.as(gobject.Object).notifyByPspec(properties.title.impl.param_spec);
@@ -1684,7 +1682,7 @@ pub const Surface = extern struct {
/// unless this is unset (null).
pub fn setTitleOverride(self: *Self, title: ?[:0]const u8) void {
const priv = self.private();
- if (priv.title_override) |v| glib.free(@constCast(@ptrCast(v)));
+ if (priv.title_override) |v| glib.free(@ptrCast(@constCast(v)));
priv.title_override = null;
if (title) |v| priv.title_override = glib.ext.dupeZ(u8, v);
self.as(gobject.Object).notifyByPspec(properties.@"title-override".impl.param_spec);
@@ -1698,7 +1696,7 @@ pub const Surface = extern struct {
/// Set the pwd for this surface, copies the value.
pub fn setPwd(self: *Self, pwd: ?[:0]const u8) void {
const priv = self.private();
- if (priv.pwd) |v| glib.free(@constCast(@ptrCast(v)));
+ if (priv.pwd) |v| glib.free(@ptrCast(@constCast(v)));
priv.pwd = null;
if (pwd) |v| priv.pwd = glib.ext.dupeZ(u8, v);
self.as(gobject.Object).notifyByPspec(properties.pwd.impl.param_spec);
@@ -1783,7 +1781,7 @@ pub const Surface = extern struct {
pub fn setMouseHoverUrl(self: *Self, url: ?[:0]const u8) void {
const priv = self.private();
- if (priv.mouse_hover_url) |v| glib.free(@constCast(@ptrCast(v)));
+ if (priv.mouse_hover_url) |v| glib.free(@ptrCast(@constCast(v)));
priv.mouse_hover_url = null;
if (url) |v| priv.mouse_hover_url = glib.ext.dupeZ(u8, v);
self.as(gobject.Object).notifyByPspec(properties.@"mouse-hover-url".impl.param_spec);
@@ -2117,13 +2115,11 @@ pub const Surface = extern struct {
const alloc = Application.default().allocator();
if (ext.gValueHolds(value, gdk.FileList.getGObjectType())) {
- var data = std.ArrayList(u8).init(alloc);
- defer data.deinit();
+ var stream: std.Io.Writer.Allocating = .init(alloc);
+ defer stream.deinit();
- var shell_escape_writer: internal_os.ShellEscapeWriter(std.ArrayList(u8).Writer) = .{
- .child_writer = data.writer(),
- };
- const writer = shell_escape_writer.writer();
+ var shell_escape_writer: internal_os.ShellEscapeWriter = .init(&stream.writer);
+ const writer = &shell_escape_writer.writer;
const list: ?*glib.SList = list: {
const unboxed = value.getBoxed() orelse return 0;
@@ -2151,7 +2147,7 @@ pub const Surface = extern struct {
}
}
- const string = data.toOwnedSliceSentinel(0) catch |err| {
+ const string = stream.toOwnedSliceSentinel(0) catch |err| {
log.err("unable to convert to a slice: {}", .{err});
return 0;
};
@@ -2164,13 +2160,11 @@ pub const Surface = extern struct {
const object = value.getObject() orelse return 0;
const file = gobject.ext.cast(gio.File, object) orelse return 0;
const path = file.getPath() orelse return 0;
- var data = std.ArrayList(u8).init(alloc);
- defer data.deinit();
+ var stream: std.Io.Writer.Allocating = .init(alloc);
+ defer stream.deinit();
- var shell_escape_writer: internal_os.ShellEscapeWriter(std.ArrayList(u8).Writer) = .{
- .child_writer = data.writer(),
- };
- const writer = shell_escape_writer.writer();
+ var shell_escape_writer: internal_os.ShellEscapeWriter = .init(&stream.writer);
+ const writer = &shell_escape_writer.writer;
writer.writeAll(std.mem.span(path)) catch |err| {
log.err("unable to write path to buffer: {}", .{err});
return 0;
@@ -2180,7 +2174,7 @@ pub const Surface = extern struct {
return 0;
};
- const string = data.toOwnedSliceSentinel(0) catch |err| {
+ const string = stream.toOwnedSliceSentinel(0) catch |err| {
log.err("unable to convert to a slice: {}", .{err});
return 0;
};
diff --git a/src/apprt/gtk/class/surface_title_dialog.zig b/src/apprt/gtk/class/surface_title_dialog.zig
index de36f3090..6d3bf33de 100644
--- a/src/apprt/gtk/class/surface_title_dialog.zig
+++ b/src/apprt/gtk/class/surface_title_dialog.zig
@@ -136,7 +136,7 @@ pub const SurfaceTitleDialog = extern struct {
fn finalize(self: *Self) callconv(.c) void {
const priv = self.private();
if (priv.initial_value) |v| {
- glib.free(@constCast(@ptrCast(v)));
+ glib.free(@ptrCast(@constCast(v)));
priv.initial_value = null;
}
diff --git a/src/apprt/gtk/class/tab.zig b/src/apprt/gtk/class/tab.zig
index 373507507..941fa00a9 100644
--- a/src/apprt/gtk/class/tab.zig
+++ b/src/apprt/gtk/class/tab.zig
@@ -270,11 +270,11 @@ pub const Tab = extern struct {
fn finalize(self: *Self) callconv(.c) void {
const priv = self.private();
if (priv.tooltip) |v| {
- glib.free(@constCast(@ptrCast(v)));
+ glib.free(@ptrCast(@constCast(v)));
priv.tooltip = null;
}
if (priv.title) |v| {
- glib.free(@constCast(@ptrCast(v)));
+ glib.free(@ptrCast(@constCast(v)));
priv.title = null;
}
@@ -405,22 +405,21 @@ pub const Tab = extern struct {
};
// Use an allocator to build up our string as we write it.
- var buf: std.ArrayList(u8) = .init(Application.default().allocator());
+ var buf: std.Io.Writer.Allocating = .init(Application.default().allocator());
defer buf.deinit();
- const writer = buf.writer();
// If our bell is ringing, then we prefix the bell icon to the title.
if (bell_ringing and config.@"bell-features".title) {
- writer.writeAll("🔔 ") catch {};
+ buf.writer.writeAll("🔔 ") catch {};
}
// If we're zoomed, prefix with the magnifying glass emoji.
if (zoomed) {
- writer.writeAll("🔍 ") catch {};
+ buf.writer.writeAll("🔍 ") catch {};
}
- writer.writeAll(plain) catch return glib.ext.dupeZ(u8, plain);
- return glib.ext.dupeZ(u8, buf.items);
+ buf.writer.writeAll(plain) catch return glib.ext.dupeZ(u8, plain);
+ return glib.ext.dupeZ(u8, buf.written());
}
const C = Common(Self, Private);
diff --git a/src/apprt/gtk/gtk_version.zig b/src/apprt/gtk/gtk_version.zig
index 6f3d733a5..71edb076d 100644
--- a/src/apprt/gtk/gtk_version.zig
+++ b/src/apprt/gtk/gtk_version.zig
@@ -26,7 +26,7 @@ pub fn getRuntimeVersion() std.SemanticVersion {
}
pub fn logVersion() void {
- log.info("GTK version build={} runtime={}", .{
+ log.info("GTK version build={f} runtime={f}", .{
comptime_version,
getRuntimeVersion(),
});
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/apprt/gtk/key.zig b/src/apprt/gtk/key.zig
index a00b0312e..bf0f0e2f6 100644
--- a/src/apprt/gtk/key.zig
+++ b/src/apprt/gtk/key.zig
@@ -12,9 +12,8 @@ const winproto = @import("winproto.zig");
pub fn accelFromTrigger(
buf: []u8,
trigger: input.Binding.Trigger,
-) error{NoSpaceLeft}!?[:0]const u8 {
- var buf_stream = std.io.fixedBufferStream(buf);
- const writer = buf_stream.writer();
+) error{WriteFailed}!?[:0]const u8 {
+ var writer: std.Io.Writer = .fixed(buf);
// Modifiers
if (trigger.mods.shift) try writer.writeAll("<Shift>");
@@ -23,11 +22,11 @@ pub fn accelFromTrigger(
if (trigger.mods.super) try writer.writeAll("<Super>");
// Write our key
- if (!try writeTriggerKey(writer, trigger)) return null;
+ if (!try writeTriggerKey(&writer, trigger)) return null;
// We need to make the string null terminated.
try writer.writeByte(0);
- const slice = buf_stream.getWritten();
+ const slice = writer.buffered();
return slice[0 .. slice.len - 1 :0];
}
@@ -36,9 +35,8 @@ pub fn accelFromTrigger(
pub fn xdgShortcutFromTrigger(
buf: []u8,
trigger: input.Binding.Trigger,
-) error{NoSpaceLeft}!?[:0]const u8 {
- var buf_stream = std.io.fixedBufferStream(buf);
- const writer = buf_stream.writer();
+) error{WriteFailed}!?[:0]const u8 {
+ var writer: std.Io.Writer = .fixed(buf);
// Modifiers
if (trigger.mods.shift) try writer.writeAll("SHIFT+");
@@ -52,15 +50,18 @@ pub fn xdgShortcutFromTrigger(
// to *X11's* keysyms (which I assume is a subset of libxkbcommon's).
// I haven't been able to any evidence to back up that assumption but
// this works for now
- if (!try writeTriggerKey(writer, trigger)) return null;
+ if (!try writeTriggerKey(&writer, trigger)) return null;
// We need to make the string null terminated.
try writer.writeByte(0);
- const slice = buf_stream.getWritten();
+ const slice = writer.buffered();
return slice[0 .. slice.len - 1 :0];
}
-fn writeTriggerKey(writer: anytype, trigger: input.Binding.Trigger) error{NoSpaceLeft}!bool {
+fn writeTriggerKey(
+ writer: *std.Io.Writer,
+ trigger: input.Binding.Trigger,
+) error{WriteFailed}!bool {
switch (trigger.key) {
.physical => |k| {
const keyval = keyvalFromKey(k) orelse return false;
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,36 +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 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| {
- std.mem.doNotOptimizeAway(simd.codepointWidth(cp));
- }
- }
- }
-}
-
-fn stepUucode(ptr: *anyopaque) Benchmark.Error!void {
- const self: *CodepointWidth = @ptrCast(@alignCast(ptr));
+ var read_buf: [4096]u8 = undefined;
+ var f_reader = f.reader(&read_buf);
+ var r = &f_reader.interface;
- 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});
+ 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
@@ -203,12 +183,7 @@ fn stepUucode(ptr: *anyopaque) Benchmark.Error!void {
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)));
+ std.mem.doNotOptimizeAway(simd.codepointWidth(cp));
}
}
}
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/Config.zig b/src/build/Config.zig
index e0e81e519..643dfe928 100644
--- a/src/build/Config.zig
+++ b/src/build/Config.zig
@@ -477,7 +477,7 @@ pub fn addOptions(self: *const Config, step: *std.Build.Step.Options) !void {
step.addOption(std.SemanticVersion, "app_version", self.version);
step.addOption([:0]const u8, "app_version_string", try std.fmt.bufPrintZ(
&buf,
- "{}",
+ "{f}",
.{self.version},
));
step.addOption(
diff --git a/src/build/GhosttyBench.zig b/src/build/GhosttyBench.zig
index 5859a8bcf..c9cd5dd33 100644
--- a/src/build/GhosttyBench.zig
+++ b/src/build/GhosttyBench.zig
@@ -11,8 +11,8 @@ pub fn init(
b: *std.Build,
deps: *const SharedDeps,
) !GhosttyBench {
- var steps = std.ArrayList(*std.Build.Step.Compile).init(b.allocator);
- errdefer steps.deinit();
+ var steps: std.ArrayList(*std.Build.Step.Compile) = .empty;
+ errdefer steps.deinit(b.allocator);
// Our synthetic data generator
{
@@ -28,7 +28,7 @@ pub fn init(
});
exe.linkLibC();
_ = try deps.add(exe);
- try steps.append(exe);
+ try steps.append(b.allocator, exe);
}
// Our benchmarking application.
@@ -44,7 +44,7 @@ pub fn init(
});
exe.linkLibC();
_ = try deps.add(exe);
- try steps.append(exe);
+ try steps.append(b.allocator, exe);
}
return .{ .steps = steps.items };
diff --git a/src/build/GhosttyDist.zig b/src/build/GhosttyDist.zig
index f8c221350..092322689 100644
--- a/src/build/GhosttyDist.zig
+++ b/src/build/GhosttyDist.zig
@@ -43,10 +43,10 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist {
// embed the Ghostty version in the tarball
{
- const version = b.addWriteFiles().add("VERSION", b.fmt("{}", .{cfg.version}));
+ const version = b.addWriteFiles().add("VERSION", b.fmt("{f}", .{cfg.version}));
// --add-file uses the most recent --prefix to determine the path
// in the archive to copy the file (the directory only).
- git_archive.addArg(b.fmt("--prefix=ghostty-{}/", .{
+ git_archive.addArg(b.fmt("--prefix=ghostty-{f}/", .{
cfg.version,
}));
git_archive.addPrefixedFileArg("--add-file=", version);
@@ -65,7 +65,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist {
// --add-file uses the most recent --prefix to determine the path
// in the archive to copy the file (the directory only).
- git_archive.addArg(b.fmt("--prefix=ghostty-{}/{s}/", .{
+ git_archive.addArg(b.fmt("--prefix=ghostty-{f}/{s}/", .{
cfg.version,
std.fs.path.dirname(resource.dist).?,
}));
@@ -77,11 +77,11 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist {
// This is important. Standard source tarballs extract into
// a directory named `project-version`. This is expected by
// standard tooling such as debhelper and rpmbuild.
- b.fmt("--prefix=ghostty-{}/", .{cfg.version}),
+ b.fmt("--prefix=ghostty-{f}/", .{cfg.version}),
"-o",
});
const output = git_archive.addOutputFileArg(b.fmt(
- "ghostty-{}.tar.gz",
+ "ghostty-{f}.tar.gz",
.{cfg.version},
));
git_archive.addArg("HEAD");
@@ -89,7 +89,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist {
// The install step to put the dist into the build directory.
const install = b.addInstallFile(
output,
- b.fmt("dist/ghostty-{}.tar.gz", .{cfg.version}),
+ b.fmt("dist/ghostty-{f}.tar.gz", .{cfg.version}),
);
// The check step to ensure the archive works.
@@ -101,7 +101,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist {
// i.e. this is way `build.zig` is.
const extract_dir = check
.addOutputDirectoryArg("ghostty")
- .path(b, b.fmt("ghostty-{}", .{cfg.version}));
+ .path(b, b.fmt("ghostty-{f}", .{cfg.version}));
// Check that tests pass within the extracted directory. This isn't
// a fully hermetic test because we're sharing the Zig cache. In
diff --git a/src/build/GhosttyDocs.zig b/src/build/GhosttyDocs.zig
index b95b56f74..cd75fc061 100644
--- a/src/build/GhosttyDocs.zig
+++ b/src/build/GhosttyDocs.zig
@@ -12,8 +12,8 @@ pub fn init(
b: *std.Build,
deps: *const SharedDeps,
) !GhosttyDocs {
- var steps = std.ArrayList(*std.Build.Step).init(b.allocator);
- errdefer steps.deinit();
+ var steps: std.ArrayList(*std.Build.Step) = .empty;
+ errdefer steps.deinit(b.allocator);
const manpages = [_]struct {
name: []const u8,
@@ -52,7 +52,7 @@ pub fn init(
const generate_markdown_step = b.addRunArtifact(generate_markdown);
const markdown_output = generate_markdown_step.captureStdOut();
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
markdown_output,
"share/ghostty/doc/" ++ manpage.name ++ "." ++ manpage.section ++ ".md",
).step);
@@ -67,7 +67,7 @@ pub fn init(
});
generate_html.addFileArg(markdown_output);
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
generate_html.captureStdOut(),
"share/ghostty/doc/" ++ manpage.name ++ "." ++ manpage.section ++ ".html",
).step);
@@ -82,7 +82,7 @@ pub fn init(
});
generate_manpage.addFileArg(markdown_output);
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
generate_manpage.captureStdOut(),
"share/man/man" ++ manpage.section ++ "/" ++ manpage.name ++ "." ++ manpage.section,
).step);
diff --git a/src/build/GhosttyExe.zig b/src/build/GhosttyExe.zig
index 083aecdb5..3e63b6026 100644
--- a/src/build/GhosttyExe.zig
+++ b/src/build/GhosttyExe.zig
@@ -21,6 +21,8 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
.omit_frame_pointer = cfg.strip,
.unwind_tables = if (cfg.strip) .none else .sync,
}),
+ // Crashes on x86_64 self-hosted on 0.15.1
+ .use_llvm = true,
});
const install_step = b.addInstallArtifact(exe, .{});
diff --git a/src/build/GhosttyFrameData.zig b/src/build/GhosttyFrameData.zig
index 52c84a66c..def1dbdb3 100644
--- a/src/build/GhosttyFrameData.zig
+++ b/src/build/GhosttyFrameData.zig
@@ -40,7 +40,10 @@ pub fn distResources(b: *std.Build) struct {
} {
const exe = b.addExecutable(.{
.name = "framegen",
- .target = b.graph.host,
+ .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/build/GhosttyI18n.zig b/src/build/GhosttyI18n.zig
index b99e60426..8e31f61b3 100644
--- a/src/build/GhosttyI18n.zig
+++ b/src/build/GhosttyI18n.zig
@@ -18,8 +18,8 @@ update_step: *std.Build.Step,
pub fn init(b: *std.Build, cfg: *const Config) !GhosttyI18n {
_ = cfg;
- var steps = std.ArrayList(*std.Build.Step).init(b.allocator);
- defer steps.deinit();
+ var steps: std.ArrayList(*std.Build.Step) = .empty;
+ defer steps.deinit(b.allocator);
inline for (locales) |locale| {
// There is no encoding suffix in the LC_MESSAGES path on FreeBSD,
@@ -33,7 +33,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyI18n {
const msgfmt = b.addSystemCommand(&.{ "msgfmt", "-o", "-" });
msgfmt.addFileArg(b.path("po/" ++ locale ++ ".po"));
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
msgfmt.captureStdOut(),
std.fmt.comptimePrint(
"share/locale/{s}/LC_MESSAGES/{s}.mo",
@@ -45,7 +45,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyI18n {
return .{
.owner = b,
.update_step = try createUpdateStep(b),
- .steps = try steps.toOwnedSlice(),
+ .steps = try steps.toOwnedSlice(b.allocator),
};
}
diff --git a/src/build/GhosttyLib.zig b/src/build/GhosttyLib.zig
index b244a72c5..2ac383544 100644
--- a/src/build/GhosttyLib.zig
+++ b/src/build/GhosttyLib.zig
@@ -28,7 +28,9 @@ pub fn initStatic(
.omit_frame_pointer = deps.config.strip,
.unwind_tables = if (deps.config.strip) .none else .sync,
}),
- .linkage = .static,
+
+ // Fails on self-hosted x86_64 on macOS
+ .use_llvm = true,
});
lib.linkLibC();
@@ -40,7 +42,7 @@ pub fn initStatic(
// Add our dependencies. Get the list of all static deps so we can
// build a combined archive if necessary.
var lib_list = try deps.add(lib);
- try lib_list.append(lib.getEmittedBin());
+ try lib_list.append(b.allocator, lib.getEmittedBin());
if (!deps.config.target.result.os.tag.isDarwin()) return .{
.step = &lib.step,
@@ -69,8 +71,9 @@ pub fn initShared(
b: *std.Build,
deps: *const SharedDeps,
) !GhosttyLib {
- const lib = b.addSharedLibrary(.{
+ const lib = b.addLibrary(.{
.name = "ghostty",
+ .linkage = .dynamic,
.root_module = b.createModule(.{
.root_source_file = b.path("src/main_c.zig"),
.target = deps.config.target,
@@ -79,6 +82,9 @@ pub fn initShared(
.omit_frame_pointer = deps.config.strip,
.unwind_tables = if (deps.config.strip) .none else .sync,
}),
+
+ // Fails on self-hosted x86_64
+ .use_llvm = true,
});
_ = try deps.add(lib);
diff --git a/src/build/GhosttyLibVt.zig b/src/build/GhosttyLibVt.zig
index 9eb945293..1e57da7b1 100644
--- a/src/build/GhosttyLibVt.zig
+++ b/src/build/GhosttyLibVt.zig
@@ -24,8 +24,9 @@ pub fn initShared(
zig: *const GhosttyZig,
) !GhosttyLibVt {
const target = zig.vt.resolved_target.?;
- const lib = b.addSharedLibrary(.{
+ const lib = b.addLibrary(.{
.name = "ghostty-vt",
+ .linkage = .dynamic,
.root_module = zig.vt_c,
.version = std.SemanticVersion{ .major = 0, .minor = 1, .patch = 0 },
});
diff --git a/src/build/GhosttyResources.zig b/src/build/GhosttyResources.zig
index 0db1fd418..7880a98a0 100644
--- a/src/build/GhosttyResources.zig
+++ b/src/build/GhosttyResources.zig
@@ -10,8 +10,8 @@ const RunStep = std.Build.Step.Run;
steps: []*std.Build.Step,
pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
- var steps = std.ArrayList(*std.Build.Step).init(b.allocator);
- errdefer steps.deinit();
+ var steps: std.ArrayList(*std.Build.Step) = .empty;
+ errdefer steps.deinit(b.allocator);
// This is the exe used to generate some build data.
const build_data_exe = b.addExecutable(.{
@@ -49,7 +49,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
"share/terminfo/ghostty.terminfo",
);
- try steps.append(&source_install.step);
+ try steps.append(b.allocator, &source_install.step);
}
// Windows doesn't have the binaries below.
@@ -73,7 +73,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
"share/terminfo/ghostty.termcap",
);
- try steps.append(&cap_install.step);
+ try steps.append(b.allocator, &cap_install.step);
}
// Compile the terminfo source into a terminfo database
@@ -99,7 +99,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
.{ b.install_path, terminfo_share_dir },
));
- try steps.append(&mkdir_step.step);
+ try steps.append(b.allocator, &mkdir_step.step);
// Use cp -R instead of Step.InstallDir because we need to preserve
// symlinks in the terminfo database. Zig's InstallDir step doesn't
@@ -109,7 +109,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
copy_step.addFileArg(path);
copy_step.addArg(b.fmt("{s}/share", .{b.install_path}));
copy_step.step.dependOn(&mkdir_step.step);
- try steps.append(&copy_step.step);
+ try steps.append(b.allocator, &copy_step.step);
}
}
@@ -121,7 +121,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
.install_subdir = b.pathJoin(&.{ "ghostty", "shell-integration" }),
.exclude_extensions = &.{".md"},
});
- try steps.append(&install_step.step);
+ try steps.append(b.allocator, &install_step.step);
}
// Themes
@@ -132,7 +132,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
.install_subdir = b.pathJoin(&.{ "ghostty", "themes" }),
.exclude_extensions = &.{".md"},
});
- try steps.append(&install_step.step);
+ try steps.append(b.allocator, &install_step.step);
}
// Fish shell completions
@@ -147,7 +147,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
.install_dir = .prefix,
.install_subdir = "share/fish/vendor_completions.d",
});
- try steps.append(&install_step.step);
+ try steps.append(b.allocator, &install_step.step);
}
// zsh shell completions
@@ -162,7 +162,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
.install_dir = .prefix,
.install_subdir = "share/zsh/site-functions",
});
- try steps.append(&install_step.step);
+ try steps.append(b.allocator, &install_step.step);
}
// bash shell completions
@@ -177,7 +177,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
.install_dir = .prefix,
.install_subdir = "share/bash-completion/completions",
});
- try steps.append(&install_step.step);
+ try steps.append(b.allocator, &install_step.step);
}
// Vim and Neovim plugin
@@ -210,14 +210,14 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
.install_dir = .prefix,
.install_subdir = "share/vim/vimfiles",
});
- try steps.append(&vim_step.step);
+ try steps.append(b.allocator, &vim_step.step);
const neovim_step = b.addInstallDirectory(.{
.source_dir = wf.getDirectory(),
.install_dir = .prefix,
.install_subdir = "share/nvim/site",
});
- try steps.append(&neovim_step.step);
+ try steps.append(b.allocator, &neovim_step.step);
}
// Sublime syntax highlighting for bat cli tool
@@ -237,7 +237,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
.install_dir = .prefix,
.install_subdir = "share/bat/syntaxes",
});
- try steps.append(&install_step.step);
+ try steps.append(b.allocator, &install_step.step);
}
// App (Linux)
@@ -286,16 +286,17 @@ fn addLinuxAppResources(
// second element of the tuple.
const Template = struct { std.Build.LazyPath, []const u8 };
const templates: []const Template = templates: {
- var ts: std.ArrayList(Template) = .init(b.allocator);
+ var ts: std.ArrayList(Template) = .empty;
+ defer ts.deinit(b.allocator);
// Desktop file so that we have an icon and other metadata
- try ts.append(.{
+ try ts.append(b.allocator, .{
b.path("dist/linux/app.desktop.in"),
b.fmt("share/applications/{s}.desktop", .{app_id}),
});
// Service for DBus activation.
- try ts.append(.{
+ try ts.append(b.allocator, .{
if (cfg.flatpak)
b.path("dist/linux/dbus.service.flatpak.in")
else
@@ -320,7 +321,7 @@ fn addLinuxAppResources(
// See the following code:
//
// https://github.com/flatpak/xdg-desktop-portal/blob/7d4d48cf079147c8887da17ec6c3954acd5a285c/src/xdp-utils.c#L152-L220
- if (!cfg.flatpak) try ts.append(.{
+ if (!cfg.flatpak) try ts.append(b.allocator, .{
b.path("dist/linux/systemd.service.in"),
b.fmt(
"{s}/systemd/user/app-{s}.service",
@@ -333,12 +334,12 @@ fn addLinuxAppResources(
// AppStream metainfo so that application has rich metadata
// within app stores
- try ts.append(.{
+ try ts.append(b.allocator, .{
b.path("dist/linux/com.mitchellh.ghostty.metainfo.xml.in"),
b.fmt("share/metainfo/{s}.metainfo.xml", .{app_id}),
});
- break :templates ts.items;
+ break :templates try ts.toOwnedSlice(b.allocator);
};
// Process all our templates
@@ -361,65 +362,65 @@ fn addLinuxAppResources(
template[1],
);
- try steps.append(&copy.step);
+ try steps.append(b.allocator, &copy.step);
}
// Right click menu action for Plasma desktop
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("dist/linux/ghostty_dolphin.desktop"),
"share/kio/servicemenus/com.mitchellh.ghostty.desktop",
).step);
// Right click menu action for Nautilus. Note that this _must_ be named
// `ghostty.py`. Using the full app id causes problems (see #5468).
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("dist/linux/ghostty_nautilus.py"),
"share/nautilus-python/extensions/ghostty.py",
).step);
// Various icons that our application can use, including the icon
// that will be used for the desktop.
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("images/gnome/16.png"),
"share/icons/hicolor/16x16/apps/com.mitchellh.ghostty.png",
).step);
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("images/gnome/32.png"),
"share/icons/hicolor/32x32/apps/com.mitchellh.ghostty.png",
).step);
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("images/gnome/128.png"),
"share/icons/hicolor/128x128/apps/com.mitchellh.ghostty.png",
).step);
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("images/gnome/256.png"),
"share/icons/hicolor/256x256/apps/com.mitchellh.ghostty.png",
).step);
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("images/gnome/512.png"),
"share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png",
).step);
// Flatpaks only support icons up to 512x512.
if (!cfg.flatpak) {
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("images/gnome/1024.png"),
"share/icons/hicolor/1024x1024/apps/com.mitchellh.ghostty.png",
).step);
}
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("images/gnome/32.png"),
"share/icons/hicolor/16x16@2/apps/com.mitchellh.ghostty.png",
).step);
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("images/gnome/64.png"),
"share/icons/hicolor/32x32@2/apps/com.mitchellh.ghostty.png",
).step);
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("images/gnome/256.png"),
"share/icons/hicolor/128x128@2/apps/com.mitchellh.ghostty.png",
).step);
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
b.path("images/gnome/512.png"),
"share/icons/hicolor/256x256@2/apps/com.mitchellh.ghostty.png",
).step);
diff --git a/src/build/GhosttyWebdata.zig b/src/build/GhosttyWebdata.zig
index b0201c3ff..145bb91fa 100644
--- a/src/build/GhosttyWebdata.zig
+++ b/src/build/GhosttyWebdata.zig
@@ -12,8 +12,8 @@ pub fn init(
b: *std.Build,
deps: *const SharedDeps,
) !GhosttyWebdata {
- var steps = std.ArrayList(*std.Build.Step).init(b.allocator);
- errdefer steps.deinit();
+ var steps: std.ArrayList(*std.Build.Step) = .empty;
+ errdefer steps.deinit(b.allocator);
{
const webgen_config = b.addExecutable(.{
@@ -43,7 +43,7 @@ pub fn init(
const webgen_config_step = b.addRunArtifact(webgen_config);
const webgen_config_out = webgen_config_step.captureStdOut();
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
webgen_config_out,
"share/ghostty/webdata/config.mdx",
).step);
@@ -52,8 +52,10 @@ pub fn init(
{
const webgen_actions = b.addExecutable(.{
.name = "webgen_actions",
- .root_source_file = b.path("src/main.zig"),
- .target = b.graph.host,
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("src/main.zig"),
+ .target = b.graph.host,
+ }),
});
deps.help_strings.addImport(webgen_actions);
@@ -72,7 +74,7 @@ pub fn init(
const webgen_actions_step = b.addRunArtifact(webgen_actions);
const webgen_actions_out = webgen_actions_step.captureStdOut();
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
webgen_actions_out,
"share/ghostty/webdata/actions.mdx",
).step);
@@ -81,8 +83,10 @@ pub fn init(
{
const webgen_commands = b.addExecutable(.{
.name = "webgen_commands",
- .root_source_file = b.path("src/main.zig"),
- .target = b.graph.host,
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("src/main.zig"),
+ .target = b.graph.host,
+ }),
});
deps.help_strings.addImport(webgen_commands);
@@ -101,7 +105,7 @@ pub fn init(
const webgen_commands_step = b.addRunArtifact(webgen_commands);
const webgen_commands_out = webgen_commands_step.captureStdOut();
- try steps.append(&b.addInstallFile(
+ try steps.append(b.allocator, &b.addInstallFile(
webgen_commands_out,
"share/ghostty/webdata/commands.mdx",
).step);
diff --git a/src/build/MetallibStep.zig b/src/build/MetallibStep.zig
index 6999f8f31..fcf3055f8 100644
--- a/src/build/MetallibStep.zig
+++ b/src/build/MetallibStep.zig
@@ -44,7 +44,7 @@ pub fn create(b: *std.Build, opts: Options) ?*MetallibStep {
const self = b.allocator.create(MetallibStep) catch @panic("OOM");
const min_version = if (opts.target.query.os_version_min) |v|
- b.fmt("{}", .{v.semver})
+ b.fmt("{f}", .{v.semver})
else switch (opts.target.result.os.tag) {
.macos => "10.14",
.ios => "11.0",
diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig
index 9461d48b7..785830ab9 100644
--- a/src/build/SharedDeps.zig
+++ b/src/build/SharedDeps.zig
@@ -113,8 +113,8 @@ pub fn add(
// We maintain a list of our static libraries and return it so that
// we can build a single fat static library for the final app.
- var static_libs = LazyPathList.init(b.allocator);
- errdefer static_libs.deinit();
+ var static_libs: LazyPathList = .empty;
+ errdefer static_libs.deinit(b.allocator);
// WARNING: This is a hack!
// If we're cross-compiling to Darwin then we don't add any deps.
@@ -154,6 +154,7 @@ pub fn add(
} else {
step.linkLibrary(freetype_dep.artifact("freetype"));
try static_libs.append(
+ b.allocator,
freetype_dep.artifact("freetype").getEmittedBin(),
);
}
@@ -178,6 +179,7 @@ pub fn add(
} else {
step.linkLibrary(harfbuzz_dep.artifact("harfbuzz"));
try static_libs.append(
+ b.allocator,
harfbuzz_dep.artifact("harfbuzz").getEmittedBin(),
);
}
@@ -201,6 +203,7 @@ pub fn add(
} else {
step.linkLibrary(fontconfig_dep.artifact("fontconfig"));
try static_libs.append(
+ b.allocator,
fontconfig_dep.artifact("fontconfig").getEmittedBin(),
);
}
@@ -218,6 +221,7 @@ pub fn add(
})) |libpng_dep| {
step.linkLibrary(libpng_dep.artifact("png"));
try static_libs.append(
+ b.allocator,
libpng_dep.artifact("png").getEmittedBin(),
);
}
@@ -231,6 +235,7 @@ pub fn add(
})) |zlib_dep| {
step.linkLibrary(zlib_dep.artifact("z"));
try static_libs.append(
+ b.allocator,
zlib_dep.artifact("z").getEmittedBin(),
);
}
@@ -250,6 +255,7 @@ pub fn add(
} else {
step.linkLibrary(oniguruma_dep.artifact("oniguruma"));
try static_libs.append(
+ b.allocator,
oniguruma_dep.artifact("oniguruma").getEmittedBin(),
);
}
@@ -270,6 +276,7 @@ pub fn add(
} else {
step.linkLibrary(glslang_dep.artifact("glslang"));
try static_libs.append(
+ b.allocator,
glslang_dep.artifact("glslang").getEmittedBin(),
);
}
@@ -289,6 +296,7 @@ pub fn add(
} else {
step.linkLibrary(spirv_cross_dep.artifact("spirv_cross"));
try static_libs.append(
+ b.allocator,
spirv_cross_dep.artifact("spirv_cross").getEmittedBin(),
);
}
@@ -307,6 +315,7 @@ pub fn add(
);
step.linkLibrary(sentry_dep.artifact("sentry"));
try static_libs.append(
+ b.allocator,
sentry_dep.artifact("sentry").getEmittedBin(),
);
@@ -316,6 +325,7 @@ pub fn add(
.optimize = optimize,
})) |breakpad_dep| {
try static_libs.append(
+ b.allocator,
breakpad_dep.artifact("breakpad").getEmittedBin(),
);
}
@@ -443,6 +453,7 @@ pub fn add(
macos_dep.artifact("macos"),
);
try static_libs.append(
+ b.allocator,
macos_dep.artifact("macos").getEmittedBin(),
);
}
@@ -461,6 +472,7 @@ pub fn add(
})) |libintl_dep| {
step.linkLibrary(libintl_dep.artifact("intl"));
try static_libs.append(
+ b.allocator,
libintl_dep.artifact("intl").getEmittedBin(),
);
}
@@ -473,7 +485,10 @@ pub fn add(
})) |cimgui_dep| {
step.root_module.addImport("cimgui", cimgui_dep.module("cimgui"));
step.linkLibrary(cimgui_dep.artifact("cimgui"));
- try static_libs.append(cimgui_dep.artifact("cimgui").getEmittedBin());
+ try static_libs.append(
+ b.allocator,
+ cimgui_dep.artifact("cimgui").getEmittedBin(),
+ );
}
// Fonts
@@ -697,6 +712,7 @@ pub fn addSimd(
})) |simdutf_dep| {
m.linkLibrary(simdutf_dep.artifact("simdutf"));
if (static_libs) |v| try v.append(
+ b.allocator,
simdutf_dep.artifact("simdutf").getEmittedBin(),
);
}
@@ -708,7 +724,10 @@ pub fn addSimd(
.optimize = optimize,
})) |highway_dep| {
m.linkLibrary(highway_dep.artifact("highway"));
- if (static_libs) |v| try v.append(highway_dep.artifact("highway").getEmittedBin());
+ if (static_libs) |v| try v.append(
+ b.allocator,
+ highway_dep.artifact("highway").getEmittedBin(),
+ );
}
// utfcpp - This is used as a dependency on our hand-written C++ code
@@ -717,7 +736,10 @@ pub fn addSimd(
.optimize = optimize,
})) |utfcpp_dep| {
m.linkLibrary(utfcpp_dep.artifact("utfcpp"));
- if (static_libs) |v| try v.append(utfcpp_dep.artifact("utfcpp").getEmittedBin());
+ if (static_libs) |v| try v.append(
+ b.allocator,
+ utfcpp_dep.artifact("utfcpp").getEmittedBin(),
+ );
}
// SIMD C++ files
@@ -761,16 +783,20 @@ pub fn gtkNgDistResources(
const gresource_xml = gresource_xml: {
const xml_exe = b.addExecutable(.{
.name = "generate_gresource_xml",
- .root_source_file = b.path("src/apprt/gtk/build/gresource.zig"),
- .target = b.graph.host,
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("src/apprt/gtk/build/gresource.zig"),
+ .target = b.graph.host,
+ }),
});
const xml_run = b.addRunArtifact(xml_exe);
// Run our blueprint compiler across all of our blueprint files.
const blueprint_exe = b.addExecutable(.{
.name = "gtk_blueprint_compiler",
- .root_source_file = b.path("src/apprt/gtk/build/blueprint.zig"),
- .target = b.graph.host,
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("src/apprt/gtk/build/blueprint.zig"),
+ .target = b.graph.host,
+ }),
});
blueprint_exe.linkLibC();
blueprint_exe.linkSystemLibrary2("gtk4", dynamic_link_opts);
diff --git a/src/build/UnicodeTables.zig b/src/build/UnicodeTables.zig
index 9972c851a..aba3e8f24 100644
--- a/src/build/UnicodeTables.zig
+++ b/src/build/UnicodeTables.zig
@@ -21,6 +21,9 @@ pub fn init(b: *std.Build, uucode_tables: std.Build.LazyPath) !UnicodeTables {
.omit_frame_pointer = false,
.unwind_tables = .sync,
}),
+
+ // TODO: x86_64 self-hosted crashes
+ .use_llvm = true,
});
const symbols_exe = b.addExecutable(.{
@@ -32,6 +35,9 @@ pub fn init(b: *std.Build, uucode_tables: std.Build.LazyPath) !UnicodeTables {
.omit_frame_pointer = false,
.unwind_tables = .sync,
}),
+
+ // TODO: x86_64 self-hosted crashes
+ .use_llvm = true,
});
if (b.lazyDependency("uucode", .{
diff --git a/src/build/docker/debian/Dockerfile b/src/build/docker/debian/Dockerfile
index 73c7da7c8..815d395cd 100644
--- a/src/build/docker/debian/Dockerfile
+++ b/src/build/docker/debian/Dockerfile
@@ -29,10 +29,10 @@ COPY ./build.zig /src
# Install zig
# https://ziglang.org/download/
-RUN export ZIG_VERSION=$(sed -n -e 's/^.*requireZig("\(.*\)").*$/\1/p' build.zig) && curl -L -o /tmp/zig.tar.xz "https://ziglang.org/download/$ZIG_VERSION/zig-linux-$(uname -m)-$ZIG_VERSION.tar.xz" && \
+RUN export ZIG_VERSION=$(sed -n -e 's/^.*requireZig("\(.*\)").*$/\1/p' build.zig) && curl -L -o /tmp/zig.tar.xz "https://ziglang.org/download/$ZIG_VERSION/zig-$(uname -m)-linux-$ZIG_VERSION.tar.xz" && \
tar -xf /tmp/zig.tar.xz -C /opt && \
rm /tmp/zig.tar.xz && \
- ln -s "/opt/zig-linux-$(uname -m)-$ZIG_VERSION/zig" /usr/local/bin/zig
+ ln -s "/opt/zig-$(uname -m)-linux-$ZIG_VERSION/zig" /usr/local/bin/zig
COPY . /src
diff --git a/src/build/mdgen/main_ghostty_1.zig b/src/build/mdgen/main_ghostty_1.zig
index b3663de8d..2bb413d93 100644
--- a/src/build/mdgen/main_ghostty_1.zig
+++ b/src/build/mdgen/main_ghostty_1.zig
@@ -2,12 +2,15 @@ const std = @import("std");
const gen = @import("mdgen.zig");
pub fn main() !void {
- var gpa = std.heap.GeneralPurposeAllocator(.{}){};
+ var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
const alloc = gpa.allocator();
- const writer = std.io.getStdOut().writer();
+ var buffer: [1024]u8 = undefined;
+ var stdout_writer = std.fs.File.stdout().writer(&buffer);
+ const writer = &stdout_writer.interface;
try gen.substitute(alloc, @embedFile("ghostty_1_header.md"), writer);
try gen.genActions(writer);
try gen.genConfig(writer, true);
try gen.substitute(alloc, @embedFile("ghostty_1_footer.md"), writer);
+ try writer.flush();
}
diff --git a/src/build/mdgen/main_ghostty_5.zig b/src/build/mdgen/main_ghostty_5.zig
index 77c72b946..2123b0bce 100644
--- a/src/build/mdgen/main_ghostty_5.zig
+++ b/src/build/mdgen/main_ghostty_5.zig
@@ -2,12 +2,15 @@ const std = @import("std");
const gen = @import("mdgen.zig");
pub fn main() !void {
- var gpa = std.heap.GeneralPurposeAllocator(.{}){};
+ var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
const alloc = gpa.allocator();
- const output = std.io.getStdOut().writer();
- try gen.substitute(alloc, @embedFile("ghostty_5_header.md"), output);
- try gen.genConfig(output, false);
- try gen.genKeybindActions(output);
- try gen.substitute(alloc, @embedFile("ghostty_5_footer.md"), output);
+ var buffer: [1024]u8 = undefined;
+ var stdout_writer = std.fs.File.stdout().writer(&buffer);
+ const writer = &stdout_writer.interface;
+ try gen.substitute(alloc, @embedFile("ghostty_5_header.md"), writer);
+ try gen.genConfig(writer, false);
+ try gen.genKeybindActions(writer);
+ try gen.substitute(alloc, @embedFile("ghostty_5_footer.md"), writer);
+ try writer.flush();
}
diff --git a/src/build/mdgen/mdgen.zig b/src/build/mdgen/mdgen.zig
index 53ed02067..530c8964f 100644
--- a/src/build/mdgen/mdgen.zig
+++ b/src/build/mdgen/mdgen.zig
@@ -5,7 +5,7 @@ const Config = @import("../../config/Config.zig");
const Action = @import("../../cli/ghostty.zig").Action;
const KeybindAction = @import("../../input/Binding.zig").Action;
-pub fn substitute(alloc: std.mem.Allocator, input: []const u8, writer: anytype) !void {
+pub fn substitute(alloc: std.mem.Allocator, input: []const u8, writer: *std.Io.Writer) !void {
const output = try alloc.alloc(u8, std.mem.replacementSize(
u8,
input,
@@ -18,7 +18,7 @@ pub fn substitute(alloc: std.mem.Allocator, input: []const u8, writer: anytype)
try writer.writeAll(output);
}
-pub fn genConfig(writer: anytype, cli: bool) !void {
+pub fn genConfig(writer: *std.Io.Writer, cli: bool) !void {
try writer.writeAll(
\\
\\# CONFIGURATION OPTIONS
@@ -48,7 +48,7 @@ pub fn genConfig(writer: anytype, cli: bool) !void {
}
}
-pub fn genActions(writer: anytype) !void {
+pub fn genActions(writer: *std.Io.Writer) !void {
try writer.writeAll(
\\
\\# COMMAND LINE ACTIONS
@@ -83,7 +83,7 @@ pub fn genActions(writer: anytype) !void {
}
}
-pub fn genKeybindActions(writer: anytype) !void {
+pub fn genKeybindActions(writer: *std.Io.Writer) !void {
try writer.writeAll(
\\
\\# KEYBIND ACTIONS
diff --git a/src/build/webgen/main_actions.zig b/src/build/webgen/main_actions.zig
index 5002a5bac..85357b972 100644
--- a/src/build/webgen/main_actions.zig
+++ b/src/build/webgen/main_actions.zig
@@ -3,6 +3,8 @@ const help_strings = @import("help_strings");
const helpgen_actions = @import("../../input/helpgen_actions.zig");
pub fn main() !void {
- const output = std.io.getStdOut().writer();
- try helpgen_actions.generate(output, .markdown, true, std.heap.page_allocator);
+ var buffer: [2048]u8 = undefined;
+ var stdout_writer = std.fs.File.stdout().writer(&buffer);
+ const stdout = &stdout_writer.interface;
+ try helpgen_actions.generate(stdout, .markdown, true, std.heap.page_allocator);
}
diff --git a/src/build/webgen/main_commands.zig b/src/build/webgen/main_commands.zig
index ad5c75734..65f144522 100644
--- a/src/build/webgen/main_commands.zig
+++ b/src/build/webgen/main_commands.zig
@@ -3,14 +3,16 @@ const Action = @import("../../cli/ghostty.zig").Action;
const help_strings = @import("help_strings");
pub fn main() !void {
- const output = std.io.getStdOut().writer();
- try genActions(output);
+ var buffer: [2048]u8 = undefined;
+ var stdout_writer = std.fs.File.stdout().writer(&buffer);
+ const stdout = &stdout_writer.interface;
+ try genActions(stdout);
}
// Note: as a shortcut for defining inline editOnGithubLinks per cli action the user
// is directed to the folder view on Github. This includes a README pointing them to
// the files to edit.
-pub fn genActions(writer: anytype) !void {
+pub fn genActions(writer: *std.Io.Writer) !void {
// Write the header
try writer.writeAll(
\\---
diff --git a/src/build/webgen/main_config.zig b/src/build/webgen/main_config.zig
index 1bde2f9cc..1363fadc4 100644
--- a/src/build/webgen/main_config.zig
+++ b/src/build/webgen/main_config.zig
@@ -3,11 +3,13 @@ const Config = @import("../../config/Config.zig");
const help_strings = @import("help_strings");
pub fn main() !void {
- const output = std.io.getStdOut().writer();
- try genConfig(output);
+ var buffer: [2048]u8 = undefined;
+ var stdout_writer = std.fs.File.stdout().writer(&buffer);
+ const stdout = &stdout_writer.interface;
+ try genConfig(stdout);
}
-pub fn genConfig(writer: anytype) !void {
+pub fn genConfig(writer: *std.Io.Writer) !void {
// Write the header
try writer.writeAll(
\\---
@@ -122,7 +124,7 @@ pub fn genConfig(writer: anytype) !void {
}
}
-fn endBlock(writer: anytype, block: anytype) !void {
+fn endBlock(writer: *std.Io.Writer, block: anytype) !void {
if (block) |v| switch (v) {
.text => {},
.code => try writer.writeAll("```\n"),
diff --git a/src/cli/args.zig b/src/cli/args.zig
index b8f393864..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| {
@@ -324,7 +333,7 @@ pub fn parseIntoField(
return;
}
const raw = field.default_value_ptr orelse break :default;
- const ptr: *const field.type = @alignCast(@ptrCast(raw));
+ const ptr: *const field.type = @ptrCast(@alignCast(raw));
@field(dst, field.name) = ptr.*;
return;
}
@@ -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;
@@ -586,7 +594,7 @@ pub fn parseAutoStruct(
break :default @field(default, field.name);
} else {
const default_ptr = field.default_value_ptr orelse return error.InvalidValue;
- const typed_ptr: *const field.type = @alignCast(@ptrCast(default_ptr));
+ const typed_ptr: *const field.type = @ptrCast(@alignCast(default_ptr));
break :default typed_ptr.*;
}
};
@@ -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 {};
+
+ var writer: std.Io.Writer = .fixed(self.entry[2..]);
+
+ var entry = while (self.r.seek != self.r.end) {
+ // Reset write head
+ writer.end = 0;
+
+ _ = self.r.streamDelimiterEnding(&writer, '\n') catch |e| {
+ log.warn("cannot read from \"{s}\": {}", .{ self.filepath, e });
+ return null;
+ };
+ _ = self.r.discardDelimiterInclusive('\n') 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 entry = writer.buffered();
+ self.line += 1;
- // Ignore blank lines and comments
- if (entry.len == 0 or entry[0] == '#') continue;
-
- // 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);
-
- // 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;
- };
-
- 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];
- }
- }
+ // 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];
+ }
- break :buf entry;
+ // Ignore blank lines and comments
+ if (entry.len == 0 or entry[0] == '#') continue;
+ break entry;
+ } else return null;
+
+ 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;
-
- return .{ .file = .{
- .path = try alloc.dupe(u8, self.filepath),
- .line = self.line,
- } };
- }
- };
-}
+ // 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];
+ }
-// Constructs a LineIterator (see docs for that).
-fn lineIterator(reader: anytype) LineIterator(@TypeOf(reader)) {
- return .{ .r = reader };
-}
+ /// 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;
+
+ 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 `<action>` 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);
-}
-fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
- 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;
+
+ const result = runArgs(alloc, &iter, stderr);
+ stderr.flush() catch {};
+ return result;
+}
+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);
-}
-fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
- const stdout = std.io.getStdOut().writer();
- const stderr = std.io.getStdErr().writer();
+ 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,
+ 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 fn formatEntry(
- self: @This(),
- comptime T: type,
- value: T,
- ) !void {
- return formatter.formatEntry(
- T,
- self.name,
- value,
- self.writer,
- );
- }
- };
-}
+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,
+ );
+ }
+};
/// 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.zig b/src/crash/sentry.zig
index 820c3e9a1..555b70fe9 100644
--- a/src/crash/sentry.zig
+++ b/src/crash/sentry.zig
@@ -265,8 +265,8 @@ pub const Transport = struct {
const json = envelope.serialize();
defer sentry.free(@ptrCast(json.ptr));
var parsed: crash.Envelope = parsed: {
- var fbs = std.io.fixedBufferStream(json);
- break :parsed try crash.Envelope.parse(alloc, fbs.reader());
+ var reader: std.Io.Reader = .fixed(json);
+ break :parsed try crash.Envelope.parse(alloc, &reader);
};
defer parsed.deinit();
@@ -298,7 +298,10 @@ pub const Transport = struct {
});
const file = try std.fs.cwd().createFile(path, .{});
defer file.close();
- try file.writer().writeAll(json);
+ var buf: [4096]u8 = undefined;
+ var file_writer = file.writer(&buf);
+ try file_writer.interface.writeAll(json);
+ try file_writer.end();
log.warn("crash report written to disk path={s}", .{path});
}
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/lru.zig b/src/datastruct/lru.zig
index 7bf42e82d..1c6df69ce 100644
--- a/src/datastruct/lru.zig
+++ b/src/datastruct/lru.zig
@@ -33,8 +33,13 @@ pub fn HashMap(
) type {
return struct {
const Self = @This();
- const Map = std.HashMapUnmanaged(K, *Queue.Node, Context, max_load_percentage);
- const Queue = std.DoublyLinkedList(KV);
+ const Queue = std.DoublyLinkedList;
+ const Map = std.HashMapUnmanaged(
+ K,
+ *Entry,
+ Context,
+ max_load_percentage,
+ );
/// Map to maintain our entries.
map: Map,
@@ -46,6 +51,15 @@ pub fn HashMap(
/// misses will begin evicting entries.
capacity: Map.Size,
+ const Entry = struct {
+ data: KV,
+ node: Queue.Node,
+
+ fn fromNode(node: *Queue.Node) *Entry {
+ return @fieldParentPtr("node", node);
+ }
+ };
+
pub const KV = struct {
key: K,
value: V,
@@ -82,7 +96,7 @@ pub fn HashMap(
var it = self.queue.first;
while (it) |node| {
it = node.next;
- alloc.destroy(node);
+ alloc.destroy(Entry.fromNode(node));
}
self.map.deinit(alloc);
@@ -108,8 +122,8 @@ pub fn HashMap(
const map_gop = try self.map.getOrPutContext(alloc, key, ctx);
if (map_gop.found_existing) {
// Move to end to mark as most recently used
- self.queue.remove(map_gop.value_ptr.*);
- self.queue.append(map_gop.value_ptr.*);
+ self.queue.remove(&map_gop.value_ptr.*.node);
+ self.queue.append(&map_gop.value_ptr.*.node);
return GetOrPutResult{
.found_existing = true,
@@ -122,37 +136,34 @@ pub fn HashMap(
// We're evicting if our map insertion increased our capacity.
const evict = self.map.count() > self.capacity;
- // Get our node. If we're not evicting then we allocate a new
- // node. If we are evicting then we avoid allocation by just
- // reusing the node we would've evicted.
- var node = if (!evict) try alloc.create(Queue.Node) else node: {
+ // Get our entry. If we're not evicting then we allocate a new
+ // entry. If we are evicting then we avoid allocation by just
+ // reusing the entry we would've evicted.
+ const entry: *Entry = if (!evict) try alloc.create(Entry) else entry: {
// Our first node is the least recently used.
- const least_used = self.queue.first.?;
-
- // Move our least recently used to the end to make
- // it the most recently used.
- self.queue.remove(least_used);
+ const least_used_node = self.queue.popFirst().?;
+ const least_used_entry: *Entry = .fromNode(least_used_node);
// Remove the least used from the map
- _ = self.map.remove(least_used.data.key);
+ _ = self.map.remove(least_used_entry.data.key);
- break :node least_used;
+ break :entry least_used_entry;
};
- errdefer if (!evict) alloc.destroy(node);
+ errdefer if (!evict) alloc.destroy(entry);
- // Store our node in the map.
- map_gop.value_ptr.* = node;
+ // Store our entry in the map.
+ map_gop.value_ptr.* = entry;
- // Mark the node as most recently used
- self.queue.append(node);
+ // Mark the entry as most recently used
+ self.queue.append(&entry.node);
// Set our key
- node.data.key = key;
+ entry.data.key = key;
- return GetOrPutResult{
+ return .{
.found_existing = map_gop.found_existing,
- .value_ptr = &node.data.value,
- .evicted = if (!evict) null else node.data,
+ .value_ptr = &entry.data.value,
+ .evicted = if (!evict) null else entry.data,
};
}
@@ -193,11 +204,12 @@ pub fn HashMap(
var i: Map.Size = 0;
while (i < delta) : (i += 1) {
- const node = self.queue.first.?;
- evicted[i] = node.data.value;
+ const node = self.queue.popFirst().?;
+ const entry: *Entry = .fromNode(node);
+ evicted[i] = entry.data.value;
self.queue.remove(node);
- _ = self.map.remove(node.data.key);
- alloc.destroy(node);
+ _ = self.map.remove(entry.data.key);
+ alloc.destroy(entry);
}
self.capacity = capacity;
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;
}
+ self.formatDiagram(writer) catch {};
+ try self.formatText(writer);
+ }
- 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;
+ 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 formatText(
- self: *const Self,
- writer: anytype,
+ 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/bash.zig b/src/extra/bash.zig
index 536cadbc4..ee9a7895c 100644
--- a/src/extra/bash.zig
+++ b/src/extra/bash.zig
@@ -19,18 +19,18 @@ pub const completions = comptimeGenerateBashCompletions();
fn comptimeGenerateBashCompletions() []const u8 {
comptime {
@setEvalBranchQuota(50000);
- var counter = std.io.countingWriter(std.io.null_writer);
- try writeBashCompletions(&counter.writer());
+ var counter: std.Io.Writer.Discarding = .init(&.{});
+ try writeBashCompletions(&counter.writer);
- var buf: [counter.bytes_written]u8 = undefined;
- var stream = std.io.fixedBufferStream(&buf);
- try writeBashCompletions(stream.writer());
+ var buf: [counter.count]u8 = undefined;
+ var writer: std.Io.Writer = .fixed(&buf);
+ try writeBashCompletions(&writer);
const final = buf;
- return final[0..stream.getWritten().len];
+ return final[0..writer.end];
}
}
-fn writeBashCompletions(writer: anytype) !void {
+fn writeBashCompletions(writer: *std.Io.Writer) !void {
const pad1 = " ";
const pad2 = pad1 ++ pad1;
const pad3 = pad2 ++ pad1;
diff --git a/src/extra/fish.zig b/src/extra/fish.zig
index 5a4b38e32..7ffc23093 100644
--- a/src/extra/fish.zig
+++ b/src/extra/fish.zig
@@ -11,18 +11,18 @@ pub const completions = comptimeGenerateCompletions();
fn comptimeGenerateCompletions() []const u8 {
comptime {
@setEvalBranchQuota(50000);
- var counter = std.io.countingWriter(std.io.null_writer);
- try writeCompletions(&counter.writer());
+ var counter: std.Io.Writer.Discarding = .init(&.{});
+ try writeCompletions(&counter.writer);
- var buf: [counter.bytes_written]u8 = undefined;
- var stream = std.io.fixedBufferStream(&buf);
- try writeCompletions(stream.writer());
+ var buf: [counter.count]u8 = undefined;
+ var writer: std.Io.Writer = .fixed(&buf);
+ try writeCompletions(&writer);
const final = buf;
- return final[0..stream.getWritten().len];
+ return final[0..writer.end];
}
}
-fn writeCompletions(writer: anytype) !void {
+fn writeCompletions(writer: *std.Io.Writer) !void {
{
try writer.writeAll("set -l commands \"");
var count: usize = 0;
diff --git a/src/extra/vim.zig b/src/extra/vim.zig
index 4443fd168..2c0192d03 100644
--- a/src/extra/vim.zig
+++ b/src/extra/vim.zig
@@ -59,19 +59,20 @@ 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];
}
}
/// Writes the syntax file to the given writer.
-fn writeSyntax(writer: anytype) !void {
+fn writeSyntax(writer: *std.Io.Writer) !void {
try writer.writeAll(
\\" Vim syntax file
\\" Language: Ghostty config file
diff --git a/src/extra/zsh.zig b/src/extra/zsh.zig
index 6bddcd285..2fad4234a 100644
--- a/src/extra/zsh.zig
+++ b/src/extra/zsh.zig
@@ -12,18 +12,18 @@ const equals_required = "=-:::";
fn comptimeGenerateZshCompletions() []const u8 {
comptime {
@setEvalBranchQuota(50000);
- var counter = std.io.countingWriter(std.io.null_writer);
- try writeZshCompletions(&counter.writer());
+ var counter: std.Io.Writer.Discarding = .init(&.{});
+ try writeZshCompletions(&counter.writer);
- var buf: [counter.bytes_written]u8 = undefined;
- var stream = std.io.fixedBufferStream(&buf);
- try writeZshCompletions(stream.writer());
+ var buf: [counter.count]u8 = undefined;
+ var writer: std.Io.Writer = .fixed(&buf);
+ try writeZshCompletions(&writer);
const final = buf;
- return final[0..stream.getWritten().len];
+ return final[0..writer.end];
}
}
-fn writeZshCompletions(writer: anytype) !void {
+fn writeZshCompletions(writer: *std.Io.Writer) !void {
try writer.writeAll(
\\#compdef ghostty
\\
diff --git a/src/font/Atlas.zig b/src/font/Atlas.zig
index 68ccaddcc..e2d9a5de2 100644
--- a/src/font/Atlas.zig
+++ b/src/font/Atlas.zig
@@ -355,7 +355,7 @@ pub fn clear(self: *Atlas) void {
/// swapped because PPM expects RGB. This would be
/// easy enough to fix so next time someone needs
/// to debug a color atlas they should fix it.
-pub fn dump(self: Atlas, writer: anytype) !void {
+pub fn dump(self: Atlas, writer: *std.Io.Writer) !void {
try writer.print(
\\P{c}
\\{d} {d}
diff --git a/src/font/Collection.zig b/src/font/Collection.zig
index e91fe03ae..5ec076608 100644
--- a/src/font/Collection.zig
+++ b/src/font/Collection.zig
@@ -223,12 +223,13 @@ fn getFaceFromEntry(
// Calculate the scale factor for this
// entry now that we have a loaded face.
- entry.scale_factor = .{
- .scale = self.scaleFactor(
+ if (entry.scale_factor == .adjustment) {
+ const factor = self.scaleFactor(
face.getMetrics(),
entry.scale_factor.adjustment,
- ),
- };
+ );
+ entry.scale_factor = .{ .scale = factor };
+ }
// If our scale factor is something other
// than 1.0 then we need to resize the face.
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/opentype/sfnt.zig b/src/font/opentype/sfnt.zig
index 82c118bce..d97d9e2d5 100644
--- a/src/font/opentype/sfnt.zig
+++ b/src/font/opentype/sfnt.zig
@@ -106,7 +106,7 @@ fn FixedPoint(comptime T: type, int_bits: u64, frac_bits: u64) type {
self: Self,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
_ = fmt;
_ = options;
@@ -176,7 +176,7 @@ pub const SFNT = struct {
self: OffsetSubtable,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
_ = fmt;
_ = options;
@@ -210,7 +210,7 @@ pub const SFNT = struct {
self: TableRecord,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
_ = fmt;
_ = options;
diff --git a/src/font/shaper/feature.zig b/src/font/shaper/feature.zig
index 5fce7d6eb..40770376b 100644
--- a/src/font/shaper/feature.zig
+++ b/src/font/shaper/feature.zig
@@ -201,7 +201,7 @@ pub const Feature = struct {
self: Feature,
comptime layout: []const u8,
opts: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
_ = layout;
_ = opts;
@@ -262,7 +262,7 @@ pub const FeatureList = struct {
self: FeatureList,
comptime layout: []const u8,
opts: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
for (self.features.items, 0..) |feature, i| {
try feature.format(layout, opts, writer);
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/inspector/Inspector.zig b/src/inspector/Inspector.zig
index 27abb8657..49b05bd7f 100644
--- a/src/inspector/Inspector.zig
+++ b/src/inspector/Inspector.zig
@@ -149,7 +149,7 @@ pub fn setup() void {
font_config.FontDataOwnedByAtlas = false;
_ = cimgui.c.ImFontAtlas_AddFontFromMemoryTTF(
io.Fonts,
- @constCast(@ptrCast(font.embedded.regular)),
+ @ptrCast(@constCast(font.embedded.regular)),
font.embedded.regular.len,
font_size,
font_config,
@@ -600,6 +600,7 @@ fn renderModesWindow(self: *Inspector) void {
const t = self.surface.renderer_state.terminal;
inline for (@typeInfo(terminal.Mode).@"enum".fields) |field| {
+ @setEvalBranchQuota(6000);
const tag: terminal.modes.ModeTag = @bitCast(@as(terminal.modes.ModeTag.Backing, field.value));
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);
diff --git a/src/inspector/termio.zig b/src/inspector/termio.zig
index 49ab00ecd..03a3b0375 100644
--- a/src/inspector/termio.zig
+++ b/src/inspector/termio.zig
@@ -43,9 +43,9 @@ pub const VTEvent = struct {
) !VTEvent {
var md = Metadata.init(alloc);
errdefer md.deinit();
- var buf = std.ArrayList(u8).init(alloc);
+ var buf: std.Io.Writer.Allocating = .init(alloc);
defer buf.deinit();
- try encodeAction(alloc, buf.writer(), &md, action);
+ try encodeAction(alloc, &buf.writer, &md, action);
const str = try buf.toOwnedSliceSentinel(0);
errdefer alloc.free(str);
@@ -115,7 +115,7 @@ pub const VTEvent = struct {
/// Encode a parser action as a string that we show in the logs.
fn encodeAction(
alloc: Allocator,
- writer: anytype,
+ writer: *std.Io.Writer,
md: *Metadata,
action: terminal.Parser.Action,
) !void {
@@ -125,16 +125,16 @@ pub const VTEvent = struct {
.csi_dispatch => |v| try encodeCSI(writer, v),
.esc_dispatch => |v| try encodeEsc(writer, v),
.osc_dispatch => |v| try encodeOSC(alloc, writer, md, v),
- else => try writer.print("{}", .{action}),
+ else => try writer.print("{f}", .{action}),
}
}
- fn encodePrint(writer: anytype, action: terminal.Parser.Action) !void {
+ fn encodePrint(writer: *std.Io.Writer, action: terminal.Parser.Action) !void {
const ch = action.print;
try writer.print("'{u}' (U+{X})", .{ ch, ch });
}
- fn encodeExecute(writer: anytype, action: terminal.Parser.Action) !void {
+ fn encodeExecute(writer: *std.Io.Writer, action: terminal.Parser.Action) !void {
const ch = action.execute;
switch (ch) {
0x00 => try writer.writeAll("NUL"),
@@ -158,7 +158,7 @@ pub const VTEvent = struct {
try writer.print(" (0x{X})", .{ch});
}
- fn encodeCSI(writer: anytype, csi: terminal.Parser.Action.CSI) !void {
+ fn encodeCSI(writer: *std.Io.Writer, csi: terminal.Parser.Action.CSI) !void {
for (csi.intermediates) |v| try writer.print("{c} ", .{v});
for (csi.params, 0..) |v, i| {
if (i != 0) try writer.writeByte(';');
@@ -168,14 +168,14 @@ pub const VTEvent = struct {
try writer.writeByte(csi.final);
}
- fn encodeEsc(writer: anytype, esc: terminal.Parser.Action.ESC) !void {
+ fn encodeEsc(writer: *std.Io.Writer, esc: terminal.Parser.Action.ESC) !void {
for (esc.intermediates) |v| try writer.print("{c} ", .{v});
try writer.writeByte(esc.final);
}
fn encodeOSC(
alloc: Allocator,
- writer: anytype,
+ writer: *std.Io.Writer,
md: *Metadata,
osc: terminal.osc.Command,
) !void {
@@ -265,10 +265,10 @@ pub const VTEvent = struct {
const s = if (field.type == void)
try alloc.dupeZ(u8, tag_name)
else
- try std.fmt.allocPrintZ(alloc, "{s}={}", .{
+ try std.fmt.allocPrintSentinel(alloc, "{s}={}", .{
tag_name,
@field(value, field.name),
- });
+ }, 0);
try md.put(key, s);
}
@@ -283,7 +283,7 @@ pub const VTEvent = struct {
else => switch (Value) {
u8, u16 => try md.put(
key,
- try std.fmt.allocPrintZ(alloc, "{}", .{value}),
+ try std.fmt.allocPrintSentinel(alloc, "{}", .{value}, 0),
),
[]const u8,
diff --git a/src/main_build_data.zig b/src/main_build_data.zig
index 13e604389..9fffdd0d6 100644
--- a/src/main_build_data.zig
+++ b/src/main_build_data.zig
@@ -33,7 +33,9 @@ pub fn main() !void {
const action = action_ orelse return error.NoAction;
// Our output always goes to stdout.
- const writer = std.io.getStdOut().writer();
+ var buffer: [1024]u8 = undefined;
+ var stdout_writer = std.fs.File.stdout().writer(&buffer);
+ const writer = &stdout_writer.interface;
switch (action) {
.bash => try writer.writeAll(@import("extra/bash.zig").completions),
.fish => try writer.writeAll(@import("extra/fish.zig").completions),
diff --git a/src/main_ghostty.zig b/src/main_ghostty.zig
index 9c121b950..decfc609c 100644
--- a/src/main_ghostty.zig
+++ b/src/main_ghostty.zig
@@ -35,7 +35,9 @@ pub fn main() !MainReturn {
// a global is because the C API needs to be able to access this state;
// no other Zig code should EVER access the global state.
state.init() catch |err| {
- 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;
defer posix.exit(1);
const ErrSet = @TypeOf(err) || error{Unknown};
switch (@as(ErrSet, @errorCast(err))) {
@@ -54,6 +56,7 @@ pub fn main() !MainReturn {
else => try stderr.print("invalid CLI invocation err={}\n", .{err}),
}
+ try stderr.flush();
};
defer state.deinit();
const alloc = state.alloc;
@@ -154,8 +157,12 @@ fn logFn(
.stderr => {
// Always try default to send to stderr
- const stderr = std.io.getStdErr().writer();
- nosuspend stderr.print(level_txt ++ prefix ++ format ++ "\n", args) catch return;
+ var buffer: [1024]u8 = undefined;
+ var stderr = std.fs.File.stderr().writer(&buffer);
+ const writer = &stderr.interface;
+ nosuspend writer.print(level_txt ++ prefix ++ format ++ "\n", args) catch return;
+ // TODO: Do we want to use flushless stderr in the future?
+ writer.flush() catch {};
},
}
}
diff --git a/src/os/cgroup.zig b/src/os/cgroup.zig
index 4f13921c5..97c796f8b 100644
--- a/src/os/cgroup.zig
+++ b/src/os/cgroup.zig
@@ -19,8 +19,9 @@ pub fn current(alloc: Allocator, pid: std.os.linux.pid_t) !?[]const u8 {
defer file.close();
// Read it all into memory -- we don't expect this file to ever be that large.
- var buf_reader = std.io.bufferedReader(file.reader());
- const contents = try buf_reader.reader().readAllAlloc(
+ var reader_buf: [4096]u8 = undefined;
+ var reader = file.reader(&reader_buf);
+ const contents = try reader.interface.readAlloc(
alloc,
1 * 1024 * 1024, // 1MB
);
@@ -52,7 +53,11 @@ pub fn create(
);
const file = try std.fs.cwd().openFile(pid_path, .{ .mode = .write_only });
defer file.close();
- try file.writer().print("{}", .{pid});
+
+ var file_buf: [64]u8 = undefined;
+ var writer = file.writer(&file_buf);
+ try writer.interface.print("{}", .{pid});
+ try writer.interface.flush();
}
}
@@ -182,8 +187,9 @@ pub fn controllers(alloc: Allocator, cgroup: []const u8) ![]const u8 {
// Read it all into memory -- we don't expect this file to ever
// be that large.
- var buf_reader = std.io.bufferedReader(file.reader());
- const contents = try buf_reader.reader().readAllAlloc(
+ var reader_buf: [4096]u8 = undefined;
+ var reader = file.reader(&reader_buf);
+ const contents = try reader.interface.readAlloc(
alloc,
1 * 1024 * 1024, // 1MB
);
@@ -213,7 +219,10 @@ pub fn configureControllers(
defer file.close();
// Write
- try file.writer().writeAll(v);
+ var writer_buf: [4096]u8 = undefined;
+ var writer = file.writer(&writer_buf);
+ try writer.interface.writeAll(v);
+ try writer.interface.flush();
}
pub const Limit = union(enum) {
@@ -242,5 +251,8 @@ pub fn configureLimit(cgroup: []const u8, limit: Limit) !void {
defer file.close();
// Write our limit in bytes
- try file.writer().print("{}", .{size});
+ var writer_buf: [4096]u8 = undefined;
+ var writer = file.writer(&writer_buf);
+ try writer.interface.print("{}", .{size});
+ try writer.interface.flush();
}
diff --git a/src/os/shell.zig b/src/os/shell.zig
index 3e57031dd..a6f23e843 100644
--- a/src/os/shell.zig
+++ b/src/os/shell.zig
@@ -1,110 +1,121 @@
const std = @import("std");
const testing = std.testing;
+const Writer = std.Io.Writer;
/// Writer that escapes characters that shells treat specially to reduce the
/// risk of injection attacks or other such weirdness. Specifically excludes
/// linefeeds so that they can be used to delineate lists of file paths.
///
-/// T should be a Zig type that follows the `std.io.Writer` interface.
-pub fn ShellEscapeWriter(comptime T: type) type {
- return struct {
- child_writer: T,
+/// T should be a Zig type that follows the `std.Io.Writer` interface.
+pub const ShellEscapeWriter = struct {
+ writer: Writer,
+ child: *Writer,
- fn write(self: *ShellEscapeWriter(T), data: []const u8) error{Error}!usize {
- var count: usize = 0;
- for (data) |byte| {
- const buf = switch (byte) {
- '\\',
- '"',
- '\'',
- '$',
- '`',
- '*',
- '?',
- ' ',
- '|',
- '(',
- ')',
- => &[_]u8{ '\\', byte },
- else => &[_]u8{byte},
- };
- self.child_writer.writeAll(buf) catch return error.Error;
- count += 1;
- }
- return count;
- }
+ pub fn init(child: *Writer) ShellEscapeWriter {
+ return .{
+ .writer = .{
+ // TODO: Actually use a buffer here
+ .buffer = &.{},
+ .vtable = &.{ .drain = ShellEscapeWriter.drain },
+ },
+ .child = child,
+ };
+ }
- const Writer = std.io.Writer(*ShellEscapeWriter(T), error{Error}, write);
+ fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize {
+ const self: *ShellEscapeWriter = @fieldParentPtr("writer", w);
- pub fn init(child_writer: T) ShellEscapeWriter(T) {
- return .{ .child_writer = child_writer };
- }
+ // TODO: This is a very naive implementation and does not really make
+ // full use of the post-Writergate API. However, since we know that
+ // this is going into an Allocating writer anyways, we can be a bit
+ // less strict here.
+
+ var count: usize = 0;
+ for (data[0 .. data.len - 1]) |chunk| try self.writeEscaped(chunk, &count);
- pub fn writer(self: *ShellEscapeWriter(T)) Writer {
- return .{ .context = self };
+ for (0..splat) |_| try self.writeEscaped(data[data.len], &count);
+ return count;
+ }
+
+ fn writeEscaped(
+ self: *ShellEscapeWriter,
+ s: []const u8,
+ count: *usize,
+ ) Writer.Error!void {
+ for (s) |byte| {
+ const buf = switch (byte) {
+ '\\',
+ '"',
+ '\'',
+ '$',
+ '`',
+ '*',
+ '?',
+ ' ',
+ '|',
+ '(',
+ ')',
+ => &[_]u8{ '\\', byte },
+ else => &[_]u8{byte},
+ };
+ try self.child.writeAll(buf);
+ count.* += 1;
}
- };
-}
+ }
+};
test "shell escape 1" {
var buf: [128]u8 = undefined;
- var fmt = std.io.fixedBufferStream(&buf);
- var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
- const writer = shell.writer();
- try writer.writeAll("abc");
- try testing.expectEqualStrings("abc", fmt.getWritten());
+ var writer: std.Io.Writer = .fixed(&buf);
+ var shell: ShellEscapeWriter = .{ .child_writer = &writer };
+ try shell.writer.writeAll("abc");
+ try testing.expectEqualStrings("abc", writer.buffered());
}
test "shell escape 2" {
var buf: [128]u8 = undefined;
- var fmt = std.io.fixedBufferStream(&buf);
- var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
- const writer = shell.writer();
- try writer.writeAll("a c");
- try testing.expectEqualStrings("a\\ c", fmt.getWritten());
+ var writer: std.Io.Writer = .fixed(&buf);
+ var shell: ShellEscapeWriter = .{ .child_writer = &writer };
+ try shell.writer.writeAll("a c");
+ try testing.expectEqualStrings("a\\ c", writer.buffered());
}
test "shell escape 3" {
var buf: [128]u8 = undefined;
- var fmt = std.io.fixedBufferStream(&buf);
- var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
- const writer = shell.writer();
- try writer.writeAll("a?c");
- try testing.expectEqualStrings("a\\?c", fmt.getWritten());
+ var writer: std.Io.Writer = .fixed(&buf);
+ var shell: ShellEscapeWriter = .{ .child_writer = &writer };
+ try shell.writer.writeAll("a?c");
+ try testing.expectEqualStrings("a\\?c", writer.buffered());
}
test "shell escape 4" {
var buf: [128]u8 = undefined;
- var fmt = std.io.fixedBufferStream(&buf);
- var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
- const writer = shell.writer();
- try writer.writeAll("a\\c");
- try testing.expectEqualStrings("a\\\\c", fmt.getWritten());
+ var writer: std.Io.Writer = .fixed(&buf);
+ var shell: ShellEscapeWriter = .{ .child_writer = &writer };
+ try shell.writer.writeAll("a\\c");
+ try testing.expectEqualStrings("a\\\\c", writer.buffered());
}
test "shell escape 5" {
var buf: [128]u8 = undefined;
- var fmt = std.io.fixedBufferStream(&buf);
- var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
- const writer = shell.writer();
- try writer.writeAll("a|c");
- try testing.expectEqualStrings("a\\|c", fmt.getWritten());
+ var writer: std.Io.Writer = .fixed(&buf);
+ var shell: ShellEscapeWriter = .{ .child_writer = &writer };
+ try shell.writer.writeAll("a|c");
+ try testing.expectEqualStrings("a\\|c", writer.buffered());
}
test "shell escape 6" {
var buf: [128]u8 = undefined;
- var fmt = std.io.fixedBufferStream(&buf);
- var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
- const writer = shell.writer();
- try writer.writeAll("a\"c");
- try testing.expectEqualStrings("a\\\"c", fmt.getWritten());
+ var writer: std.Io.Writer = .fixed(&buf);
+ var shell: ShellEscapeWriter = .{ .child_writer = &writer };
+ try shell.writer.writeAll("a\"c");
+ try testing.expectEqualStrings("a\\\"c", writer.buffered());
}
test "shell escape 7" {
var buf: [128]u8 = undefined;
- var fmt = std.io.fixedBufferStream(&buf);
- var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() };
- const writer = shell.writer();
- try writer.writeAll("a(1)");
- try testing.expectEqualStrings("a\\(1\\)", fmt.getWritten());
+ var writer: std.Io.Writer = .fixed(&buf);
+ var shell: ShellEscapeWriter = .{ .child_writer = &writer };
+ try shell.writer.writeAll("a(1)");
+ try testing.expectEqualStrings("a\\(1\\)", writer.buffered());
}
diff --git a/src/pty.zig b/src/pty.zig
index 02906b778..1ab88d40f 100644
--- a/src/pty.zig
+++ b/src/pty.zig
@@ -216,7 +216,7 @@ const PosixPty = struct {
// Reset our signals
var sa: posix.Sigaction = .{
.handler = .{ .handler = posix.SIG.DFL },
- .mask = posix.empty_sigset,
+ .mask = posix.sigemptyset(),
.flags = 0,
};
posix.sigaction(posix.SIG.ABRT, &sa, null);
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..d31c36dee 100644
--- a/src/renderer/shadertoy.zig
+++ b/src/renderer/shadertoy.zig
@@ -38,8 +38,8 @@ pub fn loadFromFiles(
paths: configpkg.RepeatablePath,
target: Target,
) ![]const [:0]const u8 {
- var list = std.ArrayList([:0]const u8).init(alloc_gpa);
- defer list.deinit();
+ var list: std.ArrayList([:0]const u8) = .empty;
+ defer list.deinit(alloc_gpa);
errdefer for (list.items) |shader| alloc_gpa.free(shader);
for (paths.value.items) |item| {
@@ -56,10 +56,10 @@ pub fn loadFromFiles(
return err;
};
log.info("loaded custom shader path={s}", .{path});
- try list.append(shader);
+ try list.append(alloc_gpa, shader);
}
- return try list.toOwnedSlice();
+ return try list.toOwnedSlice(alloc_gpa);
}
/// Load a single shader from a file and convert it to the target language
@@ -73,34 +73,35 @@ pub fn loadFromFile(
defer arena.deinit();
const alloc = arena.allocator();
- // Load the shader file
- const cwd = std.fs.cwd();
- const file = try cwd.openFile(path, .{});
- defer file.close();
-
// Read it all into memory -- we don't expect shaders to be large.
- var buf_reader = std.io.bufferedReader(file.reader());
- const src = try buf_reader.reader().readAllAlloc(
- alloc,
- 4 * 1024 * 1024, // 4MB
- );
+ const src = src: {
+ // Load the shader file
+ const cwd = std.fs.cwd();
+ const file = try cwd.openFile(path, .{});
+ defer file.close();
+
+ var buf: [4096]u8 = undefined;
+ var reader = file.reader(&buf);
+ break :src try reader.interface.readAlloc(
+ alloc,
+ 4 * 1024 * 1024, // 4MB
+ );
+ };
// Convert to full GLSL
const glsl: [:0]const u8 = glsl: {
- var list = std.ArrayList(u8).init(alloc);
- try glslFromShader(list.writer(), src);
- try list.append(0);
- break :glsl list.items[0 .. list.items.len - 1 :0];
+ var stream: std.Io.Writer.Allocating = .init(alloc);
+ try glslFromShader(&stream.writer, src);
+ try stream.writer.writeByte(0);
+ break :glsl stream.written()[0 .. stream.written().len - 1 :0];
};
// Convert to SPIR-V
const spirv: []const u8 = spirv: {
- // SpirV pointer must be aligned to 4 bytes since we expect
- // a slice of words.
- var list = std.ArrayListAligned(u8, @alignOf(u32)).init(alloc);
+ var stream: std.Io.Writer.Allocating = .init(alloc);
var errlog: SpirvLog = .{ .alloc = alloc };
defer errlog.deinit();
- spirvFromGlsl(list.writer(), &errlog, glsl) catch |err| {
+ spirvFromGlsl(&stream.writer, &errlog, glsl) catch |err| {
if (errlog.info.len > 0 or errlog.debug.len > 0) {
log.warn("spirv error path={s} info={s} debug={s}", .{
path,
@@ -111,6 +112,11 @@ pub fn loadFromFile(
return err;
};
+
+ // SpirV pointer must be aligned to 4 bytes since we expect
+ // a slice of words.
+ var list: std.ArrayListAligned(u8, .of(u32)) = .empty;
+ try list.appendSlice(alloc, stream.written());
break :spirv list.items;
};
@@ -129,7 +135,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 +144,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 +337,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 +351,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 +363,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 +378,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 +398,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/synthetic/cli.zig b/src/synthetic/cli.zig
index 36832587c..b32469aab 100644
--- a/src/synthetic/cli.zig
+++ b/src/synthetic/cli.zig
@@ -90,12 +90,17 @@ fn mainActionImpl(
const rand = prng.random();
// Our output always goes to stdout.
- const writer = std.io.getStdOut().writer();
+ var buffer: [2048]u8 = undefined;
+ var stdout_writer = std.fs.File.stdout().writer(&buffer);
+ const writer = &stdout_writer.interface;
// Create our implementation
const impl = try Impl.create(alloc, opts);
defer impl.destroy(alloc);
try impl.run(writer, rand);
+
+ // Always flush
+ try writer.flush();
}
test {
diff --git a/src/synthetic/cli/Ascii.zig b/src/synthetic/cli/Ascii.zig
index 25e5bb00b..339bdee2e 100644
--- a/src/synthetic/cli/Ascii.zig
+++ b/src/synthetic/cli/Ascii.zig
@@ -23,7 +23,7 @@ pub fn destroy(self: *Ascii, alloc: Allocator) void {
alloc.destroy(self);
}
-pub fn run(self: *Ascii, writer: anytype, rand: std.Random) !void {
+pub fn run(self: *Ascii, writer: *std.Io.Writer, rand: std.Random) !void {
_ = self;
var gen: synthetic.Bytes = .{
@@ -35,10 +35,10 @@ pub fn run(self: *Ascii, writer: anytype, rand: std.Random) !void {
while (true) {
const data = try gen.next(&buf);
writer.writeAll(data) catch |err| {
- const Error = error{ NoSpaceLeft, BrokenPipe } || @TypeOf(err);
+ const Error = error{ WriteFailed, BrokenPipe } || @TypeOf(err);
switch (@as(Error, err)) {
error.BrokenPipe => return, // stdout closed
- error.NoSpaceLeft => return, // fixed buffer full
+ error.WriteFailed => return, // fixed buffer full
else => return err,
}
};
@@ -56,8 +56,6 @@ test Ascii {
const rand = prng.random();
var buf: [1024]u8 = undefined;
- var fbs = std.io.fixedBufferStream(&buf);
- const writer = fbs.writer();
-
- try impl.run(writer, rand);
+ var writer: std.Io.Writer = .fixed(&buf);
+ try impl.run(&writer, rand);
}
diff --git a/src/synthetic/cli/Osc.zig b/src/synthetic/cli/Osc.zig
index 4792cda6b..23d19e4ae 100644
--- a/src/synthetic/cli/Osc.zig
+++ b/src/synthetic/cli/Osc.zig
@@ -29,7 +29,7 @@ pub fn destroy(self: *Osc, alloc: Allocator) void {
alloc.destroy(self);
}
-pub fn run(self: *Osc, writer: anytype, rand: std.Random) !void {
+pub fn run(self: *Osc, writer: *std.Io.Writer, rand: std.Random) !void {
var gen: synthetic.Osc = .{
.rand = rand,
.p_valid = self.opts.@"p-valid",
@@ -39,10 +39,10 @@ pub fn run(self: *Osc, writer: anytype, rand: std.Random) !void {
while (true) {
const data = try gen.next(&buf);
writer.writeAll(data) catch |err| {
- const Error = error{ NoSpaceLeft, BrokenPipe } || @TypeOf(err);
+ const Error = error{ WriteFailed, BrokenPipe } || @TypeOf(err);
switch (@as(Error, err)) {
error.BrokenPipe => return, // stdout closed
- error.NoSpaceLeft => return, // fixed buffer full
+ error.WriteFailed => return, // fixed buffer full
else => return err,
}
};
@@ -60,8 +60,6 @@ test Osc {
const rand = prng.random();
var buf: [1024]u8 = undefined;
- var fbs = std.io.fixedBufferStream(&buf);
- const writer = fbs.writer();
-
- try impl.run(writer, rand);
+ var writer: std.Io.Writer = .fixed(&buf);
+ try impl.run(&writer, rand);
}
diff --git a/src/synthetic/cli/Utf8.zig b/src/synthetic/cli/Utf8.zig
index 28a11f891..3c2fddef7 100644
--- a/src/synthetic/cli/Utf8.zig
+++ b/src/synthetic/cli/Utf8.zig
@@ -23,7 +23,7 @@ pub fn destroy(self: *Utf8, alloc: Allocator) void {
alloc.destroy(self);
}
-pub fn run(self: *Utf8, writer: anytype, rand: std.Random) !void {
+pub fn run(self: *Utf8, writer: *std.Io.Writer, rand: std.Random) !void {
_ = self;
var gen: synthetic.Utf8 = .{
@@ -34,10 +34,10 @@ pub fn run(self: *Utf8, writer: anytype, rand: std.Random) !void {
while (true) {
const data = try gen.next(&buf);
writer.writeAll(data) catch |err| {
- const Error = error{ NoSpaceLeft, BrokenPipe } || @TypeOf(err);
+ const Error = error{ WriteFailed, BrokenPipe } || @TypeOf(err);
switch (@as(Error, err)) {
error.BrokenPipe => return, // stdout closed
- error.NoSpaceLeft => return, // fixed buffer full
+ error.WriteFailed => return, // fixed buffer full
else => return err,
}
};
@@ -55,8 +55,6 @@ test Utf8 {
const rand = prng.random();
var buf: [1024]u8 = undefined;
- var fbs = std.io.fixedBufferStream(&buf);
- const writer = fbs.writer();
-
- try impl.run(writer, rand);
+ var writer: std.Io.Writer = .fixed(&buf);
+ try impl.run(&writer, rand);
}
diff --git a/src/terminal/PageList.zig b/src/terminal/PageList.zig
index 8aeb6f6dc..9bf116598 100644
--- a/src/terminal/PageList.zig
+++ b/src/terminal/PageList.zig
@@ -56,7 +56,7 @@ const std_size = Page.layout(std_capacity).total_size;
/// allocator because we need memory that is zero-initialized and page-aligned.
const PagePool = std.heap.MemoryPoolAligned(
[std_size]u8,
- std.heap.page_size_min,
+ .fromByteUnits(std.heap.page_size_min),
);
/// List of pins, known as "tracked" pins. These are pins that are kept
@@ -388,11 +388,18 @@ pub fn reset(self: *PageList) void {
const page_arena = &self.pool.pages.arena;
var it = page_arena.state.buffer_list.first;
while (it) |node| : (it = node.next) {
- // The fully allocated buffer
- const alloc_buf = @as([*]u8, @ptrCast(node))[0..node.data];
+ // WARN: Since HeapAllocator's BufNode is not public API,
+ // we have to hardcode its layout here. We do a comptime assert
+ // on Zig version to verify we check it on every bump.
+ const BufNode = struct {
+ data: usize,
+ node: std.SinglyLinkedList.Node,
+ };
+ const buf_node: *BufNode = @fieldParentPtr("node", node);
+ // The fully allocated buffer
+ const alloc_buf = @as([*]u8, @ptrCast(buf_node))[0..buf_node.data];
// The buffer minus our header
- const BufNode = @TypeOf(page_arena.state.buffer_list).Node;
const data_buf = alloc_buf[@sizeOf(BufNode)..];
@memset(data_buf, 0);
}
@@ -2075,7 +2082,7 @@ inline fn createPageExt(
else
try page_alloc.alignedAlloc(
u8,
- std.heap.page_size_min,
+ .fromByteUnits(std.heap.page_size_min),
layout.total_size,
);
errdefer if (pooled)
@@ -2676,7 +2683,7 @@ pub const EncodeUtf8Options = struct {
/// predates this and is a thin wrapper around it so the tests all live there.
pub fn encodeUtf8(
self: *const PageList,
- writer: anytype,
+ writer: *std.Io.Writer,
opts: EncodeUtf8Options,
) anyerror!void {
// We don't currently use self at all. There is an argument that this
@@ -2716,7 +2723,7 @@ pub fn encodeUtf8(
/// 1 | etc.| | 4
/// +-----+ :
/// +--------+
-pub fn diagram(self: *const PageList, writer: anytype) !void {
+pub fn diagram(self: *const PageList, writer: *std.Io.Writer) !void {
const active_pin = self.getTopLeft(.active);
var active = false;
diff --git a/src/terminal/Parser.zig b/src/terminal/Parser.zig
index 05cbe7957..ca2fd3718 100644
--- a/src/terminal/Parser.zig
+++ b/src/terminal/Parser.zig
@@ -97,13 +97,9 @@ pub const Action = union(enum) {
// Implement formatter for logging
pub fn format(
self: CSI,
- comptime layout: []const u8,
- opts: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
- _ = layout;
- _ = opts;
- try std.fmt.format(writer, "ESC [ {s} {any} {c}", .{
+ try writer.print("ESC [ {s} {any} {c}", .{
self.intermediates,
self.params,
self.final,
@@ -118,13 +114,9 @@ pub const Action = union(enum) {
// Implement formatter for logging
pub fn format(
self: ESC,
- comptime layout: []const u8,
- opts: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
- _ = layout;
- _ = opts;
- try std.fmt.format(writer, "ESC {s} {c}", .{
+ try writer.print("ESC {s} {c}", .{
self.intermediates,
self.final,
});
@@ -142,11 +134,8 @@ pub const Action = union(enum) {
// print out custom formats for some of our primitives.
pub fn format(
self: Action,
- comptime layout: []const u8,
- opts: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
- _ = layout;
const T = Action;
const info = @typeInfo(T).@"union";
@@ -162,21 +151,20 @@ pub const Action = union(enum) {
const value = @field(self, u_field.name);
switch (@TypeOf(value)) {
// Unicode
- u21 => try std.fmt.format(writer, "'{u}' (U+{X})", .{ value, value }),
+ u21 => try writer.print("'{u}' (U+{X})", .{ value, value }),
// Byte
- u8 => try std.fmt.format(writer, "0x{x}", .{value}),
+ u8 => try writer.print("0x{x}", .{value}),
// Note: we don't do ASCII (u8) because there are a lot
// of invisible characters we don't want to handle right
// now.
// All others do the default behavior
- else => try std.fmt.formatType(
- @field(self, u_field.name),
+ else => try writer.printValue(
"any",
- opts,
- writer,
+ .{},
+ @field(self, u_field.name),
3,
),
}
@@ -391,7 +379,7 @@ inline fn doAction(self: *Parser, action: TransitionAction, c: u8) ?Action {
// We only allow colon or mixed separators for the 'm' command.
if (c != 'm' and self.params_sep.count() > 0) {
log.warn(
- "CSI colon or mixed separators only allowed for 'm' command, got: {}",
+ "CSI colon or mixed separators only allowed for 'm' command, got: {f}",
.{result},
);
break :csi_dispatch null;
diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig
index 0c60dcec8..a98407af7 100644
--- a/src/terminal/Screen.zig
+++ b/src/terminal/Screen.zig
@@ -2168,17 +2168,21 @@ pub const SelectionString = struct {
/// Returns the raw text associated with a selection. This will unwrap
/// soft-wrapped edges. The returned slice is owned by the caller and allocated
/// using alloc, not the allocator associated with the screen (unless they match).
-pub fn selectionString(self: *Screen, alloc: Allocator, opts: SelectionString) ![:0]const u8 {
+pub fn selectionString(
+ self: *Screen,
+ alloc: Allocator,
+ opts: SelectionString,
+) ![:0]const u8 {
// Use an ArrayList so that we can grow the array as we go. We
// build an initial capacity of just our rows in our selection times
// columns. It can be more or less based on graphemes, newlines, etc.
- var strbuilder = std.ArrayList(u8).init(alloc);
- defer strbuilder.deinit();
+ var strbuilder: std.ArrayList(u8) = .empty;
+ defer strbuilder.deinit(alloc);
// If we're building a stringmap, create our builder for the pins.
const MapBuilder = std.ArrayList(Pin);
- var mapbuilder: ?MapBuilder = if (opts.map != null) MapBuilder.init(alloc) else null;
- defer if (mapbuilder) |*b| b.deinit();
+ var mapbuilder: ?MapBuilder = if (opts.map != null) .empty else null;
+ defer if (mapbuilder) |*b| b.deinit(alloc);
const sel_ordered = opts.sel.ordered(self, .forward);
const sel_start: Pin = start: {
@@ -2235,9 +2239,9 @@ pub fn selectionString(self: *Screen, alloc: Allocator, opts: SelectionString) !
const raw: u21 = if (cell.hasText()) cell.content.codepoint else 0;
const char = if (raw > 0) raw else ' ';
const encode_len = try std.unicode.utf8Encode(char, &buf);
- try strbuilder.appendSlice(buf[0..encode_len]);
+ try strbuilder.appendSlice(alloc, buf[0..encode_len]);
if (mapbuilder) |*b| {
- for (0..encode_len) |_| try b.append(.{
+ for (0..encode_len) |_| try b.append(alloc, .{
.node = chunk.node,
.y = @intCast(y),
.x = @intCast(x),
@@ -2248,9 +2252,9 @@ pub fn selectionString(self: *Screen, alloc: Allocator, opts: SelectionString) !
const cps = chunk.node.data.lookupGrapheme(cell).?;
for (cps) |cp| {
const encode_len = try std.unicode.utf8Encode(cp, &buf);
- try strbuilder.appendSlice(buf[0..encode_len]);
+ try strbuilder.appendSlice(alloc, buf[0..encode_len]);
if (mapbuilder) |*b| {
- for (0..encode_len) |_| try b.append(.{
+ for (0..encode_len) |_| try b.append(alloc, .{
.node = chunk.node,
.y = @intCast(y),
.x = @intCast(x),
@@ -2265,8 +2269,8 @@ pub fn selectionString(self: *Screen, alloc: Allocator, opts: SelectionString) !
if (!is_final_row and
(!row.wrap or sel_ordered.rectangle))
{
- try strbuilder.append('\n');
- if (mapbuilder) |*b| try b.append(.{
+ try strbuilder.append(alloc, '\n');
+ if (mapbuilder) |*b| try b.append(alloc, .{
.node = chunk.node,
.y = @intCast(y),
.x = chunk.node.data.size.cols - 1,
@@ -2281,11 +2285,11 @@ pub fn selectionString(self: *Screen, alloc: Allocator, opts: SelectionString) !
// If we have a mapbuilder, we need to setup our string map.
if (mapbuilder) |*b| {
- var strclone = try strbuilder.clone();
- defer strclone.deinit();
- const str = try strclone.toOwnedSliceSentinel(0);
+ var strclone = try strbuilder.clone(alloc);
+ defer strclone.deinit(alloc);
+ const str = try strclone.toOwnedSliceSentinel(alloc, 0);
errdefer alloc.free(str);
- const map = try b.toOwnedSlice();
+ const map = try b.toOwnedSlice(alloc);
errdefer alloc.free(map);
opts.map.?.* = .{ .string = str, .map = map };
}
@@ -2306,7 +2310,7 @@ pub fn selectionString(self: *Screen, alloc: Allocator, opts: SelectionString) !
const i = strbuilder.items.len;
strbuilder.items.len += trimmed.len;
std.mem.copyForwards(u8, strbuilder.items[i..], trimmed);
- try strbuilder.append('\n');
+ try strbuilder.append(alloc, '\n');
}
// Remove all trailing newlines
@@ -2317,7 +2321,7 @@ pub fn selectionString(self: *Screen, alloc: Allocator, opts: SelectionString) !
}
// Get our final string
- const string = try strbuilder.toOwnedSliceSentinel(0);
+ const string = try strbuilder.toOwnedSliceSentinel(alloc, 0);
errdefer alloc.free(string);
return string;
@@ -2902,7 +2906,7 @@ pub fn promptPath(
/// one byte at a time.
pub fn dumpString(
self: *const Screen,
- writer: anytype,
+ writer: *std.Io.Writer,
opts: PageList.EncodeUtf8Options,
) anyerror!void {
try self.pages.encodeUtf8(writer, opts);
@@ -2915,10 +2919,10 @@ pub fn dumpStringAlloc(
alloc: Allocator,
tl: point.Point,
) ![]const u8 {
- var builder = std.ArrayList(u8).init(alloc);
+ var builder: std.Io.Writer.Allocating = .init(alloc);
defer builder.deinit();
- try self.dumpString(builder.writer(), .{
+ try self.dumpString(&builder.writer, .{
.tl = self.pages.getTopLeft(tl),
.br = self.pages.getBottomRight(tl) orelse return error.UnknownPoint,
.unwrap = false,
@@ -2934,10 +2938,10 @@ pub fn dumpStringAllocUnwrapped(
alloc: Allocator,
tl: point.Point,
) ![]const u8 {
- var builder = std.ArrayList(u8).init(alloc);
+ var builder: std.Io.Writer.Allocating = .init(alloc);
defer builder.deinit();
- try self.dumpString(builder.writer(), .{
+ try self.dumpString(&builder.writer, .{
.tl = self.pages.getTopLeft(tl),
.br = self.pages.getBottomRight(tl) orelse return error.UnknownPoint,
.unwrap = true,
@@ -9030,33 +9034,33 @@ test "Screen UTF8 cell map with newlines" {
var cell_map = Page.CellMap.init(alloc);
defer cell_map.deinit();
- var builder = std.ArrayList(u8).init(alloc);
+ var builder: std.Io.Writer.Allocating = .init(alloc);
defer builder.deinit();
- try s.dumpString(builder.writer(), .{
+ try s.dumpString(&builder.writer, .{
.tl = s.pages.getTopLeft(.screen),
.br = s.pages.getBottomRight(.screen),
.cell_map = &cell_map,
});
- try testing.expectEqual(7, builder.items.len);
- try testing.expectEqualStrings("A\n\nB\n\nC", builder.items);
- try testing.expectEqual(builder.items.len, cell_map.items.len);
+ try testing.expectEqual(7, builder.written().len);
+ try testing.expectEqualStrings("A\n\nB\n\nC", builder.written());
+ try testing.expectEqual(builder.written().len, cell_map.map.items.len);
try testing.expectEqual(Page.CellMapEntry{
.x = 0,
.y = 0,
- }, cell_map.items[0]);
+ }, cell_map.map.items[0]);
try testing.expectEqual(Page.CellMapEntry{
.x = 1,
.y = 0,
- }, cell_map.items[1]);
+ }, cell_map.map.items[1]);
try testing.expectEqual(Page.CellMapEntry{
.x = 0,
.y = 1,
- }, cell_map.items[2]);
+ }, cell_map.map.items[2]);
try testing.expectEqual(Page.CellMapEntry{
.x = 0,
.y = 2,
- }, cell_map.items[3]);
+ }, cell_map.map.items[3]);
}
test "Screen UTF8 cell map with blank prefix" {
@@ -9068,32 +9072,32 @@ test "Screen UTF8 cell map with blank prefix" {
s.cursorAbsolute(2, 1);
try s.testWriteString("B");
- var cell_map = Page.CellMap.init(alloc);
+ var cell_map: Page.CellMap = .init(alloc);
defer cell_map.deinit();
- var builder = std.ArrayList(u8).init(alloc);
+ var builder: std.Io.Writer.Allocating = .init(alloc);
defer builder.deinit();
- try s.dumpString(builder.writer(), .{
+ try s.dumpString(&builder.writer, .{
.tl = s.pages.getTopLeft(.screen),
.br = s.pages.getBottomRight(.screen),
.cell_map = &cell_map,
});
- try testing.expectEqualStrings("\n B", builder.items);
- try testing.expectEqual(builder.items.len, cell_map.items.len);
+ try testing.expectEqualStrings("\n B", builder.written());
+ try testing.expectEqual(builder.written().len, cell_map.map.items.len);
try testing.expectEqual(Page.CellMapEntry{
.x = 0,
.y = 0,
- }, cell_map.items[0]);
+ }, cell_map.map.items[0]);
try testing.expectEqual(Page.CellMapEntry{
.x = 0,
.y = 1,
- }, cell_map.items[1]);
+ }, cell_map.map.items[1]);
try testing.expectEqual(Page.CellMapEntry{
.x = 1,
.y = 1,
- }, cell_map.items[2]);
+ }, cell_map.map.items[2]);
try testing.expectEqual(Page.CellMapEntry{
.x = 2,
.y = 1,
- }, cell_map.items[3]);
+ }, cell_map.map.items[3]);
}
diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig
index 9857d4798..69bcbcb84 100644
--- a/src/terminal/Terminal.zig
+++ b/src/terminal/Terminal.zig
@@ -223,7 +223,7 @@ pub fn init(
.left = 0,
.right = cols - 1,
},
- .pwd = std.ArrayList(u8).init(alloc),
+ .pwd = .empty,
.modes = .{
.values = opts.default_modes,
.default = opts.default_modes,
@@ -235,10 +235,15 @@ pub fn deinit(self: *Terminal, alloc: Allocator) void {
self.tabstops.deinit(alloc);
self.screen.deinit();
self.secondary_screen.deinit();
- self.pwd.deinit();
+ self.pwd.deinit(alloc);
self.* = undefined;
}
+/// The general allocator we should use for this terminal.
+fn gpa(self: *Terminal) Allocator {
+ return self.screen.alloc;
+}
+
/// Print UTF-8 encoded string to the terminal.
pub fn printString(self: *Terminal, str: []const u8) !void {
const view = try std.unicode.Utf8View.init(str);
@@ -2531,7 +2536,7 @@ pub fn resize(
/// Set the pwd for the terminal.
pub fn setPwd(self: *Terminal, pwd: []const u8) !void {
self.pwd.clearRetainingCapacity();
- try self.pwd.appendSlice(pwd);
+ try self.pwd.appendSlice(self.gpa(), pwd);
}
/// Returns the pwd for the terminal, if any. The memory is owned by the
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/bitmap_allocator.zig b/src/terminal/bitmap_allocator.zig
index 724c71be5..894172b4c 100644
--- a/src/terminal/bitmap_allocator.zig
+++ b/src/terminal/bitmap_allocator.zig
@@ -34,7 +34,7 @@ pub fn BitmapAllocator(comptime chunk_size: comptime_int) type {
assert(std.math.isPowerOfTwo(chunk_size));
}
- pub const base_align = @alignOf(u64);
+ pub const base_align: std.mem.Alignment = .fromByteUnits(@alignOf(u64));
pub const bitmap_bit_size = @bitSizeOf(u64);
/// The bitmap of available chunks. Each bit represents a chunk. A
@@ -49,7 +49,7 @@ pub fn BitmapAllocator(comptime chunk_size: comptime_int) type {
/// Initialize the allocator map with a given buf and memory layout.
pub fn init(buf: OffsetBuf, l: Layout) Self {
- assert(@intFromPtr(buf.start()) % base_align == 0);
+ assert(base_align.check(@intFromPtr(buf.start())));
// Initialize our bitmaps to all 1s to note that all chunks are free.
const bitmap = buf.member(u64, l.bitmap_start);
@@ -92,7 +92,7 @@ pub fn BitmapAllocator(comptime chunk_size: comptime_int) type {
return error.OutOfMemory;
const chunks = self.chunks.ptr(base);
- const ptr: [*]T = @alignCast(@ptrCast(&chunks[idx * chunk_size]));
+ const ptr: [*]T = @ptrCast(@alignCast(&chunks[idx * chunk_size]));
return ptr[0..n];
}
diff --git a/src/terminal/dcs.zig b/src/terminal/dcs.zig
index e4d0f3de2..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
),
@@ -83,7 +83,7 @@ pub const Handler = struct {
// https://github.com/mitchellh/ghostty/issues/517
'q' => .{
.state = .{
- .xtgettcap = try std.ArrayList(u8).initCapacity(
+ .xtgettcap = try .initCapacity(
alloc,
128, // Arbitrary choice
),
@@ -134,11 +134,11 @@ pub const Handler = struct {
} else unreachable,
.xtgettcap => |*list| {
- if (list.items.len >= self.max_bytes) {
+ if (list.written().len >= self.max_bytes) {
return error.OutOfMemory;
}
- try list.append(byte);
+ try list.writer.writeByte(byte);
},
.decrqss => |*buffer| {
@@ -170,11 +170,12 @@ pub const Handler = struct {
break :tmux .{ .tmux = .{ .exit = {} } };
} else unreachable,
- .xtgettcap => |list| xtgettcap: {
- for (list.items, 0..) |b, i| {
- list.items[i] = std.ascii.toUpper(b);
- }
- break :xtgettcap .{ .xtgettcap = .{ .data = list } };
+ .xtgettcap => |*list| xtgettcap: {
+ // Note: purposely do not deinit our state here because
+ // we copy it into the resulting command.
+ const items = list.written();
+ for (items, 0..) |b, i| items[i] = std.ascii.toUpper(b);
+ break :xtgettcap .{ .xtgettcap = .{ .data = list.* } };
},
.decrqss => |buffer| .{ .decrqss = switch (buffer.len) {
@@ -216,8 +217,8 @@ pub const Command = union(enum) {
else
void,
- pub fn deinit(self: Command) void {
- switch (self) {
+ pub fn deinit(self: *Command) void {
+ switch (self.*) {
.xtgettcap => |*v| v.data.deinit(),
.decrqss => {},
.tmux => {},
@@ -225,16 +226,16 @@ pub const Command = union(enum) {
}
pub const XTGETTCAP = struct {
- data: std.ArrayList(u8),
+ data: std.Io.Writer.Allocating,
i: usize = 0,
/// Returns the next terminfo key being requested and null
/// when there are no more keys. The returned value is NOT hex-decoded
/// because we expect to use a comptime lookup table.
pub fn next(self: *XTGETTCAP) ?[]const u8 {
- if (self.i >= self.data.items.len) return null;
-
- var rem = self.data.items[self.i..];
+ const items = self.data.written();
+ if (self.i >= items.len) return null;
+ var rem = items[self.i..];
const idx = std.mem.indexOf(u8, rem, ";") orelse rem.len;
// Note that if we're at the end, idx + 1 is len + 1 so we're over
@@ -271,7 +272,7 @@ const State = union(enum) {
ignore: void,
/// XTGETTCAP
- xtgettcap: std.ArrayList(u8),
+ xtgettcap: std.Io.Writer.Allocating,
/// DECRQSS
decrqss: struct {
diff --git a/src/terminal/hash_map.zig b/src/terminal/hash_map.zig
index 9a16be3b2..23b10950e 100644
--- a/src/terminal/hash_map.zig
+++ b/src/terminal/hash_map.zig
@@ -88,7 +88,7 @@ pub fn OffsetHashMap(
/// Initialize a new HashMap with the given capacity and backing
/// memory. The backing memory must be aligned to base_align.
pub fn init(buf: OffsetBuf, l: Layout) Self {
- assert(@intFromPtr(buf.start()) % base_align == 0);
+ assert(base_align.check(@intFromPtr(buf.start())));
const m = Unmanaged.init(buf, l);
return .{ .metadata = getOffset(
@@ -124,7 +124,11 @@ fn HashMapUnmanaged(
const header_align = @alignOf(Header);
const key_align = if (@sizeOf(K) == 0) 1 else @alignOf(K);
const val_align = if (@sizeOf(V) == 0) 1 else @alignOf(V);
- const base_align = @max(header_align, key_align, val_align);
+ const base_align: mem.Alignment = .fromByteUnits(@max(
+ header_align,
+ key_align,
+ val_align,
+ ));
// This is actually a midway pointer to the single buffer containing
// a `Header` field, the `Metadata`s and `Entry`s.
@@ -287,7 +291,7 @@ fn HashMapUnmanaged(
/// Initialize a hash map with a given capacity and a buffer. The
/// buffer must fit within the size defined by `layoutForCapacity`.
pub fn init(buf: OffsetBuf, layout: Layout) Self {
- assert(@intFromPtr(buf.start()) % base_align == 0);
+ assert(base_align.check(@intFromPtr(buf.start())));
// Get all our main pointers
const metadata_buf = buf.rebase(@sizeOf(Header));
@@ -862,7 +866,11 @@ fn HashMapUnmanaged(
// Our total memory size required is the end of our values
// aligned to the base required alignment.
- const total_size = std.mem.alignForward(usize, vals_end, base_align);
+ const total_size = std.mem.alignForward(
+ usize,
+ vals_end,
+ base_align.toByteUnits(),
+ );
// The offsets we actually store in the map are from the
// metadata pointer so that we can use self.metadata as
@@ -1126,15 +1134,15 @@ test "HashMap put and remove loop in random order" {
defer alloc.free(buf);
var map = Map.init(.init(buf), layout);
- var keys = std.ArrayList(u32).init(alloc);
- defer keys.deinit();
+ var keys: std.ArrayList(u32) = .empty;
+ defer keys.deinit(alloc);
const size = 32;
const iterations = 100;
var i: u32 = 0;
while (i < size) : (i += 1) {
- try keys.append(i);
+ try keys.append(alloc, i);
}
var prng = std.Random.DefaultPrng.init(0);
const random = prng.random();
diff --git a/src/terminal/kitty/color.zig b/src/terminal/kitty/color.zig
index b23e30ad8..099002f39 100644
--- a/src/terminal/kitty/color.zig
+++ b/src/terminal/kitty/color.zig
@@ -42,13 +42,8 @@ pub const Kind = union(enum) {
pub fn format(
self: Kind,
- comptime layout: []const u8,
- opts: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
- _ = layout;
- _ = opts;
-
switch (self) {
.palette => |p| try writer.print("{d}", .{p}),
.special => |s| try writer.print("{s}", .{@tagName(s)}),
@@ -61,11 +56,11 @@ test "OSC: kitty color protocol kind string" {
var buf: [256]u8 = undefined;
{
- const actual = try std.fmt.bufPrint(&buf, "{}", .{Kind{ .special = .foreground }});
+ const actual = try std.fmt.bufPrint(&buf, "{f}", .{Kind{ .special = .foreground }});
try testing.expectEqualStrings("foreground", actual);
}
{
- const actual = try std.fmt.bufPrint(&buf, "{}", .{Kind{ .palette = 42 }});
+ const actual = try std.fmt.bufPrint(&buf, "{f}", .{Kind{ .palette = 42 }});
try testing.expectEqualStrings("42", actual);
}
}
diff --git a/src/terminal/kitty/graphics_command.zig b/src/terminal/kitty/graphics_command.zig
index dcb4850c9..99a7cdaac 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 {
@@ -276,7 +276,7 @@ pub const Response = struct {
placement_id: u32 = 0,
message: []const u8 = "OK",
- pub fn encode(self: Response, writer: anytype) !void {
+ pub fn encode(self: Response, writer: *std.Io.Writer) !void {
// We only encode a result if we have either an id or an image number.
if (self.id == 0 and self.image_number == 0) return;
@@ -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);
@@ -1214,41 +1214,41 @@ test "all i32 values" {
test "response: encode nothing without ID or image number" {
const testing = std.testing;
var buf: [1024]u8 = undefined;
- var fbs = std.io.fixedBufferStream(&buf);
+ var writer: std.Io.Writer = .fixed(&buf);
var r: Response = .{};
- try r.encode(fbs.writer());
- try testing.expectEqualStrings("", fbs.getWritten());
+ try r.encode(&writer);
+ try testing.expectEqualStrings("", writer.buffered());
}
test "response: encode with only image id" {
const testing = std.testing;
var buf: [1024]u8 = undefined;
- var fbs = std.io.fixedBufferStream(&buf);
+ var writer: std.Io.Writer = .fixed(&buf);
var r: Response = .{ .id = 4 };
- try r.encode(fbs.writer());
- try testing.expectEqualStrings("\x1b_Gi=4;OK\x1b\\", fbs.getWritten());
+ try r.encode(&writer);
+ try testing.expectEqualStrings("\x1b_Gi=4;OK\x1b\\", writer.buffered());
}
test "response: encode with only image number" {
const testing = std.testing;
var buf: [1024]u8 = undefined;
- var fbs = std.io.fixedBufferStream(&buf);
+ var writer: std.Io.Writer = .fixed(&buf);
var r: Response = .{ .image_number = 4 };
- try r.encode(fbs.writer());
- try testing.expectEqualStrings("\x1b_GI=4;OK\x1b\\", fbs.getWritten());
+ try r.encode(&writer);
+ try testing.expectEqualStrings("\x1b_GI=4;OK\x1b\\", writer.buffered());
}
test "response: encode with image ID and number" {
const testing = std.testing;
var buf: [1024]u8 = undefined;
- var fbs = std.io.fixedBufferStream(&buf);
+ var writer: std.Io.Writer = .fixed(&buf);
var r: Response = .{ .id = 12, .image_number = 4 };
- try r.encode(fbs.writer());
- try testing.expectEqualStrings("\x1b_Gi=12,I=4;OK\x1b\\", fbs.getWritten());
+ try r.encode(&writer);
+ try testing.expectEqualStrings("\x1b_Gi=12,I=4;OK\x1b\\", writer.buffered());
}
test "delete range command 1" {
@@ -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/osc.zig b/src/terminal/osc.zig
index 800257c3d..897a5ef0f 100644
--- a/src/terminal/osc.zig
+++ b/src/terminal/osc.zig
@@ -274,7 +274,7 @@ pub const Terminator = enum {
self: Terminator,
comptime _: []const u8,
_: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
try writer.writeAll(self.string());
}
@@ -475,7 +475,7 @@ pub const Parser = struct {
// Some commands have their own memory management we need to clear.
switch (self.command) {
- .kitty_color_protocol => |*v| v.list.deinit(),
+ .kitty_color_protocol => |*v| v.list.deinit(self.alloc.?),
.color_operation => |*v| v.requests.deinit(self.alloc.?),
else => {},
}
@@ -821,15 +821,15 @@ pub const Parser = struct {
.@"21" => switch (c) {
';' => kitty: {
- const alloc = self.alloc orelse {
+ if (self.alloc == null) {
log.info("OSC 21 requires an allocator, but none was provided", .{});
self.state = .invalid;
break :kitty;
- };
+ }
self.command = .{
.kitty_color_protocol = .{
- .list = std.ArrayList(kitty_color.OSC.Request).init(alloc),
+ .list = .empty,
},
};
@@ -1553,18 +1553,22 @@ pub const Parser = struct {
return;
}
+ // Asserted when the command is set to kitty_color_protocol
+ // that we have an allocator.
+ const alloc = self.alloc.?;
+
if (kind == .key_only or value.len == 0) {
- v.list.append(.{ .reset = key }) catch |err| {
+ v.list.append(alloc, .{ .reset = key }) catch |err| {
log.warn("unable to append kitty color protocol option: {}", .{err});
return;
};
} else if (mem.eql(u8, "?", value)) {
- v.list.append(.{ .query = key }) catch |err| {
+ v.list.append(alloc, .{ .query = key }) catch |err| {
log.warn("unable to append kitty color protocol option: {}", .{err});
return;
};
} else {
- v.list.append(.{
+ v.list.append(alloc, .{
.set = .{
.key = key,
.color = RGB.parse(value) catch |err| switch (err) {
diff --git a/src/terminal/page.zig b/src/terminal/page.zig
index b2fe993d2..331168a27 100644
--- a/src/terminal/page.zig
+++ b/src/terminal/page.zig
@@ -86,7 +86,7 @@ pub const Page = struct {
assert(std.heap.page_size_min % @max(
@alignOf(Row),
@alignOf(Cell),
- style.Set.base_align,
+ style.Set.base_align.toByteUnits(),
) == 0);
}
@@ -1528,7 +1528,21 @@ pub const Page = struct {
};
/// See cell_map
- pub const CellMap = std.ArrayList(CellMapEntry);
+ pub const CellMap = struct {
+ alloc: Allocator,
+ map: std.ArrayList(CellMapEntry),
+
+ pub fn init(alloc: Allocator) CellMap {
+ return .{
+ .alloc = alloc,
+ .map = .empty,
+ };
+ }
+
+ pub fn deinit(self: *CellMap) void {
+ self.map.deinit(self.alloc);
+ }
+ };
/// The x/y coordinate of a single cell in the cell map.
pub const CellMapEntry = struct {
@@ -1547,7 +1561,7 @@ pub const Page = struct {
/// it makes it easier to test input contents.
pub fn encodeUtf8(
self: *const Page,
- writer: anytype,
+ writer: *std.Io.Writer,
opts: EncodeUtf8Options,
) anyerror!EncodeUtf8Options.TrailingUtf8State {
var blank_rows: usize = opts.preceding.rows;
@@ -1583,7 +1597,7 @@ pub const Page = struct {
// This is tested in Screen.zig, i.e. one test is
// "cell map with newlines"
if (opts.cell_map) |cell_map| {
- try cell_map.append(.{
+ try cell_map.map.append(cell_map.alloc, .{
.x = last_x,
.y = @intCast(y - blank_rows + i - 1),
});
@@ -1618,9 +1632,9 @@ pub const Page = struct {
continue;
}
if (blank_cells > 0) {
- try writer.writeByteNTimes(' ', blank_cells);
+ try writer.splatByteAll(' ', blank_cells);
if (opts.cell_map) |cell_map| {
- for (0..blank_cells) |i| try cell_map.append(.{
+ for (0..blank_cells) |i| try cell_map.map.append(cell_map.alloc, .{
.x = @intCast(x - blank_cells + i),
.y = y,
});
@@ -1634,7 +1648,7 @@ pub const Page = struct {
try writer.print("{u}", .{cell.content.codepoint});
if (opts.cell_map) |cell_map| {
last_x = x + 1;
- try cell_map.append(.{
+ try cell_map.map.append(cell_map.alloc, .{
.x = x,
.y = y,
});
@@ -1645,7 +1659,7 @@ pub const Page = struct {
try writer.print("{u}", .{cell.content.codepoint});
if (opts.cell_map) |cell_map| {
last_x = x + 1;
- try cell_map.append(.{
+ try cell_map.map.append(cell_map.alloc, .{
.x = x,
.y = y,
});
@@ -1653,7 +1667,7 @@ pub const Page = struct {
for (self.lookupGrapheme(cell).?) |cp| {
try writer.print("{u}", .{cp});
- if (opts.cell_map) |cell_map| try cell_map.append(.{
+ if (opts.cell_map) |cell_map| try cell_map.map.append(cell_map.alloc, .{
.x = x,
.y = y,
});
@@ -1743,25 +1757,25 @@ pub const Page = struct {
const dirty_end: usize = dirty_start + (dirty_usize_length * @sizeOf(usize));
const styles_layout: style.Set.Layout = .init(cap.styles);
- const styles_start = alignForward(usize, dirty_end, style.Set.base_align);
+ const styles_start = alignForward(usize, dirty_end, style.Set.base_align.toByteUnits());
const styles_end = styles_start + styles_layout.total_size;
const grapheme_alloc_layout = GraphemeAlloc.layout(cap.grapheme_bytes);
- const grapheme_alloc_start = alignForward(usize, styles_end, GraphemeAlloc.base_align);
+ const grapheme_alloc_start = alignForward(usize, styles_end, GraphemeAlloc.base_align.toByteUnits());
const grapheme_alloc_end = grapheme_alloc_start + grapheme_alloc_layout.total_size;
const grapheme_count = @divFloor(cap.grapheme_bytes, grapheme_chunk);
const grapheme_map_layout = GraphemeMap.layout(@intCast(grapheme_count));
- const grapheme_map_start = alignForward(usize, grapheme_alloc_end, GraphemeMap.base_align);
+ const grapheme_map_start = alignForward(usize, grapheme_alloc_end, GraphemeMap.base_align.toByteUnits());
const grapheme_map_end = grapheme_map_start + grapheme_map_layout.total_size;
const string_layout = StringAlloc.layout(cap.string_bytes);
- const string_start = alignForward(usize, grapheme_map_end, StringAlloc.base_align);
+ const string_start = alignForward(usize, grapheme_map_end, StringAlloc.base_align.toByteUnits());
const string_end = string_start + string_layout.total_size;
const hyperlink_count = @divFloor(cap.hyperlink_bytes, @sizeOf(hyperlink.Set.Item));
const hyperlink_set_layout: hyperlink.Set.Layout = .init(@intCast(hyperlink_count));
- const hyperlink_set_start = alignForward(usize, string_end, hyperlink.Set.base_align);
+ const hyperlink_set_start = alignForward(usize, string_end, hyperlink.Set.base_align.toByteUnits());
const hyperlink_set_end = hyperlink_set_start + hyperlink_set_layout.total_size;
const hyperlink_map_count: u32 = count: {
@@ -1773,7 +1787,7 @@ pub const Page = struct {
break :count std.math.ceilPowerOfTwoAssert(u32, mult);
};
const hyperlink_map_layout = hyperlink.Map.layout(hyperlink_map_count);
- const hyperlink_map_start = alignForward(usize, hyperlink_set_end, hyperlink.Map.base_align);
+ const hyperlink_map_start = alignForward(usize, hyperlink_set_end, hyperlink.Map.base_align.toByteUnits());
const hyperlink_map_end = hyperlink_map_start + hyperlink_map_layout.total_size;
const total_size = alignForward(usize, hyperlink_map_end, std.heap.page_size_min);
@@ -1867,12 +1881,12 @@ pub const Capacity = struct {
// for rows & cells (which will allow us to calculate the number of
// rows we can fit at a certain column width) we need to layout the
// "meta" members of the page (i.e. everything else) from the end.
- const hyperlink_map_start = alignBackward(usize, layout.total_size - layout.hyperlink_map_layout.total_size, hyperlink.Map.base_align);
- const hyperlink_set_start = alignBackward(usize, hyperlink_map_start - layout.hyperlink_set_layout.total_size, hyperlink.Set.base_align);
- const string_alloc_start = alignBackward(usize, hyperlink_set_start - layout.string_alloc_layout.total_size, StringAlloc.base_align);
- const grapheme_map_start = alignBackward(usize, string_alloc_start - layout.grapheme_map_layout.total_size, GraphemeMap.base_align);
- const grapheme_alloc_start = alignBackward(usize, grapheme_map_start - layout.grapheme_alloc_layout.total_size, GraphemeAlloc.base_align);
- const styles_start = alignBackward(usize, grapheme_alloc_start - layout.styles_layout.total_size, style.Set.base_align);
+ const hyperlink_map_start = alignBackward(usize, layout.total_size - layout.hyperlink_map_layout.total_size, hyperlink.Map.base_align.toByteUnits());
+ const hyperlink_set_start = alignBackward(usize, hyperlink_map_start - layout.hyperlink_set_layout.total_size, hyperlink.Set.base_align.toByteUnits());
+ const string_alloc_start = alignBackward(usize, hyperlink_set_start - layout.string_alloc_layout.total_size, StringAlloc.base_align.toByteUnits());
+ const grapheme_map_start = alignBackward(usize, string_alloc_start - layout.grapheme_map_layout.total_size, GraphemeMap.base_align.toByteUnits());
+ const grapheme_alloc_start = alignBackward(usize, grapheme_map_start - layout.grapheme_alloc_layout.total_size, GraphemeAlloc.base_align.toByteUnits());
+ const styles_start = alignBackward(usize, grapheme_alloc_start - layout.styles_layout.total_size, style.Set.base_align.toByteUnits());
// The size per row is:
// - The row metadata itself
diff --git a/src/terminal/ref_counted_set.zig b/src/terminal/ref_counted_set.zig
index 153e331a6..e07de4e97 100644
--- a/src/terminal/ref_counted_set.zig
+++ b/src/terminal/ref_counted_set.zig
@@ -59,12 +59,12 @@ pub fn RefCountedSet(
return struct {
const Self = @This();
- pub const base_align = @max(
+ pub const base_align: std.mem.Alignment = .fromByteUnits(@max(
@alignOf(Context),
@alignOf(Layout),
@alignOf(Item),
@alignOf(Id),
- );
+ ));
/// Set item
pub const Item = struct {
diff --git a/src/terminal/search.zig b/src/terminal/search.zig
index b3c6494a3..d9f6c5663 100644
--- a/src/terminal/search.zig
+++ b/src/terminal/search.zig
@@ -55,7 +55,7 @@ pub const PageListSearch = struct {
needle: []const u8,
) Allocator.Error!PageListSearch {
var window = try SlidingWindow.init(alloc, needle);
- errdefer window.deinit(alloc);
+ errdefer window.deinit();
return .{
.list = list,
@@ -63,16 +63,13 @@ pub const PageListSearch = struct {
};
}
- pub fn deinit(self: *PageListSearch, alloc: Allocator) void {
- self.window.deinit(alloc);
+ pub fn deinit(self: *PageListSearch) void {
+ self.window.deinit();
}
/// Find the next match for the needle in the pagelist. This returns
/// null when there are no more matches.
- pub fn next(
- self: *PageListSearch,
- alloc: Allocator,
- ) Allocator.Error!?Selection {
+ pub fn next(self: *PageListSearch) Allocator.Error!?Selection {
// Try to search for the needle in the window. If we find a match
// then we can return that and we're done.
if (self.window.next()) |sel| return sel;
@@ -89,7 +86,7 @@ pub const PageListSearch = struct {
// until we find a match or we reach the end of the pagelist.
// This append then next pattern limits memory usage of the window.
while (node_) |node| : (node_ = node.next) {
- try self.window.append(alloc, node);
+ try self.window.append(node);
if (self.window.next()) |sel| return sel;
}
@@ -115,6 +112,14 @@ pub const PageListSearch = struct {
/// and repeat the process. This will always maintain the minimum
/// required memory to search for the needle.
const SlidingWindow = struct {
+ /// The allocator to use for all the data within this window. We
+ /// store this rather than passing it around because its already
+ /// part of multiple elements (eg. Meta's CellMap) and we want to
+ /// ensure we always use a consistent allocator. Additionally, only
+ /// a small amount of sliding windows are expected to be in use
+ /// at any one time so the memory overhead isn't that large.
+ alloc: Allocator,
+
/// The data buffer is a circular buffer of u8 that contains the
/// encoded page text that we can use to search for the needle.
data: DataBuf,
@@ -163,6 +168,7 @@ const SlidingWindow = struct {
errdefer alloc.free(overlap_buf);
return .{
+ .alloc = alloc,
.data = data,
.meta = meta,
.needle = needle,
@@ -170,13 +176,13 @@ const SlidingWindow = struct {
};
}
- pub fn deinit(self: *SlidingWindow, alloc: Allocator) void {
- alloc.free(self.overlap_buf);
- self.data.deinit(alloc);
+ pub fn deinit(self: *SlidingWindow) void {
+ self.alloc.free(self.overlap_buf);
+ self.data.deinit(self.alloc);
var meta_it = self.meta.iterator(.forward);
while (meta_it.next()) |meta| meta.deinit();
- self.meta.deinit(alloc);
+ self.meta.deinit(self.alloc);
}
/// Clear all data but retain allocated capacity.
@@ -206,7 +212,10 @@ const SlidingWindow = struct {
// Search the first slice for the needle.
if (std.mem.indexOf(u8, slices[0], self.needle)) |idx| {
- return self.selection(idx, self.needle.len);
+ return self.selection(
+ idx,
+ self.needle.len,
+ );
}
// Search the overlap buffer for the needle.
@@ -244,7 +253,10 @@ const SlidingWindow = struct {
// Search the last slice for the needle.
if (std.mem.indexOf(u8, slices[1], self.needle)) |idx| {
- return self.selection(slices[0].len + idx, self.needle.len);
+ return self.selection(
+ slices[0].len + idx,
+ self.needle.len,
+ );
}
// No match. We keep `needle.len - 1` bytes available to
@@ -254,15 +266,15 @@ const SlidingWindow = struct {
var saved: usize = 0;
while (meta_it.next()) |meta| {
const needed = self.needle.len - 1 - saved;
- if (meta.cell_map.items.len >= needed) {
+ if (meta.cell_map.map.items.len >= needed) {
// We save up to this meta. We set our data offset
// to exactly where it needs to be to continue
// searching.
- self.data_offset = meta.cell_map.items.len - needed;
+ self.data_offset = meta.cell_map.map.items.len - needed;
break;
}
- saved += meta.cell_map.items.len;
+ saved += meta.cell_map.map.items.len;
} else {
// If we exited the while loop naturally then we
// never got the amount we needed and so there is
@@ -284,7 +296,7 @@ const SlidingWindow = struct {
var prune_data_len: usize = 0;
for (0..prune_count) |_| {
const meta = meta_it.next().?;
- prune_data_len += meta.cell_map.items.len;
+ prune_data_len += meta.cell_map.map.items.len;
meta.deinit();
}
self.meta.deleteOldest(prune_count);
@@ -384,16 +396,16 @@ const SlidingWindow = struct {
// meta_i is the index we expect to find the match in the
// cell map within this meta if it contains it.
const meta_i = idx - offset.*;
- if (meta_i >= meta.cell_map.items.len) {
+ if (meta_i >= meta.cell_map.map.items.len) {
// This meta doesn't contain the match. This means we
// can also prune this set of data because we only look
// forward.
- offset.* += meta.cell_map.items.len;
+ offset.* += meta.cell_map.map.items.len;
continue;
}
// We found the meta that contains the start of the match.
- const map = meta.cell_map.items[meta_i];
+ const map = meta.cell_map.map.items[meta_i];
return .{
.node = meta.node,
.y = map.y,
@@ -411,13 +423,15 @@ const SlidingWindow = struct {
/// via a search (via next()).
pub fn append(
self: *SlidingWindow,
- alloc: Allocator,
node: *PageList.List.Node,
) Allocator.Error!void {
// Initialize our metadata for the node.
var meta: Meta = .{
.node = node,
- .cell_map = .init(alloc),
+ .cell_map = .{
+ .alloc = self.alloc,
+ .map = .empty,
+ },
};
errdefer meta.deinit();
@@ -425,27 +439,27 @@ const SlidingWindow = struct {
// temporary memory, and then copy it into our circular buffer.
// In the future, we should benchmark and see if we can encode
// directly into the circular buffer.
- var encoded: std.ArrayListUnmanaged(u8) = .{};
- defer encoded.deinit(alloc);
+ var encoded: std.Io.Writer.Allocating = .init(self.alloc);
+ defer encoded.deinit();
// Encode the page into the buffer.
const page: *const Page = &meta.node.data;
_ = page.encodeUtf8(
- encoded.writer(alloc),
+ &encoded.writer,
.{ .cell_map = &meta.cell_map },
) catch {
// writer uses anyerror but the only realistic error on
// an ArrayList is out of memory.
return error.OutOfMemory;
};
- assert(meta.cell_map.items.len == encoded.items.len);
+ assert(meta.cell_map.map.items.len == encoded.written().len);
// Ensure our buffers are big enough to store what we need.
- try self.data.ensureUnusedCapacity(alloc, encoded.items.len);
- try self.meta.ensureUnusedCapacity(alloc, 1);
+ try self.data.ensureUnusedCapacity(self.alloc, encoded.written().len);
+ try self.meta.ensureUnusedCapacity(self.alloc, 1);
// Append our new node to the circular buffer.
- try self.data.appendSlice(encoded.items);
+ try self.data.appendSlice(encoded.written());
try self.meta.append(meta);
self.assertIntegrity();
@@ -462,7 +476,7 @@ const SlidingWindow = struct {
// Integrity check: verify our data matches our metadata exactly.
var meta_it = self.meta.iterator(.forward);
var data_len: usize = 0;
- while (meta_it.next()) |m| data_len += m.cell_map.items.len;
+ while (meta_it.next()) |m| data_len += m.cell_map.map.items.len;
assert(data_len == self.data.len());
// Integrity check: verify our data offset is within bounds.
@@ -480,11 +494,11 @@ test "PageListSearch single page" {
try testing.expect(s.pages.pages.first == s.pages.pages.last);
var search = try PageListSearch.init(alloc, &s.pages, "boo!");
- defer search.deinit(alloc);
+ defer search.deinit();
// We should be able to find two matches.
{
- const sel = (try search.next(alloc)).?;
+ const sel = (try search.next()).?;
try testing.expectEqual(point.Point{ .active = .{
.x = 7,
.y = 0,
@@ -495,7 +509,7 @@ test "PageListSearch single page" {
} }, s.pages.pointFromPin(.active, sel.end()).?);
}
{
- const sel = (try search.next(alloc)).?;
+ const sel = (try search.next()).?;
try testing.expectEqual(point.Point{ .active = .{
.x = 19,
.y = 0,
@@ -505,8 +519,8 @@ test "PageListSearch single page" {
.y = 0,
} }, s.pages.pointFromPin(.active, sel.end()).?);
}
- try testing.expect((try search.next(alloc)) == null);
- try testing.expect((try search.next(alloc)) == null);
+ try testing.expect((try search.next()) == null);
+ try testing.expect((try search.next()) == null);
}
test "SlidingWindow empty on init" {
@@ -514,7 +528,7 @@ test "SlidingWindow empty on init" {
const alloc = testing.allocator;
var w = try SlidingWindow.init(alloc, "boo!");
- defer w.deinit(alloc);
+ defer w.deinit();
try testing.expectEqual(0, w.data.len());
try testing.expectEqual(0, w.meta.len());
}
@@ -524,7 +538,7 @@ test "SlidingWindow single append" {
const alloc = testing.allocator;
var w = try SlidingWindow.init(alloc, "boo!");
- defer w.deinit(alloc);
+ defer w.deinit();
var s = try Screen.init(alloc, 80, 24, 0);
defer s.deinit();
@@ -533,7 +547,7 @@ test "SlidingWindow single append" {
// We want to test single-page cases.
try testing.expect(s.pages.pages.first == s.pages.pages.last);
const node: *PageList.List.Node = s.pages.pages.first.?;
- try w.append(alloc, node);
+ try w.append(node);
// We should be able to find two matches.
{
@@ -567,7 +581,7 @@ test "SlidingWindow single append no match" {
const alloc = testing.allocator;
var w = try SlidingWindow.init(alloc, "nope!");
- defer w.deinit(alloc);
+ defer w.deinit();
var s = try Screen.init(alloc, 80, 24, 0);
defer s.deinit();
@@ -576,7 +590,7 @@ test "SlidingWindow single append no match" {
// We want to test single-page cases.
try testing.expect(s.pages.pages.first == s.pages.pages.last);
const node: *PageList.List.Node = s.pages.pages.first.?;
- try w.append(alloc, node);
+ try w.append(node);
// No matches
try testing.expect(w.next() == null);
@@ -591,7 +605,7 @@ test "SlidingWindow two pages" {
const alloc = testing.allocator;
var w = try SlidingWindow.init(alloc, "boo!");
- defer w.deinit(alloc);
+ defer w.deinit();
var s = try Screen.init(alloc, 80, 24, 1000);
defer s.deinit();
@@ -609,8 +623,8 @@ test "SlidingWindow two pages" {
// Add both pages
const node: *PageList.List.Node = s.pages.pages.first.?;
- try w.append(alloc, node);
- try w.append(alloc, node.next.?);
+ try w.append(node);
+ try w.append(node.next.?);
// Search should find two matches
{
@@ -644,7 +658,7 @@ test "SlidingWindow two pages match across boundary" {
const alloc = testing.allocator;
var w = try SlidingWindow.init(alloc, "hello, world");
- defer w.deinit(alloc);
+ defer w.deinit();
var s = try Screen.init(alloc, 80, 24, 1000);
defer s.deinit();
@@ -661,8 +675,8 @@ test "SlidingWindow two pages match across boundary" {
// Add both pages
const node: *PageList.List.Node = s.pages.pages.first.?;
- try w.append(alloc, node);
- try w.append(alloc, node.next.?);
+ try w.append(node);
+ try w.append(node.next.?);
// Search should find a match
{
@@ -688,7 +702,7 @@ test "SlidingWindow two pages no match prunes first page" {
const alloc = testing.allocator;
var w = try SlidingWindow.init(alloc, "nope!");
- defer w.deinit(alloc);
+ defer w.deinit();
var s = try Screen.init(alloc, 80, 24, 1000);
defer s.deinit();
@@ -706,8 +720,8 @@ test "SlidingWindow two pages no match prunes first page" {
// Add both pages
const node: *PageList.List.Node = s.pages.pages.first.?;
- try w.append(alloc, node);
- try w.append(alloc, node.next.?);
+ try w.append(node);
+ try w.append(node.next.?);
// Search should find nothing
try testing.expect(w.next() == null);
@@ -737,18 +751,18 @@ test "SlidingWindow two pages no match keeps both pages" {
try s.testWriteString("hello. boo!");
// Imaginary needle for search. Doesn't match!
- var needle_list = std.ArrayList(u8).init(alloc);
- defer needle_list.deinit();
- try needle_list.appendNTimes('x', first_page_rows * s.pages.cols);
+ var needle_list: std.ArrayList(u8) = .empty;
+ defer needle_list.deinit(alloc);
+ try needle_list.appendNTimes(alloc, 'x', first_page_rows * s.pages.cols);
const needle: []const u8 = needle_list.items;
var w = try SlidingWindow.init(alloc, needle);
- defer w.deinit(alloc);
+ defer w.deinit();
// Add both pages
const node: *PageList.List.Node = s.pages.pages.first.?;
- try w.append(alloc, node);
- try w.append(alloc, node.next.?);
+ try w.append(node);
+ try w.append(node.next.?);
// Search should find nothing
try testing.expect(w.next() == null);
@@ -763,7 +777,7 @@ test "SlidingWindow single append across circular buffer boundary" {
const alloc = testing.allocator;
var w = try SlidingWindow.init(alloc, "abc");
- defer w.deinit(alloc);
+ defer w.deinit();
var s = try Screen.init(alloc, 80, 24, 0);
defer s.deinit();
@@ -776,8 +790,8 @@ test "SlidingWindow single append across circular buffer boundary" {
// our implementation changes our test will fail.
try testing.expect(s.pages.pages.first == s.pages.pages.last);
const node: *PageList.List.Node = s.pages.pages.first.?;
- try w.append(alloc, node);
- try w.append(alloc, node);
+ try w.append(node);
+ try w.append(node);
{
// No wrap around yet
const slices = w.data.getPtrSlice(0, w.data.len());
@@ -793,7 +807,7 @@ test "SlidingWindow single append across circular buffer boundary" {
w.needle = "boo";
// Add new page, now wraps
- try w.append(alloc, node);
+ try w.append(node);
{
const slices = w.data.getPtrSlice(0, w.data.len());
try testing.expect(slices[0].len > 0);
@@ -818,7 +832,7 @@ test "SlidingWindow single append match on boundary" {
const alloc = testing.allocator;
var w = try SlidingWindow.init(alloc, "abcd");
- defer w.deinit(alloc);
+ defer w.deinit();
var s = try Screen.init(alloc, 80, 24, 0);
defer s.deinit();
@@ -831,8 +845,8 @@ test "SlidingWindow single append match on boundary" {
// our implementation changes our test will fail.
try testing.expect(s.pages.pages.first == s.pages.pages.last);
const node: *PageList.List.Node = s.pages.pages.first.?;
- try w.append(alloc, node);
- try w.append(alloc, node);
+ try w.append(node);
+ try w.append(node);
{
// No wrap around yet
const slices = w.data.getPtrSlice(0, w.data.len());
@@ -848,7 +862,7 @@ test "SlidingWindow single append match on boundary" {
w.needle = "boo!";
// Add new page, now wraps
- try w.append(alloc, node);
+ try w.append(node);
{
const slices = w.data.getPtrSlice(0, w.data.len());
try testing.expect(slices[0].len > 0);
diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig
index db43aae47..c85e72f0f 100644
--- a/src/terminal/stream.zig
+++ b/src/terminal/stream.zig
@@ -288,13 +288,13 @@ pub fn Stream(comptime Handler: type) type {
for (actions) |action_opt| {
const action = action_opt orelse continue;
- if (comptime debug) log.info("action: {}", .{action});
+ if (comptime debug) log.info("action: {f}", .{action});
// If this handler handles everything manually then we do nothing
// if it can be processed.
if (@hasDecl(T, "handleManually")) {
const processed = self.handler.handleManually(action) catch |err| err: {
- log.warn("error handling action manually err={} action={}", .{
+ log.warn("error handling action manually err={} action={f}", .{
err,
action,
});
@@ -341,7 +341,7 @@ pub fn Stream(comptime Handler: type) type {
pub inline fn execute(self: *Self, c: u8) !void {
const c0: ansi.C0 = @enumFromInt(c);
- if (comptime debug) log.info("execute: {}", .{c0});
+ if (comptime debug) log.info("execute: {f}", .{c0});
switch (c0) {
// We ignore SOH/STX: https://github.com/microsoft/terminal/issues/10786
.NUL, .SOH, .STX => {},
@@ -399,12 +399,12 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid cursor up command: {}", .{input});
+ log.warn("invalid cursor up command: {f}", .{input});
return;
},
},
false,
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI A with intermediates: {s}",
@@ -419,12 +419,12 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid cursor down command: {}", .{input});
+ log.warn("invalid cursor down command: {f}", .{input});
return;
},
},
false,
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI B with intermediates: {s}",
@@ -439,11 +439,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid cursor right command: {}", .{input});
+ log.warn("invalid cursor right command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI C with intermediates: {s}",
@@ -458,11 +458,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid cursor left command: {}", .{input});
+ log.warn("invalid cursor left command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI D with intermediates: {s}",
@@ -477,12 +477,12 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid cursor up command: {}", .{input});
+ log.warn("invalid cursor up command: {f}", .{input});
return;
},
},
true,
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI E with intermediates: {s}",
@@ -497,12 +497,12 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid cursor down command: {}", .{input});
+ log.warn("invalid cursor down command: {f}", .{input});
return;
},
},
true,
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI F with intermediates: {s}",
@@ -516,8 +516,8 @@ pub fn Stream(comptime Handler: type) type {
0 => if (@hasDecl(T, "setCursorCol")) switch (input.params.len) {
0 => try self.handler.setCursorCol(1),
1 => try self.handler.setCursorCol(input.params[0]),
- else => log.warn("invalid HPA command: {}", .{input}),
- } else log.warn("unimplemented CSI callback: {}", .{input}),
+ else => log.warn("invalid HPA command: {f}", .{input}),
+ } else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI G with intermediates: {s}",
@@ -532,8 +532,8 @@ pub fn Stream(comptime Handler: type) type {
0 => try self.handler.setCursorPos(1, 1),
1 => try self.handler.setCursorPos(input.params[0], 1),
2 => try self.handler.setCursorPos(input.params[0], input.params[1]),
- else => log.warn("invalid CUP command: {}", .{input}),
- } else log.warn("unimplemented CSI callback: {}", .{input}),
+ else => log.warn("invalid CUP command: {f}", .{input}),
+ } else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI H with intermediates: {s}",
@@ -548,11 +548,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid horizontal tab command: {}", .{input});
+ log.warn("invalid horizontal tab command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI I with intermediates: {s}",
@@ -569,7 +569,7 @@ pub fn Stream(comptime Handler: type) type {
};
const protected = protected_ orelse {
- log.warn("invalid erase display command: {}", .{input});
+ log.warn("invalid erase display command: {f}", .{input});
return;
};
@@ -580,12 +580,12 @@ pub fn Stream(comptime Handler: type) type {
};
const mode = mode_ orelse {
- log.warn("invalid erase display command: {}", .{input});
+ log.warn("invalid erase display command: {f}", .{input});
return;
};
try self.handler.eraseDisplay(mode, protected);
- } else log.warn("unimplemented CSI callback: {}", .{input}),
+ } else log.warn("unimplemented CSI callback: {f}", .{input}),
// Erase Line
'K' => if (@hasDecl(T, "eraseLine")) {
@@ -596,7 +596,7 @@ pub fn Stream(comptime Handler: type) type {
};
const protected = protected_ orelse {
- log.warn("invalid erase line command: {}", .{input});
+ log.warn("invalid erase line command: {f}", .{input});
return;
};
@@ -607,12 +607,12 @@ pub fn Stream(comptime Handler: type) type {
};
const mode = mode_ orelse {
- log.warn("invalid erase line command: {}", .{input});
+ log.warn("invalid erase line command: {f}", .{input});
return;
};
try self.handler.eraseLine(mode, protected);
- } else log.warn("unimplemented CSI callback: {}", .{input}),
+ } else log.warn("unimplemented CSI callback: {f}", .{input}),
// IL - Insert Lines
// TODO: test
@@ -620,8 +620,8 @@ pub fn Stream(comptime Handler: type) type {
0 => if (@hasDecl(T, "insertLines")) switch (input.params.len) {
0 => try self.handler.insertLines(1),
1 => try self.handler.insertLines(input.params[0]),
- else => log.warn("invalid IL command: {}", .{input}),
- } else log.warn("unimplemented CSI callback: {}", .{input}),
+ else => log.warn("invalid IL command: {f}", .{input}),
+ } else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI L with intermediates: {s}",
@@ -635,8 +635,8 @@ pub fn Stream(comptime Handler: type) type {
0 => if (@hasDecl(T, "deleteLines")) switch (input.params.len) {
0 => try self.handler.deleteLines(1),
1 => try self.handler.deleteLines(input.params[0]),
- else => log.warn("invalid DL command: {}", .{input}),
- } else log.warn("unimplemented CSI callback: {}", .{input}),
+ else => log.warn("invalid DL command: {f}", .{input}),
+ } else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI M with intermediates: {s}",
@@ -651,11 +651,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid delete characters command: {}", .{input});
+ log.warn("invalid delete characters command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI P with intermediates: {s}",
@@ -671,11 +671,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid scroll up command: {}", .{input});
+ log.warn("invalid scroll up command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI S with intermediates: {s}",
@@ -690,11 +690,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid scroll down command: {}", .{input});
+ log.warn("invalid scroll down command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI T with intermediates: {s}",
@@ -711,7 +711,7 @@ pub fn Stream(comptime Handler: type) type {
if (@hasDecl(T, "tabSet"))
try self.handler.tabSet()
else
- log.warn("unimplemented tab set callback: {}", .{input});
+ log.warn("unimplemented tab set callback: {f}", .{input});
return;
}
@@ -725,12 +725,12 @@ pub fn Stream(comptime Handler: type) type {
2 => if (@hasDecl(T, "tabClear"))
try self.handler.tabClear(.current)
else
- log.warn("unimplemented tab clear callback: {}", .{input}),
+ log.warn("unimplemented tab clear callback: {f}", .{input}),
5 => if (@hasDecl(T, "tabClear"))
try self.handler.tabClear(.all)
else
- log.warn("unimplemented tab clear callback: {}", .{input}),
+ log.warn("unimplemented tab clear callback: {f}", .{input}),
else => {},
},
@@ -738,7 +738,7 @@ pub fn Stream(comptime Handler: type) type {
else => {},
}
- log.warn("invalid cursor tabulation control: {}", .{input});
+ log.warn("invalid cursor tabulation control: {f}", .{input});
return;
},
@@ -746,8 +746,8 @@ pub fn Stream(comptime Handler: type) type {
if (@hasDecl(T, "tabReset"))
try self.handler.tabReset()
else
- log.warn("unimplemented tab reset callback: {}", .{input});
- } else log.warn("invalid cursor tabulation control: {}", .{input}),
+ log.warn("unimplemented tab reset callback: {f}", .{input});
+ } else log.warn("invalid cursor tabulation control: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI W with intermediates: {s}",
@@ -762,11 +762,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid erase characters command: {}", .{input});
+ log.warn("invalid erase characters command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI X with intermediates: {s}",
@@ -781,11 +781,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid horizontal tab back command: {}", .{input});
+ log.warn("invalid horizontal tab back command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI Z with intermediates: {s}",
@@ -800,11 +800,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid HPR command: {}", .{input});
+ log.warn("invalid HPR command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI a with intermediates: {s}",
@@ -819,11 +819,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid print repeat command: {}", .{input});
+ log.warn("invalid print repeat command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI b with intermediates: {s}",
@@ -842,12 +842,12 @@ pub fn Stream(comptime Handler: type) type {
},
else => @as(?ansi.DeviceAttributeReq, null),
} orelse {
- log.warn("invalid device attributes command: {}", .{input});
+ log.warn("invalid device attributes command: {f}", .{input});
return;
};
try self.handler.deviceAttributes(req, input.params);
- } else log.warn("unimplemented CSI callback: {}", .{input}),
+ } else log.warn("unimplemented CSI callback: {f}", .{input}),
// VPA - Cursor Vertical Position Absolute
'd' => switch (input.intermediates.len) {
@@ -856,11 +856,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid VPA command: {}", .{input});
+ log.warn("invalid VPA command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI d with intermediates: {s}",
@@ -875,11 +875,11 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
- log.warn("invalid VPR command: {}", .{input});
+ log.warn("invalid VPR command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI e with intermediates: {s}",
@@ -894,11 +894,11 @@ pub fn Stream(comptime Handler: type) type {
switch (input.params.len) {
1 => @enumFromInt(input.params[0]),
else => {
- log.warn("invalid tab clear command: {}", .{input});
+ log.warn("invalid tab clear command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input}),
+ ) else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
"ignoring unimplemented CSI g with intermediates: {s}",
@@ -913,7 +913,7 @@ pub fn Stream(comptime Handler: type) type {
if (input.intermediates.len == 1 and
input.intermediates[0] == '?') break :ansi false;
- log.warn("invalid set mode command: {}", .{input});
+ log.warn("invalid set mode command: {f}", .{input});
break :mode;
};
@@ -924,7 +924,7 @@ pub fn Stream(comptime Handler: type) type {
log.warn("unimplemented mode: {}", .{mode_int});
}
}
- } else log.warn("unimplemented CSI callback: {}", .{input}),
+ } else log.warn("unimplemented CSI callback: {f}", .{input}),
// RM - Reset Mode
'l' => if (@hasDecl(T, "setMode")) mode: {
@@ -933,7 +933,7 @@ pub fn Stream(comptime Handler: type) type {
if (input.intermediates.len == 1 and
input.intermediates[0] == '?') break :ansi false;
- log.warn("invalid set mode command: {}", .{input});
+ log.warn("invalid set mode command: {f}", .{input});
break :mode;
};
@@ -944,7 +944,7 @@ pub fn Stream(comptime Handler: type) type {
log.warn("unimplemented mode: {}", .{mode_int});
}
}
- } else log.warn("unimplemented CSI callback: {}", .{input}),
+ } else log.warn("unimplemented CSI callback: {f}", .{input}),
// SGR - Select Graphic Rendition
'm' => switch (input.intermediates.len) {
@@ -958,7 +958,7 @@ pub fn Stream(comptime Handler: type) type {
// log.info("SGR attribute: {}", .{attr});
try self.handler.setAttribute(attr);
}
- } else log.warn("unimplemented CSI callback: {}", .{input}),
+ } else log.warn("unimplemented CSI callback: {f}", .{input}),
1 => switch (input.intermediates[0]) {
'>' => if (@hasDecl(T, "setModifyKeyFormat")) blk: {
@@ -974,13 +974,13 @@ pub fn Stream(comptime Handler: type) type {
2 => .{ .function_keys = {} },
4 => .{ .other_keys = .none },
else => {
- log.warn("invalid setModifyKeyFormat: {}", .{input});
+ log.warn("invalid setModifyKeyFormat: {f}", .{input});
break :blk;
},
};
if (input.params.len > 2) {
- log.warn("invalid setModifyKeyFormat: {}", .{input});
+ log.warn("invalid setModifyKeyFormat: {f}", .{input});
break :blk;
}
@@ -1000,7 +1000,7 @@ pub fn Stream(comptime Handler: type) type {
}
try self.handler.setModifyKeyFormat(format);
- } else log.warn("unimplemented setModifyKeyFormat: {}", .{input}),
+ } else log.warn("unimplemented setModifyKeyFormat: {f}", .{input}),
else => log.warn(
"unknown CSI m with intermediate: {}",
@@ -1029,12 +1029,12 @@ pub fn Stream(comptime Handler: type) type {
input.intermediates[0] == '?')
{
if (!@hasDecl(T, "deviceStatusReport")) {
- log.warn("unimplemented CSI callback: {}", .{input});
+ log.warn("unimplemented CSI callback: {f}", .{input});
return;
}
if (input.params.len != 1) {
- log.warn("invalid device status report command: {}", .{input});
+ log.warn("invalid device status report command: {f}", .{input});
return;
}
@@ -1043,12 +1043,12 @@ pub fn Stream(comptime Handler: type) type {
if (input.intermediates.len == 1 and
input.intermediates[0] == '?') break :question true;
- log.warn("invalid set mode command: {}", .{input});
+ log.warn("invalid set mode command: {f}", .{input});
return;
};
const req = device_status.reqFromInt(input.params[0], question) orelse {
- log.warn("invalid device status report command: {}", .{input});
+ log.warn("invalid device status report command: {f}", .{input});
return;
};
@@ -1067,7 +1067,7 @@ pub fn Stream(comptime Handler: type) type {
// only support reverting back to modify other keys in
// numeric except format.
try self.handler.setModifyKeyFormat(.{ .other_keys = .numeric_except });
- } else log.warn("unimplemented setModifyKeyFormat: {}", .{input}),
+ } else log.warn("unimplemented setModifyKeyFormat: {f}", .{input}),
else => log.warn(
"unknown CSI n with intermediate: {}",
@@ -1101,13 +1101,13 @@ pub fn Stream(comptime Handler: type) type {
};
if (input.params.len != 1) {
- log.warn("invalid DECRQM command: {}", .{input});
+ log.warn("invalid DECRQM command: {f}", .{input});
break :decrqm;
}
if (@hasDecl(T, "requestMode")) {
try self.handler.requestMode(input.params[0], ansi_mode);
- } else log.warn("unimplemented DECRQM callback: {}", .{input});
+ } else log.warn("unimplemented DECRQM callback: {f}", .{input});
},
else => log.warn(
@@ -1126,11 +1126,11 @@ pub fn Stream(comptime Handler: type) type {
0 => ansi.CursorStyle.default,
1 => @enumFromInt(input.params[0]),
else => {
- log.warn("invalid set curor style command: {}", .{input});
+ log.warn("invalid set curor style command: {f}", .{input});
return;
},
},
- ) else log.warn("unimplemented CSI callback: {}", .{input});
+ ) else log.warn("unimplemented CSI callback: {f}", .{input});
},
// DECSCA
@@ -1147,12 +1147,12 @@ pub fn Stream(comptime Handler: type) type {
};
const mode = mode_ orelse {
- log.warn("invalid set protected mode command: {}", .{input});
+ log.warn("invalid set protected mode command: {f}", .{input});
return;
};
try self.handler.setProtectedMode(mode);
- } else log.warn("unimplemented CSI callback: {}", .{input});
+ } else log.warn("unimplemented CSI callback: {f}", .{input});
},
// XTVERSION
@@ -1180,10 +1180,10 @@ pub fn Stream(comptime Handler: type) type {
0 => try self.handler.setTopAndBottomMargin(0, 0),
1 => try self.handler.setTopAndBottomMargin(input.params[0], 0),
2 => try self.handler.setTopAndBottomMargin(input.params[0], input.params[1]),
- else => log.warn("invalid DECSTBM command: {}", .{input}),
+ else => log.warn("invalid DECSTBM command: {f}", .{input}),
}
} else log.warn(
- "unimplemented CSI callback: {}",
+ "unimplemented CSI callback: {f}",
.{input},
),
@@ -1203,13 +1203,13 @@ pub fn Stream(comptime Handler: type) type {
},
else => log.warn(
- "unknown CSI s with intermediate: {}",
+ "unknown CSI s with intermediate: {f}",
.{input},
),
},
else => log.warn(
- "ignoring unimplemented CSI s with intermediates: {s}",
+ "ignoring unimplemented CSI s with intermediates: {f}",
.{input},
),
},
@@ -1225,10 +1225,10 @@ pub fn Stream(comptime Handler: type) type {
0 => try self.handler.setLeftAndRightMarginAmbiguous(),
1 => try self.handler.setLeftAndRightMargin(input.params[0], 0),
2 => try self.handler.setLeftAndRightMargin(input.params[0], input.params[1]),
- else => log.warn("invalid DECSLRM command: {}", .{input}),
+ else => log.warn("invalid DECSLRM command: {f}", .{input}),
}
} else log.warn(
- "unimplemented CSI callback: {}",
+ "unimplemented CSI callback: {f}",
.{input},
),
@@ -1254,30 +1254,30 @@ pub fn Stream(comptime Handler: type) type {
0 => false,
1 => true,
else => {
- log.warn("invalid XTSHIFTESCAPE command: {}", .{input});
+ log.warn("invalid XTSHIFTESCAPE command: {f}", .{input});
break :capture;
},
},
else => {
- log.warn("invalid XTSHIFTESCAPE command: {}", .{input});
+ log.warn("invalid XTSHIFTESCAPE command: {f}", .{input});
break :capture;
},
};
try self.handler.setMouseShiftCapture(capture);
} else log.warn(
- "unimplemented CSI callback: {}",
+ "unimplemented CSI callback: {f}",
.{input},
),
else => log.warn(
- "unknown CSI s with intermediate: {}",
+ "unknown CSI s with intermediate: {f}",
.{input},
),
},
else => log.warn(
- "ignoring unimplemented CSI s with intermediates: {s}",
+ "ignoring unimplemented CSI s with intermediates: {f}",
.{input},
),
},
@@ -1296,7 +1296,7 @@ pub fn Stream(comptime Handler: type) type {
.{},
);
} else log.warn(
- "ignoring CSI 14 t with extra parameters: {}",
+ "ignoring CSI 14 t with extra parameters: {f}",
.{input},
),
16 => if (input.params.len == 1) {
@@ -1308,7 +1308,7 @@ pub fn Stream(comptime Handler: type) type {
.{},
);
} else log.warn(
- "ignoring CSI 16 t with extra parameters: {s}",
+ "ignoring CSI 16 t with extra parameters: {f}",
.{input},
),
18 => if (input.params.len == 1) {
@@ -1320,7 +1320,7 @@ pub fn Stream(comptime Handler: type) type {
.{},
);
} else log.warn(
- "ignoring CSI 18 t with extra parameters: {s}",
+ "ignoring CSI 18 t with extra parameters: {f}",
.{input},
),
21 => if (input.params.len == 1) {
@@ -1332,7 +1332,7 @@ pub fn Stream(comptime Handler: type) type {
.{},
);
} else log.warn(
- "ignoring CSI 21 t with extra parameters: {s}",
+ "ignoring CSI 21 t with extra parameters: {f}",
.{input},
),
inline 22, 23 => |number| if ((input.params.len == 2 or
@@ -1359,21 +1359,21 @@ pub fn Stream(comptime Handler: type) type {
.{},
);
} else log.warn(
- "ignoring CSI 22/23 t with extra parameters: {s}",
+ "ignoring CSI 22/23 t with extra parameters: {f}",
.{input},
),
else => log.warn(
- "ignoring CSI t with unimplemented parameter: {s}",
+ "ignoring CSI t with unimplemented parameter: {f}",
.{input},
),
}
} else log.err(
- "ignoring CSI t with no parameters: {s}",
+ "ignoring CSI t with no parameters: {f}",
.{input},
);
},
else => log.warn(
- "ignoring unimplemented CSI t with intermediates: {s}",
+ "ignoring unimplemented CSI t with intermediates: {f}",
.{input},
),
},
@@ -1382,7 +1382,7 @@ pub fn Stream(comptime Handler: type) type {
0 => if (@hasDecl(T, "restoreCursor"))
try self.handler.restoreCursor()
else
- log.warn("unimplemented CSI callback: {}", .{input}),
+ log.warn("unimplemented CSI callback: {f}", .{input}),
// Kitty keyboard protocol
1 => switch (input.intermediates[0]) {
@@ -1393,7 +1393,7 @@ pub fn Stream(comptime Handler: type) type {
'>' => if (@hasDecl(T, "pushKittyKeyboard")) push: {
const flags: u5 = if (input.params.len == 1)
std.math.cast(u5, input.params[0]) orelse {
- log.warn("invalid pushKittyKeyboard command: {}", .{input});
+ log.warn("invalid pushKittyKeyboard command: {f}", .{input});
break :push;
}
else
@@ -1414,7 +1414,7 @@ pub fn Stream(comptime Handler: type) type {
'=' => if (@hasDecl(T, "setKittyKeyboard")) set: {
const flags: u5 = if (input.params.len >= 1)
std.math.cast(u5, input.params[0]) orelse {
- log.warn("invalid setKittyKeyboard command: {}", .{input});
+ log.warn("invalid setKittyKeyboard command: {f}", .{input});
break :set;
}
else
@@ -1430,7 +1430,7 @@ pub fn Stream(comptime Handler: type) type {
2 => .@"or",
3 => .not,
else => {
- log.warn("invalid setKittyKeyboard command: {}", .{input});
+ log.warn("invalid setKittyKeyboard command: {f}", .{input});
break :set;
},
};
@@ -1442,13 +1442,13 @@ pub fn Stream(comptime Handler: type) type {
},
else => log.warn(
- "unknown CSI s with intermediate: {}",
+ "unknown CSI s with intermediate: {f}",
.{input},
),
},
else => log.warn(
- "ignoring unimplemented CSI u: {}",
+ "ignoring unimplemented CSI u: {f}",
.{input},
),
},
@@ -1458,11 +1458,11 @@ pub fn Stream(comptime Handler: type) type {
0 => if (@hasDecl(T, "insertBlanks")) switch (input.params.len) {
0 => try self.handler.insertBlanks(1),
1 => try self.handler.insertBlanks(input.params[0]),
- else => log.warn("invalid ICH command: {}", .{input}),
- } else log.warn("unimplemented CSI callback: {}", .{input}),
+ else => log.warn("invalid ICH command: {f}", .{input}),
+ } else log.warn("unimplemented CSI callback: {f}", .{input}),
else => log.warn(
- "ignoring unimplemented CSI @: {}",
+ "ignoring unimplemented CSI @: {f}",
.{input},
),
},
@@ -1487,13 +1487,13 @@ pub fn Stream(comptime Handler: type) type {
break :decsasd true;
};
- if (!success) log.warn("unimplemented CSI callback: {}", .{input});
+ if (!success) log.warn("unimplemented CSI callback: {f}", .{input});
},
else => if (@hasDecl(T, "csiUnimplemented"))
try self.handler.csiUnimplemented(input)
else
- log.warn("unimplemented CSI action: {}", .{input}),
+ log.warn("unimplemented CSI action: {f}", .{input}),
}
}
@@ -1690,10 +1690,10 @@ pub fn Stream(comptime Handler: type) type {
'7' => if (@hasDecl(T, "saveCursor")) switch (action.intermediates.len) {
0 => try self.handler.saveCursor(),
else => {
- log.warn("invalid command: {}", .{action});
+ log.warn("invalid command: {f}", .{action});
return;
},
- } else log.warn("unimplemented ESC callback: {}", .{action}),
+ } else log.warn("unimplemented ESC callback: {f}", .{action}),
'8' => blk: {
switch (action.intermediates.len) {
@@ -1701,14 +1701,14 @@ pub fn Stream(comptime Handler: type) type {
0 => if (@hasDecl(T, "restoreCursor")) {
try self.handler.restoreCursor();
break :blk {};
- } else log.warn("unimplemented restore cursor callback: {}", .{action}),
+ } else log.warn("unimplemented restore cursor callback: {f}", .{action}),
1 => switch (action.intermediates[0]) {
// DECALN - Fill Screen with E
'#' => if (@hasDecl(T, "decaln")) {
try self.handler.decaln();
break :blk {};
- } else log.warn("unimplemented ESC callback: {}", .{action}),
+ } else log.warn("unimplemented ESC callback: {f}", .{action}),
else => {},
},
@@ -1716,146 +1716,146 @@ pub fn Stream(comptime Handler: type) type {
else => {}, // fall through
}
- log.warn("unimplemented ESC action: {}", .{action});
+ log.warn("unimplemented ESC action: {f}", .{action});
},
// IND - Index
'D' => if (@hasDecl(T, "index")) switch (action.intermediates.len) {
0 => try self.handler.index(),
else => {
- log.warn("invalid index command: {}", .{action});
+ log.warn("invalid index command: {f}", .{action});
return;
},
- } else log.warn("unimplemented ESC callback: {}", .{action}),
+ } else log.warn("unimplemented ESC callback: {f}", .{action}),
// NEL - Next Line
'E' => if (@hasDecl(T, "nextLine")) switch (action.intermediates.len) {
0 => try self.handler.nextLine(),
else => {
- log.warn("invalid next line command: {}", .{action});
+ log.warn("invalid next line command: {f}", .{action});
return;
},
- } else log.warn("unimplemented ESC callback: {}", .{action}),
+ } else log.warn("unimplemented ESC callback: {f}", .{action}),
// HTS - Horizontal Tab Set
'H' => if (@hasDecl(T, "tabSet")) switch (action.intermediates.len) {
0 => try self.handler.tabSet(),
else => {
- log.warn("invalid tab set command: {}", .{action});
+ log.warn("invalid tab set command: {f}", .{action});
return;
},
- } else log.warn("unimplemented tab set callback: {}", .{action}),
+ } else log.warn("unimplemented tab set callback: {f}", .{action}),
// RI - Reverse Index
'M' => if (@hasDecl(T, "reverseIndex")) switch (action.intermediates.len) {
0 => try self.handler.reverseIndex(),
else => {
- log.warn("invalid reverse index command: {}", .{action});
+ log.warn("invalid reverse index command: {f}", .{action});
return;
},
- } else log.warn("unimplemented ESC callback: {}", .{action}),
+ } else log.warn("unimplemented ESC callback: {f}", .{action}),
// SS2 - Single Shift 2
'N' => if (@hasDecl(T, "invokeCharset")) switch (action.intermediates.len) {
0 => try self.handler.invokeCharset(.GL, .G2, true),
else => {
- log.warn("invalid single shift 2 command: {}", .{action});
+ log.warn("invalid single shift 2 command: {f}", .{action});
return;
},
- } else log.warn("unimplemented invokeCharset: {}", .{action}),
+ } else log.warn("unimplemented invokeCharset: {f}", .{action}),
// SS3 - Single Shift 3
'O' => if (@hasDecl(T, "invokeCharset")) switch (action.intermediates.len) {
0 => try self.handler.invokeCharset(.GL, .G3, true),
else => {
- log.warn("invalid single shift 3 command: {}", .{action});
+ log.warn("invalid single shift 3 command: {f}", .{action});
return;
},
- } else log.warn("unimplemented invokeCharset: {}", .{action}),
+ } else log.warn("unimplemented invokeCharset: {f}", .{action}),
// SPA - Start of Guarded Area
'V' => if (@hasDecl(T, "setProtectedMode") and action.intermediates.len == 0) {
try self.handler.setProtectedMode(ansi.ProtectedMode.iso);
- } else log.warn("unimplemented ESC callback: {}", .{action}),
+ } else log.warn("unimplemented ESC callback: {f}", .{action}),
// EPA - End of Guarded Area
'W' => if (@hasDecl(T, "setProtectedMode") and action.intermediates.len == 0) {
try self.handler.setProtectedMode(ansi.ProtectedMode.off);
- } else log.warn("unimplemented ESC callback: {}", .{action}),
+ } else log.warn("unimplemented ESC callback: {f}", .{action}),
// DECID
'Z' => if (@hasDecl(T, "deviceAttributes") and action.intermediates.len == 0) {
try self.handler.deviceAttributes(.primary, &.{});
- } else log.warn("unimplemented ESC callback: {}", .{action}),
+ } else log.warn("unimplemented ESC callback: {f}", .{action}),
// RIS - Full Reset
'c' => if (@hasDecl(T, "fullReset")) switch (action.intermediates.len) {
0 => try self.handler.fullReset(),
else => {
- log.warn("invalid full reset command: {}", .{action});
+ log.warn("invalid full reset command: {f}", .{action});
return;
},
- } else log.warn("unimplemented ESC callback: {}", .{action}),
+ } else log.warn("unimplemented ESC callback: {f}", .{action}),
// LS2 - Locking Shift 2
'n' => if (@hasDecl(T, "invokeCharset")) switch (action.intermediates.len) {
0 => try self.handler.invokeCharset(.GL, .G2, false),
else => {
- log.warn("invalid single shift 2 command: {}", .{action});
+ log.warn("invalid single shift 2 command: {f}", .{action});
return;
},
- } else log.warn("unimplemented invokeCharset: {}", .{action}),
+ } else log.warn("unimplemented invokeCharset: {f}", .{action}),
// LS3 - Locking Shift 3
'o' => if (@hasDecl(T, "invokeCharset")) switch (action.intermediates.len) {
0 => try self.handler.invokeCharset(.GL, .G3, false),
else => {
- log.warn("invalid single shift 3 command: {}", .{action});
+ log.warn("invalid single shift 3 command: {f}", .{action});
return;
},
- } else log.warn("unimplemented invokeCharset: {}", .{action}),
+ } else log.warn("unimplemented invokeCharset: {f}", .{action}),
// LS1R - Locking Shift 1 Right
'~' => if (@hasDecl(T, "invokeCharset")) switch (action.intermediates.len) {
0 => try self.handler.invokeCharset(.GR, .G1, false),
else => {
- log.warn("invalid locking shift 1 right command: {}", .{action});
+ log.warn("invalid locking shift 1 right command: {f}", .{action});
return;
},
- } else log.warn("unimplemented invokeCharset: {}", .{action}),
+ } else log.warn("unimplemented invokeCharset: {f}", .{action}),
// LS2R - Locking Shift 2 Right
'}' => if (@hasDecl(T, "invokeCharset")) switch (action.intermediates.len) {
0 => try self.handler.invokeCharset(.GR, .G2, false),
else => {
- log.warn("invalid locking shift 2 right command: {}", .{action});
+ log.warn("invalid locking shift 2 right command: {f}", .{action});
return;
},
- } else log.warn("unimplemented invokeCharset: {}", .{action}),
+ } else log.warn("unimplemented invokeCharset: {f}", .{action}),
// LS3R - Locking Shift 3 Right
'|' => if (@hasDecl(T, "invokeCharset")) switch (action.intermediates.len) {
0 => try self.handler.invokeCharset(.GR, .G3, false),
else => {
- log.warn("invalid locking shift 3 right command: {}", .{action});
+ log.warn("invalid locking shift 3 right command: {f}", .{action});
return;
},
- } else log.warn("unimplemented invokeCharset: {}", .{action}),
+ } else log.warn("unimplemented invokeCharset: {f}", .{action}),
// Set application keypad mode
'=' => if (@hasDecl(T, "setMode") and action.intermediates.len == 0) {
try self.handler.setMode(.keypad_keys, true);
- } else log.warn("unimplemented setMode: {}", .{action}),
+ } else log.warn("unimplemented setMode: {f}", .{action}),
// Reset application keypad mode
'>' => if (@hasDecl(T, "setMode") and action.intermediates.len == 0) {
try self.handler.setMode(.keypad_keys, false);
- } else log.warn("unimplemented setMode: {}", .{action}),
+ } else log.warn("unimplemented setMode: {f}", .{action}),
else => if (@hasDecl(T, "escUnimplemented"))
try self.handler.escUnimplemented(action)
else
- log.warn("unimplemented ESC action: {}", .{action}),
+ log.warn("unimplemented ESC action: {f}", .{action}),
// Sets ST (string terminator). We don't have to do anything
// because our parser always accepts ST.
diff --git a/src/terminal/style.zig b/src/terminal/style.zig
index 4f51cbc71..eac577a53 100644
--- a/src/terminal/style.zig
+++ b/src/terminal/style.zig
@@ -60,7 +60,7 @@ pub const Style = struct {
self: Color,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
_ = fmt;
_ = options;
@@ -228,7 +228,7 @@ pub const Style = struct {
self: Style,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
_ = fmt;
_ = options;
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..91fee1ace 100644
--- a/src/terminfo/Source.zig
+++ b/src/terminfo/Source.zig
@@ -43,7 +43,7 @@ pub const Capability = struct {
/// Encode as a terminfo source file. The encoding is always done in a
/// human-readable format with whitespace. Fields are always written in the
/// order of the slices on this struct; this will not do any reordering.
-pub fn encode(self: Source, writer: anytype) !void {
+pub fn encode(self: Source, writer: *std.Io.Writer) !void {
// Encode the names in the order specified
for (self.names, 0..) |name, i| {
if (i != 0) try writer.writeAll("|");
@@ -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];
},
},
};
@@ -229,8 +230,8 @@ test "encode" {
// Encode
var buf: [1024]u8 = undefined;
- var buf_stream = std.io.fixedBufferStream(&buf);
- try src.encode(buf_stream.writer());
+ var writer: std.Io.Writer = .fixed(&buf);
+ try src.encode(&writer);
const expected =
"ghostty|xterm-ghostty|Ghostty,\n" ++
@@ -238,5 +239,5 @@ test "encode" {
"\tccc@,\n" ++
"\tcolors#256,\n" ++
"\tbel=^G,\n";
- try std.testing.expectEqualStrings(@as([]const u8, expected), buf_stream.getWritten());
+ try std.testing.expectEqualStrings(@as([]const u8, expected), writer.buffered());
}
diff --git a/src/terminfo/ghostty.zig b/src/terminfo/ghostty.zig
index f96154c9b..6451836e7 100644
--- a/src/terminfo/ghostty.zig
+++ b/src/terminfo/ghostty.zig
@@ -391,7 +391,7 @@ pub const ghostty: Source = .{
test "encode" {
// Encode
var buf: [1024 * 16]u8 = undefined;
- var buf_stream = std.io.fixedBufferStream(&buf);
- try ghostty.encode(buf_stream.writer());
- try std.testing.expect(buf_stream.getWritten().len > 0);
+ var writer: std.Io.Writer = .fixed(&buf);
+ try ghostty.encode(&writer);
+ try std.testing.expect(writer.buffered().len > 0);
}
diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig
index 77fd2cc68..319ae0ee6 100644
--- a/src/termio/Exec.zig
+++ b/src/termio/Exec.zig
@@ -591,6 +591,17 @@ const Subprocess = struct {
flatpak_command: ?FlatpakHostCommand = null,
linux_cgroup: Command.LinuxCgroup = Command.linux_cgroup_default,
+ const ArgsFormatter = struct {
+ args: []const [:0]const u8,
+
+ pub fn format(this: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void {
+ for (this.args, 0..) |a, i| {
+ if (i > 0) try writer.writeAll(", ");
+ try writer.print("`{s}`", .{a});
+ }
+ }
+ };
+
/// Initialize the subprocess. This will NOT start it, this only sets
/// up the internal state necessary to start it later.
pub fn init(gpa: Allocator, cfg: Config) !Subprocess {
@@ -897,7 +908,7 @@ const Subprocess = struct {
self.pty = null;
};
- log.debug("starting command command={s}", .{self.args});
+ log.debug("starting command command={f}", .{ArgsFormatter{ .args = self.args }});
// If we can't access the cwd, then don't set any cwd and inherit.
// This is important because our cwd can be set by the shell (OSC 7)
@@ -960,7 +971,7 @@ const Subprocess = struct {
const pid = try cmd.spawn(alloc);
errdefer killCommandFlatpak(cmd);
- log.info("started subcommand on host via flatpak API path={s} pid={?}", .{
+ log.info("started subcommand on host via flatpak API path={s} pid={}", .{
self.args[0],
pid,
});
@@ -1157,7 +1168,7 @@ const Subprocess = struct {
const res = posix.waitpid(pid, std.c.W.NOHANG);
log.debug("waitpid result={}", .{res.pid});
if (res.pid != 0) break;
- std.time.sleep(10 * std.time.ns_per_ms);
+ std.Thread.sleep(10 * std.time.ns_per_ms);
}
},
}
@@ -1180,7 +1191,7 @@ const Subprocess = struct {
const pgid = c.getpgid(pid);
if (pgid == my_pgid) {
log.warn("pgid is our own, retrying", .{});
- std.time.sleep(10 * std.time.ns_per_ms);
+ std.Thread.sleep(10 * std.time.ns_per_ms);
continue;
}
@@ -1429,7 +1440,7 @@ fn execCommand(
// grow if necessary for a longer command (uncommon).
9,
);
- defer args.deinit();
+ defer args.deinit(alloc);
// The reason for executing login this way is unclear. This
// comment will attempt to explain but prepare for a truly
@@ -1476,40 +1487,41 @@ fn execCommand(
// macOS.
//
// Awesome.
- try args.append("/usr/bin/login");
- if (hush) try args.append("-q");
- try args.append("-flp");
- try args.append(username);
+ try args.append(alloc, "/usr/bin/login");
+ if (hush) try args.append(alloc, "-q");
+ try args.append(alloc, "-flp");
+ try args.append(alloc, username);
switch (command) {
// Direct args can be passed directly to login, since
// login uses execvp we don't need to worry about PATH
// searching.
- .direct => |v| try args.appendSlice(v),
+ .direct => |v| try args.appendSlice(alloc, v),
.shell => |v| {
// Use "exec" to replace the bash process with
// our intended command so we don't have a parent
// process hanging around.
- const cmd = try std.fmt.allocPrintZ(
+ const cmd = try std.fmt.allocPrintSentinel(
alloc,
"exec -l {s}",
.{v},
+ 0,
);
// We execute bash with "--noprofile --norc" so that it doesn't
// load startup files so that (1) our shell integration doesn't
// break and (2) user configuration doesn't mess this process
// up.
- try args.append("/bin/bash");
- try args.append("--noprofile");
- try args.append("--norc");
- try args.append("-c");
- try args.append(cmd);
+ try args.append(alloc, "/bin/bash");
+ try args.append(alloc, "--noprofile");
+ try args.append(alloc, "--norc");
+ try args.append(alloc, "-c");
+ try args.append(alloc, cmd);
},
}
- return try args.toOwnedSlice();
+ return try args.toOwnedSlice(alloc);
}
return switch (command) {
@@ -1518,7 +1530,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 +1551,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" {
diff --git a/src/termio/stream_handler.zig b/src/termio/stream_handler.zig
index 9a7e8b416..06ff29809 100644
--- a/src/termio/stream_handler.zig
+++ b/src/termio/stream_handler.zig
@@ -310,11 +310,11 @@ pub const StreamHandler = struct {
.kitty => |*kitty_cmd| {
if (self.terminal.kittyGraphics(self.alloc, kitty_cmd)) |resp| {
var buf: [1024]u8 = undefined;
- var buf_stream = std.io.fixedBufferStream(&buf);
- try resp.encode(buf_stream.writer());
- const final = buf_stream.getWritten();
+ var writer: std.Io.Writer = .fixed(&buf);
+ try resp.encode(&writer);
+ const final = writer.buffered();
if (final.len > 2) {
- log.debug("kitty graphics response: {s}", .{std.fmt.fmtSliceHexLower(final)});
+ log.debug("kitty graphics response: {x}", .{final});
self.messageWriter(try termio.Message.writeReq(self.alloc, final));
}
}
@@ -1141,7 +1141,7 @@ pub const StreamHandler = struct {
// We need to unescape the path. We first try to unescape onto
// the stack and fall back to heap allocation if we have to.
- var pathBuf: [1024]u8 = undefined;
+ var path_buf: [1024]u8 = undefined;
const path, const heap = path: {
// Get the raw string of the URI. Its unclear to me if the various
// tags of this enum guarantee no percent-encoding so we just
@@ -1156,15 +1156,16 @@ pub const StreamHandler = struct {
break :path .{ path, false };
// First try to stack-allocate
- var fba = std.heap.FixedBufferAllocator.init(&pathBuf);
- if (std.fmt.allocPrint(fba.allocator(), "{raw}", .{uri.path})) |v|
- break :path .{ v, false }
- else |_| {}
+ var stack_writer: std.Io.Writer = .fixed(&path_buf);
+ if (uri.path.formatRaw(&stack_writer)) |_| {
+ break :path .{ stack_writer.buffered(), false };
+ } else |_| {}
// Fall back to heap
- if (std.fmt.allocPrint(self.alloc, "{raw}", .{uri.path})) |v|
- break :path .{ v, true }
- else |_| {}
+ var alloc_writer: std.Io.Writer.Allocating = .init(self.alloc);
+ if (uri.path.formatRaw(&alloc_writer.writer)) |_| {
+ break :path .{ alloc_writer.written(), true };
+ } else |_| {}
// Fall back to using it directly...
log.warn("failed to unescape OSC 7 path, using it directly path={s}", .{path});
@@ -1471,15 +1472,15 @@ pub const StreamHandler = struct {
self: *StreamHandler,
request: terminal.kitty.color.OSC,
) !void {
- var buf = std.ArrayList(u8).init(self.alloc);
- defer buf.deinit();
- const writer = buf.writer();
+ var stream: std.Io.Writer.Allocating = .init(self.alloc);
+ defer stream.deinit();
+ const writer = &stream.writer;
for (request.list.items) |item| {
switch (item) {
.query => |key| {
// If the writer buffer is empty, we need to write our prefix
- if (buf.items.len == 0) try writer.writeAll("\x1b]21");
+ if (stream.written().len == 0) try writer.writeAll("\x1b]21");
const color: terminal.color.RGB = switch (key) {
.palette => |palette| self.terminal.color_palette.colors[palette],
@@ -1488,17 +1489,17 @@ pub const StreamHandler = struct {
.background => self.background_color orelse self.default_background_color,
.cursor => self.cursor_color orelse self.default_cursor_color,
else => {
- log.warn("ignoring unsupported kitty color protocol key: {}", .{key});
+ log.warn("ignoring unsupported kitty color protocol key: {f}", .{key});
continue;
},
},
} orelse {
- try writer.print(";{}=", .{key});
+ try writer.print(";{f}=", .{key});
continue;
};
try writer.print(
- ";{}=rgb:{x:0>2}/{x:0>2}/{x:0>2}",
+ ";{f}=rgb:{x:0>2}/{x:0>2}/{x:0>2}",
.{ key, color.r, color.g, color.b },
);
},
@@ -1525,7 +1526,7 @@ pub const StreamHandler = struct {
},
else => {
log.warn(
- "ignoring unsupported kitty color protocol key: {}",
+ "ignoring unsupported kitty color protocol key: {f}",
.{v.key},
);
continue;
@@ -1560,7 +1561,7 @@ pub const StreamHandler = struct {
},
else => {
log.warn(
- "ignoring unsupported kitty color protocol key: {}",
+ "ignoring unsupported kitty color protocol key: {f}",
.{key},
);
continue;
@@ -1576,12 +1577,12 @@ pub const StreamHandler = struct {
}
// If we had any writes to our buffer, we queue them now
- if (buf.items.len > 0) {
+ if (stream.written().len > 0) {
try writer.writeAll(request.terminator.string());
self.messageWriter(.{
.write_alloc = .{
.alloc = self.alloc,
- .data = try buf.toOwnedSlice(),
+ .data = try stream.toOwnedSlice(),
},
});
}
diff --git a/src/unicode/Properties.zig b/src/unicode/Properties.zig
index b7840743a..c8c4a581c 100644
--- a/src/unicode/Properties.zig
+++ b/src/unicode/Properties.zig
@@ -24,13 +24,9 @@ pub fn eql(a: Properties, b: Properties) bool {
// Needed for lut.Generator
pub fn format(
self: Properties,
- comptime layout: []const u8,
- opts: std.fmt.FormatOptions,
- writer: anytype,
+ writer: *std.Io.Writer,
) !void {
- _ = layout;
- _ = opts;
- try std.fmt.format(writer,
+ try writer.print(
\\.{{
\\ .width= {},
\\ .grapheme_boundary_class= .{s},
diff --git a/src/unicode/lut.zig b/src/unicode/lut.zig
index e10c5c0b8..da90f1ee7 100644
--- a/src/unicode/lut.zig
+++ b/src/unicode/lut.zig
@@ -54,12 +54,14 @@ pub fn Generator(
defer blocks_map.deinit();
// Our stages
- var stage1 = std.ArrayList(u16).init(alloc);
- defer stage1.deinit();
- var stage2 = std.ArrayList(u16).init(alloc);
- defer stage2.deinit();
- var stage3 = std.ArrayList(Elem).init(alloc);
- defer stage3.deinit();
+ var stage1: std.ArrayList(u16) = .empty;
+ var stage2: std.ArrayList(u16) = .empty;
+ var stage3: std.ArrayList(Elem) = .empty;
+ defer {
+ stage1.deinit(alloc);
+ stage2.deinit(alloc);
+ stage3.deinit(alloc);
+ }
var block: Block = undefined;
var block_len: u16 = 0;
@@ -74,7 +76,7 @@ pub fn Generator(
}
const idx = stage3.items.len;
- try stage3.append(elem);
+ try stage3.append(alloc, elem);
break :block_idx idx;
};
@@ -96,11 +98,11 @@ pub fn Generator(
u16,
stage2.items.len,
) orelse return error.Stage2TooLarge;
- for (block[0..block_len]) |entry| try stage2.append(entry);
+ for (block[0..block_len]) |entry| try stage2.append(alloc, entry);
}
// Map stage1 => stage2 and reset our block
- try stage1.append(gop.value_ptr.*);
+ try stage1.append(alloc, gop.value_ptr.*);
block_len = 0;
}
@@ -109,11 +111,11 @@ pub fn Generator(
assert(stage2.items.len <= std.math.maxInt(u16));
assert(stage3.items.len <= std.math.maxInt(u16));
- const stage1_owned = try stage1.toOwnedSlice();
+ const stage1_owned = try stage1.toOwnedSlice(alloc);
errdefer alloc.free(stage1_owned);
- const stage2_owned = try stage2.toOwnedSlice();
+ const stage2_owned = try stage2.toOwnedSlice(alloc);
errdefer alloc.free(stage2_owned);
- const stage3_owned = try stage3.toOwnedSlice();
+ const stage3_owned = try stage3.toOwnedSlice(alloc);
errdefer alloc.free(stage3_owned);
return .{
@@ -145,7 +147,7 @@ pub fn Tables(comptime Elem: type) type {
/// Writes the lookup table as Zig to the given writer. The
/// written file exports three constants: stage1, stage2, and
/// stage3. These can be used to rebuild the lookup table in Zig.
- pub fn writeZig(self: *const Self, writer: anytype) !void {
+ pub fn writeZig(self: *const Self, writer: *std.Io.Writer) !void {
try writer.print(
\\//! This file is auto-generated. Do not edit.
\\
@@ -168,7 +170,13 @@ pub fn Tables(comptime Elem: type) type {
\\
\\pub const stage3: [{}]Elem = .{{
, .{self.stage3.len});
- for (self.stage3) |entry| try writer.print("{},", .{entry});
+ for (self.stage3) |entry| {
+ if (@typeInfo(@TypeOf(entry)) == .@"struct" and
+ @hasDecl(@TypeOf(entry), "format"))
+ try writer.print("{f},", .{entry})
+ else
+ try writer.print("{},", .{entry});
+ }
try writer.writeAll(
\\};
\\ };
diff --git a/src/unicode/props_uucode.zig b/src/unicode/props_uucode.zig
index ba0511ea4..6aed7d7d5 100644
--- a/src/unicode/props_uucode.zig
+++ b/src/unicode/props_uucode.zig
@@ -84,7 +84,11 @@ pub fn main() !void {
defer alloc.free(t.stage1);
defer alloc.free(t.stage2);
defer alloc.free(t.stage3);
- try t.writeZig(std.io.getStdOut().writer());
+
+ var buf: [4096]u8 = undefined;
+ var stdout = std.fs.File.stdout().writer(&buf);
+ try t.writeZig(&stdout.interface);
+ try stdout.end();
// Uncomment when manually debugging to see our table sizes.
// std.log.warn("stage1={} stage2={} stage3={}", .{
diff --git a/src/unicode/symbols_uucode.zig b/src/unicode/symbols_uucode.zig
index 3da019e81..8cbd59211 100644
--- a/src/unicode/symbols_uucode.zig
+++ b/src/unicode/symbols_uucode.zig
@@ -30,7 +30,11 @@ pub fn main() !void {
defer alloc.free(t.stage1);
defer alloc.free(t.stage2);
defer alloc.free(t.stage3);
- try t.writeZig(std.io.getStdOut().writer());
+
+ var buf: [4096]u8 = undefined;
+ var stdout = std.fs.File.stdout().writer(&buf);
+ try t.writeZig(&stdout.interface);
+ try stdout.end();
// Uncomment when manually debugging to see our table sizes.
// std.log.warn("stage1={} stage2={} stage3={}", .{