diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1638b0fd9..9893106dd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,6 @@ jobs: - build-dist - build-examples - build-flatpak - - build-freebsd - build-libghostty-vt - build-linux - build-linux-libghostty @@ -333,7 +332,7 @@ jobs: run: nix build .#ghostty-releasefast - name: Check version - run: result/bin/ghostty +version | grep -q 'builtin.OptimizeMode.ReleaseFast' + run: result/bin/ghostty +version | grep -q '.ReleaseFast' - name: Check to see if the binary has been stripped run: nm result/bin/.ghostty-wrapped 2>&1 | grep -q 'no symbols' @@ -342,7 +341,7 @@ jobs: run: nix build .#ghostty-debug - name: Check version - run: result/bin/ghostty +version | grep -q 'builtin.OptimizeMode.Debug' + run: result/bin/ghostty +version | grep -q '.Debug' - name: Check to see if the binary has not been stripped run: nm result/bin/.ghostty-wrapped 2>&1 | grep -q 'main_ghostty.main' @@ -513,7 +512,7 @@ jobs: $fileContent = Get-Content -Path "build.zig" -Raw $pattern = 'buildpkg\.requireZig\("(.*?)"\);' $zigVersion = [regex]::Match($fileContent, $pattern).Groups[1].Value - $version = "zig-windows-x86_64-$zigVersion" + $version = "zig-x86_64-windows-$zigVersion" Write-Output $version $uri = "https://ziglang.org/download/$zigVersion/$version.zip" Invoke-WebRequest -Uri "$uri" -OutFile ".\zig-windows.zip" @@ -1138,6 +1137,7 @@ jobs: name: Build on FreeBSD needs: test runs-on: namespace-profile-mitchellh-sm-systemd + if: false # FIXME: FreeBSD does not yet ship with Zig 0.15 strategy: matrix: release: diff --git a/build.zig b/build.zig index 62fa77511..7b66af81a 100644 --- a/build.zig +++ b/build.zig @@ -4,7 +4,7 @@ const builtin = @import("builtin"); const buildpkg = @import("src/build/main.zig"); comptime { - buildpkg.requireZig("0.14.0"); + buildpkg.requireZig("0.15.1"); } pub fn build(b: *std.Build) !void { @@ -249,8 +249,6 @@ pub fn build(b: *std.Build) !void { { const mod_vt_test = b.addTest(.{ .root_module = mod.vt, - .target = config.target, - .optimize = config.optimize, .filters = test_filters, }); const mod_vt_test_run = b.addRunArtifact(mod_vt_test); @@ -258,8 +256,6 @@ pub fn build(b: *std.Build) !void { const mod_vt_c_test = b.addTest(.{ .root_module = mod.vt_c, - .target = config.target, - .optimize = config.optimize, .filters = test_filters, }); const mod_vt_c_test_run = b.addRunArtifact(mod_vt_c_test); @@ -280,6 +276,8 @@ pub fn build(b: *std.Build) !void { .omit_frame_pointer = false, .unwind_tables = .sync, }), + // Crash on x86_64 without this + .use_llvm = true, }); if (config.emit_test_exe) b.installArtifact(test_exe); _ = try deps.add(test_exe); @@ -289,7 +287,7 @@ pub fn build(b: *std.Build) !void { test_step.dependOn(&test_run.step); // Normal tests always test our libghostty modules - test_step.dependOn(test_lib_vt_step); + //test_step.dependOn(test_lib_vt_step); // Valgrind test running const valgrind_run = b.addSystemCommand(&.{ diff --git a/build.zig.zon b/build.zig.zon index 992284bf7..a1885f7f7 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -3,54 +3,55 @@ .version = "1.2.1", .paths = .{""}, .fingerprint = 0x64407a2a0b4147e5, - .minimum_zig_version = "0.14.1", + .minimum_zig_version = "0.15.1", .dependencies = .{ // Zig libs .libxev = .{ // mitchellh/libxev - .url = "https://github.com/mitchellh/libxev/archive/7f803181b158a10fec8619f793e3b4df515566cb.tar.gz", - .hash = "libxev-0.0.0-86vtc2UaEwDfiTKX3iBI-s_hdzfzWQUarT3MUrmUQl-Q", + .url = "https://github.com/mitchellh/libxev/archive/34fa50878aec6e5fa8f532867001ab3c36fae23e.tar.gz", + .hash = "libxev-0.0.0-86vtc4IcEwCqEYxEYoN_3KXmc6A9VLcm22aVImfvecYs", .lazy = true, }, .vaxis = .{ // rockorager/libvaxis - .url = "git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23", - .hash = "vaxis-0.1.0-BWNV_FUICQAFZnTCL11TUvnUr1Y0_ZdqtXHhd51d76Rn", + .url = "https://github.com/rockorager/libvaxis/archive/9fc9015d5f147568e18c5e7ca28f15bf8b293760.tar.gz", + .hash = "vaxis-0.5.1-BWNV_O8fCQAeUeVrESVc-2BdXloEXkFqReDJL7Q6XTSZ", .lazy = true, }, .z2d = .{ // vancluever/z2d - .url = "https://github.com/vancluever/z2d/archive/refs/tags/v0.8.1.tar.gz", - .hash = "z2d-0.8.1-j5P_Hq8vDwB8ZaDA54-SzESDLF2zznG_zvTHiQNJImZP", + .url = "https://github.com/vancluever/z2d/archive/a1237f6881d99b75abd8a20a934e62e34b44a005.tar.gz", + .hash = "z2d-0.8.2-pre-j5P_HlVRFgCsBTQ3EgUoKbYHx5JMnyH1mHsOSPiafnef", .lazy = true, }, .zig_objc = .{ // mitchellh/zig-objc - .url = "https://github.com/mitchellh/zig-objc/archive/c9e917a4e15a983b672ca779c7985d738a2d517c.tar.gz", - .hash = "zig_objc-0.0.0-Ir_SpwsPAQBJgi9YRm2ubJMfdoysSq5gKpsIj3izQ8Zk", + .url = "https://github.com/mitchellh/zig-objc/archive/f356ed02833f0f1b8e84d50bed9e807bf7cdc0ae.tar.gz", + .hash = "zig_objc-0.0.0-Ir_Sp5gTAQCvxxR7oVIrPXxXwsfKgVP7_wqoOQrZjFeK", .lazy = true, }, .zig_js = .{ // mitchellh/zig-js - .url = "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz", - .hash = "N-V-__8AAB9YCQBaZtQjJZVndk-g_GDIK-NTZcIa63bFp9yZ", + .url = "https://github.com/mitchellh/zig-js/archive/04db83c617da1956ac5adc1cb9ba1e434c1cb6fd.tar.gz", + .hash = "zig_js-0.0.0-rjCAV-6GAADxFug7rDmPH-uM_XcnJ5NmuAMJCAscMjhi", .lazy = true, }, .uucode = .{ - .url = "https://github.com/jacobsandlund/uucode/archive/190706c6b56f0842d29778007f74f7d3d1335fc5.tar.gz", - .hash = "uucode-0.1.0-ZZjBPpAFQABNCvd9cVPBg4I7233Ays-NWfWphPNqGbyE", + // TODO: currently the use-llvm branch because its broken on self-hosted + .url = "https://github.com/jacobsandlund/uucode/archive/f81f8ef8518b8ec5a7fca30ec5fdbc76cc6197df.tar.gz", + .hash = "uucode-0.1.0-ZZjBPjQHQADuCy1VMWftjrMl3iWqgMpUugWVQJG6_7xT", }, .zig_wayland = .{ // codeberg ifreund/zig-wayland - .url = "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz", - .hash = "wayland-0.4.0-dev-lQa1kjfIAQCmhhQu3xF0KH-94-TzeMXOqfnP0-Dg6Wyy", + .url = "https://codeberg.org/ifreund/zig-wayland/archive/1b5c038ec10da20ed3a15b0b2a6db1c21383e8ea.tar.gz", + .hash = "wayland-0.5.0-dev-lQa1khrMAQDJDwYFKpdH3HizherB7sHo5dKMECfvxQHe", .lazy = true, }, .zf = .{ // natecraddock/zf - .url = "https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz", - .hash = "zf-0.10.3-OIRy8aiIAACLrBllz0zjxaH0aOe5oNm3KtEMyCntST-9", + .url = "https://github.com/jcollie/zf/archive/52ad2e5528ab754f77437edf08a07b5ec843661c.tar.gz", + .hash = "zf-0.10.3-OIRy8QGJAACJcu3tCGtfbJnnd3Y4QL7OW_X8PJ8u_ASR", .lazy = true, }, .gobject = .{ diff --git a/build.zig.zon.json b/build.zig.zon.json index 83625f765..445f39827 100644 --- a/build.zig.zon.json +++ b/build.zig.zon.json @@ -64,10 +64,10 @@ "url": "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz", "hash": "sha256-/syVtGzwXo4/yKQUdQ4LparQDYnp/fF16U/wQcrxoDo=" }, - "libxev-0.0.0-86vtc2UaEwDfiTKX3iBI-s_hdzfzWQUarT3MUrmUQl-Q": { + "libxev-0.0.0-86vtc4IcEwCqEYxEYoN_3KXmc6A9VLcm22aVImfvecYs": { "name": "libxev", - "url": "https://github.com/mitchellh/libxev/archive/7f803181b158a10fec8619f793e3b4df515566cb.tar.gz", - "hash": "sha256-KaozYKEhhT/6sInef7/8O/60LDBJN+8QmdLuNY1Gkmc=" + "url": "https://github.com/mitchellh/libxev/archive/34fa50878aec6e5fa8f532867001ab3c36fae23e.tar.gz", + "hash": "sha256-YAPqa5bkpRihKPkyMn15oRvTCZaxO3O66ymRY3lIfdc=" }, "N-V-__8AAG3RoQEyRC2Vw7Qoro5SYBf62IHn3HjqtNVY6aWK": { "name": "libxml2", @@ -109,15 +109,20 @@ "url": "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz", "hash": "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8=" }, - "uucode-0.1.0-ZZjBPpAFQABNCvd9cVPBg4I7233Ays-NWfWphPNqGbyE": { + "uucode-0.1.0-ZZjBPjQHQADuCy1VMWftjrMl3iWqgMpUugWVQJG6_7xT": { "name": "uucode", - "url": "https://github.com/jacobsandlund/uucode/archive/190706c6b56f0842d29778007f74f7d3d1335fc5.tar.gz", - "hash": "sha256-iq9Oyns5e5Tnz2BKPPPTuyJ03BN4bK0dsmSPE1s0wig=" + "url": "https://github.com/jacobsandlund/uucode/archive/f81f8ef8518b8ec5a7fca30ec5fdbc76cc6197df.tar.gz", + "hash": "sha256-VomSYOF8fRJwb/8GtVG/QqR6c95zSkQt4649C/4KXAc=" }, - "vaxis-0.1.0-BWNV_FUICQAFZnTCL11TUvnUr1Y0_ZdqtXHhd51d76Rn": { + "vaxis-0.5.1-BWNV_H0PCQAeMusmtLzh9P9xO2IW242GZ2IRe9iKYhcA": { "name": "vaxis", - "url": "git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23", - "hash": "sha256-bNZ3oveT6vPChjimPJ/GGfcdivlAeJdl/xfWM+S/MHY=" + "url": "https://github.com/rockorager/libvaxis/archive/1bf887aa7e3736bad69fd4e277a378946edb0f2a.tar.gz", + "hash": "sha256-eq5YC26OY0i2cdQJ0ZXMZ+o2vHQLEFNNGzQt5Zuz4BM=" + }, + "vaxis-0.5.1-BWNV_O8fCQAeUeVrESVc-2BdXloEXkFqReDJL7Q6XTSZ": { + "name": "vaxis", + "url": "https://github.com/rockorager/libvaxis/archive/9fc9015d5f147568e18c5e7ca28f15bf8b293760.tar.gz", + "hash": "sha256-7H5a0J7uUsrzlO7JNAf/Ussi9WxvmsbyJSmhqvl+rqI=" }, "N-V-__8AAKrHGAAs2shYq8UkE6bGcR1QJtLTyOE_lcosMn6t": { "name": "wayland", @@ -134,40 +139,50 @@ "url": "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz", "hash": "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM=" }, - "z2d-0.8.1-j5P_Hq8vDwB8ZaDA54-SzESDLF2zznG_zvTHiQNJImZP": { + "z2d-0.8.2-pre-j5P_HlVRFgCsBTQ3EgUoKbYHx5JMnyH1mHsOSPiafnef": { "name": "z2d", - "url": "https://github.com/vancluever/z2d/archive/refs/tags/v0.8.1.tar.gz", - "hash": "sha256-0DbDKSYA1ejhVx/WbOkwTgD57PNRFcnRviqBh8xpPZ0=" + "url": "https://github.com/vancluever/z2d/archive/a1237f6881d99b75abd8a20a934e62e34b44a005.tar.gz", + "hash": "sha256-5/qRZAIh1U42v7jql9W0jr2zzQZtu39DxJPLVrSybJg=" }, - "zf-0.10.3-OIRy8aiIAACLrBllz0zjxaH0aOe5oNm3KtEMyCntST-9": { + "zf-0.10.3-OIRy8QGJAACJcu3tCGtfbJnnd3Y4QL7OW_X8PJ8u_ASR": { "name": "zf", - "url": "https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz", - "hash": "sha256-3nulNQd/4rZ4paeXJYXwAliNNyRNsIOX/q3z1JB8C7I=" + "url": "https://github.com/jcollie/zf/archive/52ad2e5528ab754f77437edf08a07b5ec843661c.tar.gz", + "hash": "sha256-8BinbanSfZeBA8SBAopVxwJObN36/BTpxVHABKicsMQ=" }, - "zg-0.13.4-AAAAAGiZ7QLz4pvECFa_wG4O4TP4FLABHHbemH2KakWM": { + "zg-0.14.1-oGqU3J4_tAKBfyes3AWleKDjo-IcYvnEwaB8qxOqFMwM": { "name": "zg", - "url": "git+https://codeberg.org/atman/zg#4a002763419a34d61dcbb1f415821b83b9bf8ddc", - "hash": "sha256-fo3l6cjkrr/godElTGnQzalBsasN7J73IDIRmw7v1gA=" + "url": "git+https://codeberg.org/ivanstepanovftw/zg#4fe689e56ce2ed5a8f59308b471bccd7da89fac9", + "hash": "sha256-P0ieLuOQ05wKVaMmeNKJIxCWMIdyeKkmhsj8Ps80BGU=" }, - "N-V-__8AAB9YCQBaZtQjJZVndk-g_GDIK-NTZcIa63bFp9yZ": { + "zg-0.15.1-oGqU3M0-tALZCy7boQS86znlBloyKx6--JriGlY0Paa9": { + "name": "zg", + "url": "https://codeberg.org/chaten/zg/archive/749197a3f9d25e211615960c02380a3d659b20f9.tar.gz", + "hash": "sha256-BZhz1nPqxK6hdsJQ66n7Jk4zMgFSGLXm8eU0CX/7mDI=" + }, + "zig_js-0.0.0-rjCAV-6GAADxFug7rDmPH-uM_XcnJ5NmuAMJCAscMjhi": { "name": "zig_js", - "url": "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz", - "hash": "sha256-fyNeCVbC9UAaKJY6JhAZlT0A479M/AKYMPIWEZbDWD0=" + "url": "https://github.com/mitchellh/zig-js/archive/04db83c617da1956ac5adc1cb9ba1e434c1cb6fd.tar.gz", + "hash": "sha256-TCAY5WAV05UEuAkDhq2c6Tk/ODgAhdnDI3O/flb8c6M=" }, - "zig_objc-0.0.0-Ir_SpwsPAQBJgi9YRm2ubJMfdoysSq5gKpsIj3izQ8Zk": { + "zig_objc-0.0.0-Ir_Sp5gTAQCvxxR7oVIrPXxXwsfKgVP7_wqoOQrZjFeK": { "name": "zig_objc", - "url": "https://github.com/mitchellh/zig-objc/archive/c9e917a4e15a983b672ca779c7985d738a2d517c.tar.gz", - "hash": "sha256-o3vl7qfkSi0bKXa6JWuF92qMEGP8Af/shcip5nRo5Nw=" + "url": "https://github.com/mitchellh/zig-objc/archive/f356ed02833f0f1b8e84d50bed9e807bf7cdc0ae.tar.gz", + "hash": "sha256-3YSvc3YlNW/NciyzCQnzsujXAmZ89XlxSqfqvArAjsw=" }, - "wayland-0.4.0-dev-lQa1kjfIAQCmhhQu3xF0KH-94-TzeMXOqfnP0-Dg6Wyy": { + "wayland-0.5.0-dev-lQa1khrMAQDJDwYFKpdH3HizherB7sHo5dKMECfvxQHe": { "name": "zig_wayland", - "url": "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz", - "hash": "sha256-E77GZ15APYbbO1WzmuJi8eG9/iQFbc2CgkNBxjCLUhk=" + "url": "https://codeberg.org/ifreund/zig-wayland/archive/1b5c038ec10da20ed3a15b0b2a6db1c21383e8ea.tar.gz", + "hash": "sha256-TxRrc17Q1Sf1IOO/cdPpP3LD0PpYOujt06SFH3B5Ek4=" }, - "zigimg-0.1.0-lly-O6N2EABOxke8dqyzCwhtUCAafqP35zC7wsZ4Ddxj": { + "zigimg-0.1.0-8_eo2mWmEgBoqdr0sH9O5GTqDHthkoEPM5_tipcBRreL": { "name": "zigimg", - "url": "git+https://github.com/TUSF/zigimg#31268548fe3276c0e95f318a6c0d2ab10565b58d", - "hash": "sha256-oblfr2FIzuqq0FLo/RrzCwUX1NJJuT53EwD3nP3KwN0=" + "url": "git+https://github.com/ivanstepanovftw/zigimg#aa4c31db872612c39edbb79f753b3cd9a79fe726", + "hash": "sha256-Ko5RuxxTAvpUHCnWEdHqNl7b+PVUAxg1/OPmzGGjdt0=" + }, + "zigimg-0.1.0-8_eo2vHnEwCIVW34Q14Ec-xUlzIoVg86-7FU2ypPtxms": { + "name": "zigimg", + "url": "https://github.com/ivanstepanovftw/zigimg/archive/d7b7ab0ba0899643831ef042bd73289510b39906.tar.gz", + "hash": "sha256-LB7Xa6KzVRRUSwwnyWM+y6fDG+kIDjfnoBDJO1obxVM=" }, "N-V-__8AAB0eQwD-0MdOEBmz7intriBReIsIDNlukNVoNu6o": { "name": "zlib", diff --git a/build.zig.zon.nix b/build.zig.zon.nix index abd5a37c5..0111f0769 100644 --- a/build.zig.zon.nix +++ b/build.zig.zon.nix @@ -187,11 +187,11 @@ in }; } { - name = "libxev-0.0.0-86vtc2UaEwDfiTKX3iBI-s_hdzfzWQUarT3MUrmUQl-Q"; + name = "libxev-0.0.0-86vtc4IcEwCqEYxEYoN_3KXmc6A9VLcm22aVImfvecYs"; path = fetchZigArtifact { name = "libxev"; - url = "https://github.com/mitchellh/libxev/archive/7f803181b158a10fec8619f793e3b4df515566cb.tar.gz"; - hash = "sha256-KaozYKEhhT/6sInef7/8O/60LDBJN+8QmdLuNY1Gkmc="; + url = "https://github.com/mitchellh/libxev/archive/34fa50878aec6e5fa8f532867001ab3c36fae23e.tar.gz"; + hash = "sha256-YAPqa5bkpRihKPkyMn15oRvTCZaxO3O66ymRY3lIfdc="; }; } { @@ -259,19 +259,27 @@ in }; } { - name = "uucode-0.1.0-ZZjBPpAFQABNCvd9cVPBg4I7233Ays-NWfWphPNqGbyE"; + name = "uucode-0.1.0-ZZjBPjQHQADuCy1VMWftjrMl3iWqgMpUugWVQJG6_7xT"; path = fetchZigArtifact { name = "uucode"; - url = "https://github.com/jacobsandlund/uucode/archive/190706c6b56f0842d29778007f74f7d3d1335fc5.tar.gz"; - hash = "sha256-iq9Oyns5e5Tnz2BKPPPTuyJ03BN4bK0dsmSPE1s0wig="; + url = "https://github.com/jacobsandlund/uucode/archive/f81f8ef8518b8ec5a7fca30ec5fdbc76cc6197df.tar.gz"; + hash = "sha256-VomSYOF8fRJwb/8GtVG/QqR6c95zSkQt4649C/4KXAc="; }; } { - name = "vaxis-0.1.0-BWNV_FUICQAFZnTCL11TUvnUr1Y0_ZdqtXHhd51d76Rn"; + name = "vaxis-0.5.1-BWNV_H0PCQAeMusmtLzh9P9xO2IW242GZ2IRe9iKYhcA"; path = fetchZigArtifact { name = "vaxis"; - url = "git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23"; - hash = "sha256-bNZ3oveT6vPChjimPJ/GGfcdivlAeJdl/xfWM+S/MHY="; + url = "https://github.com/rockorager/libvaxis/archive/1bf887aa7e3736bad69fd4e277a378946edb0f2a.tar.gz"; + hash = "sha256-eq5YC26OY0i2cdQJ0ZXMZ+o2vHQLEFNNGzQt5Zuz4BM="; + }; + } + { + name = "vaxis-0.5.1-BWNV_O8fCQAeUeVrESVc-2BdXloEXkFqReDJL7Q6XTSZ"; + path = fetchZigArtifact { + name = "vaxis"; + url = "https://github.com/rockorager/libvaxis/archive/9fc9015d5f147568e18c5e7ca28f15bf8b293760.tar.gz"; + hash = "sha256-7H5a0J7uUsrzlO7JNAf/Ussi9WxvmsbyJSmhqvl+rqI="; }; } { @@ -299,59 +307,75 @@ in }; } { - name = "z2d-0.8.1-j5P_Hq8vDwB8ZaDA54-SzESDLF2zznG_zvTHiQNJImZP"; + name = "z2d-0.8.2-pre-j5P_HlVRFgCsBTQ3EgUoKbYHx5JMnyH1mHsOSPiafnef"; path = fetchZigArtifact { name = "z2d"; - url = "https://github.com/vancluever/z2d/archive/refs/tags/v0.8.1.tar.gz"; - hash = "sha256-0DbDKSYA1ejhVx/WbOkwTgD57PNRFcnRviqBh8xpPZ0="; + url = "https://github.com/vancluever/z2d/archive/a1237f6881d99b75abd8a20a934e62e34b44a005.tar.gz"; + hash = "sha256-5/qRZAIh1U42v7jql9W0jr2zzQZtu39DxJPLVrSybJg="; }; } { - name = "zf-0.10.3-OIRy8aiIAACLrBllz0zjxaH0aOe5oNm3KtEMyCntST-9"; + name = "zf-0.10.3-OIRy8QGJAACJcu3tCGtfbJnnd3Y4QL7OW_X8PJ8u_ASR"; path = fetchZigArtifact { name = "zf"; - url = "https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz"; - hash = "sha256-3nulNQd/4rZ4paeXJYXwAliNNyRNsIOX/q3z1JB8C7I="; + url = "https://github.com/jcollie/zf/archive/52ad2e5528ab754f77437edf08a07b5ec843661c.tar.gz"; + hash = "sha256-8BinbanSfZeBA8SBAopVxwJObN36/BTpxVHABKicsMQ="; }; } { - name = "zg-0.13.4-AAAAAGiZ7QLz4pvECFa_wG4O4TP4FLABHHbemH2KakWM"; + name = "zg-0.14.1-oGqU3J4_tAKBfyes3AWleKDjo-IcYvnEwaB8qxOqFMwM"; path = fetchZigArtifact { name = "zg"; - url = "git+https://codeberg.org/atman/zg#4a002763419a34d61dcbb1f415821b83b9bf8ddc"; - hash = "sha256-fo3l6cjkrr/godElTGnQzalBsasN7J73IDIRmw7v1gA="; + url = "git+https://codeberg.org/ivanstepanovftw/zg#4fe689e56ce2ed5a8f59308b471bccd7da89fac9"; + hash = "sha256-P0ieLuOQ05wKVaMmeNKJIxCWMIdyeKkmhsj8Ps80BGU="; }; } { - name = "N-V-__8AAB9YCQBaZtQjJZVndk-g_GDIK-NTZcIa63bFp9yZ"; + name = "zg-0.15.1-oGqU3M0-tALZCy7boQS86znlBloyKx6--JriGlY0Paa9"; + path = fetchZigArtifact { + name = "zg"; + url = "https://codeberg.org/chaten/zg/archive/749197a3f9d25e211615960c02380a3d659b20f9.tar.gz"; + hash = "sha256-BZhz1nPqxK6hdsJQ66n7Jk4zMgFSGLXm8eU0CX/7mDI="; + }; + } + { + name = "zig_js-0.0.0-rjCAV-6GAADxFug7rDmPH-uM_XcnJ5NmuAMJCAscMjhi"; path = fetchZigArtifact { name = "zig_js"; - url = "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz"; - hash = "sha256-fyNeCVbC9UAaKJY6JhAZlT0A479M/AKYMPIWEZbDWD0="; + url = "https://github.com/mitchellh/zig-js/archive/04db83c617da1956ac5adc1cb9ba1e434c1cb6fd.tar.gz"; + hash = "sha256-TCAY5WAV05UEuAkDhq2c6Tk/ODgAhdnDI3O/flb8c6M="; }; } { - name = "zig_objc-0.0.0-Ir_SpwsPAQBJgi9YRm2ubJMfdoysSq5gKpsIj3izQ8Zk"; + name = "zig_objc-0.0.0-Ir_Sp5gTAQCvxxR7oVIrPXxXwsfKgVP7_wqoOQrZjFeK"; path = fetchZigArtifact { name = "zig_objc"; - url = "https://github.com/mitchellh/zig-objc/archive/c9e917a4e15a983b672ca779c7985d738a2d517c.tar.gz"; - hash = "sha256-o3vl7qfkSi0bKXa6JWuF92qMEGP8Af/shcip5nRo5Nw="; + url = "https://github.com/mitchellh/zig-objc/archive/f356ed02833f0f1b8e84d50bed9e807bf7cdc0ae.tar.gz"; + hash = "sha256-3YSvc3YlNW/NciyzCQnzsujXAmZ89XlxSqfqvArAjsw="; }; } { - name = "wayland-0.4.0-dev-lQa1kjfIAQCmhhQu3xF0KH-94-TzeMXOqfnP0-Dg6Wyy"; + name = "wayland-0.5.0-dev-lQa1khrMAQDJDwYFKpdH3HizherB7sHo5dKMECfvxQHe"; path = fetchZigArtifact { name = "zig_wayland"; - url = "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz"; - hash = "sha256-E77GZ15APYbbO1WzmuJi8eG9/iQFbc2CgkNBxjCLUhk="; + url = "https://codeberg.org/ifreund/zig-wayland/archive/1b5c038ec10da20ed3a15b0b2a6db1c21383e8ea.tar.gz"; + hash = "sha256-TxRrc17Q1Sf1IOO/cdPpP3LD0PpYOujt06SFH3B5Ek4="; }; } { - name = "zigimg-0.1.0-lly-O6N2EABOxke8dqyzCwhtUCAafqP35zC7wsZ4Ddxj"; + name = "zigimg-0.1.0-8_eo2mWmEgBoqdr0sH9O5GTqDHthkoEPM5_tipcBRreL"; path = fetchZigArtifact { name = "zigimg"; - url = "git+https://github.com/TUSF/zigimg#31268548fe3276c0e95f318a6c0d2ab10565b58d"; - hash = "sha256-oblfr2FIzuqq0FLo/RrzCwUX1NJJuT53EwD3nP3KwN0="; + url = "git+https://github.com/ivanstepanovftw/zigimg#aa4c31db872612c39edbb79f753b3cd9a79fe726"; + hash = "sha256-Ko5RuxxTAvpUHCnWEdHqNl7b+PVUAxg1/OPmzGGjdt0="; + }; + } + { + name = "zigimg-0.1.0-8_eo2vHnEwCIVW34Q14Ec-xUlzIoVg86-7FU2ypPtxms"; + path = fetchZigArtifact { + name = "zigimg"; + url = "https://github.com/ivanstepanovftw/zigimg/archive/d7b7ab0ba0899643831ef042bd73289510b39906.tar.gz"; + hash = "sha256-LB7Xa6KzVRRUSwwnyWM+y6fDG+kIDjfnoBDJO1obxVM="; }; } { diff --git a/build.zig.zon.txt b/build.zig.zon.txt index 453a12347..7dda3d294 100644 --- a/build.zig.zon.txt +++ b/build.zig.zon.txt @@ -1,7 +1,7 @@ -git+https://codeberg.org/atman/zg#4a002763419a34d61dcbb1f415821b83b9bf8ddc -git+https://github.com/TUSF/zigimg#31268548fe3276c0e95f318a6c0d2ab10565b58d -git+https://github.com/rockorager/libvaxis#1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23 -https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz +git+https://codeberg.org/ivanstepanovftw/zg#4fe689e56ce2ed5a8f59308b471bccd7da89fac9 +git+https://github.com/ivanstepanovftw/zigimg#aa4c31db872612c39edbb79f753b3cd9a79fe726 +https://codeberg.org/chaten/zg/archive/749197a3f9d25e211615960c02380a3d659b20f9.tar.gz +https://codeberg.org/ifreund/zig-wayland/archive/1b5c038ec10da20ed3a15b0b2a6db1c21383e8ea.tar.gz https://deps.files.ghostty.org/JetBrainsMono-2.304.tar.gz https://deps.files.ghostty.org/NerdFontsSymbolsOnly-3.4.0.tar.gz https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz @@ -24,12 +24,15 @@ https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0 https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz -https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz https://github.com/ghostty-org/zig-gobject/releases/download/2025-09-20-20-1/ghostty-gobject-2025-09-20-20-1.tar.zst -https://github.com/jacobsandlund/uucode/archive/190706c6b56f0842d29778007f74f7d3d1335fc5.tar.gz +https://github.com/ivanstepanovftw/zigimg/archive/d7b7ab0ba0899643831ef042bd73289510b39906.tar.gz +https://github.com/jacobsandlund/uucode/archive/f81f8ef8518b8ec5a7fca30ec5fdbc76cc6197df.tar.gz +https://github.com/jcollie/zf/archive/52ad2e5528ab754f77437edf08a07b5ec843661c.tar.gz https://github.com/mbadolato/iTerm2-Color-Schemes/releases/download/release-20250922-150534-d28055b/ghostty-themes.tgz -https://github.com/mitchellh/libxev/archive/7f803181b158a10fec8619f793e3b4df515566cb.tar.gz -https://github.com/mitchellh/zig-objc/archive/c9e917a4e15a983b672ca779c7985d738a2d517c.tar.gz -https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz -https://github.com/vancluever/z2d/archive/refs/tags/v0.8.1.tar.gz +https://github.com/mitchellh/libxev/archive/34fa50878aec6e5fa8f532867001ab3c36fae23e.tar.gz +https://github.com/mitchellh/zig-js/archive/04db83c617da1956ac5adc1cb9ba1e434c1cb6fd.tar.gz +https://github.com/mitchellh/zig-objc/archive/f356ed02833f0f1b8e84d50bed9e807bf7cdc0ae.tar.gz +https://github.com/rockorager/libvaxis/archive/1bf887aa7e3736bad69fd4e277a378946edb0f2a.tar.gz +https://github.com/rockorager/libvaxis/archive/9fc9015d5f147568e18c5e7ca28f15bf8b293760.tar.gz +https://github.com/vancluever/z2d/archive/a1237f6881d99b75abd8a20a934e62e34b44a005.tar.gz diff --git a/example/c-vt/build.zig.zon b/example/c-vt/build.zig.zon index 3230f440e..5da1a9168 100644 --- a/example/c-vt/build.zig.zon +++ b/example/c-vt/build.zig.zon @@ -2,7 +2,7 @@ .name = .c_vt, .version = "0.0.0", .fingerprint = 0x413a8529b1255f9a, - .minimum_zig_version = "0.14.1", + .minimum_zig_version = "0.15.1", .dependencies = .{ // Ghostty dependency. In reality, you'd probably use a URL-based // dependency like the one showed (and commented out) below this one. diff --git a/example/zig-vt/build.zig.zon b/example/zig-vt/build.zig.zon index 852e736ca..bc7246de5 100644 --- a/example/zig-vt/build.zig.zon +++ b/example/zig-vt/build.zig.zon @@ -2,7 +2,7 @@ .name = .zig_vt, .version = "0.0.0", .fingerprint = 0x6045575a7a8387e6, - .minimum_zig_version = "0.14.1", + .minimum_zig_version = "0.15.1", .dependencies = .{ // Ghostty dependency. In reality, you'd probably use a URL-based // dependency like the one showed (and commented out) below this one. diff --git a/flake.lock b/flake.lock index bbd4567e3..349248668 100644 --- a/flake.lock +++ b/flake.lock @@ -36,15 +36,15 @@ }, "nixpkgs": { "locked": { - "lastModified": 1748189127, - "narHash": "sha256-zRDR+EbbeObu4V2X5QCd2Bk5eltfDlCr5yvhBwUT6pY=", - "rev": "7c43f080a7f28b2774f3b3f43234ca11661bf334", + "lastModified": 315532800, + "narHash": "sha256-YwoXN6fthkakCFD7nXPcUK+rkNr6ZTNTuF8zdGaxZo0=", + "rev": "dc704e6102e76aad573f63b74c742cd96f8f1e6c", "type": "tarball", - "url": "https://releases.nixos.org/nixos/25.05/nixos-25.05.802491.7c43f080a7f2/nixexprs.tar.xz" + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre870318.dc704e6102e7/nixexprs.tar.xz" }, "original": { "type": "tarball", - "url": "https://channels.nixos.org/nixos-25.05/nixexprs.tar.xz" + "url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz" } }, "nixpkgs_2": { @@ -97,11 +97,11 @@ ] }, "locked": { - "lastModified": 1748261582, - "narHash": "sha256-3i0IL3s18hdDlbsf0/E+5kyPRkZwGPbSFngq5eToiAA=", + "lastModified": 1759192380, + "narHash": "sha256-0BWJgt4OSzxCESij5oo8WLWrPZ+1qLp8KUQe32QeV4Q=", "owner": "mitchellh", "repo": "zig-overlay", - "rev": "aafb1b093fb838f7a02613b719e85ec912914221", + "rev": "0bcd1401ed43d10f10cbded49624206553e92f57", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 7d72a9af3..18241a447 100644 --- a/flake.nix +++ b/flake.nix @@ -5,7 +5,9 @@ # We want to stay as up to date as possible but need to be careful that the # glibc versions used by our dependencies from Nix are compatible with the # system glibc that the user is building for. - nixpkgs.url = "https://channels.nixos.org/nixos-25.05/nixexprs.tar.xz"; + # + # We are currently on unstable to get Zig 0.15 for our package.nix + nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; flake-utils.url = "github:numtide/flake-utils"; # Used for shell.nix @@ -47,7 +49,7 @@ pkgs = nixpkgs.legacyPackages.${system}; in { devShell.${system} = pkgs.callPackage ./nix/devShell.nix { - zig = zig.packages.${system}."0.14.1"; + zig = zig.packages.${system}."0.15.1"; wraptest = pkgs.callPackage ./nix/wraptest.nix {}; zon2nix = zon2nix; }; diff --git a/flatpak/dependencies.yml b/flatpak/dependencies.yml index 082107923..667e4662c 100644 --- a/flatpak/dependencies.yml +++ b/flatpak/dependencies.yml @@ -13,12 +13,12 @@ modules: - chmod a+x /app/zig/zig sources: - type: archive - sha256: 24aeeec8af16c381934a6cd7d95c807a8cb2cf7df9fa40d359aa884195c4716c - url: https://ziglang.org/download/0.14.1/zig-x86_64-linux-0.14.1.tar.xz + sha256: c61c5da6edeea14ca51ecd5e4520c6f4189ef5250383db33d01848293bfafe05 + url: https://ziglang.org/download/0.15.1/zig-x86_64-linux-0.15.1.tar.xz only-arches: [x86_64] - type: archive - sha256: f7a654acc967864f7a050ddacfaa778c7504a0eca8d2b678839c21eea47c992b - url: https://ziglang.org/download/0.14.1/zig-aarch64-linux-0.14.1.tar.xz + sha256: bb4a8d2ad735e7fba764c497ddf4243cb129fece4148da3222a7046d3f1f19fe + url: https://ziglang.org/download/0.15.1/zig-aarch64-linux-0.15.1.tar.xz only-arches: [aarch64] - name: bzip2-redirect diff --git a/flatpak/zig-packages.json b/flatpak/zig-packages.json index beea0dc04..5d0ed3108 100644 --- a/flatpak/zig-packages.json +++ b/flatpak/zig-packages.json @@ -79,9 +79,9 @@ }, { "type": "archive", - "url": "https://github.com/mitchellh/libxev/archive/7f803181b158a10fec8619f793e3b4df515566cb.tar.gz", - "dest": "vendor/p/libxev-0.0.0-86vtc2UaEwDfiTKX3iBI-s_hdzfzWQUarT3MUrmUQl-Q", - "sha256": "29aa3360a121853ffab089de7fbffc3bfeb42c304937ef1099d2ee358d469267" + "url": "https://github.com/mitchellh/libxev/archive/34fa50878aec6e5fa8f532867001ab3c36fae23e.tar.gz", + "dest": "vendor/p/libxev-0.0.0-86vtc4IcEwCqEYxEYoN_3KXmc6A9VLcm22aVImfvecYs", + "sha256": "6003ea6b96e4a518a128f932327d79a11bd30996b13b73baeb29916379487dd7" }, { "type": "archive", @@ -133,15 +133,21 @@ }, { "type": "archive", - "url": "https://github.com/jacobsandlund/uucode/archive/190706c6b56f0842d29778007f74f7d3d1335fc5.tar.gz", - "dest": "vendor/p/uucode-0.1.0-ZZjBPpAFQABNCvd9cVPBg4I7233Ays-NWfWphPNqGbyE", - "sha256": "8aaf4eca7b397b94e7cf604a3cf3d3bb2274dc13786cad1db2648f135b34c228" + "url": "https://github.com/jacobsandlund/uucode/archive/f81f8ef8518b8ec5a7fca30ec5fdbc76cc6197df.tar.gz", + "dest": "vendor/p/uucode-0.1.0-ZZjBPjQHQADuCy1VMWftjrMl3iWqgMpUugWVQJG6_7xT", + "sha256": "56899260e17c7d12706fff06b551bf42a47a73de734a442de3ae3d0bfe0a5c07" }, { - "type": "git", - "url": "https://github.com/rockorager/libvaxis", - "commit": "1f41c121e8fc153d9ce8c6eb64b2bbab68ad7d23", - "dest": "vendor/p/vaxis-0.1.0-BWNV_FUICQAFZnTCL11TUvnUr1Y0_ZdqtXHhd51d76Rn" + "type": "archive", + "url": "https://github.com/rockorager/libvaxis/archive/1bf887aa7e3736bad69fd4e277a378946edb0f2a.tar.gz", + "dest": "vendor/p/vaxis-0.5.1-BWNV_H0PCQAeMusmtLzh9P9xO2IW242GZ2IRe9iKYhcA", + "sha256": "7aae580b6e8e6348b671d409d195cc67ea36bc740b10534d1b342de59bb3e013" + }, + { + "type": "archive", + "url": "https://github.com/rockorager/libvaxis/archive/9fc9015d5f147568e18c5e7ca28f15bf8b293760.tar.gz", + "dest": "vendor/p/vaxis-0.5.1-BWNV_O8fCQAeUeVrESVc-2BdXloEXkFqReDJL7Q6XTSZ", + "sha256": "ec7e5ad09eee52caf394eec93407ff52cb22f56c6f9ac6f22529a1aaf97eaea2" }, { "type": "archive", @@ -163,45 +169,57 @@ }, { "type": "archive", - "url": "https://github.com/vancluever/z2d/archive/refs/tags/v0.8.1.tar.gz", - "dest": "vendor/p/z2d-0.8.1-j5P_Hq8vDwB8ZaDA54-SzESDLF2zznG_zvTHiQNJImZP", - "sha256": "d036c3292600d5e8e1571fd66ce9304e00f9ecf35115c9d1be2a8187cc693d9d" + "url": "https://github.com/vancluever/z2d/archive/a1237f6881d99b75abd8a20a934e62e34b44a005.tar.gz", + "dest": "vendor/p/z2d-0.8.2-pre-j5P_HlVRFgCsBTQ3EgUoKbYHx5JMnyH1mHsOSPiafnef", + "sha256": "e7fa91640221d54e36bfb8ea97d5b48ebdb3cd066dbb7f43c493cb56b4b26c98" }, { "type": "archive", - "url": "https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz", - "dest": "vendor/p/zf-0.10.3-OIRy8aiIAACLrBllz0zjxaH0aOe5oNm3KtEMyCntST-9", - "sha256": "de7ba535077fe2b678a5a7972585f002588d37244db08397feadf3d4907c0bb2" + "url": "https://github.com/jcollie/zf/archive/52ad2e5528ab754f77437edf08a07b5ec843661c.tar.gz", + "dest": "vendor/p/zf-0.10.3-OIRy8QGJAACJcu3tCGtfbJnnd3Y4QL7OW_X8PJ8u_ASR", + "sha256": "f018a76da9d27d978103c481028a55c7024e6cddfafc14e9c551c004a89cb0c4" }, { "type": "git", - "url": "https://codeberg.org/atman/zg", - "commit": "4a002763419a34d61dcbb1f415821b83b9bf8ddc", - "dest": "vendor/p/zg-0.13.4-AAAAAGiZ7QLz4pvECFa_wG4O4TP4FLABHHbemH2KakWM" + "url": "https://codeberg.org/ivanstepanovftw/zg", + "commit": "4fe689e56ce2ed5a8f59308b471bccd7da89fac9", + "dest": "vendor/p/zg-0.14.1-oGqU3J4_tAKBfyes3AWleKDjo-IcYvnEwaB8qxOqFMwM" }, { "type": "archive", - "url": "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz", - "dest": "vendor/p/N-V-__8AAB9YCQBaZtQjJZVndk-g_GDIK-NTZcIa63bFp9yZ", - "sha256": "7f235e0956c2f5401a28963a261019953d00e3bf4cfc029830f2161196c3583d" + "url": "https://codeberg.org/chaten/zg/archive/749197a3f9d25e211615960c02380a3d659b20f9.tar.gz", + "dest": "vendor/p/zg-0.15.1-oGqU3M0-tALZCy7boQS86znlBloyKx6--JriGlY0Paa9", + "sha256": "059873d673eac4aea176c250eba9fb264e3332015218b5e6f1e534097ffb9832" }, { "type": "archive", - "url": "https://github.com/mitchellh/zig-objc/archive/c9e917a4e15a983b672ca779c7985d738a2d517c.tar.gz", - "dest": "vendor/p/zig_objc-0.0.0-Ir_SpwsPAQBJgi9YRm2ubJMfdoysSq5gKpsIj3izQ8Zk", - "sha256": "a37be5eea7e44a2d1b2976ba256b85f76a8c1063fc01ffec85c8a9e67468e4dc" + "url": "https://github.com/mitchellh/zig-js/archive/04db83c617da1956ac5adc1cb9ba1e434c1cb6fd.tar.gz", + "dest": "vendor/p/zig_js-0.0.0-rjCAV-6GAADxFug7rDmPH-uM_XcnJ5NmuAMJCAscMjhi", + "sha256": "4c2018e56015d39504b8090386ad9ce9393f38380085d9c32373bf7e56fc73a3" }, { "type": "archive", - "url": "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz", - "dest": "vendor/p/wayland-0.4.0-dev-lQa1kjfIAQCmhhQu3xF0KH-94-TzeMXOqfnP0-Dg6Wyy", - "sha256": "13bec6675e403d86db3b55b39ae262f1e1bdfe24056dcd82824341c6308b5219" + "url": "https://github.com/mitchellh/zig-objc/archive/f356ed02833f0f1b8e84d50bed9e807bf7cdc0ae.tar.gz", + "dest": "vendor/p/zig_objc-0.0.0-Ir_Sp5gTAQCvxxR7oVIrPXxXwsfKgVP7_wqoOQrZjFeK", + "sha256": "dd84af737625356fcd722cb30909f3b2e8d702667cf579714aa7eabc0ac08ecc" + }, + { + "type": "archive", + "url": "https://codeberg.org/ifreund/zig-wayland/archive/1b5c038ec10da20ed3a15b0b2a6db1c21383e8ea.tar.gz", + "dest": "vendor/p/wayland-0.5.0-dev-lQa1khrMAQDJDwYFKpdH3HizherB7sHo5dKMECfvxQHe", + "sha256": "4f146b735ed0d527f520e3bf71d3e93f72c3d0fa583ae8edd3a4851f7079124e" }, { "type": "git", - "url": "https://github.com/TUSF/zigimg", - "commit": "31268548fe3276c0e95f318a6c0d2ab10565b58d", - "dest": "vendor/p/zigimg-0.1.0-lly-O6N2EABOxke8dqyzCwhtUCAafqP35zC7wsZ4Ddxj" + "url": "https://github.com/ivanstepanovftw/zigimg", + "commit": "aa4c31db872612c39edbb79f753b3cd9a79fe726", + "dest": "vendor/p/zigimg-0.1.0-8_eo2mWmEgBoqdr0sH9O5GTqDHthkoEPM5_tipcBRreL" + }, + { + "type": "archive", + "url": "https://github.com/ivanstepanovftw/zigimg/archive/d7b7ab0ba0899643831ef042bd73289510b39906.tar.gz", + "dest": "vendor/p/zigimg-0.1.0-8_eo2vHnEwCIVW34Q14Ec-xUlzIoVg86-7FU2ypPtxms", + "sha256": "2c1ed76ba2b35514544b0c27c9633ecba7c31be9080e37e7a010c93b5a1bc553" }, { "type": "archive", diff --git a/nix/package.nix b/nix/package.nix index fcc80b9dc..73d31c3b9 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -10,7 +10,7 @@ git, ncurses, pkg-config, - zig_0_14, + zig_0_15, pandoc, revision ? "dirty", optimize ? "Debug", @@ -27,7 +27,7 @@ # https://github.com/ziglang/zig/issues/14281#issuecomment-1624220653 is # ultimately acted on and has made its way to a nixpkgs implementation, this # can probably be removed in favor of that. - zig_hook = zig_0_14.hook.overrideAttrs { + zig_hook = zig_0_15.hook.overrideAttrs { zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize} --color off"; }; gi_typelib_path = import ./build-support/gi-typelib-path.nix { diff --git a/pkg/apple-sdk/build.zig b/pkg/apple-sdk/build.zig index 18a6c0968..c573c3910 100644 --- a/pkg/apple-sdk/build.zig +++ b/pkg/apple-sdk/build.zig @@ -46,19 +46,19 @@ pub fn addPaths( // find the SDK path. const libc = try std.zig.LibCInstallation.findNative(.{ .allocator = b.allocator, - .target = step.rootModuleTarget(), + .target = &step.rootModuleTarget(), .verbose = false, }); // Render the file compatible with the `--libc` Zig flag. - var list: std.ArrayList(u8) = .init(b.allocator); - defer list.deinit(); - try libc.render(list.writer()); + var stream: std.io.Writer.Allocating = .init(b.allocator); + defer stream.deinit(); + try libc.render(&stream.writer); // Create a temporary file to store the libc path because // `--libc` expects a file path. const wf = b.addWriteFiles(); - const path = wf.add("libc.txt", list.items); + const path = wf.add("libc.txt", stream.written()); // Determine our framework path. Zig has a bug where it doesn't // parse this from the libc txt file for `-framework` flags: diff --git a/pkg/breakpad/build.zig b/pkg/breakpad/build.zig index 9ab6b89cd..56d51b159 100644 --- a/pkg/breakpad/build.zig +++ b/pkg/breakpad/build.zig @@ -19,9 +19,8 @@ pub fn build(b: *std.Build) !void { try apple_sdk.addPaths(b, lib); } - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{}); + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); if (b.lazyDependency("breakpad", .{})) |upstream| { lib.addIncludePath(upstream.path("src")); diff --git a/pkg/cimgui/build.zig b/pkg/cimgui/build.zig index f14bc1242..b94f11943 100644 --- a/pkg/cimgui/build.zig +++ b/pkg/cimgui/build.zig @@ -55,19 +55,19 @@ pub fn build(b: *std.Build) !void { if (imgui_) |imgui| lib.addIncludePath(imgui.path("")); module.addIncludePath(b.path("vendor")); - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{ + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.appendSlice(b.allocator, &.{ "-DCIMGUI_FREETYPE=1", "-DIMGUI_USE_WCHAR32=1", "-DIMGUI_DISABLE_OBSOLETE_FUNCTIONS=1", }); if (target.result.os.tag == .windows) { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DIMGUI_IMPL_API=extern\t\"C\"\t__declspec(dllexport)", }); } else { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DIMGUI_IMPL_API=extern\t\"C\"", }); } diff --git a/pkg/fontconfig/build.zig b/pkg/fontconfig/build.zig index c9ea517ed..7c87d1f2e 100644 --- a/pkg/fontconfig/build.zig +++ b/pkg/fontconfig/build.zig @@ -82,9 +82,9 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu lib.addIncludePath(b.path("override/include")); module.addIncludePath(b.path("override/include")); - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{ + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.appendSlice(b.allocator, &.{ "-DHAVE_DIRENT_H", "-DHAVE_FCNTL_H", "-DHAVE_STDLIB_H", @@ -129,12 +129,12 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu }); switch (target.result.ptrBitWidth()) { - 32 => try flags.appendSlice(&.{ + 32 => try flags.appendSlice(b.allocator, &.{ "-DSIZEOF_VOID_P=4", "-DALIGNOF_VOID_P=4", }), - 64 => try flags.appendSlice(&.{ + 64 => try flags.appendSlice(b.allocator, &.{ "-DSIZEOF_VOID_P=8", "-DALIGNOF_VOID_P=8", }), @@ -142,14 +142,14 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu else => @panic("unsupported arch"), } if (target.result.os.tag == .windows) { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DFC_CACHEDIR=\"LOCAL_APPDATA_FONTCONFIG_CACHE\"", "-DFC_TEMPLATEDIR=\"c:/share/fontconfig/conf.avail\"", "-DCONFIGDIR=\"c:/etc/fonts/conf.d\"", "-DFC_DEFAULT_FONTS=\"\\tWINDOWSFONTDIR\\n\\tWINDOWSUSERFONTDIR\\n\"", }); } else { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DHAVE_FSTATFS", "-DHAVE_FSTATVFS", "-DHAVE_GETOPT", @@ -173,13 +173,13 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu }); if (target.result.os.tag == .freebsd) { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DFC_TEMPLATEDIR=\"/usr/local/etc/fonts/conf.avail\"", "-DFONTCONFIG_PATH=\"/usr/local/etc/fonts\"", "-DCONFIGDIR=\"/usr/local/etc/fonts/conf.d\"", }); } else { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DFC_TEMPLATEDIR=\"/usr/share/fontconfig/conf.avail\"", "-DFONTCONFIG_PATH=\"/etc/fonts\"", "-DCONFIGDIR=\"/usr/local/fontconfig/conf.d\"", @@ -187,7 +187,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu } if (target.result.os.tag == .linux) { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DHAVE_SYS_STATFS_H", "-DHAVE_SYS_VFS_H", }); @@ -214,14 +214,14 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu // Libxml2 _ = b.systemIntegrationOption("libxml2", .{}); // So it shows up in help if (libxml2_enabled) { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DENABLE_LIBXML2", "-DLIBXML_STATIC", "-DLIBXML_PUSH_ENABLED", }); if (target.result.os.tag == .windows) { // NOTE: this should be defined on all targets - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-Werror=implicit-function-declaration", }); } diff --git a/pkg/freetype/build.zig b/pkg/freetype/build.zig index d000442be..a25dc18da 100644 --- a/pkg/freetype/build.zig +++ b/pkg/freetype/build.zig @@ -77,9 +77,9 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu try apple_sdk.addPaths(b, lib); } - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{ + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.appendSlice(b.allocator, &.{ "-DFT2_BUILD_LIBRARY", "-DFT_CONFIG_OPTION_SYSTEM_ZLIB=1", @@ -103,7 +103,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu // Libpng _ = b.systemIntegrationOption("libpng", .{}); // So it shows up in help if (libpng_enabled) { - try flags.append("-DFT_CONFIG_OPTION_USE_PNG=1"); + try flags.append(b.allocator, "-DFT_CONFIG_OPTION_USE_PNG=1"); if (b.systemIntegrationOption("libpng", .{})) { lib.linkSystemLibrary2("libpng", dynamic_link_opts); diff --git a/pkg/freetype/face.zig b/pkg/freetype/face.zig index 84178b860..f8714d4fe 100644 --- a/pkg/freetype/face.zig +++ b/pkg/freetype/face.zig @@ -193,8 +193,8 @@ pub const Face = struct { ) void { c.FT_Set_Transform( self.handle, - @constCast(@ptrCast(matrix)), - @constCast(@ptrCast(delta)), + @ptrCast(@constCast(matrix)), + @ptrCast(@constCast(delta)), ); } }; diff --git a/pkg/glslang/build.zig b/pkg/glslang/build.zig index 52993a662..746a41497 100644 --- a/pkg/glslang/build.zig +++ b/pkg/glslang/build.zig @@ -59,9 +59,9 @@ fn buildGlslang( try apple_sdk.addPaths(b, lib); } - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{ + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.appendSlice(b.allocator, &.{ "-fno-sanitize=undefined", "-fno-sanitize-trap=undefined", }); diff --git a/pkg/gtk4-layer-shell/build.zig b/pkg/gtk4-layer-shell/build.zig index 543faf129..b9cf78a23 100644 --- a/pkg/gtk4-layer-shell/build.zig +++ b/pkg/gtk4-layer-shell/build.zig @@ -36,10 +36,13 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu const optimize = options.optimize; // Shared library - const lib = b.addSharedLibrary(.{ + const lib = b.addLibrary(.{ .name = "gtk4-layer-shell", - .target = target, - .optimize = optimize, + .linkage = .dynamic, + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + }), }); b.installArtifact(lib); diff --git a/pkg/harfbuzz/build.zig b/pkg/harfbuzz/build.zig index bf247461a..8696c0203 100644 --- a/pkg/harfbuzz/build.zig +++ b/pkg/harfbuzz/build.zig @@ -111,13 +111,13 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu const dynamic_link_opts = options.dynamic_link_opts; - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{ + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.appendSlice(b.allocator, &.{ "-DHAVE_STDBOOL_H", }); if (target.result.os.tag != .windows) { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DHAVE_UNISTD_H", "-DHAVE_SYS_MMAN_H", "-DHAVE_PTHREAD=1", @@ -127,7 +127,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu // Freetype _ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help if (freetype_enabled) { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DHAVE_FREETYPE=1", // Let's just assume a new freetype @@ -153,7 +153,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu } if (coretext_enabled) { - try flags.appendSlice(&.{"-DHAVE_CORETEXT=1"}); + try flags.appendSlice(b.allocator, &.{"-DHAVE_CORETEXT=1"}); lib.linkFramework("CoreText"); module.linkFramework("CoreText", .{}); } diff --git a/pkg/highway/build.zig b/pkg/highway/build.zig index 1013f1643..4c75de49e 100644 --- a/pkg/highway/build.zig +++ b/pkg/highway/build.zig @@ -31,9 +31,9 @@ pub fn build(b: *std.Build) !void { try apple_sdk.addPaths(b, lib); } - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{ + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.appendSlice(b.allocator, &.{ // Avoid changing binaries based on the current time and date. "-Wno-builtin-macro-redefined", "-D__DATE__=\"redacted\"", @@ -69,7 +69,7 @@ pub fn build(b: *std.Build) !void { "-fno-vectorize", }); if (target.result.os.tag != .windows) { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-fmath-errno", "-fno-exceptions", }); diff --git a/pkg/libintl/build.zig b/pkg/libintl/build.zig index 0e32648e7..32221e5ad 100644 --- a/pkg/libintl/build.zig +++ b/pkg/libintl/build.zig @@ -22,9 +22,9 @@ pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{ + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.appendSlice(b.allocator, &.{ "-DHAVE_CONFIG_H", "-DLOCALEDIR=\"\"", }); diff --git a/pkg/libpng/build.zig b/pkg/libpng/build.zig index 11ed29b18..dbedac632 100644 --- a/pkg/libpng/build.zig +++ b/pkg/libpng/build.zig @@ -46,9 +46,9 @@ pub fn build(b: *std.Build) !void { } if (b.lazyDependency("libpng", .{})) |upstream| { - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{ + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.appendSlice(b.allocator, &.{ "-DPNG_ARM_NEON_OPT=0", "-DPNG_POWERPC_VSX_OPT=0", "-DPNG_INTEL_SSE_OPT=0", diff --git a/pkg/libxml2/build.zig b/pkg/libxml2/build.zig index acebfaf63..a9b3e4b1a 100644 --- a/pkg/libxml2/build.zig +++ b/pkg/libxml2/build.zig @@ -25,9 +25,9 @@ pub fn build(b: *std.Build) !void { lib.addIncludePath(b.path("override/config/posix")); } - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{ + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.appendSlice(b.allocator, &.{ // Version info, hardcoded comptime "-DLIBXML_VERSION=" ++ Version.number(), comptime "-DLIBXML_VERSION_STRING=" ++ Version.string(), @@ -46,7 +46,7 @@ pub fn build(b: *std.Build) !void { "-DWITHOUT_TRIO=1", }); if (target.result.os.tag != .windows) { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DHAVE_ARPA_INET_H=1", "-DHAVE_ARPA_NAMESER_H=1", "-DHAVE_DL_H=1", @@ -74,25 +74,25 @@ pub fn build(b: *std.Build) !void { var nameBuf: [32]u8 = undefined; const name = std.ascii.upperString(&nameBuf, field.name); const define = try std.fmt.allocPrint(b.allocator, "-DLIBXML_{s}_ENABLED=1", .{name}); - try flags.append(define); + try flags.append(b.allocator, define); if (std.mem.eql(u8, field.name, "history")) { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DHAVE_LIBHISTORY=1", "-DHAVE_LIBREADLINE=1", }); } if (std.mem.eql(u8, field.name, "mem_debug")) { - try flags.append("-DDEBUG_MEMORY_LOCATION=1"); + try flags.append(b.allocator, "-DDEBUG_MEMORY_LOCATION=1"); } if (std.mem.eql(u8, field.name, "regexp")) { - try flags.append("-DLIBXML_UNICODE_ENABLED=1"); + try flags.append(b.allocator, "-DLIBXML_UNICODE_ENABLED=1"); } if (std.mem.eql(u8, field.name, "run_debug")) { - try flags.append("-DLIBXML_DEBUG_RUNTIME=1"); + try flags.append(b.allocator, "-DLIBXML_DEBUG_RUNTIME=1"); } if (std.mem.eql(u8, field.name, "thread")) { - try flags.append("-DHAVE_LIBPTHREAD=1"); + try flags.append(b.allocator, "-DHAVE_LIBPTHREAD=1"); } } } diff --git a/pkg/macos/foundation/array.zig b/pkg/macos/foundation/array.zig index d3a977539..7b580eb03 100644 --- a/pkg/macos/foundation/array.zig +++ b/pkg/macos/foundation/array.zig @@ -68,7 +68,7 @@ pub const MutableArray = opaque { comptime Elem: type, value: *const Elem, ) void { - CFArrayAppendValue(self, @constCast(@ptrCast(value))); + CFArrayAppendValue(self, @ptrCast(@constCast(value))); } pub fn removeValue(self: *MutableArray, idx: usize) void { diff --git a/pkg/macos/foundation/attributed_string.zig b/pkg/macos/foundation/attributed_string.zig index de509b2c0..c7f27d7d7 100644 --- a/pkg/macos/foundation/attributed_string.zig +++ b/pkg/macos/foundation/attributed_string.zig @@ -10,7 +10,7 @@ pub const AttributedString = opaque { str: *foundation.String, attributes: *foundation.Dictionary, ) Allocator.Error!*AttributedString { - return @constCast(@ptrCast(c.CFAttributedStringCreate( + return @ptrCast(@constCast(c.CFAttributedStringCreate( null, @ptrCast(str), @ptrCast(attributes), diff --git a/pkg/macos/foundation/dictionary.zig b/pkg/macos/foundation/dictionary.zig index 90642e59a..a529442ac 100644 --- a/pkg/macos/foundation/dictionary.zig +++ b/pkg/macos/foundation/dictionary.zig @@ -17,8 +17,8 @@ pub const Dictionary = opaque { return @as(?*Dictionary, @ptrFromInt(@intFromPtr(c.CFDictionaryCreate( null, - @constCast(@ptrCast(if (keys) |slice| slice.ptr else null)), - @constCast(@ptrCast(if (values) |slice| slice.ptr else null)), + @ptrCast(@constCast(if (keys) |slice| slice.ptr else null)), + @ptrCast(@constCast(if (values) |slice| slice.ptr else null)), @intCast(if (keys) |slice| slice.len else 0), &c.kCFTypeDictionaryKeyCallBacks, &c.kCFTypeDictionaryValueCallBacks, diff --git a/pkg/macos/os/log.zig b/pkg/macos/os/log.zig index 32ecb3296..219c914da 100644 --- a/pkg/macos/os/log.zig +++ b/pkg/macos/os/log.zig @@ -32,10 +32,11 @@ pub const Log = opaque { comptime format: []const u8, args: anytype, ) void { - const str = nosuspend std.fmt.allocPrintZ( + const str = nosuspend std.fmt.allocPrintSentinel( alloc, format, args, + 0, ) catch return; defer alloc.free(str); zig_os_log_with_type(self, typ, str.ptr); diff --git a/pkg/macos/text/font.zig b/pkg/macos/text/font.zig index 383861d62..ea37891f5 100644 --- a/pkg/macos/text/font.zig +++ b/pkg/macos/text/font.zig @@ -68,7 +68,7 @@ pub const Font = opaque { } pub fn copyTable(self: *Font, tag: FontTableTag) ?*foundation.Data { - return @constCast(@ptrCast(c.CTFontCopyTable( + return @ptrCast(@constCast(c.CTFontCopyTable( @ptrCast(self), @intFromEnum(tag), c.kCTFontTableOptionNoOptions, @@ -90,7 +90,7 @@ pub const Font = opaque { } pub fn createPathForGlyph(self: *Font, glyph: graphics.Glyph) ?*graphics.Path { - return @constCast(@ptrCast(c.CTFontCreatePathForGlyph( + return @ptrCast(@constCast(c.CTFontCreatePathForGlyph( @ptrCast(self), glyph, null, diff --git a/pkg/macos/text/line.zig b/pkg/macos/text/line.zig index 135fd8558..248f8e645 100644 --- a/pkg/macos/text/line.zig +++ b/pkg/macos/text/line.zig @@ -51,7 +51,7 @@ pub const Line = opaque { } pub fn getGlyphRuns(self: *Line) *foundation.Array { - return @constCast(@ptrCast(c.CTLineGetGlyphRuns(@ptrCast(self)))); + return @ptrCast(@constCast(c.CTLineGetGlyphRuns(@ptrCast(self)))); } }; diff --git a/pkg/macos/video/display_link.zig b/pkg/macos/video/display_link.zig index 4bbf58a0c..7d6b437f9 100644 --- a/pkg/macos/video/display_link.zig +++ b/pkg/macos/video/display_link.zig @@ -74,7 +74,7 @@ pub const DisplayLink = opaque { callbackFn( displayLink, - @alignCast(@ptrCast(inner_userinfo)), + @ptrCast(@alignCast(inner_userinfo)), ); return c.kCVReturnSuccess; } diff --git a/pkg/oniguruma/build.zig b/pkg/oniguruma/build.zig index 77e3b6f65..ea39b4814 100644 --- a/pkg/oniguruma/build.zig +++ b/pkg/oniguruma/build.zig @@ -100,9 +100,8 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu .SIZEOF_VOIDP = t.ptrBitWidth() / t.cTypeBitSize(.char), })); - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{}); + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); lib.addCSourceFiles(.{ .root = upstream.path(""), .flags = flags.items, diff --git a/pkg/oniguruma/init.zig b/pkg/oniguruma/init.zig index 933e50b5a..ea64724c2 100644 --- a/pkg/oniguruma/init.zig +++ b/pkg/oniguruma/init.zig @@ -6,7 +6,7 @@ const errors = @import("errors.zig"); /// the encodings that the program will use. pub fn init(encs: []const *Encoding) !void { _ = try errors.convertError(c.onig_initialize( - @constCast(@ptrCast(@alignCast(encs.ptr))), + @ptrCast(@alignCast(@constCast(encs.ptr))), @intCast(encs.len), )); } diff --git a/pkg/sentry/build.zig b/pkg/sentry/build.zig index 95900ae8f..3c88df56d 100644 --- a/pkg/sentry/build.zig +++ b/pkg/sentry/build.zig @@ -26,22 +26,21 @@ pub fn build(b: *std.Build) !void { try apple_sdk.addPaths(b, lib); } - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{}); + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); if (target.result.os.tag == .windows) { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DSENTRY_WITH_UNWINDER_DBGHELP", }); } else { - try flags.appendSlice(&.{ + try flags.appendSlice(b.allocator, &.{ "-DSENTRY_WITH_UNWINDER_LIBBACKTRACE", }); } switch (backend) { - .crashpad => try flags.append("-DSENTRY_BACKEND_CRASHPAD"), - .breakpad => try flags.append("-DSENTRY_BACKEND_BREAKPAD"), - .inproc => try flags.append("-DSENTRY_BACKEND_INPROC"), + .crashpad => try flags.append(b.allocator, "-DSENTRY_BACKEND_CRASHPAD"), + .breakpad => try flags.append(b.allocator, "-DSENTRY_BACKEND_BREAKPAD"), + .inproc => try flags.append(b.allocator, "-DSENTRY_BACKEND_INPROC"), .none => {}, } diff --git a/pkg/simdutf/build.zig b/pkg/simdutf/build.zig index f96eeae45..f2ddfeba4 100644 --- a/pkg/simdutf/build.zig +++ b/pkg/simdutf/build.zig @@ -20,11 +20,11 @@ pub fn build(b: *std.Build) !void { try apple_sdk.addPaths(b, lib); } - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); // Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414 // (See root Ghostty build.zig on why we do this) - try flags.appendSlice(&.{"-DSIMDUTF_IMPLEMENTATION_ICELAKE=0"}); + try flags.appendSlice(b.allocator, &.{"-DSIMDUTF_IMPLEMENTATION_ICELAKE=0"}); lib.addCSourceFiles(.{ .flags = flags.items, diff --git a/pkg/spirv-cross/build.zig b/pkg/spirv-cross/build.zig index ff7f15c94..003ec43cf 100644 --- a/pkg/spirv-cross/build.zig +++ b/pkg/spirv-cross/build.zig @@ -64,9 +64,9 @@ fn buildSpirvCross( try apple_sdk.addPaths(b, lib); } - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{ + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.appendSlice(b.allocator, &.{ "-DSPIRV_CROSS_C_API_GLSL=1", "-DSPIRV_CROSS_C_API_MSL=1", diff --git a/pkg/utfcpp/build.zig b/pkg/utfcpp/build.zig index 341b35578..e06813b83 100644 --- a/pkg/utfcpp/build.zig +++ b/pkg/utfcpp/build.zig @@ -19,9 +19,8 @@ pub fn build(b: *std.Build) !void { try apple_sdk.addPaths(b, lib); } - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{}); + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); lib.addCSourceFiles(.{ .flags = flags.items, diff --git a/pkg/wuffs/build.zig b/pkg/wuffs/build.zig index 57d89e6b6..3d9f83daa 100644 --- a/pkg/wuffs/build.zig +++ b/pkg/wuffs/build.zig @@ -17,11 +17,11 @@ pub fn build(b: *std.Build) !void { }); unit_tests.linkLibC(); - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.append("-DWUFFS_IMPLEMENTATION"); + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.append(b.allocator, "-DWUFFS_IMPLEMENTATION"); inline for (@import("src/c.zig").defines) |key| { - try flags.append("-D" ++ key); + try flags.append(b.allocator, "-D" ++ key); } if (b.lazyDependency("wuffs", .{})) |wuffs_dep| { diff --git a/pkg/wuffs/src/jpeg.zig b/pkg/wuffs/src/jpeg.zig index c07278eed..700ba01b9 100644 --- a/pkg/wuffs/src/jpeg.zig +++ b/pkg/wuffs/src/jpeg.zig @@ -31,7 +31,7 @@ pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData { } var source_buffer: c.wuffs_base__io_buffer = .{ - .data = .{ .ptr = @constCast(@ptrCast(data.ptr)), .len = data.len }, + .data = .{ .ptr = @ptrCast(@constCast(data.ptr)), .len = data.len }, .meta = .{ .wi = data.len, .ri = 0, diff --git a/pkg/wuffs/src/png.zig b/pkg/wuffs/src/png.zig index 1f37bb375..d79ae5b56 100644 --- a/pkg/wuffs/src/png.zig +++ b/pkg/wuffs/src/png.zig @@ -31,7 +31,7 @@ pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData { } var source_buffer: c.wuffs_base__io_buffer = .{ - .data = .{ .ptr = @constCast(@ptrCast(data.ptr)), .len = data.len }, + .data = .{ .ptr = @ptrCast(@constCast(data.ptr)), .len = data.len }, .meta = .{ .wi = data.len, .ri = 0, diff --git a/pkg/zlib/build.zig b/pkg/zlib/build.zig index caa557454..246ab1bcb 100644 --- a/pkg/zlib/build.zig +++ b/pkg/zlib/build.zig @@ -26,9 +26,9 @@ pub fn build(b: *std.Build) !void { .{ .include_extensions = &.{".h"} }, ); - var flags = std.ArrayList([]const u8).init(b.allocator); - defer flags.deinit(); - try flags.appendSlice(&.{ + var flags: std.ArrayList([]const u8) = .empty; + defer flags.deinit(b.allocator); + try flags.appendSlice(b.allocator, &.{ "-DHAVE_SYS_TYPES_H", "-DHAVE_STDINT_H", "-DHAVE_STDDEF_H", diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index e48fa93c8..2e434843c 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -52,7 +52,7 @@ parts: rm -rf $CRAFT_PART_SRC/* if [[ -n $arch ]]; then - curl -LO --retry-connrefused --retry 10 https://ziglang.org/download/0.14.0/zig-linux-$arch-0.14.0.tar.xz + curl -LO --retry-connrefused --retry 10 https://ziglang.org/download/0.15.1/zig-$arch-linux-0.15.1.tar.xz else echo "Unsupported arch" exit 1 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( \\ \\ @@ -157,12 +159,14 @@ pub fn main() !void { \\ \\ ); + + 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( \\ \\ @@ -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( \\ \\ @@ -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(""); @@ -23,11 +22,11 @@ pub fn accelFromTrigger( if (trigger.mods.super) try writer.writeAll(""); // 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,12 +166,15 @@ fn stepSimd(ptr: *anyopaque) Benchmark.Error!void { const self: *CodepointWidth = @ptrCast(@alignCast(ptr)); const f = self.data_f orelse return; - var r = std.io.bufferedReader(f.reader()); + var read_buf: [4096]u8 = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; + var d: UTF8Decoder = .{}; var buf: [4096]u8 align(std.atomic.cache_line) = undefined; while (true) { - const n = r.read(&buf) catch |err| { - log.warn("error reading data file err={}", .{err}); + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); return error.BenchmarkFailed; }; if (n == 0) break; // EOF reached @@ -185,35 +189,6 @@ fn stepSimd(ptr: *anyopaque) Benchmark.Error!void { } } -fn stepUucode(ptr: *anyopaque) Benchmark.Error!void { - const self: *CodepointWidth = @ptrCast(@alignCast(ptr)); - - const f = self.data_f orelse return; - var r = std.io.bufferedReader(f.reader()); - var d: UTF8Decoder = .{}; - var buf: [4096]u8 align(std.atomic.cache_line) = undefined; - while (true) { - const n = r.read(&buf) catch |err| { - log.warn("error reading data file err={}", .{err}); - return error.BenchmarkFailed; - }; - if (n == 0) break; // EOF reached - - for (buf[0..n]) |c| { - const cp_, const consumed = d.next(c); - assert(consumed); - if (cp_) |cp| { - // This is the same trick we do in terminal.zig so we - // keep it here. - std.mem.doNotOptimizeAway(if (cp <= 0xFF) - 1 - else - uucode.get(.width, @intCast(cp))); - } - } - } -} - test CodepointWidth { const testing = std.testing; const alloc = testing.allocator; diff --git a/src/benchmark/GraphemeBreak.zig b/src/benchmark/GraphemeBreak.zig index e576c71ef..a1b3380f0 100644 --- a/src/benchmark/GraphemeBreak.zig +++ b/src/benchmark/GraphemeBreak.zig @@ -8,7 +8,6 @@ const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Benchmark = @import("Benchmark.zig"); const options = @import("options.zig"); -const uucode = @import("uucode"); const UTF8Decoder = @import("../terminal/UTF8Decoder.zig"); const unicode = @import("../unicode/main.zig"); @@ -39,9 +38,6 @@ pub const Mode = enum { /// Ghostty's table-based approach. table, - - /// uucode implementation - uucode, }; /// Create a new terminal stream handler for the given arguments. @@ -64,7 +60,6 @@ pub fn benchmark(self: *GraphemeBreak) Benchmark { .stepFn = switch (self.opts.mode) { .noop => stepNoop, .table => stepTable, - .uucode => stepUucode, }, .setupFn = setup, .teardownFn = teardown, @@ -95,12 +90,15 @@ fn stepNoop(ptr: *anyopaque) Benchmark.Error!void { const self: *GraphemeBreak = @ptrCast(@alignCast(ptr)); const f = self.data_f orelse return; - var r = std.io.bufferedReader(f.reader()); + var read_buf: [4096]u8 = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; + var d: UTF8Decoder = .{}; var buf: [4096]u8 align(std.atomic.cache_line) = undefined; while (true) { - const n = r.read(&buf) catch |err| { - log.warn("error reading data file err={}", .{err}); + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); return error.BenchmarkFailed; }; if (n == 0) break; // EOF reached @@ -115,14 +113,17 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void { const self: *GraphemeBreak = @ptrCast(@alignCast(ptr)); const f = self.data_f orelse return; - var r = std.io.bufferedReader(f.reader()); + var read_buf: [4096]u8 = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; + var d: UTF8Decoder = .{}; var state: unicode.GraphemeBreakState = .{}; var cp1: u21 = 0; var buf: [4096]u8 align(std.atomic.cache_line) = undefined; while (true) { - const n = r.read(&buf) catch |err| { - log.warn("error reading data file err={}", .{err}); + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); return error.BenchmarkFailed; }; if (n == 0) break; // EOF reached @@ -138,33 +139,6 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void { } } -fn stepUucode(ptr: *anyopaque) Benchmark.Error!void { - const self: *GraphemeBreak = @ptrCast(@alignCast(ptr)); - - const f = self.data_f orelse return; - var r = std.io.bufferedReader(f.reader()); - var d: UTF8Decoder = .{}; - var state: uucode.grapheme.BreakState = .default; - var cp1: u21 = 0; - var buf: [4096]u8 align(std.atomic.cache_line) = undefined; - while (true) { - const n = r.read(&buf) catch |err| { - log.warn("error reading data file err={}", .{err}); - return error.BenchmarkFailed; - }; - if (n == 0) break; // EOF reached - - for (buf[0..n]) |c| { - const cp_, const consumed = d.next(c); - assert(consumed); - if (cp_) |cp2| { - std.mem.doNotOptimizeAway(uucode.grapheme.isBreak(cp1, @intCast(cp2), &state)); - cp1 = cp2; - } - } - } -} - test GraphemeBreak { const testing = std.testing; const alloc = testing.allocator; diff --git a/src/benchmark/IsSymbol.zig b/src/benchmark/IsSymbol.zig index 97af0657a..dffa5071a 100644 --- a/src/benchmark/IsSymbol.zig +++ b/src/benchmark/IsSymbol.zig @@ -90,7 +90,8 @@ fn stepUucode(ptr: *anyopaque) Benchmark.Error!void { const self: *IsSymbol = @ptrCast(@alignCast(ptr)); const f = self.data_f orelse return; - var r = std.io.bufferedReader(f.reader()); + var read_buf: [4096]u8 = undefined; + var r = f.reader(&read_buf); var d: UTF8Decoder = .{}; var buf: [4096]u8 align(std.atomic.cache_line) = undefined; while (true) { @@ -114,7 +115,8 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void { const self: *IsSymbol = @ptrCast(@alignCast(ptr)); const f = self.data_f orelse return; - var r = std.io.bufferedReader(f.reader()); + var read_buf: [4096]u8 = undefined; + var r = f.reader(&read_buf); var d: UTF8Decoder = .{}; var buf: [4096]u8 align(std.atomic.cache_line) = undefined; while (true) { diff --git a/src/benchmark/TerminalParser.zig b/src/benchmark/TerminalParser.zig index 3065c1ed6..f13b44552 100644 --- a/src/benchmark/TerminalParser.zig +++ b/src/benchmark/TerminalParser.zig @@ -75,14 +75,16 @@ fn step(ptr: *anyopaque) Benchmark.Error!void { // the benchmark results and... I know writing this that we // aren't currently IO bound. const f = self.data_f orelse return; - var r = std.io.bufferedReader(f.reader()); + var read_buf: [4096]u8 = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; var p: terminalpkg.Parser = .init(); - var buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var buf: [4096]u8 = undefined; while (true) { - const n = r.read(&buf) catch |err| { - log.warn("error reading data file err={}", .{err}); + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); return error.BenchmarkFailed; }; if (n == 0) break; // EOF reached diff --git a/src/benchmark/TerminalStream.zig b/src/benchmark/TerminalStream.zig index 71ab1fdfc..ecce509f3 100644 --- a/src/benchmark/TerminalStream.zig +++ b/src/benchmark/TerminalStream.zig @@ -113,17 +113,19 @@ fn step(ptr: *anyopaque) Benchmark.Error!void { // the benchmark results and... I know writing this that we // aren't currently IO bound. const f = self.data_f orelse return; - var r = std.io.bufferedReader(f.reader()); - var buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var read_buf: [4096]u8 = undefined; + var f_reader = f.reader(&read_buf); + const r = &f_reader.interface; + + var buf: [4096]u8 = undefined; while (true) { - const n = r.read(&buf) catch |err| { - log.warn("error reading data file err={}", .{err}); + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); return error.BenchmarkFailed; }; if (n == 0) break; // EOF reached - const chunk = buf[0..n]; - self.stream.nextSlice(chunk) catch |err| { + self.stream.nextSlice(buf[0..n]) catch |err| { log.warn("error processing data file chunk err={}", .{err}); return error.BenchmarkFailed; }; diff --git a/src/benchmark/options.zig b/src/benchmark/options.zig index 867be6afc..049e80f48 100644 --- a/src/benchmark/options.zig +++ b/src/benchmark/options.zig @@ -10,7 +10,7 @@ pub fn dataFile(path_: ?[]const u8) !?std.fs.File { const path = path_ orelse return null; // Stdin - if (std.mem.eql(u8, path, "-")) return std.io.getStdIn(); + if (std.mem.eql(u8, path, "-")) return .stdin(); // Normal file const file = try std.fs.cwd().openFile(path, .{}); diff --git a/src/build/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(©_step.step); + try steps.append(b.allocator, ©_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(©.step); + try steps.append(b.allocator, ©.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 {}; - // Trim any whitespace (including CR) around it - const trim = std.mem.trim(u8, entry, whitespace ++ "\r"); - if (trim.len != entry.len) { - std.mem.copyForwards(u8, entry, trim); - entry = entry[0..trim.len]; - } + var writer: std.Io.Writer = .fixed(self.entry[2..]); - // Ignore blank lines and comments - if (entry.len == 0 or entry[0] == '#') continue; + var entry = while (self.r.seek != self.r.end) { + // Reset write head + writer.end = 0; - // Trim spaces around '=' - if (mem.indexOf(u8, entry, "=")) |idx| { - const key = std.mem.trim(u8, entry[0..idx], whitespace); - const value = value: { - var value = std.mem.trim(u8, entry[idx + 1 ..], whitespace); + _ = self.r.streamDelimiterEnding(&writer, '\n') catch |e| { + log.warn("cannot read from \"{s}\": {}", .{ self.filepath, e }); + return null; + }; + _ = self.r.discardDelimiterInclusive('\n') catch {}; - // Detect a quoted string. - if (value.len >= 2 and - value[0] == '"' and - value[value.len - 1] == '"') - { - // Trim quotes since our CLI args processor expects - // quotes to already be gone. - value = value[1 .. value.len - 1]; - } + var entry = writer.buffered(); + self.line += 1; - break :value value; - }; + // Trim any whitespace (including CR) around it + const trim = std.mem.trim(u8, entry, whitespace ++ "\r"); + if (trim.len != entry.len) { + std.mem.copyForwards(u8, entry, trim); + entry = entry[0..trim.len]; + } - const len = key.len + value.len + 1; - if (entry.len != len) { - std.mem.copyForwards(u8, entry, key); - entry[key.len] = '='; - std.mem.copyForwards(u8, entry[key.len + 1 ..], value); - entry = entry[0..len]; - } - } + // Ignore blank lines and comments + if (entry.len == 0 or entry[0] == '#') continue; + break entry; + } else return null; - break :buf entry; + if (mem.indexOf(u8, entry, "=")) |idx| { + const key = std.mem.trim(u8, entry[0..idx], whitespace); + const value = value: { + var value = std.mem.trim(u8, entry[idx + 1 ..], whitespace); + + // Detect a quoted string. + if (value.len >= 2 and + value[0] == '"' and + value[value.len - 1] == '"') + { + // Trim quotes since our CLI args processor expects + // quotes to already be gone. + value = value[1 .. value.len - 1]; } + + break :value value; }; - // We need to reslice so that we include our '--' at the beginning - // of our buffer so that we can trick the CLI parser to treat it - // as CLI args. - return self.entry[0 .. buf.len + 2]; + const len = key.len + value.len + 1; + if (entry.len != len) { + std.mem.copyForwards(u8, entry, key); + entry[key.len] = '='; + std.mem.copyForwards(u8, entry[key.len + 1 ..], value); + entry = entry[0..len]; + } } - /// Returns a location for a diagnostic message. - pub fn location( - self: *const Self, - alloc: Allocator, - ) Allocator.Error!?diags.Location { - // If we have no filepath then we have no location. - if (self.filepath.len == 0) return null; + // We need to reslice so that we include our '--' at the beginning + // of our buffer so that we can trick the CLI parser to treat it + // as CLI args. + return self.entry[0 .. entry.len + 2]; + } - return .{ .file = .{ - .path = try alloc.dupe(u8, self.filepath), - .line = self.line, - } }; - } - }; -} + /// Returns a location for a diagnostic message. + pub fn location( + self: *const Self, + alloc: Allocator, + ) Allocator.Error!?diags.Location { + // If we have no filepath then we have no location. + if (self.filepath.len == 0) return null; -// Constructs a LineIterator (see docs for that). -fn lineIterator(reader: anytype) LineIterator(@TypeOf(reader)) { - return .{ .r = reader }; -} + return .{ .file = .{ + .path = try alloc.dupe(u8, self.filepath), + .line = self.line, + } }; + } +}; /// An iterator valid for arg parsing from a slice. pub const SliceIterator = struct { @@ -1526,7 +1525,7 @@ pub fn sliceIterator(slice: []const []const u8) SliceIterator { test "LineIterator" { const testing = std.testing; - var fbs = std.io.fixedBufferStream( + var reader: std.Io.Reader = .fixed( \\A \\B=42 \\C @@ -1541,7 +1540,7 @@ test "LineIterator" { \\F= "value " ); - var iter = lineIterator(fbs.reader()); + var iter: LineIterator = .init(&reader); try testing.expectEqualStrings("--A", iter.next().?); try testing.expectEqualStrings("--B=42", iter.next().?); try testing.expectEqualStrings("--C", iter.next().?); @@ -1554,9 +1553,9 @@ test "LineIterator" { test "LineIterator end in newline" { const testing = std.testing; - var fbs = std.io.fixedBufferStream("A\n\n"); + var reader: std.Io.Reader = .fixed("A\n\n"); - var iter = lineIterator(fbs.reader()); + var iter: LineIterator = .init(&reader); try testing.expectEqualStrings("--A", iter.next().?); try testing.expectEqual(@as(?[]const u8, null), iter.next()); try testing.expectEqual(@as(?[]const u8, null), iter.next()); @@ -1564,9 +1563,9 @@ test "LineIterator end in newline" { test "LineIterator spaces around '='" { const testing = std.testing; - var fbs = std.io.fixedBufferStream("A = B\n\n"); + var reader: std.Io.Reader = .fixed("A = B\n\n"); - var iter = lineIterator(fbs.reader()); + var iter: LineIterator = .init(&reader); try testing.expectEqualStrings("--A=B", iter.next().?); try testing.expectEqual(@as(?[]const u8, null), iter.next()); try testing.expectEqual(@as(?[]const u8, null), iter.next()); @@ -1574,18 +1573,18 @@ test "LineIterator spaces around '='" { test "LineIterator no value" { const testing = std.testing; - var fbs = std.io.fixedBufferStream("A = \n\n"); + var reader: std.Io.Reader = .fixed("A = \n\n"); - var iter = lineIterator(fbs.reader()); + var iter: LineIterator = .init(&reader); try testing.expectEqualStrings("--A=", iter.next().?); try testing.expectEqual(@as(?[]const u8, null), iter.next()); } test "LineIterator with CRLF line endings" { const testing = std.testing; - var fbs = std.io.fixedBufferStream("A\r\nB = C\r\n"); + var reader: std.Io.Reader = .fixed("A\r\nB = C\r\n"); - var iter = lineIterator(fbs.reader()); + var iter: LineIterator = .init(&reader); try testing.expectEqualStrings("--A", iter.next().?); try testing.expectEqualStrings("--B=C", iter.next().?); try testing.expectEqual(@as(?[]const u8, null), iter.next()); diff --git a/src/cli/boo.zig b/src/cli/boo.zig index 72b282ef6..756b6d77a 100644 --- a/src/cli/boo.zig +++ b/src/cli/boo.zig @@ -6,7 +6,7 @@ const Allocator = std.mem.Allocator; const help_strings = @import("help_strings"); const vaxis = @import("vaxis"); -const framedata = @import("framedata"); +const framedata = @embedFile("framedata"); const vxfw = vaxis.vxfw; @@ -218,17 +218,20 @@ var frames: []const []const u8 = undefined; /// Decompress the frames into a slice of individual frames fn decompressFrames(gpa: Allocator) !void { - var fbs = std.io.fixedBufferStream(framedata.compressed); - var list = std.ArrayList(u8).init(gpa); + var src: std.Io.Reader = .fixed(framedata); - try std.compress.flate.decompress(fbs.reader(), list.writer()); - decompressed_data = try list.toOwnedSlice(); + // var buf: [std.compress.flate.max_window_len]u8 = undefined; + var decompress: std.compress.flate.Decompress = .init(&src, .raw, &.{}); - var frame_list = try std.ArrayList([]const u8).initCapacity(gpa, 235); + var out: std.Io.Writer.Allocating = .init(gpa); + _ = try decompress.reader.streamRemaining(&out.writer); + decompressed_data = try out.toOwnedSlice(); + + var frame_list: std.ArrayList([]const u8) = try .initCapacity(gpa, 235); var frame_iter = std.mem.splitScalar(u8, decompressed_data, '\x01'); while (frame_iter.next()) |frame| { - try frame_list.append(frame); + try frame_list.append(gpa, frame); } - frames = try frame_list.toOwnedSlice(); + frames = try frame_list.toOwnedSlice(gpa); } diff --git a/src/cli/crash_report.zig b/src/cli/crash_report.zig index c6a383563..f0940fdab 100644 --- a/src/cli/crash_report.zig +++ b/src/cli/crash_report.zig @@ -38,21 +38,35 @@ pub fn run(alloc_gpa: Allocator) !u8 { try args.parse(Options, alloc_gpa, &opts, &iter); } + var buffer: [1024]u8 = undefined; + var stdout_file: std.fs.File = .stdout(); + var stdout_writer = stdout_file.writer(&buffer); + const stdout = &stdout_writer.interface; + + const result = runInner(alloc, &stdout_file, stdout); + stdout.flush() catch {}; + return result; +} + +fn runInner( + alloc: Allocator, + stdout_file: *std.fs.File, + stdout: *std.Io.Writer, +) !u8 { const crash_dir = try crash.defaultDir(alloc); - var reports = std.ArrayList(crash.Report).init(alloc); + var reports: std.ArrayList(crash.Report) = .empty; + errdefer reports.deinit(alloc); var it = try crash_dir.iterator(); - while (try it.next()) |report| try reports.append(.{ + while (try it.next()) |report| try reports.append(alloc, .{ .name = try alloc.dupe(u8, report.name), .mtime = report.mtime, }); - const stdout = std.io.getStdOut(); - // If we have no reports, then we're done. If we have a tty then we // print a message, otherwise we do nothing. if (reports.items.len == 0) { - if (std.posix.isatty(stdout.handle)) { + if (std.posix.isatty(stdout_file.handle)) { try stdout.writeAll("No crash reports! 👻\n"); } return 0; @@ -60,16 +74,15 @@ pub fn run(alloc_gpa: Allocator) !u8 { std.mem.sort(crash.Report, reports.items, {}, lt); - const writer = stdout.writer(); for (reports.items) |report| { var buf: [128]u8 = undefined; const now = std.time.nanoTimestamp(); const diff = now - report.mtime; const since = if (diff <= 0) "now" else s: { const d = Config.Duration{ .duration = @intCast(diff) }; - break :s try std.fmt.bufPrint(&buf, "{s} ago", .{d.round(std.time.ns_per_s)}); + break :s try std.fmt.bufPrint(&buf, "{f} ago", .{d.round(std.time.ns_per_s)}); }; - try writer.print("{s} ({s})\n", .{ report.name, since }); + try stdout.print("{s} ({s})\n", .{ report.name, since }); } return 0; diff --git a/src/cli/diagnostics.zig b/src/cli/diagnostics.zig index 2c6cb3b30..2af8bb4f8 100644 --- a/src/cli/diagnostics.zig +++ b/src/cli/diagnostics.zig @@ -16,7 +16,7 @@ pub const Diagnostic = struct { message: [:0]const u8, /// Write the full user-friendly diagnostic message to the writer. - pub fn write(self: *const Diagnostic, writer: anytype) !void { + pub fn format(self: *const Diagnostic, writer: *std.Io.Writer) !void { switch (self.location) { .none => {}, .cli => |index| try writer.print("cli:{}:", .{index}), @@ -157,11 +157,14 @@ pub const DiagnosticList = struct { errdefer _ = self.list.pop(); if (comptime precompute_enabled) { - var buf = std.ArrayList(u8).init(alloc); - defer buf.deinit(); - try diag.write(buf.writer()); + var stream: std.Io.Writer.Allocating = .init(alloc); + defer stream.deinit(); + diag.format(&stream.writer) catch |err| switch (err) { + // WriteFailed in this instance can only mean an OOM + error.WriteFailed => return error.OutOfMemory, + }; - const owned: [:0]const u8 = try buf.toOwnedSliceSentinel(0); + const owned: [:0]const u8 = try stream.toOwnedSliceSentinel(0); errdefer alloc.free(owned); try self.precompute.messages.append(alloc, owned); diff --git a/src/cli/edit_config.zig b/src/cli/edit_config.zig index 116843037..f103ca4a0 100644 --- a/src/cli/edit_config.zig +++ b/src/cli/edit_config.zig @@ -47,7 +47,9 @@ pub fn run(alloc: Allocator) !u8 { // not using `exec` anymore and because this command isn't performance // critical where setting up the defer cleanup is a problem. - const stderr = std.io.getStdErr().writer(); + var buffer: [1024]u8 = undefined; + var stderr_writer = std.fs.File.stderr().writer(&buffer); + const stderr = &stderr_writer.interface; var opts: Options = .{}; defer opts.deinit(); @@ -58,6 +60,13 @@ pub fn run(alloc: Allocator) !u8 { try args.parse(Options, alloc, &opts, &iter); } + const result = runInner(alloc, stderr); + // Flushing *shouldn't* fail but... + stderr.flush() catch {}; + return result; +} + +fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 { // We load the configuration once because that will write our // default configuration files to disk. We don't use the config. var config = try Config.load(alloc); @@ -133,23 +142,13 @@ pub fn run(alloc: Allocator) !u8 { // so this is not a big deal. comptime assert(builtin.link_libc); - var buf: std.ArrayListUnmanaged(u8) = .empty; - errdefer buf.deinit(alloc); - - const writer = buf.writer(alloc); - var shellescape: internal_os.ShellEscapeWriter(std.ArrayListUnmanaged(u8).Writer) = .init(writer); - var shellescapewriter = shellescape.writer(); - - try writer.writeAll(editor); - try writer.writeByte(' '); - try shellescapewriter.writeAll(path); - - const command = try buf.toOwnedSliceSentinel(alloc, 0); - defer alloc.free(command); - + const editorZ = try alloc.dupeZ(u8, editor); + defer alloc.free(editorZ); + const pathZ = try alloc.dupeZ(u8, path); + defer alloc.free(pathZ); const err = std.posix.execvpeZ( - "sh", - &.{ "sh", "-c", command }, + editorZ, + &.{ editorZ, pathZ }, std.c.environ, ); diff --git a/src/cli/ghostty.zig b/src/cli/ghostty.zig index adb715d68..f6ac7d93d 100644 --- a/src/cli/ghostty.zig +++ b/src/cli/ghostty.zig @@ -107,12 +107,18 @@ pub const Action = enum { // for all commands by just changing this one place. if (std.mem.eql(u8, field.name, @tagName(self))) { - const stdout = std.io.getStdOut().writer(); + var buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&buffer); + const stdout = &stdout_writer.interface; const text = @field(help_strings.Action, field.name) ++ "\n"; stdout.writeAll(text) catch |write_err| { std.log.warn("failed to write help text: {}\n", .{write_err}); break :err 1; }; + stdout.flush() catch |flush_err| { + std.log.warn("failed to flush help text: {}\n", .{flush_err}); + break :err 1; + }; break :err 0; } diff --git a/src/cli/help.zig b/src/cli/help.zig index 0528dc1c2..a2b4dde80 100644 --- a/src/cli/help.zig +++ b/src/cli/help.zig @@ -30,7 +30,9 @@ pub fn run(alloc: Allocator) !u8 { try args.parse(Options, alloc, &opts, &iter); } - const stdout = std.io.getStdOut().writer(); + var buffer: [2048]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&buffer); + const stdout = &stdout_writer.interface; try stdout.writeAll( \\Usage: ghostty [+action] [options] \\ @@ -70,6 +72,7 @@ pub fn run(alloc: Allocator) !u8 { \\where `` is one of actions listed above. \\ ); + try stdout.flush(); return 0; } diff --git a/src/cli/list_actions.zig b/src/cli/list_actions.zig index 6f5ce06a2..682eed251 100644 --- a/src/cli/list_actions.zig +++ b/src/cli/list_actions.zig @@ -37,8 +37,15 @@ pub fn run(alloc: Allocator) !u8 { try args.parse(Options, alloc, &opts, &iter); } - const stdout = std.io.getStdOut().writer(); - try helpgen_actions.generate(stdout, .plaintext, opts.docs, std.heap.page_allocator); + var stdout: std.fs.File = .stdout(); + var buffer: [4096]u8 = undefined; + var stdout_writer = stdout.writer(&buffer); + try helpgen_actions.generate( + &stdout_writer.interface, + .plaintext, + opts.docs, + std.heap.page_allocator, + ); return 0; } diff --git a/src/cli/list_colors.zig b/src/cli/list_colors.zig index 63945de99..50c12a693 100644 --- a/src/cli/list_colors.zig +++ b/src/cli/list_colors.zig @@ -39,11 +39,9 @@ pub fn run(alloc: Allocator) !u8 { try args.parse(Options, alloc, &opts, &iter); } - const stdout = std.io.getStdOut(); - - var keys = std.ArrayList([]const u8).init(alloc); - defer keys.deinit(); - for (x11_color.map.keys()) |key| try keys.append(key); + var keys: std.ArrayList([]const u8) = .empty; + defer keys.deinit(alloc); + for (x11_color.map.keys()) |key| try keys.append(alloc, key); std.mem.sortUnstable([]const u8, keys.items, {}, struct { fn lessThan(_: void, lhs: []const u8, rhs: []const u8) bool { @@ -52,12 +50,15 @@ pub fn run(alloc: Allocator) !u8 { }.lessThan); // Despite being under the posix namespace, this also works on Windows as of zig 0.13.0 + var stdout: std.fs.File = .stdout(); if (tui.can_pretty_print and !opts.plain and std.posix.isatty(stdout.handle)) { var arena = std.heap.ArenaAllocator.init(alloc); defer arena.deinit(); return prettyPrint(arena.allocator(), keys.items); } else { - const writer = stdout.writer(); + var buffer: [4096]u8 = undefined; + var stdout_writer = stdout.writer(&buffer); + const writer = &stdout_writer.interface; for (keys.items) |name| { const rgb = x11_color.map.get(name).?; try writer.print("{s} = #{x:0>2}{x:0>2}{x:0>2}\n", .{ @@ -74,19 +75,17 @@ pub fn run(alloc: Allocator) !u8 { fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 { // Set up vaxis - var tty = try vaxis.Tty.init(); + var buf: [1024]u8 = undefined; + var tty = try vaxis.Tty.init(&buf); defer tty.deinit(); var vx = try vaxis.init(alloc, .{}); - defer vx.deinit(alloc, tty.anyWriter()); + defer vx.deinit(alloc, tty.writer()); // We know we are ghostty, so let's enable mode 2027. Vaxis normally does this but you need an // event loop to auto-enable it. vx.caps.unicode = .unicode; - try tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_set); - defer tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_reset) catch {}; - - var buf_writer = tty.bufferedWriter(); - const writer = buf_writer.writer().any(); + try tty.writer().writeAll(vaxis.ctlseqs.unicode_set); + defer tty.writer().writeAll(vaxis.ctlseqs.unicode_reset) catch {}; const winsize: vaxis.Winsize = switch (builtin.os.tag) { // We use some default, it doesn't really matter for what @@ -100,7 +99,7 @@ fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 { else => try vaxis.Tty.getWinsize(tty.fd), }; - try vx.resize(alloc, tty.anyWriter(), winsize); + try vx.resize(alloc, tty.writer(), winsize); const win = vx.window(); @@ -203,11 +202,8 @@ fn prettyPrint(alloc: Allocator, keys: [][]const u8) !u8 { } // output the data - try vx.prettyPrint(writer); + try vx.prettyPrint(tty.writer()); } - // be sure to flush! - try buf_writer.flush(); - return 0; } diff --git a/src/cli/list_fonts.zig b/src/cli/list_fonts.zig index 58246d3ad..396c4e8a6 100644 --- a/src/cli/list_fonts.zig +++ b/src/cli/list_fonts.zig @@ -77,7 +77,9 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { // Its possible to build Ghostty without font discovery! if (comptime font.Discover == void) { - const stderr = std.io.getStdErr().writer(); + var buffer: [1024]u8 = undefined; + var stderr_writer = std.fs.File.stderr().writer(&buffer); + const stderr = &stderr_writer.interface; try stderr.print( \\Ghostty was built without a font discovery mechanism. This is a compile-time \\option. Please review how Ghostty was built from source, contact the @@ -85,15 +87,18 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { , .{}, ); + try stderr.flush(); return 1; } - const stdout = std.io.getStdOut().writer(); + var buffer: [2048]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&buffer); + const stdout = &stdout_writer.interface; // We'll be putting our fonts into a list categorized by family // so it is easier to read the output. - var families = std.ArrayList([]const u8).init(alloc); - var map = std.StringHashMap(std.ArrayListUnmanaged([]const u8)).init(alloc); + var families: std.ArrayList([]const u8) = .empty; + var map: std.StringHashMap(std.ArrayListUnmanaged([]const u8)) = .init(alloc); // Look up all available fonts var disco = font.Discover.init(); @@ -123,7 +128,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { const gop = try map.getOrPut(family); if (!gop.found_existing) { - try families.append(family); + try families.append(alloc, family); gop.value_ptr.* = .{}; } try gop.value_ptr.append(alloc, full_name); @@ -155,5 +160,6 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { try stdout.print("\n", .{}); } + try stdout.flush(); return 0; } diff --git a/src/cli/list_keybinds.zig b/src/cli/list_keybinds.zig index 94f445eea..a8899a4f5 100644 --- a/src/cli/list_keybinds.zig +++ b/src/cli/list_keybinds.zig @@ -64,27 +64,38 @@ pub fn run(alloc: Allocator) !u8 { var config = if (opts.default) try Config.default(alloc) else try Config.load(alloc); defer config.deinit(); - const stdout = std.io.getStdOut(); + var buffer: [1024]u8 = undefined; + const stdout: std.fs.File = .stdout(); + var stdout_writer = stdout.writer(&buffer); + const writer = &stdout_writer.interface; - // Despite being under the posix namespace, this also works on Windows as of zig 0.13.0 - if (tui.can_pretty_print and !opts.plain and std.posix.isatty(stdout.handle)) { + if (tui.can_pretty_print and !opts.plain and stdout.isTty()) { var arena = std.heap.ArenaAllocator.init(alloc); defer arena.deinit(); return prettyPrint(arena.allocator(), config.keybind); } else { try config.keybind.formatEntryDocs( - configpkg.entryFormatter("keybind", stdout.writer()), + configpkg.entryFormatter("keybind", writer), opts.docs, ); } + // Don't forget to flush! + try writer.flush(); return 0; } -const TriggerList = std.SinglyLinkedList(Binding.Trigger); +const TriggerNode = struct { + data: Binding.Trigger, + node: std.SinglyLinkedList.Node = .{}, + + pub fn get(node: *std.SinglyLinkedList.Node) *TriggerNode { + return @fieldParentPtr("node", node); + } +}; const ChordBinding = struct { - triggers: TriggerList, + triggers: std.SinglyLinkedList, action: Binding.Action, // Order keybinds based on various properties @@ -109,7 +120,8 @@ const ChordBinding = struct { const lhs_count: usize = blk: { var count: usize = 0; var maybe_trigger = lhs.triggers.first; - while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) { + while (maybe_trigger) |node| : (maybe_trigger = node.next) { + const trigger: *TriggerNode = .get(node); if (trigger.data.mods.super) count += 1; if (trigger.data.mods.ctrl) count += 1; if (trigger.data.mods.shift) count += 1; @@ -120,7 +132,8 @@ const ChordBinding = struct { const rhs_count: usize = blk: { var count: usize = 0; var maybe_trigger = rhs.triggers.first; - while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) { + while (maybe_trigger) |node| : (maybe_trigger = node.next) { + const trigger: *TriggerNode = .get(node); if (trigger.data.mods.super) count += 1; if (trigger.data.mods.ctrl) count += 1; if (trigger.data.mods.shift) count += 1; @@ -137,8 +150,8 @@ const ChordBinding = struct { var l_trigger = lhs.triggers.first; var r_trigger = rhs.triggers.first; while (l_trigger != null and r_trigger != null) { - const l_int = l_trigger.?.data.mods.int(); - const r_int = r_trigger.?.data.mods.int(); + const l_int = TriggerNode.get(l_trigger.?).data.mods.int(); + const r_int = TriggerNode.get(r_trigger.?).data.mods.int(); if (l_int != r_int) { return l_int > r_int; @@ -154,13 +167,13 @@ const ChordBinding = struct { while (l_trigger != null and r_trigger != null) { const lhs_key: c_int = blk: { - switch (l_trigger.?.data.key) { + switch (TriggerNode.get(l_trigger.?).data.key) { .physical => |key| break :blk @intFromEnum(key), .unicode => |key| break :blk @intCast(key), } }; const rhs_key: c_int = blk: { - switch (r_trigger.?.data.key) { + switch (TriggerNode.get(r_trigger.?).data.key) { .physical => |key| break :blk @intFromEnum(key), .unicode => |key| break :blk @intCast(key), } @@ -186,19 +199,18 @@ const ChordBinding = struct { fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 { // Set up vaxis - var tty = try vaxis.Tty.init(); + var buf: [1024]u8 = undefined; + var tty = try vaxis.Tty.init(&buf); defer tty.deinit(); var vx = try vaxis.init(alloc, .{}); - defer vx.deinit(alloc, tty.anyWriter()); + const writer = tty.writer(); + defer vx.deinit(alloc, writer); // We know we are ghostty, so let's enable mode 2027. Vaxis normally does this but you need an // event loop to auto-enable it. vx.caps.unicode = .unicode; - try tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_set); - defer tty.anyWriter().writeAll(vaxis.ctlseqs.unicode_reset) catch {}; - - var buf_writer = tty.bufferedWriter(); - const writer = buf_writer.writer().any(); + try writer.writeAll(vaxis.ctlseqs.unicode_set); + defer writer.writeAll(vaxis.ctlseqs.unicode_reset) catch {}; const winsize: vaxis.Winsize = switch (builtin.os.tag) { // We use some default, it doesn't really matter for what @@ -212,7 +224,7 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 { else => try vaxis.Tty.getWinsize(tty.fd), }; - try vx.resize(alloc, tty.anyWriter(), winsize); + try vx.resize(alloc, writer, winsize); const win = vx.window(); @@ -234,7 +246,9 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 { var result: vaxis.Window.PrintResult = .{ .col = 0, .row = 0, .overflow = false }; var maybe_trigger = bind.triggers.first; - while (maybe_trigger) |trigger| : (maybe_trigger = trigger.next) { + while (maybe_trigger) |node| : (maybe_trigger = node.next) { + const trigger: *TriggerNode = .get(node); + if (trigger.data.mods.super) { result = win.printSegment(.{ .text = "super", .style = super_style }, .{ .col_offset = result.col }); result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col }); @@ -252,18 +266,18 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 { result = win.printSegment(.{ .text = " + " }, .{ .col_offset = result.col }); } const key = switch (trigger.data.key) { - .physical => |k| try std.fmt.allocPrint(alloc, "{s}", .{@tagName(k)}), + .physical => |k| try std.fmt.allocPrint(alloc, "{t}", .{k}), .unicode => |c| try std.fmt.allocPrint(alloc, "{u}", .{c}), }; result = win.printSegment(.{ .text = key }, .{ .col_offset = result.col }); // Print a separator between chorded keys - if (trigger.next != null) { + if (trigger.node.next != null) { result = win.printSegment(.{ .text = " > ", .style = .{ .bold = true, .fg = .{ .index = 6 } } }, .{ .col_offset = result.col }); } } - const action = try std.fmt.allocPrint(alloc, "{}", .{bind.action}); + const action = try std.fmt.allocPrint(alloc, "{f}", .{bind.action}); // If our action has an argument, we print the argument in a different color if (std.mem.indexOfScalar(u8, action, ':')) |idx| { _ = win.print(&.{ @@ -276,29 +290,33 @@ fn prettyPrint(alloc: Allocator, keybinds: Config.Keybinds) !u8 { } try vx.prettyPrint(writer); } - try buf_writer.flush(); + try writer.flush(); return 0; } -fn iterateBindings(alloc: Allocator, iter: anytype, win: *const vaxis.Window) !struct { []ChordBinding, u16 } { +fn iterateBindings( + alloc: Allocator, + iter: anytype, + win: *const vaxis.Window, +) !struct { []ChordBinding, u16 } { var widest_chord: u16 = 0; - var bindings = std.ArrayList(ChordBinding).init(alloc); + var bindings: std.ArrayList(ChordBinding) = .empty; while (iter.next()) |bind| { const width = blk: { - var buf = std.ArrayList(u8).init(alloc); + var buf: std.Io.Writer.Allocating = .init(alloc); const t = bind.key_ptr.*; - if (t.mods.super) try std.fmt.format(buf.writer(), "super + ", .{}); - if (t.mods.ctrl) try std.fmt.format(buf.writer(), "ctrl + ", .{}); - if (t.mods.alt) try std.fmt.format(buf.writer(), "alt + ", .{}); - if (t.mods.shift) try std.fmt.format(buf.writer(), "shift + ", .{}); + if (t.mods.super) try buf.writer.print("super + ", .{}); + if (t.mods.ctrl) try buf.writer.print("ctrl + ", .{}); + if (t.mods.alt) try buf.writer.print("alt + ", .{}); + if (t.mods.shift) try buf.writer.print("shift + ", .{}); switch (t.key) { - .physical => |k| try std.fmt.format(buf.writer(), "{s}", .{@tagName(k)}), - .unicode => |c| try std.fmt.format(buf.writer(), "{u}", .{c}), + .physical => |k| try buf.writer.print("{t}", .{k}), + .unicode => |c| try buf.writer.print("{u}", .{c}), } - break :blk win.gwidth(buf.items); + break :blk win.gwidth(buf.written()); }; switch (bind.value_ptr.*) { @@ -310,28 +328,28 @@ fn iterateBindings(alloc: Allocator, iter: anytype, win: *const vaxis.Window) !s // Prepend the current keybind onto the list of sub-binds for (sub_bindings) |*nb| { - const prepend_node = try alloc.create(TriggerList.Node); - prepend_node.* = TriggerList.Node{ .data = bind.key_ptr.* }; - nb.triggers.prepend(prepend_node); + const prepend_node = try alloc.create(TriggerNode); + prepend_node.* = .{ .data = bind.key_ptr.* }; + nb.triggers.prepend(&prepend_node.node); } // Add the longest sub-bind width to the current bind width along with a padding // of 5 for the ' > ' spacer widest_chord = @max(widest_chord, width + max_width + 5); - try bindings.appendSlice(sub_bindings); + try bindings.appendSlice(alloc, sub_bindings); }, .leaf => |leaf| { - const node = try alloc.create(TriggerList.Node); - node.* = TriggerList.Node{ .data = bind.key_ptr.* }; - const triggers = TriggerList{ - .first = node, - }; + const node = try alloc.create(TriggerNode); + node.* = .{ .data = bind.key_ptr.* }; widest_chord = @max(widest_chord, width); - try bindings.append(.{ .triggers = triggers, .action = leaf.action }); + try bindings.append(alloc, .{ + .triggers = .{ .first = &node.node }, + .action = leaf.action, + }); }, } } - return .{ try bindings.toOwnedSlice(), widest_chord }; + return .{ try bindings.toOwnedSlice(alloc), widest_chord }; } diff --git a/src/cli/list_themes.zig b/src/cli/list_themes.zig index 0c0acfe84..cc6cfaf3e 100644 --- a/src/cli/list_themes.zig +++ b/src/cli/list_themes.zig @@ -57,9 +57,12 @@ const ThemeListElement = struct { .host = .{ .raw = "" }, .path = .{ .raw = self.path }, }; - var buf = std.ArrayList(u8).init(alloc); + var buf: std.Io.Writer.Allocating = .init(alloc); errdefer buf.deinit(); - try uri.writeToStream(.{ .scheme = true, .authority = true, .path = true }, buf.writer()); + try uri.writeToStream( + &buf.writer, + .{ .scheme = true, .authority = true, .path = true }, + ); return buf.toOwnedSlice(); } }; @@ -114,8 +117,14 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 { var arena = std.heap.ArenaAllocator.init(gpa_alloc); const alloc = arena.allocator(); - const stderr = std.io.getStdErr().writer(); - const stdout = std.io.getStdOut().writer(); + var stdout_buf: [4096]u8 = undefined; + var stdout_file: std.fs.File = .stdout(); + var stdout_writer = stdout_file.writer(&stdout_buf); + const stdout = &stdout_writer.interface; + + var stderr_buf: [4096]u8 = undefined; + var stderr_writer = std.fs.File.stderr().writer(&stderr_buf); + const stderr = &stderr_writer.interface; const resources_dir = global_state.resources_dir.app(); if (resources_dir == null) @@ -124,9 +133,9 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 { var count: usize = 0; - var themes = std.ArrayList(ThemeListElement).init(alloc); + var themes: std.ArrayList(ThemeListElement) = .empty; - var it = themepkg.LocationIterator{ .arena_alloc = arena.allocator() }; + var it: themepkg.LocationIterator = .{ .arena_alloc = arena.allocator() }; while (try it.next()) |loc| { var dir = std.fs.cwd().openDir(loc.dir, .{ .iterate = true }) catch |err| switch (err) { @@ -148,7 +157,7 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 { count += 1; const path = try std.fs.path.join(alloc, &.{ loc.dir, entry.name }); - try themes.append(.{ + try themes.append(alloc, .{ .path = path, .location = loc.location, .theme = try alloc.dupe(u8, entry.name), @@ -166,18 +175,20 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 { std.mem.sortUnstable(ThemeListElement, themes.items, {}, ThemeListElement.lessThan); - if (tui.can_pretty_print and !opts.plain and std.posix.isatty(std.io.getStdOut().handle)) { + if (tui.can_pretty_print and !opts.plain and stdout_file.isTty()) { try preview(gpa_alloc, themes.items, opts.color); return 0; } for (themes.items) |theme| { if (opts.path) - try stdout.print("{s} ({s}) {s}\n", .{ theme.theme, @tagName(theme.location), theme.path }) + try stdout.print("{s} ({t}) {s}\n", .{ theme.theme, theme.location, theme.path }) else - try stdout.print("{s} ({s})\n", .{ theme.theme, @tagName(theme.location) }); + try stdout.print("{s} ({t})\n", .{ theme.theme, theme.location }); } + // Don't forget to flush! + try stdout.flush(); return 0; } @@ -209,23 +220,28 @@ const Preview = struct { text_input: vaxis.widgets.TextInput, theme_filter: ColorScheme, - pub fn init(allocator: std.mem.Allocator, themes: []ThemeListElement, theme_filter: ColorScheme) !*Preview { + pub fn init( + allocator: std.mem.Allocator, + themes: []ThemeListElement, + theme_filter: ColorScheme, + buf: []u8, + ) !*Preview { const self = try allocator.create(Preview); self.* = .{ .allocator = allocator, .should_quit = false, - .tty = try vaxis.Tty.init(), + .tty = try .init(buf), .vx = try vaxis.init(allocator, .{}), .mouse = null, .themes = themes, - .filtered = try std.ArrayList(usize).initCapacity(allocator, themes.len), + .filtered = try .initCapacity(allocator, themes.len), .current = 0, .window = 0, .hex = false, .mode = .normal, .color_scheme = .light, - .text_input = vaxis.widgets.TextInput.init(allocator, &self.vx.unicode), + .text_input = .init(allocator, &self.vx.unicode), .theme_filter = theme_filter, }; @@ -236,9 +252,9 @@ const Preview = struct { pub fn deinit(self: *Preview) void { const allocator = self.allocator; - self.filtered.deinit(); + self.filtered.deinit(allocator); self.text_input.deinit(); - self.vx.deinit(allocator, self.tty.anyWriter()); + self.vx.deinit(allocator, self.tty.writer()); self.tty.deinit(); allocator.destroy(self); } @@ -251,12 +267,14 @@ const Preview = struct { try loop.init(); try loop.start(); - try self.vx.enterAltScreen(self.tty.anyWriter()); - try self.vx.setTitle(self.tty.anyWriter(), "👻 Ghostty Theme Preview 👻"); - try self.vx.queryTerminal(self.tty.anyWriter(), 1 * std.time.ns_per_s); - try self.vx.setMouseMode(self.tty.anyWriter(), true); + const writer = self.tty.writer(); + + try self.vx.enterAltScreen(writer); + try self.vx.setTitle(writer, "👻 Ghostty Theme Preview 👻"); + try self.vx.queryTerminal(writer, 1 * std.time.ns_per_s); + try self.vx.setMouseMode(writer, true); if (self.vx.caps.color_scheme_updates) - try self.vx.subscribeToColorSchemeUpdates(self.tty.anyWriter()); + try self.vx.subscribeToColorSchemeUpdates(writer); while (!self.should_quit) { var arena = std.heap.ArenaAllocator.init(self.allocator); @@ -269,9 +287,8 @@ const Preview = struct { } try self.draw(alloc); - var buffered = self.tty.bufferedWriter(); - try self.vx.render(buffered.writer().any()); - try buffered.flush(); + try self.vx.render(writer); + try writer.flush(); } } @@ -308,11 +325,11 @@ const Preview = struct { const string = try std.ascii.allocLowerString(self.allocator, buffer); defer self.allocator.free(string); - var tokens = std.ArrayList([]const u8).init(self.allocator); - defer tokens.deinit(); + var tokens: std.ArrayList([]const u8) = .empty; + defer tokens.deinit(self.allocator); var it = std.mem.tokenizeScalar(u8, string, ' '); - while (it.next()) |token| try tokens.append(token); + while (it.next()) |token| try tokens.append(self.allocator, token); for (self.themes, 0..) |*theme, i| { try theme_config.loadFile(theme_config._arena.?.allocator(), theme.path); @@ -322,13 +339,13 @@ const Preview = struct { .to_lower = true, .plain = true, }); - if (theme.rank != null) try self.filtered.append(i); + if (theme.rank != null) try self.filtered.append(self.allocator, i); } } else { for (self.themes, 0..) |*theme, i| { try theme_config.loadFile(theme_config._arena.?.allocator(), theme.path); if (shouldIncludeTheme(self.theme_filter, theme_config)) { - try self.filtered.append(i); + try self.filtered.append(self.allocator, i); theme.rank = null; } } @@ -421,13 +438,13 @@ const Preview = struct { self.hex = false; if (key.matches('c', .{})) try self.vx.copyToSystemClipboard( - self.tty.anyWriter(), + self.tty.writer(), self.themes[self.filtered.items[self.current]].theme, alloc, ) else if (key.matches('c', .{ .shift = true })) try self.vx.copyToSystemClipboard( - self.tty.anyWriter(), + self.tty.writer(), self.themes[self.filtered.items[self.current]].path, alloc, ); @@ -471,7 +488,7 @@ const Preview = struct { }, .color_scheme => |color_scheme| self.color_scheme = color_scheme, .mouse => |mouse| self.mouse = mouse, - .winsize => |ws| try self.vx.resize(self.allocator, self.tty.anyWriter(), ws), + .winsize => |ws| try self.vx.resize(self.allocator, self.tty.writer(), ws), } } @@ -1044,14 +1061,14 @@ const Preview = struct { ); } - var buf = std.ArrayList(u8).init(alloc); + var buf: std.Io.Writer.Allocating = .init(alloc); defer buf.deinit(); for (config._diagnostics.items(), 0..) |diag, captured_i| { const i: u16 = @intCast(captured_i); - try diag.write(buf.writer()); + try diag.format(&buf.writer); _ = child.printSegment( .{ - .text = buf.items, + .text = buf.written(), .style = self.ui_err(), }, .{ @@ -1319,7 +1336,7 @@ const Preview = struct { .{ .text = "const ", .style = color5 }, .{ .text = "stdout ", .style = standard }, .{ .text = "=", .style = color5 }, - .{ .text = " std.io.getStdOut().writer();", .style = standard }, + .{ .text = " std.Io.getStdOut().writer();", .style = standard }, }, .{ .row_offset = 7, @@ -1651,7 +1668,13 @@ fn color(config: Config, palette: usize) vaxis.Color { const lorem_ipsum = @embedFile("lorem_ipsum.txt"); fn preview(allocator: std.mem.Allocator, themes: []ThemeListElement, theme_filter: ColorScheme) !void { - var app = try Preview.init(allocator, themes, theme_filter); + var buf: [4096]u8 = undefined; + var app = try Preview.init( + allocator, + themes, + theme_filter, + &buf, + ); defer app.deinit(); try app.run(); } diff --git a/src/cli/new_window.zig b/src/cli/new_window.zig index 343175b4e..f3f4740d1 100644 --- a/src/cli/new_window.zig +++ b/src/cli/new_window.zig @@ -26,7 +26,7 @@ pub const Options = struct { // If it's not `-e` continue with the standard argument parsning. if (!std.mem.eql(u8, arg, "-e")) return true; - var arguments: std.ArrayListUnmanaged([:0]const u8) = .empty; + var arguments: std.ArrayList([:0]const u8) = .empty; errdefer { for (arguments.items) |argument| alloc.free(argument); arguments.deinit(alloc); @@ -99,12 +99,21 @@ pub const Options = struct { pub fn run(alloc: Allocator) !u8 { var iter = try args.argsIterator(alloc); defer iter.deinit(); - return try runArgs(alloc, &iter); + + var buffer: [1024]u8 = undefined; + var stderr_writer = std.fs.File.stderr().writer(&buffer); + const stderr = &stderr_writer.interface; + + const result = runArgs(alloc, &iter, stderr); + stderr.flush() catch {}; + return result; } -fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { - const stderr = std.io.getStdErr().writer(); - +fn runArgs( + alloc_gpa: Allocator, + argsIter: anytype, + stderr: *std.Io.Writer, +) !u8 { var opts: Options = .{}; defer opts.deinit(); @@ -126,9 +135,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { inner: inline for (@typeInfo(Options).@"struct".fields) |field| { if (field.name[0] == '_') continue :inner; if (std.mem.eql(u8, field.name, diagnostic.key)) { - try stderr.writeAll("config error: "); - try diagnostic.write(stderr); - try stderr.writeAll("\n"); + try stderr.print("config error: {f}\n", .{diagnostic}); exit = true; } } diff --git a/src/cli/show_config.zig b/src/cli/show_config.zig index 3f22c75c2..1b73b77c1 100644 --- a/src/cli/show_config.zig +++ b/src/cli/show_config.zig @@ -77,7 +77,10 @@ pub fn run(alloc: Allocator) !u8 { // For some reason `std.fmt.format` isn't working here but it works in // tests so we just do configfmt.format. - const stdout = std.io.getStdOut().writer(); - try configfmt.format("", .{}, stdout); + var stdout: std.fs.File = .stdout(); + var buffer: [4096]u8 = undefined; + var stdout_writer = stdout.writer(&buffer); + try configfmt.format(&stdout_writer.interface); + try stdout_writer.end(); return 0; } diff --git a/src/cli/show_face.zig b/src/cli/show_face.zig index e3b596bcd..9dee777b3 100644 --- a/src/cli/show_face.zig +++ b/src/cli/show_face.zig @@ -64,13 +64,32 @@ pub const Options = struct { pub fn run(alloc: Allocator) !u8 { var iter = try args.argsIterator(alloc); defer iter.deinit(); - return try runArgs(alloc, &iter); + + var stdout_buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + const stdout = &stdout_writer.interface; + + var stderr_buffer: [1024]u8 = undefined; + var stderr_writer = std.fs.File.stdout().writer(&stderr_buffer); + const stderr = &stderr_writer.interface; + + const result = runArgs( + alloc, + &iter, + stdout, + stderr, + ); + stdout.flush() catch {}; + stderr.flush() catch {}; + return result; } -fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { - const stdout = std.io.getStdOut().writer(); - const stderr = std.io.getStdErr().writer(); - +fn runArgs( + alloc_gpa: Allocator, + argsIter: anytype, + stdout: *std.Io.Writer, + stderr: *std.Io.Writer, +) !u8 { // Its possible to build Ghostty without font discovery! if (comptime font.Discover == void) { try stderr.print( @@ -104,9 +123,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { inner: inline for (@typeInfo(Options).@"struct".fields) |field| { if (field.name[0] == '_') continue :inner; if (std.mem.eql(u8, field.name, diagnostic.key)) { - try stderr.writeAll("config error: "); - try diagnostic.write(stderr); - try stderr.writeAll("\n"); + try stderr.print("config error: {f}\n", .{diagnostic}); exit = true; } } @@ -138,9 +155,7 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { if (field.name[0] == '_') continue :inner; if (std.mem.eql(u8, field.name, diagnostic.key) and (diagnostic.location == .none or diagnostic.location == .cli)) continue :outer; } - try stderr.writeAll("config error: "); - try diagnostic.write(stderr); - try stderr.writeAll("\n"); + try stderr.print("config error: {f}\n", .{diagnostic}); } } @@ -189,8 +204,8 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { fn lookup( alloc: std.mem.Allocator, - stdout: anytype, - stderr: anytype, + stdout: *std.Io.Writer, + stderr: *std.Io.Writer, font_grid: *font.SharedGrid, style: font.Style, presentation: ?font.Presentation, diff --git a/src/cli/ssh-cache/DiskCache.zig b/src/cli/ssh-cache/DiskCache.zig index db138cf37..608155dfd 100644 --- a/src/cli/ssh-cache/DiskCache.zig +++ b/src/cli/ssh-cache/DiskCache.zig @@ -57,8 +57,6 @@ pub fn clear(self: DiskCache) !void { pub const AddResult = enum { added, updated }; -pub const AddError = std.fs.Dir.MakeError || std.fs.File.OpenError || std.fs.File.LockError || std.fs.File.ReadError || std.fs.File.WriteError || std.posix.RealPathError || std.posix.RenameError || Allocator.Error || error{ HostnameIsInvalid, CacheIsLocked }; - /// Add or update a hostname entry in the cache. /// Returns AddResult.added for new entries or AddResult.updated for existing ones. /// The cache file is created if it doesn't exist with secure permissions (0600). @@ -66,7 +64,7 @@ pub fn add( self: DiskCache, alloc: Allocator, hostname: []const u8, -) AddError!AddResult { +) !AddResult { if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid; // Create cache directory if needed @@ -130,15 +128,13 @@ pub fn add( return result; } -pub const RemoveError = std.fs.Dir.OpenError || std.fs.File.OpenError || std.fs.File.ReadError || std.fs.File.WriteError || std.posix.RealPathError || std.posix.RenameError || Allocator.Error || error{ HostnameIsInvalid, CacheIsLocked }; - /// Remove a hostname entry from the cache. /// No error is returned if the hostname doesn't exist or the cache file is missing. pub fn remove( self: DiskCache, alloc: Allocator, hostname: []const u8, -) RemoveError!void { +) !void { if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid; // Open our file @@ -199,7 +195,7 @@ pub fn contains( return entries.contains(hostname); } -fn fixupPermissions(file: std.fs.File) !void { +fn fixupPermissions(file: std.fs.File) (std.fs.File.StatError || std.fs.File.ChmodError)!void { // Windows does not support chmod if (comptime builtin.os.tag == .windows) return; @@ -211,14 +207,12 @@ fn fixupPermissions(file: std.fs.File) !void { } } -pub const WriteCacheFileError = std.fs.Dir.OpenError || std.fs.File.OpenError || std.fs.File.WriteError || std.fs.Dir.RealPathAllocError || std.posix.RealPathError || std.posix.RenameError || error{FileTooBig}; - fn writeCacheFile( self: DiskCache, alloc: Allocator, entries: std.StringHashMap(Entry), expire_days: ?u32, -) WriteCacheFileError!void { +) !void { var td: TempDir = try .init(); defer td.deinit(); @@ -227,14 +221,18 @@ fn writeCacheFile( const tmp_path = try td.dir.realpathAlloc(alloc, "ssh-cache"); defer alloc.free(tmp_path); - const writer = tmp_file.writer(); + var buf: [1024]u8 = undefined; + var writer = tmp_file.writer(&buf); var iter = entries.iterator(); while (iter.next()) |kv| { // Only write non-expired entries if (kv.value_ptr.isExpired(expire_days)) continue; - try kv.value_ptr.format(writer); + try kv.value_ptr.format(&writer.interface); } + // Don't forget to flush!! + try writer.interface.flush(); + // Atomic replace try std.fs.renameAbsolute(tmp_path, self.path); } @@ -278,8 +276,12 @@ pub fn deinitEntries( fn readEntries( alloc: Allocator, file: std.fs.File, -) (std.fs.File.ReadError || Allocator.Error || error{FileTooBig})!std.StringHashMap(Entry) { - const content = try file.readToEndAlloc(alloc, MAX_CACHE_SIZE); +) !std.StringHashMap(Entry) { + var reader = file.reader(&.{}); + const content = try reader.interface.allocRemaining( + alloc, + .limited(MAX_CACHE_SIZE), + ); defer alloc.free(content); var entries = std.StringHashMap(Entry).init(alloc); @@ -403,10 +405,12 @@ test "disk cache clear" { // Create our path var td: TempDir = try .init(); defer td.deinit(); + var buf: [4096]u8 = undefined; { var file = try td.dir.createFile("cache", .{}); defer file.close(); - try file.writer().writeAll("HELLO!"); + var file_writer = file.writer(&buf); + try file_writer.interface.writeAll("HELLO!"); } const path = try td.dir.realpathAlloc(alloc, "cache"); defer alloc.free(path); @@ -429,10 +433,14 @@ test "disk cache operations" { // Create our path var td: TempDir = try .init(); defer td.deinit(); + var buf: [4096]u8 = undefined; { var file = try td.dir.createFile("cache", .{}); defer file.close(); - try file.writer().writeAll("HELLO!"); + var file_writer = file.writer(&buf); + const writer = &file_writer.interface; + try writer.writeAll("HELLO!"); + try writer.flush(); } const path = try td.dir.realpathAlloc(alloc, "cache"); defer alloc.free(path); diff --git a/src/cli/ssh-cache/Entry.zig b/src/cli/ssh-cache/Entry.zig index 3a691be80..f3403dbd4 100644 --- a/src/cli/ssh-cache/Entry.zig +++ b/src/cli/ssh-cache/Entry.zig @@ -33,7 +33,7 @@ pub fn parse(line: []const u8) ?Entry { }; } -pub fn format(self: Entry, writer: anytype) !void { +pub fn format(self: Entry, writer: *std.Io.Writer) !void { try writer.print( "{s}|{d}|{s}\n", .{ self.hostname, self.timestamp, self.terminfo_version }, diff --git a/src/cli/ssh_cache.zig b/src/cli/ssh_cache.zig index 1099f0112..9434e9771 100644 --- a/src/cli/ssh_cache.zig +++ b/src/cli/ssh_cache.zig @@ -61,9 +61,30 @@ pub fn run(alloc_gpa: Allocator) !u8 { try args.parse(Options, alloc_gpa, &opts, &iter); } - const stdout = std.io.getStdOut().writer(); - const stderr = std.io.getStdErr().writer(); + var stdout_buffer: [1024]u8 = undefined; + var stdout_file: std.fs.File = .stdout(); + var stdout_writer = stdout_file.writer(&stdout_buffer); + const stdout = &stdout_writer.interface; + var stderr_buffer: [1024]u8 = undefined; + var stderr_file: std.fs.File = .stderr(); + var stderr_writer = stderr_file.writer(&stderr_buffer); + const stderr = &stderr_writer.interface; + + const result = runInner(alloc, opts, stdout, stderr); + + // Flushing *shouldn't* fail but... + stdout.flush() catch {}; + stderr.flush() catch {}; + return result; +} + +pub fn runInner( + alloc: Allocator, + opts: Options, + stdout: *std.Io.Writer, + stderr: *std.Io.Writer, +) !u8 { // Setup our disk cache to the standard location const cache_path = try DiskCache.defaultPath(alloc, "ghostty"); const cache: DiskCache = .{ .path = cache_path }; @@ -165,7 +186,7 @@ pub fn run(alloc_gpa: Allocator) !u8 { fn listEntries( alloc: Allocator, entries: *const std.StringHashMap(Entry), - writer: anytype, + writer: *std.Io.Writer, ) !void { if (entries.count() == 0) { try writer.print("No hosts in cache.\n", .{}); @@ -173,12 +194,12 @@ fn listEntries( } // Sort entries by hostname for consistent output - var items = std.ArrayList(Entry).init(alloc); - defer items.deinit(); + var items: std.ArrayList(Entry) = .empty; + defer items.deinit(alloc); var iter = entries.iterator(); while (iter.next()) |kv| { - try items.append(kv.value_ptr.*); + try items.append(alloc, kv.value_ptr.*); } std.mem.sort(Entry, items.items, {}, struct { diff --git a/src/cli/validate_config.zig b/src/cli/validate_config.zig index 114843e9a..55d861402 100644 --- a/src/cli/validate_config.zig +++ b/src/cli/validate_config.zig @@ -40,8 +40,19 @@ pub fn run(alloc: std.mem.Allocator) !u8 { try args.parse(Options, alloc, &opts, &iter); } - const stdout = std.io.getStdOut().writer(); + var buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&buffer); + const stdout = &stdout_writer.interface; + const result = runInner(alloc, opts, stdout); + try stdout_writer.end(); + return result; +} +fn runInner( + alloc: std.mem.Allocator, + opts: Options, + stdout: *std.Io.Writer, +) !u8 { var cfg = try Config.default(alloc); defer cfg.deinit(); @@ -58,15 +69,9 @@ pub fn run(alloc: std.mem.Allocator) !u8 { try cfg.finalize(); if (cfg._diagnostics.items().len > 0) { - var buf = std.ArrayList(u8).init(alloc); - defer buf.deinit(); - for (cfg._diagnostics.items()) |diag| { - try diag.write(buf.writer()); - try stdout.print("{s}\n", .{buf.items}); - buf.clearRetainingCapacity(); + try stdout.print("{f}\n", .{diag}); } - return 1; } diff --git a/src/cli/version.zig b/src/cli/version.zig index 22608fa88..cf8e66fa6 100644 --- a/src/cli/version.zig +++ b/src/cli/version.zig @@ -15,8 +15,12 @@ pub const Options = struct {}; /// The `version` command is used to display information about Ghostty. Recognized as /// either `+version` or `--version`. pub fn run(alloc: Allocator) !u8 { - const stdout = std.io.getStdOut().writer(); - const tty = std.io.getStdOut().isTty(); + var buffer: [1024]u8 = undefined; + const stdout_file: std.fs.File = .stdout(); + var stdout_writer = stdout_file.writer(&buffer); + + const stdout = &stdout_writer.interface; + const tty = stdout_file.isTty(); if (tty) if (build_config.version.build) |commit_hash| { try stdout.print( @@ -29,7 +33,7 @@ pub fn run(alloc: Allocator) !u8 { try stdout.print("Version\n", .{}); try stdout.print(" - version: {s}\n", .{build_config.version_string}); - try stdout.print(" - channel: {s}\n", .{@tagName(build_config.release_channel)}); + try stdout.print(" - channel: {t}\n", .{build_config.release_channel}); try stdout.print("Build Config\n", .{}); try stdout.print(" - Zig version : {s}\n", .{builtin.zig_version_string}); @@ -37,20 +41,20 @@ pub fn run(alloc: Allocator) !u8 { try stdout.print(" - app runtime : {}\n", .{build_config.app_runtime}); try stdout.print(" - font engine : {}\n", .{build_config.font_backend}); try stdout.print(" - renderer : {}\n", .{renderer.Renderer}); - try stdout.print(" - libxev : {s}\n", .{@tagName(xev.backend)}); + try stdout.print(" - libxev : {t}\n", .{xev.backend}); if (comptime build_config.app_runtime == .gtk) { if (comptime builtin.os.tag == .linux) { const kernel_info = internal_os.getKernelInfo(alloc); defer if (kernel_info) |k| alloc.free(k); try stdout.print(" - kernel version: {s}\n", .{kernel_info orelse "Kernel information unavailable"}); } - try stdout.print(" - desktop env : {s}\n", .{@tagName(internal_os.desktopEnvironment())}); + try stdout.print(" - desktop env : {t}\n", .{internal_os.desktopEnvironment()}); try stdout.print(" - GTK version :\n", .{}); - try stdout.print(" build : {}\n", .{gtk_version.comptime_version}); - try stdout.print(" runtime : {}\n", .{gtk_version.getRuntimeVersion()}); + try stdout.print(" build : {f}\n", .{gtk_version.comptime_version}); + try stdout.print(" runtime : {f}\n", .{gtk_version.getRuntimeVersion()}); try stdout.print(" - libadwaita : enabled\n", .{}); - try stdout.print(" build : {}\n", .{adw_version.comptime_version}); - try stdout.print(" runtime : {}\n", .{adw_version.getRuntimeVersion()}); + try stdout.print(" build : {f}\n", .{adw_version.comptime_version}); + try stdout.print(" runtime : {f}\n", .{adw_version.getRuntimeVersion()}); if (comptime build_options.x11) { try stdout.print(" - libX11 : enabled\n", .{}); } else { @@ -65,5 +69,8 @@ pub fn run(alloc: Allocator) !u8 { try stdout.print(" - libwayland : disabled\n", .{}); } } + + // Don't forget to flush! + try stdout.flush(); return 0; } diff --git a/src/config/Config.zig b/src/config/Config.zig index 8f811e9a4..caaf5feb8 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -3417,10 +3417,10 @@ pub fn loadFile(self: *Config, alloc: Allocator, path: []const u8) !void { defer file.close(); std.log.info("reading configuration file path={s}", .{path}); - var buf_reader = std.io.bufferedReader(file.reader()); - const reader = buf_reader.reader(); - const Iter = cli.args.LineIterator(@TypeOf(reader)); - var iter: Iter = .{ .r = reader, .filepath = path }; + var buf: [2048]u8 = undefined; + var file_reader = file.reader(&buf); + const reader = &file_reader.interface; + var iter: cli.args.LineIterator = .{ .r = reader, .filepath = path }; try self.loadIter(alloc, &iter); try self.expandPaths(std.fs.path.dirname(path).?); } @@ -3457,8 +3457,10 @@ fn writeConfigTemplate(path: []const u8) !void { } const file = try std.fs.createFileAbsolute(path, .{}); defer file.close(); - try std.fmt.format( - file.writer(), + var buf: [4096]u8 = undefined; + var file_writer = file.writer(&buf); + const writer = &file_writer.interface; + try writer.print( @embedFile("./config-template"), .{ .path = path }, ); @@ -3628,17 +3630,17 @@ pub fn loadCliArgs(self: *Config, alloc_gpa: Allocator) !void { // Next, take all remaining args and use that to build up // a command to execute. - var builder = std.ArrayList([:0]const u8).init(arena_alloc); - errdefer builder.deinit(); + var builder: std.ArrayList([:0]const u8) = .empty; + errdefer builder.deinit(arena_alloc); for (args) |arg_raw| { const arg = std.mem.sliceTo(arg_raw, 0); const copy = try arena_alloc.dupeZ(u8, arg); try self._replay_steps.append(arena_alloc, .{ .arg = copy }); - try builder.append(copy); + try builder.append(arena_alloc, copy); } self.@"_xdg-terminal-exec" = true; - self.@"initial-command" = .{ .direct = try builder.toOwnedSlice() }; + self.@"initial-command" = .{ .direct = try builder.toOwnedSlice(arena_alloc) }; return; } } @@ -3710,13 +3712,13 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void { // PRIOR to the "-e" in our replay steps, since everything // after "-e" becomes an "initial-command". To do this, we // dupe the values if we find it. - var replay_suffix = std.ArrayList(Replay.Step).init(alloc_gpa); - defer replay_suffix.deinit(); + var replay_suffix: std.ArrayList(Replay.Step) = .empty; + defer replay_suffix.deinit(alloc_gpa); for (self._replay_steps.items, 0..) |step, i| if (step == .@"-e") { // We don't need to clone the steps because they should // all be allocated in our arena and we're keeping our // arena. - try replay_suffix.appendSlice(self._replay_steps.items[i..]); + try replay_suffix.appendSlice(alloc_gpa, self._replay_steps.items[i..]); // Remove our old values. Again, don't need to free any // memory here because its all part of our arena. @@ -3744,10 +3746,11 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void { // We must only load a unique file once if (try loaded.fetchPut(path, {}) != null) { const diag: cli.Diagnostic = .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "config-file {s}: cycle detected", .{path}, + 0, ), }; @@ -3759,10 +3762,11 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void { var file = std.fs.openFileAbsolute(path, .{}) catch |err| { if (err != error.FileNotFound or !optional) { const diag: cli.Diagnostic = .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "error opening config-file {s}: {}", .{ path, err }, + 0, ), }; @@ -3778,10 +3782,11 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void { .file => {}, else => |kind| { const diag: cli.Diagnostic = .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "config-file {s}: not reading because file type is {s}", .{ path, @tagName(kind) }, + 0, ), }; @@ -3792,10 +3797,10 @@ pub fn loadRecursiveFiles(self: *Config, alloc_gpa: Allocator) !void { } log.info("loading config-file path={s}", .{path}); - var buf_reader = std.io.bufferedReader(file.reader()); - const reader = buf_reader.reader(); - const Iter = cli.args.LineIterator(@TypeOf(reader)); - var iter: Iter = .{ .r = reader, .filepath = path }; + var buf: [2048]u8 = undefined; + var file_reader = file.reader(&buf); + const reader = &file_reader.interface; + var iter: cli.args.LineIterator = .{ .r = reader, .filepath = path }; try self.loadIter(alloc_gpa, &iter); try self.expandPaths(std.fs.path.dirname(path).?); } @@ -3944,10 +3949,10 @@ fn loadTheme(self: *Config, theme: Theme) !void { errdefer new_config.deinit(); // Load our theme - var buf_reader = std.io.bufferedReader(file.reader()); - const reader = buf_reader.reader(); - const Iter = cli.args.LineIterator(@TypeOf(reader)); - var iter: Iter = .{ .r = reader, .filepath = path }; + var buf: [2048]u8 = undefined; + var file_reader = file.reader(&buf); + const reader = &file_reader.interface; + var iter: cli.args.LineIterator = .{ .r = reader, .filepath = path }; try new_config.loadIter(alloc_gpa, &iter); // Setup our replay to be conditional. @@ -4190,7 +4195,7 @@ pub fn finalize(self: *Config) !void { if (self.@"quit-after-last-window-closed-delay") |duration| { if (duration.duration < 5 * std.time.ns_per_s) { log.warn( - "quit-after-last-window-closed-delay is set to a very short value ({}), which might cause problems", + "quit-after-last-window-closed-delay is set to a very short value ({f}), which might cause problems", .{duration}, ); } @@ -4221,22 +4226,23 @@ pub fn parseManuallyHook( // Build up the command. We don't clean this up because we take // ownership in our allocator. - var command: std.ArrayList([:0]const u8) = .init(alloc); - errdefer command.deinit(); + var command: std.ArrayList([:0]const u8) = .empty; + errdefer command.deinit(alloc); while (iter.next()) |param| { const copy = try alloc.dupeZ(u8, param); try self._replay_steps.append(alloc, .{ .arg = copy }); - try command.append(copy); + try command.append(alloc, copy); } if (command.items.len == 0) { try self._diagnostics.append(alloc, .{ .location = try cli.Location.fromIter(iter, alloc), - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( alloc, "missing command after {s}", .{arg}, + 0, ), }); @@ -4371,10 +4377,11 @@ pub fn addDiagnosticFmt( ) Allocator.Error!void { const alloc = self._arena.?.allocator(); try self._diagnostics.append(alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( alloc, fmt, args, + 0, ), }); } @@ -4892,7 +4899,7 @@ pub const Color = struct { } /// Used by Formatter - pub fn formatEntry(self: Color, formatter: anytype) !void { + pub fn formatEntry(self: Color, formatter: formatterpkg.EntryFormatter) !void { var buf: [128]u8 = undefined; try formatter.formatEntry( []const u8, @@ -4959,12 +4966,12 @@ pub const Color = struct { test "formatConfig" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var color: Color = .{ .r = 10, .g = 11, .b = 12 }; - try color.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = #0a0b0c\n", buf.items); + try color.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = #0a0b0c\n", buf.written()); } test "parseCLI with whitespace" { @@ -4995,7 +5002,7 @@ pub const TerminalColor = union(enum) { } /// Used by Formatter - pub fn formatEntry(self: TerminalColor, formatter: anytype) !void { + pub fn formatEntry(self: TerminalColor, formatter: formatterpkg.EntryFormatter) !void { switch (self) { .color => try self.color.formatEntry(formatter), @@ -5030,12 +5037,12 @@ pub const TerminalColor = union(enum) { test "formatConfig" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var sc: TerminalColor = .@"cell-foreground"; - try sc.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try testing.expectEqualSlices(u8, "a = cell-foreground\n", buf.items); + try sc.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try testing.expectEqualSlices(u8, "a = cell-foreground\n", buf.written()); } }; @@ -5051,7 +5058,7 @@ pub const BoldColor = union(enum) { } /// Used by Formatter - pub fn formatEntry(self: BoldColor, formatter: anytype) !void { + pub fn formatEntry(self: BoldColor, formatter: formatterpkg.EntryFormatter) !void { switch (self) { .color => try self.color.formatEntry(formatter), .bright => try formatter.formatEntry( @@ -5082,12 +5089,12 @@ pub const BoldColor = union(enum) { test "formatConfig" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var sc: BoldColor = .bright; - try sc.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try testing.expectEqualSlices(u8, "a = bright\n", buf.items); + try sc.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try testing.expectEqualSlices(u8, "a = bright\n", buf.written()); } }; @@ -5174,8 +5181,7 @@ pub const ColorList = struct { // Build up the value of our config. Our buffer size should be // sized to contain all possible maximum values. var buf: [1024]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - var writer = fbs.writer(); + var writer: std.Io.Writer = .fixed(&buf); for (self.colors.items, 0..) |color, i| { var color_buf: [128]u8 = undefined; const color_str = try color.formatBuf(&color_buf); @@ -5185,7 +5191,7 @@ pub const ColorList = struct { try formatter.formatEntry( []const u8, - fbs.getWritten(), + writer.buffered(), ); } @@ -5214,7 +5220,7 @@ pub const ColorList = struct { test "format" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -5223,8 +5229,8 @@ pub const ColorList = struct { var p: Self = .{}; try p.parseCLI(alloc, "black,white"); - try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = #000000,#ffffff\n", buf.items); + try p.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = #000000,#ffffff\n", buf.written()); } }; @@ -5285,7 +5291,7 @@ pub const Palette = struct { } /// Used by Formatter - pub fn formatEntry(self: Self, formatter: anytype) !void { + pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void { var buf: [128]u8 = undefined; for (0.., self.value) |k, v| { try formatter.formatEntry( @@ -5340,12 +5346,12 @@ pub const Palette = struct { test "formatConfig" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var list: Self = .{}; - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = 0=#1d1f21\n", buf.items[0..14]); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = 0=#1d1f21\n", buf.written()[0..14]); } test "parseCLI with whitespace" { @@ -5439,7 +5445,7 @@ pub const RepeatableString = struct { } /// Used by Formatter - pub fn formatEntry(self: Self, formatter: anytype) !void { + pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void { // If no items, we want to render an empty field. if (self.list.items.len == 0) { try formatter.formatEntry(void, {}); @@ -5486,17 +5492,17 @@ pub const RepeatableString = struct { test "formatConfig empty" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var list: Self = .{}; - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = \n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = \n", buf.written()); } test "formatConfig single item" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -5505,13 +5511,13 @@ pub const RepeatableString = struct { var list: Self = .{}; try list.parseCLI(alloc, "A"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = A\n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = A\n", buf.written()); } test "formatConfig multiple items" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -5521,8 +5527,8 @@ pub const RepeatableString = struct { var list: Self = .{}; try list.parseCLI(alloc, "A"); try list.parseCLI(alloc, "B"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = A\na = B\n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = A\na = B\n", buf.written()); } }; @@ -5638,7 +5644,7 @@ pub const RepeatableFontVariation = struct { test "formatConfig single" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -5647,8 +5653,8 @@ pub const RepeatableFontVariation = struct { var list: Self = .{}; try list.parseCLI(alloc, "wght = 200"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = wght=200\n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = wght=200\n", buf.written()); } }; @@ -6449,7 +6455,7 @@ pub const Keybinds = struct { } /// Like formatEntry but has an option to include docs. - pub fn formatEntryDocs(self: Keybinds, formatter: anytype, docs: bool) !void { + pub fn formatEntryDocs(self: Keybinds, formatter: formatterpkg.EntryFormatter, docs: bool) !void { if (self.set.bindings.size == 0) { try formatter.formatEntry(void, {}); return; @@ -6478,14 +6484,14 @@ pub const Keybinds = struct { } } - var buffer_stream = std.io.fixedBufferStream(&buf); - std.fmt.format(buffer_stream.writer(), "{}", .{k}) catch return error.OutOfMemory; - try v.formatEntries(&buffer_stream, formatter); + var writer: std.Io.Writer = .fixed(&buf); + writer.print("{f}", .{k}) catch return error.OutOfMemory; + try v.formatEntries(&writer, formatter); } } /// Used by Formatter - pub fn formatEntry(self: Keybinds, formatter: anytype) !void { + pub fn formatEntry(self: Keybinds, formatter: formatterpkg.EntryFormatter) !void { try self.formatEntryDocs(formatter, false); } @@ -6502,7 +6508,7 @@ pub const Keybinds = struct { test "formatConfig single" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -6511,14 +6517,14 @@ pub const Keybinds = struct { var list: Keybinds = .{}; try list.parseCLI(alloc, "shift+a=csi:hello"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = shift+a=csi:hello\n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = shift+a=csi:hello\n", buf.written()); } // Regression test for https://github.com/ghostty-org/ghostty/issues/2734 test "formatConfig multiple items" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -6528,7 +6534,7 @@ pub const Keybinds = struct { var list: Keybinds = .{}; try list.parseCLI(alloc, "ctrl+z>1=goto_tab:1"); try list.parseCLI(alloc, "ctrl+z>2=goto_tab:2"); - try list.formatEntry(formatterpkg.entryFormatter("keybind", buf.writer())); + try list.formatEntry(formatterpkg.entryFormatter("keybind", &buf.writer)); // Note they turn into translated keys because they match // their ASCII mapping. @@ -6537,12 +6543,12 @@ pub const Keybinds = struct { \\keybind = ctrl+z>1=goto_tab:1 \\ ; - try std.testing.expectEqualStrings(want, buf.items); + try std.testing.expectEqualStrings(want, buf.written()); } test "formatConfig multiple items nested" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -6554,7 +6560,7 @@ pub const Keybinds = struct { try list.parseCLI(alloc, "ctrl+a>ctrl+b>w=close_window"); try list.parseCLI(alloc, "ctrl+a>ctrl+c>t=new_tab"); try list.parseCLI(alloc, "ctrl+b>ctrl+d>a=previous_tab"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); // NB: This does not currently retain the order of the keybinds. const want = @@ -6564,7 +6570,7 @@ pub const Keybinds = struct { \\a = ctrl+b>ctrl+d>a=previous_tab \\ ; - try std.testing.expectEqualStrings(want, buf.items); + try std.testing.expectEqualStrings(want, buf.written()); } }; @@ -6790,7 +6796,7 @@ pub const RepeatableCodepointMap = struct { test "formatConfig single" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -6799,13 +6805,13 @@ pub const RepeatableCodepointMap = struct { var list: Self = .{}; try list.parseCLI(alloc, "U+ABCD=Comic Sans"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = U+ABCD=Comic Sans\n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = U+ABCD=Comic Sans\n", buf.written()); } test "formatConfig range" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -6814,13 +6820,13 @@ pub const RepeatableCodepointMap = struct { var list: Self = .{}; try list.parseCLI(alloc, "U+0001 - U+0005=Verdana"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = U+0001-U+0005=Verdana\n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = U+0001-U+0005=Verdana\n", buf.written()); } test "formatConfig multiple" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -6829,12 +6835,12 @@ pub const RepeatableCodepointMap = struct { var list: Self = .{}; try list.parseCLI(alloc, "U+0006-U+0009, U+ABCD=Courier"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); try std.testing.expectEqualSlices(u8, \\a = U+0006-U+0009=Courier \\a = U+ABCD=Courier \\ - , buf.items); + , buf.written()); } }; @@ -6886,7 +6892,7 @@ pub const FontStyle = union(enum) { } /// Used by Formatter - pub fn formatEntry(self: Self, formatter: anytype) !void { + pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void { switch (self) { .default, .false => try formatter.formatEntry( []const u8, @@ -6918,7 +6924,7 @@ pub const FontStyle = union(enum) { test "formatConfig default" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -6927,13 +6933,13 @@ pub const FontStyle = union(enum) { var p: Self = .{ .default = {} }; try p.parseCLI(alloc, "default"); - try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = default\n", buf.items); + try p.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = default\n", buf.written()); } test "formatConfig false" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -6942,13 +6948,13 @@ pub const FontStyle = union(enum) { var p: Self = .{ .default = {} }; try p.parseCLI(alloc, "false"); - try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = false\n", buf.items); + try p.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = false\n", buf.written()); } test "formatConfig named" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -6957,8 +6963,8 @@ pub const FontStyle = union(enum) { var p: Self = .{ .default = {} }; try p.parseCLI(alloc, "bold"); - try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = bold\n", buf.items); + try p.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = bold\n", buf.written()); } }; @@ -7018,7 +7024,7 @@ pub const RepeatableLink = struct { } /// Used by Formatter - pub fn formatEntry(self: Self, formatter: anytype) !void { + pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void { // This currently can't be set so we don't format anything. _ = self; _ = formatter; @@ -7128,7 +7134,10 @@ pub const RepeatableCommand = struct { } /// Used by Formatter - pub fn formatEntry(self: RepeatableCommand, formatter: anytype) !void { + pub fn formatEntry( + self: RepeatableCommand, + formatter: formatterpkg.EntryFormatter, + ) !void { if (self.value.items.len == 0) { try formatter.formatEntry(void, {}); return; @@ -7136,22 +7145,23 @@ pub const RepeatableCommand = struct { for (self.value.items) |item| { var buf: [4096]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - var writer = fbs.writer(); + var writer: std.Io.Writer = .fixed(&buf); - writer.writeAll("title:\"") catch return error.OutOfMemory; - std.zig.stringEscape(item.title, "", .{}, writer) catch return error.OutOfMemory; - writer.writeAll("\"") catch return error.OutOfMemory; + writer.print( + "title:\"{f}\"", + .{std.zig.fmtString(item.title)}, + ) catch return error.OutOfMemory; if (item.description.len > 0) { - writer.writeAll(",description:\"") catch return error.OutOfMemory; - std.zig.stringEscape(item.description, "", .{}, writer) catch return error.OutOfMemory; - writer.writeAll("\"") catch return error.OutOfMemory; + writer.print( + ",description:\"{f}\"", + .{std.zig.fmtString(item.description)}, + ) catch return error.OutOfMemory; } - writer.print(",action:\"{}\"", .{item.action}) catch return error.OutOfMemory; + writer.print(",action:\"{f}\"", .{item.action}) catch return error.OutOfMemory; - try formatter.formatEntry([]const u8, fbs.getWritten()); + try formatter.formatEntry([]const u8, writer.buffered()); } } @@ -7197,17 +7207,17 @@ pub const RepeatableCommand = struct { test "RepeatableCommand formatConfig empty" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var list: RepeatableCommand = .{}; - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = \n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = \n", buf.written()); } test "RepeatableCommand formatConfig single item" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -7216,13 +7226,13 @@ pub const RepeatableCommand = struct { var list: RepeatableCommand = .{}; try list.parseCLI(alloc, "title:Bobr, action:text:Bober"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = title:\"Bobr\",action:\"text:Bober\"\n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = title:\"Bobr\",action:\"text:Bober\"\n", buf.written()); } test "RepeatableCommand formatConfig multiple items" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -7232,14 +7242,12 @@ pub const RepeatableCommand = struct { var list: RepeatableCommand = .{}; try list.parseCLI(alloc, "title:Bobr, action:text:kurwa"); try list.parseCLI(alloc, "title:Ja, description: pierdole, action:text:jakie bydle"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = title:\"Bobr\",action:\"text:kurwa\"\na = title:\"Ja\",description:\"pierdole\",action:\"text:jakie bydle\"\n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = title:\"Bobr\",action:\"text:kurwa\"\na = title:\"Ja\",description:\"pierdole\",action:\"text:jakie bydle\"\n", buf.written()); } test "RepeatableCommand parseCLI commas" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); - defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); defer arena.deinit(); @@ -7455,14 +7463,14 @@ pub const MouseScrollMultiplier = struct { } /// Used by Formatter - pub fn formatEntry(self: Self, formatter: anytype) !void { - var buf: [32]u8 = undefined; - const formatted = std.fmt.bufPrint( - &buf, + pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void { + var buf: [4096]u8 = undefined; + var writer: std.Io.Writer = .fixed(&buf); + writer.print( "precision:{d},discrete:{d}", .{ self.precision, self.discrete }, ) catch return error.OutOfMemory; - try formatter.formatEntry([]const u8, formatted); + try formatter.formatEntry([]const u8, writer.buffered()); } test "parse" { @@ -7505,12 +7513,12 @@ pub const MouseScrollMultiplier = struct { test "format entry MouseScrollMultiplier" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var args: Self = .{ .precision = 1.5, .discrete = 2.5 }; - try args.formatEntry(formatterpkg.entryFormatter("mouse-scroll-multiplier", buf.writer())); - try testing.expectEqualSlices(u8, "mouse-scroll-multiplier = precision:1.5,discrete:2.5\n", buf.items); + try args.formatEntry(formatterpkg.entryFormatter("mouse-scroll-multiplier", &buf.writer)); + try testing.expectEqualSlices(u8, "mouse-scroll-multiplier = precision:1.5,discrete:2.5\n", buf.written()); } }; @@ -7627,7 +7635,7 @@ pub const QuickTerminalSize = struct { return error.MissingUnit; } - fn format(self: Size, writer: anytype) !void { + fn format(self: Size, writer: *std.Io.Writer) !void { switch (self) { .percentage => |v| try writer.print("{d}%", .{v}), .pixels => |v| try writer.print("{}px", .{v}), @@ -7745,20 +7753,19 @@ pub const QuickTerminalSize = struct { }; } - pub fn formatEntry(self: QuickTerminalSize, formatter: anytype) !void { + pub fn formatEntry(self: QuickTerminalSize, formatter: formatterpkg.EntryFormatter) !void { const primary = self.primary orelse return; var buf: [4096]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - const writer = fbs.writer(); + var writer: std.Io.Writer = .fixed(&buf); - primary.format(writer) catch return error.OutOfMemory; + primary.format(&writer) catch return error.OutOfMemory; if (self.secondary) |secondary| { writer.writeByte(',') catch return error.OutOfMemory; - secondary.format(writer) catch return error.OutOfMemory; + secondary.format(&writer) catch return error.OutOfMemory; } - try formatter.formatEntry([]const u8, fbs.getWritten()); + try formatter.formatEntry([]const u8, writer.buffered()); } test "parse QuickTerminalSize" { @@ -8318,15 +8325,17 @@ pub const Duration = struct { return if (value) |v| .{ .duration = v } else error.ValueRequired; } - pub fn formatEntry(self: Duration, formatter: anytype) !void { + pub fn formatEntry(self: Duration, formatter: formatterpkg.EntryFormatter) !void { var buf: [64]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - const writer = fbs.writer(); - try self.format("", .{}, writer); - try formatter.formatEntry([]const u8, fbs.getWritten()); + var writer: std.Io.Writer = .fixed(&buf); + try self.format(&writer); + try formatter.formatEntry([]const u8, writer.buffered()); } - pub fn format(self: Duration, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + pub fn format( + self: Duration, + writer: *std.Io.Writer, + ) !void { var value = self.duration; var i: usize = 0; for (units) |unit| { @@ -8393,7 +8402,7 @@ pub const WindowPadding = struct { } } - pub fn formatEntry(self: Self, formatter: anytype) !void { + pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void { var buf: [128]u8 = undefined; if (self.top_left == self.bottom_right) { try formatter.formatEntry( @@ -8555,7 +8564,7 @@ test "test format" { inline for (Duration.units) |unit| { const d: Duration = .{ .duration = unit.factor }; var actual_buf: [16]u8 = undefined; - const actual = try std.fmt.bufPrint(&actual_buf, "{}", .{d}); + const actual = try std.fmt.bufPrint(&actual_buf, "{f}", .{d}); var expected_buf: [16]u8 = undefined; const expected = if (!std.mem.eql(u8, unit.name, "us")) try std.fmt.bufPrint(&expected_buf, "1{s}", .{unit.name}) @@ -8566,12 +8575,12 @@ test "test format" { } test "test entryFormatter" { - var buf = std.ArrayList(u8).init(std.testing.allocator); + var buf: std.Io.Writer.Allocating = .init(std.testing.allocator); defer buf.deinit(); var p: Duration = .{ .duration = std.math.maxInt(u64) }; - try p.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualStrings("a = 584y 49w 23h 34m 33s 709ms 551µs 615ns\n", buf.items); + try p.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualStrings("a = 584y 49w 23h 34m 33s 709ms 551µs 615ns\n", buf.written()); } const TestIterator = struct { @@ -8681,15 +8690,20 @@ test "clone can then change conditional state" { // Setup our test theme var td = try internal_os.TempDir.init(); defer td.deinit(); + var buf: [4096]u8 = undefined; { var file = try td.dir.createFile("theme_light", .{}); defer file.close(); - try file.writer().writeAll(@embedFile("testdata/theme_light")); + var writer = file.writer(&buf); + try writer.interface.writeAll(@embedFile("testdata/theme_light")); + try writer.end(); } { var file = try td.dir.createFile("theme_dark", .{}); defer file.close(); - try file.writer().writeAll(@embedFile("testdata/theme_dark")); + var writer = file.writer(&buf); + try writer.interface.writeAll(@embedFile("testdata/theme_dark")); + try writer.end(); } var light_buf: [std.fs.max_path_bytes]u8 = undefined; const light = try td.dir.realpath("theme_light", &light_buf); @@ -8815,10 +8829,13 @@ test "theme loading" { // Setup our test theme var td = try internal_os.TempDir.init(); defer td.deinit(); + var buf: [4096]u8 = undefined; { var file = try td.dir.createFile("theme", .{}); defer file.close(); - try file.writer().writeAll(@embedFile("testdata/theme_simple")); + var writer = file.writer(&buf); + try writer.interface.writeAll(@embedFile("testdata/theme_simple")); + try writer.end(); } var path_buf: [std.fs.max_path_bytes]u8 = undefined; const path = try td.dir.realpath("theme", &path_buf); @@ -8851,10 +8868,13 @@ test "theme loading preserves conditional state" { // Setup our test theme var td = try internal_os.TempDir.init(); defer td.deinit(); + var buf: [4096]u8 = undefined; { var file = try td.dir.createFile("theme", .{}); defer file.close(); - try file.writer().writeAll(@embedFile("testdata/theme_simple")); + var writer = file.writer(&buf); + try writer.interface.writeAll(@embedFile("testdata/theme_simple")); + try writer.end(); } var path_buf: [std.fs.max_path_bytes]u8 = undefined; const path = try td.dir.realpath("theme", &path_buf); @@ -8881,10 +8901,13 @@ test "theme priority is lower than config" { // Setup our test theme var td = try internal_os.TempDir.init(); defer td.deinit(); + var buf: [4096]u8 = undefined; { var file = try td.dir.createFile("theme", .{}); defer file.close(); - try file.writer().writeAll(@embedFile("testdata/theme_simple")); + var writer = file.writer(&buf); + try writer.interface.writeAll(@embedFile("testdata/theme_simple")); + try writer.end(); } var path_buf: [std.fs.max_path_bytes]u8 = undefined; const path = try td.dir.realpath("theme", &path_buf); @@ -8915,15 +8938,20 @@ test "theme loading correct light/dark" { // Setup our test theme var td = try internal_os.TempDir.init(); defer td.deinit(); + var buf: [4096]u8 = undefined; { var file = try td.dir.createFile("theme_light", .{}); defer file.close(); - try file.writer().writeAll(@embedFile("testdata/theme_light")); + var writer = file.writer(&buf); + try writer.interface.writeAll(@embedFile("testdata/theme_light")); + try writer.end(); } { var file = try td.dir.createFile("theme_dark", .{}); defer file.close(); - try file.writer().writeAll(@embedFile("testdata/theme_dark")); + var writer = file.writer(&buf); + try writer.interface.writeAll(@embedFile("testdata/theme_dark")); + try writer.end(); } var light_buf: [std.fs.max_path_bytes]u8 = undefined; const light = try td.dir.realpath("theme_light", &light_buf); diff --git a/src/config/RepeatableStringMap.zig b/src/config/RepeatableStringMap.zig index 6f143e95d..d5e634333 100644 --- a/src/config/RepeatableStringMap.zig +++ b/src/config/RepeatableStringMap.zig @@ -104,7 +104,7 @@ pub fn equal(self: RepeatableStringMap, other: RepeatableStringMap) bool { } /// Used by formatter -pub fn formatEntry(self: RepeatableStringMap, formatter: anytype) !void { +pub fn formatEntry(self: RepeatableStringMap, formatter: formatterpkg.EntryFormatter) !void { // If no items, we want to render an empty field. if (self.map.count() == 0) { try formatter.formatEntry(void, {}); @@ -146,12 +146,12 @@ test "RepeatableStringMap: parseCLI" { test "RepeatableStringMap: formatConfig empty" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var list: RepeatableStringMap = .{}; - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = \n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = \n", buf.written()); } test "RepeatableStringMap: formatConfig single item" { @@ -162,20 +162,20 @@ test "RepeatableStringMap: formatConfig single item" { const alloc = arena.allocator(); { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var map: RepeatableStringMap = .{}; try map.parseCLI(alloc, "A=B"); - try map.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = A=B\n", buf.items); + try map.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = A=B\n", buf.written()); } { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var map: RepeatableStringMap = .{}; try map.parseCLI(alloc, " A = B "); - try map.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = A=B\n", buf.items); + try map.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = A=B\n", buf.written()); } } @@ -187,12 +187,12 @@ test "RepeatableStringMap: formatConfig multiple items" { const alloc = arena.allocator(); { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var list: RepeatableStringMap = .{}; try list.parseCLI(alloc, "A=B"); try list.parseCLI(alloc, "B = C"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = A=B\na = B=C\n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = A=B\na = B=C\n", buf.written()); } } diff --git a/src/config/command.zig b/src/config/command.zig index 9efeb199e..e0cdc641b 100644 --- a/src/config/command.zig +++ b/src/config/command.zig @@ -166,21 +166,20 @@ pub const Command = union(enum) { }; } - pub fn formatEntry(self: Self, formatter: anytype) !void { + pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void { switch (self) { .shell => |v| try formatter.formatEntry([]const u8, v), .direct => |v| { var buf: [4096]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - const writer = fbs.writer(); + var writer: std.Io.Writer = .fixed(&buf); writer.writeAll("direct:") catch return error.OutOfMemory; for (v) |arg| { writer.writeAll(arg) catch return error.OutOfMemory; writer.writeByte(' ') catch return error.OutOfMemory; } - const written = fbs.getWritten(); + const written = writer.buffered(); try formatter.formatEntry( []const u8, written[0..@intCast(written.len - 1)], @@ -292,13 +291,13 @@ pub const Command = union(enum) { defer arena.deinit(); const alloc = arena.allocator(); - var buf = std.ArrayList(u8).init(alloc); + var buf: std.Io.Writer.Allocating = .init(alloc); defer buf.deinit(); var v: Self = undefined; try v.parseCLI(alloc, "echo hello"); - try v.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = echo hello\n", buf.items); + try v.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = echo hello\n", buf.written()); } test "Command: formatConfig direct" { @@ -307,13 +306,13 @@ pub const Command = union(enum) { defer arena.deinit(); const alloc = arena.allocator(); - var buf = std.ArrayList(u8).init(alloc); + var buf: std.Io.Writer.Allocating = .init(alloc); defer buf.deinit(); var v: Self = undefined; try v.parseCLI(alloc, "direct: echo hello"); - try v.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = direct:echo hello\n", buf.items); + try v.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = direct:echo hello\n", buf.written()); } }; diff --git a/src/config/edit.zig b/src/config/edit.zig index 38dc98169..07bb7ee5a 100644 --- a/src/config/edit.zig +++ b/src/config/edit.zig @@ -89,8 +89,8 @@ fn configPath(alloc_arena: Allocator) ![]const u8 { /// Returns a const list of possible paths the main config file could be /// in for the current OS. fn configPathCandidates(alloc_arena: Allocator) ![]const []const u8 { - var paths = try std.ArrayList([]const u8).initCapacity(alloc_arena, 2); - errdefer paths.deinit(); + var paths: std.ArrayList([]const u8) = try .initCapacity(alloc_arena, 2); + errdefer paths.deinit(alloc_arena); if (comptime builtin.os.tag == .macos) { paths.appendAssumeCapacity(try internal_os.macos.appSupportDir( diff --git a/src/config/formatter.zig b/src/config/formatter.zig index a42395c19..dcf99167d 100644 --- a/src/config/formatter.zig +++ b/src/config/formatter.zig @@ -8,38 +8,36 @@ const Key = @import("key.zig").Key; /// Returns a single entry formatter for the given field name and writer. pub fn entryFormatter( name: []const u8, - writer: anytype, -) EntryFormatter(@TypeOf(writer)) { + writer: *std.Io.Writer, +) EntryFormatter { return .{ .name = name, .writer = writer }; } /// The entry formatter type for a given writer. -pub fn EntryFormatter(comptime WriterType: type) type { - return struct { - name: []const u8, - writer: WriterType, +pub const EntryFormatter = struct { + name: []const u8, + writer: *std.Io.Writer, - pub fn formatEntry( - self: @This(), - comptime T: type, - value: T, - ) !void { - return formatter.formatEntry( - T, - self.name, - value, - self.writer, - ); - } - }; -} + pub fn formatEntry( + self: @This(), + comptime T: type, + value: T, + ) !void { + return formatter.formatEntry( + T, + self.name, + value, + self.writer, + ); + } +}; /// Format a single type with the given name and value. pub fn formatEntry( comptime T: type, name: []const u8, value: T, - writer: anytype, + writer: *std.Io.Writer, ) !void { switch (@typeInfo(T)) { .bool, .int => { @@ -53,7 +51,7 @@ pub fn formatEntry( }, .@"enum" => { - try writer.print("{s} = {s}\n", .{ name, @tagName(value) }); + try writer.print("{s} = {t}\n", .{ name, value }); return; }, @@ -143,19 +141,14 @@ pub const FileFormatter = struct { /// Implements std.fmt so it can be used directly with std.fmt. pub fn format( self: FileFormatter, - comptime layout: []const u8, - opts: std.fmt.FormatOptions, - writer: anytype, - ) !void { + writer: *std.Io.Writer, + ) std.Io.Writer.Error!void { @setEvalBranchQuota(10_000); - _ = layout; - _ = opts; - // If we're change-tracking then we need the default config to // compare against. var default: ?Config = if (self.changed) - try .default(self.alloc) + Config.default(self.alloc) catch return error.WriteFailed else null; defer if (default) |*v| v.deinit(); @@ -179,12 +172,12 @@ pub const FileFormatter = struct { } } - try formatEntry( + formatEntry( field.type, field.name, value, writer, - ); + ) catch return error.WriteFailed; if (do_docs) try writer.print("\n", .{}); } @@ -198,7 +191,7 @@ test "format default config" { var cfg = try Config.default(alloc); defer cfg.deinit(); - var buf = std.ArrayList(u8).init(alloc); + var buf: std.Io.Writer.Allocating = .init(alloc); defer buf.deinit(); // We just make sure this works without errors. We aren't asserting output. @@ -206,9 +199,9 @@ test "format default config" { .alloc = alloc, .config = &cfg, }; - try std.fmt.format(buf.writer(), "{}", .{fmt}); + try fmt.format(&buf.writer); - //std.log.warn("{s}", .{buf.items}); + //std.log.warn("{s}", .{buf.written()}); } test "format default config changed" { @@ -218,7 +211,7 @@ test "format default config changed" { defer cfg.deinit(); cfg.@"font-size" = 42; - var buf = std.ArrayList(u8).init(alloc); + var buf: std.Io.Writer.Allocating = .init(alloc); defer buf.deinit(); // We just make sure this works without errors. We aren't asserting output. @@ -227,26 +220,26 @@ test "format default config changed" { .config = &cfg, .changed = true, }; - try std.fmt.format(buf.writer(), "{}", .{fmt}); + try fmt.format(&buf.writer); - //std.log.warn("{s}", .{buf.items}); + //std.log.warn("{s}", .{buf.written()}); } test "formatEntry bool" { const testing = std.testing; { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); - try formatEntry(bool, "a", true, buf.writer()); - try testing.expectEqualStrings("a = true\n", buf.items); + try formatEntry(bool, "a", true, &buf.writer); + try testing.expectEqualStrings("a = true\n", buf.written()); } { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); - try formatEntry(bool, "a", false, buf.writer()); - try testing.expectEqualStrings("a = false\n", buf.items); + try formatEntry(bool, "a", false, &buf.writer); + try testing.expectEqualStrings("a = false\n", buf.written()); } } @@ -254,10 +247,10 @@ test "formatEntry int" { const testing = std.testing; { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); - try formatEntry(u8, "a", 123, buf.writer()); - try testing.expectEqualStrings("a = 123\n", buf.items); + try formatEntry(u8, "a", 123, &buf.writer); + try testing.expectEqualStrings("a = 123\n", buf.written()); } } @@ -265,10 +258,10 @@ test "formatEntry float" { const testing = std.testing; { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); - try formatEntry(f64, "a", 0.7, buf.writer()); - try testing.expectEqualStrings("a = 0.7\n", buf.items); + try formatEntry(f64, "a", 0.7, &buf.writer); + try testing.expectEqualStrings("a = 0.7\n", buf.written()); } } @@ -277,10 +270,10 @@ test "formatEntry enum" { const Enum = enum { one, two, three }; { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); - try formatEntry(Enum, "a", .two, buf.writer()); - try testing.expectEqualStrings("a = two\n", buf.items); + try formatEntry(Enum, "a", .two, &buf.writer); + try testing.expectEqualStrings("a = two\n", buf.written()); } } @@ -288,10 +281,10 @@ test "formatEntry void" { const testing = std.testing; { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); - try formatEntry(void, "a", {}, buf.writer()); - try testing.expectEqualStrings("a = \n", buf.items); + try formatEntry(void, "a", {}, &buf.writer); + try testing.expectEqualStrings("a = \n", buf.written()); } } @@ -299,17 +292,17 @@ test "formatEntry optional" { const testing = std.testing; { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); - try formatEntry(?bool, "a", null, buf.writer()); - try testing.expectEqualStrings("a = \n", buf.items); + try formatEntry(?bool, "a", null, &buf.writer); + try testing.expectEqualStrings("a = \n", buf.written()); } { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); - try formatEntry(?bool, "a", false, buf.writer()); - try testing.expectEqualStrings("a = false\n", buf.items); + try formatEntry(?bool, "a", false, &buf.writer); + try testing.expectEqualStrings("a = false\n", buf.written()); } } @@ -317,10 +310,10 @@ test "formatEntry string" { const testing = std.testing; { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); - try formatEntry([]const u8, "a", "hello", buf.writer()); - try testing.expectEqualStrings("a = hello\n", buf.items); + try formatEntry([]const u8, "a", "hello", &buf.writer); + try testing.expectEqualStrings("a = hello\n", buf.written()); } } @@ -332,9 +325,9 @@ test "formatEntry packed struct" { }; { - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); - try formatEntry(Value, "a", .{}, buf.writer()); - try testing.expectEqualStrings("a = one,no-two\n", buf.items); + try formatEntry(Value, "a", .{}, &buf.writer); + try testing.expectEqualStrings("a = one,no-two\n", buf.written()); } } diff --git a/src/config/io.zig b/src/config/io.zig index 8be4be551..9d9a127e8 100644 --- a/src/config/io.zig +++ b/src/config/io.zig @@ -94,10 +94,9 @@ pub const ReadableIO = union(enum) { }; } - pub fn formatEntry(self: Self, formatter: anytype) !void { + pub fn formatEntry(self: Self, formatter: formatterpkg.EntryFormatter) !void { var buf: [4096]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - const writer = fbs.writer(); + var writer: std.Io.Writer = .fixed(&buf); switch (self) { inline else => |v, tag| { writer.writeAll(@tagName(tag)) catch return error.OutOfMemory; @@ -106,10 +105,9 @@ pub const ReadableIO = union(enum) { }, } - const written = fbs.getWritten(); try formatter.formatEntry( []const u8, - written, + writer.buffered(), ); } @@ -144,13 +142,13 @@ pub const ReadableIO = union(enum) { defer arena.deinit(); const alloc = arena.allocator(); - var buf = std.ArrayList(u8).init(alloc); + var buf: std.Io.Writer.Allocating = .init(alloc); defer buf.deinit(); var v: Self = undefined; try v.parseCLI(alloc, "raw:foo"); - try v.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = raw:foo\n", buf.items); + try v.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = raw:foo\n", buf.written()); } }; @@ -222,7 +220,7 @@ pub const RepeatableReadableIO = struct { /// Used by Formatter pub fn formatEntry( self: Self, - formatter: anytype, + formatter: formatterpkg.EntryFormatter, ) !void { if (self.list.items.len == 0) { try formatter.formatEntry(void, {}); diff --git a/src/config/path.zig b/src/config/path.zig index 651dbdb3a..aeba69b94 100644 --- a/src/config/path.zig +++ b/src/config/path.zig @@ -79,7 +79,7 @@ pub const Path = union(enum) { } /// Used by formatter. - pub fn formatEntry(self: *const Path, formatter: anytype) !void { + pub fn formatEntry(self: *const Path, formatter: formatterpkg.EntryFormatter) !void { var buf: [std.fs.max_path_bytes + 1]u8 = undefined; const value = switch (self.*) { .optional => |path| std.fmt.bufPrint( @@ -154,10 +154,11 @@ pub const Path = union(enum) { &buf, ) catch |err| { try diags.append(arena_alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "error expanding home directory for path {s}: {}", .{ path, err }, + 0, ), }); @@ -194,10 +195,11 @@ pub const Path = union(enum) { } try diags.append(arena_alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "error resolving file path {s}: {}", .{ path, err }, + 0, ), }); @@ -306,7 +308,7 @@ pub const Path = union(enum) { test "formatConfig single item" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -315,13 +317,13 @@ pub const Path = union(enum) { var item: Path = undefined; try item.parseCLI(alloc, "A"); - try item.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = A\n", buf.items); + try item.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = A\n", buf.written()); } test "formatConfig multiple items" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -331,8 +333,8 @@ pub const Path = union(enum) { var item: Path = undefined; try item.parseCLI(alloc, "A"); try item.parseCLI(alloc, "?B"); - try item.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = ?B\n", buf.items); + try item.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = ?B\n", buf.written()); } }; @@ -382,7 +384,7 @@ pub const RepeatablePath = struct { } /// Used by Formatter - pub fn formatEntry(self: RepeatablePath, formatter: anytype) !void { + pub fn formatEntry(self: RepeatablePath, formatter: formatterpkg.EntryFormatter) !void { if (self.value.items.len == 0) { try formatter.formatEntry(void, {}); return; @@ -453,17 +455,17 @@ pub const RepeatablePath = struct { test "formatConfig empty" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var list: RepeatablePath = .{}; - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = \n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = \n", buf.written()); } test "formatConfig single item" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -472,13 +474,13 @@ pub const RepeatablePath = struct { var list: RepeatablePath = .{}; try list.parseCLI(alloc, "A"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = A\n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = A\n", buf.written()); } test "formatConfig multiple items" { const testing = std.testing; - var buf = std.ArrayList(u8).init(testing.allocator); + var buf: std.Io.Writer.Allocating = .init(testing.allocator); defer buf.deinit(); var arena = ArenaAllocator.init(testing.allocator); @@ -488,7 +490,7 @@ pub const RepeatablePath = struct { var list: RepeatablePath = .{}; try list.parseCLI(alloc, "A"); try list.parseCLI(alloc, "?B"); - try list.formatEntry(formatterpkg.entryFormatter("a", buf.writer())); - try std.testing.expectEqualSlices(u8, "a = A\na = ?B\n", buf.items); + try list.formatEntry(formatterpkg.entryFormatter("a", &buf.writer)); + try std.testing.expectEqualSlices(u8, "a = A\na = ?B\n", buf.written()); } }; diff --git a/src/config/theme.zig b/src/config/theme.zig index 8fa7c93dc..b1188a5c4 100644 --- a/src/config/theme.zig +++ b/src/config/theme.zig @@ -125,10 +125,11 @@ pub fn open( ) orelse return null; const stat = file.stat() catch |err| { try diags.append(arena_alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "not reading theme from \"{s}\": {}", .{ theme, err }, + 0, ), }); return null; @@ -137,10 +138,11 @@ pub fn open( .file => {}, else => { try diags.append(arena_alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "not reading theme from \"{s}\": it is a {s}", .{ theme, @tagName(stat.kind) }, + 0, ), }); return null; @@ -152,10 +154,11 @@ pub fn open( const basename = std.fs.path.basename(theme); if (!std.mem.eql(u8, theme, basename)) { try diags.append(arena_alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "theme \"{s}\" cannot include path separators unless it is an absolute path", .{theme}, + 0, ), }); return null; @@ -170,10 +173,11 @@ pub fn open( if (cwd.openFile(path, .{})) |file| { const stat = file.stat() catch |err| { try diags.append(arena_alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "not reading theme from \"{s}\": {}", .{ theme, err }, + 0, ), }); return null; @@ -182,10 +186,11 @@ pub fn open( .file => {}, else => { try diags.append(arena_alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "not reading theme from \"{s}\": it is a {s}", .{ theme, @tagName(stat.kind) }, + 0, ), }); return null; @@ -202,10 +207,11 @@ pub fn open( // Anything else is an error we log and give up on. else => { try diags.append(arena_alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "failed to load theme \"{s}\" from the file \"{s}\": {}", .{ theme, path, err }, + 0, ), }); @@ -222,10 +228,11 @@ pub fn open( while (try it.next()) |loc| { const path = try std.fs.path.join(arena_alloc, &.{ loc.dir, theme }); try diags.append(arena_alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "theme \"{s}\" not found, tried path \"{s}\"", .{ theme, path }, + 0, ), }); } @@ -249,17 +256,19 @@ pub fn openAbsolute( return std.fs.openFileAbsolute(theme, .{}) catch |err| { switch (err) { error.FileNotFound => try diags.append(arena_alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "failed to load theme from the path \"{s}\"", .{theme}, + 0, ), }), else => try diags.append(arena_alloc, .{ - .message = try std.fmt.allocPrintZ( + .message = try std.fmt.allocPrintSentinel( arena_alloc, "failed to load theme from the path \"{s}\": {}", .{ theme, err }, + 0, ), }), } diff --git a/src/crash/sentry.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; } - - if (std.mem.eql(u8, fmt, "diagram")) { - self.formatDiagram(writer) catch - try writer.writeAll("failed to draw split tree diagram"); - } else if (std.mem.eql(u8, fmt, "text")) { - try self.formatText(writer, .root, 0); - } else if (fmt.len == 0) { - self.formatDiagram(writer) catch {}; - try self.formatText(writer, .root, 0); - } else { - return error.InvalidFormat; - } + self.formatDiagram(writer) catch {}; + try self.formatText(writer); } - fn formatText( - self: *const Self, - writer: anytype, + pub fn formatText(self: Self, writer: *std.Io.Writer) std.Io.Writer.Error!void { + if (self.nodes.len == 0) { + try writer.writeAll("empty"); + return; + } + try self.formatTextInner(writer, .root, 0); + } + + fn formatTextInner( + self: Self, + writer: *std.Io.Writer, current: Node.Handle, depth: usize, - ) !void { + ) std.Io.Writer.Error!void { for (0..depth) |_| try writer.writeAll(" "); if (self.zoomed) |zoomed| if (zoomed == current) { @@ -1075,20 +1063,25 @@ pub fn SplitTree(comptime V: type) type { try writer.print("leaf: {d}\n", .{current}), .split => |s| { - try writer.print("split (layout: {s}, ratio: {d:.2})\n", .{ - @tagName(s.layout), + try writer.print("split (layout: {t}, ratio: {d:.2})\n", .{ + s.layout, s.ratio, }); - try self.formatText(writer, s.left, depth + 1); - try self.formatText(writer, s.right, depth + 1); + try self.formatTextInner(writer, s.left, depth + 1); + try self.formatTextInner(writer, s.right, depth + 1); }, } } - fn formatDiagram( - self: *const Self, - writer: anytype, - ) !void { + pub fn formatDiagram( + self: Self, + writer: *std.Io.Writer, + ) std.Io.Writer.Error!void { + if (self.nodes.len == 0) { + try writer.writeAll("empty"); + return; + } + // Use our arena's GPA to allocate some intermediate memory. // Requiring allocation for formatting is nasty but this is really // only used for debugging and testing and shouldn't hit OOM @@ -1099,7 +1092,7 @@ pub fn SplitTree(comptime V: type) type { // Get our spatial representation. const sp = spatial: { - const sp = try self.spatial(alloc); + const sp = self.spatial(alloc) catch return error.WriteFailed; // Scale our spatial representation to have minimum width/height 1. var min_w: f16 = 1; @@ -1111,7 +1104,7 @@ pub fn SplitTree(comptime V: type) type { const ratio_w: f16 = 1 / min_w; const ratio_h: f16 = 1 / min_h; - const slots = try alloc.dupe(Spatial.Slot, sp.slots); + const slots = alloc.dupe(Spatial.Slot, sp.slots) catch return error.WriteFailed; for (slots) |*slot| { slot.x *= ratio_w; slot.y *= ratio_h; @@ -1168,9 +1161,9 @@ pub fn SplitTree(comptime V: type) type { width *= cell_width; height *= cell_height; - const rows = try alloc.alloc([]u8, height); + const rows = alloc.alloc([]u8, height) catch return error.WriteFailed; for (0..rows.len) |y| { - rows[y] = try alloc.alloc(u8, width + 1); + rows[y] = alloc.alloc(u8, width + 1) catch return error.WriteFailed; @memset(rows[y], ' '); rows[y][width] = '\n'; } @@ -1223,7 +1216,7 @@ pub fn SplitTree(comptime V: type) type { const label: []const u8 = if (@hasDecl(View, "splitTreeLabel")) node.leaf.splitTreeLabel() else - try std.fmt.bufPrint(&buf, "{d}", .{handle}); + std.fmt.bufPrint(&buf, "{d}", .{handle}) catch return error.WriteFailed; // Draw the handle in the center const x_mid = width / 2 + x; @@ -1231,7 +1224,7 @@ pub fn SplitTree(comptime V: type) type { const label_width = label.len; const label_start = x_mid - label_width / 2; const row = grid[y_mid][label_start..]; - _ = try std.fmt.bufPrint(row, "{s}", .{label}); + _ = std.fmt.bufPrint(row, "{s}", .{label}) catch return error.WriteFailed; } // Output every row @@ -1339,7 +1332,7 @@ test "SplitTree: empty tree" { var t: TestTree = .empty; defer t.deinit(); - const str = try std.fmt.allocPrint(alloc, "{}", .{t}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{t}); defer alloc.free(str); try testing.expectEqualStrings(str, \\empty @@ -1353,7 +1346,7 @@ test "SplitTree: single node" { var t: TestTree = try .init(alloc, &v); defer t.deinit(); - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{t}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(t, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---+ @@ -1383,7 +1376,7 @@ test "SplitTree: split horizontal" { defer t3.deinit(); { - const str = try std.fmt.allocPrint(alloc, "{}", .{t3}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{t3}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---++---+ @@ -1415,7 +1408,7 @@ test "SplitTree: split horizontal" { defer t4.deinit(); { - const str = try std.fmt.allocPrint(alloc, "{}", .{t4}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{t4}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+--------++---++---+ @@ -1449,7 +1442,7 @@ test "SplitTree: split horizontal" { defer t5.deinit(); { - const str = try std.fmt.allocPrint(alloc, "{}", .{t5}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{t5}); defer alloc.free(str); try testing.expectEqualStrings( \\+------------------++--------++---++---+ @@ -1547,7 +1540,7 @@ test "SplitTree: split vertical" { ); defer t3.deinit(); - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{t3}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(t3, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---+ @@ -1583,7 +1576,7 @@ test "SplitTree: split horizontal with zero ratio" { const split = splitAB; { - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---+ @@ -1617,7 +1610,7 @@ test "SplitTree: split vertical with zero ratio" { const split = splitAB; { - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---+ @@ -1651,7 +1644,7 @@ test "SplitTree: split horizontal with full width" { const split = splitAB; { - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---+ @@ -1685,7 +1678,7 @@ test "SplitTree: split vertical with full width" { const split = splitAB; { - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---+ @@ -1727,7 +1720,7 @@ test "SplitTree: remove leaf" { ); defer t4.deinit(); - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{t4}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(t4, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---+ @@ -1772,7 +1765,7 @@ test "SplitTree: split twice, remove intermediary" { defer split2.deinit(); { - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split2}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split2, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---++---+ @@ -1798,7 +1791,7 @@ test "SplitTree: split twice, remove intermediary" { defer split3.deinit(); { - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split3}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split3, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---+ @@ -1883,7 +1876,7 @@ test "SplitTree: spatial goto" { const split = splitBD; { - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---++---+ @@ -1943,7 +1936,7 @@ test "SplitTree: spatial goto" { defer equal.deinit(); { - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{equal}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(equal, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---++---+ @@ -1979,7 +1972,7 @@ test "SplitTree: resize" { defer split.deinit(); { - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{split}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+---++---+ @@ -2005,7 +1998,7 @@ test "SplitTree: resize" { 0.25, ); defer resized.deinit(); - const str = try std.fmt.allocPrint(alloc, "{diagram}", .{resized}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(resized, .formatDiagram)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\+-------------++---+ @@ -2026,7 +2019,7 @@ test "SplitTree: clone empty tree" { defer t2.deinit(); { - const str = try std.fmt.allocPrint(alloc, "{}", .{t2}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{t2}); defer alloc.free(str); try testing.expectEqualStrings(str, \\empty @@ -2064,7 +2057,7 @@ test "SplitTree: zoom" { }); { - const str = try std.fmt.allocPrint(alloc, "{text}", .{split}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatText)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\split (layout: horizontal, ratio: 0.50) @@ -2079,7 +2072,7 @@ test "SplitTree: zoom" { defer clone.deinit(); { - const str = try std.fmt.allocPrint(alloc, "{text}", .{clone}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(clone, .formatText)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\split (layout: horizontal, ratio: 0.50) @@ -2122,7 +2115,7 @@ test "SplitTree: split resets zoom" { defer split.deinit(); { - const str = try std.fmt.allocPrint(alloc, "{text}", .{split}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(split, .formatText)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\split (layout: horizontal, ratio: 0.50) @@ -2178,7 +2171,7 @@ test "SplitTree: remove and zoom" { defer removed.deinit(); try testing.expect(removed.zoomed == null); - const str = try std.fmt.allocPrint(alloc, "{text}", .{removed}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(removed, .formatText)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\leaf: B @@ -2201,7 +2194,7 @@ test "SplitTree: remove and zoom" { ); defer removed.deinit(); - const str = try std.fmt.allocPrint(alloc, "{text}", .{removed}); + const str = try std.fmt.allocPrint(alloc, "{f}", .{std.fmt.alt(removed, .formatText)}); defer alloc.free(str); try testing.expectEqualStrings(str, \\(zoomed) leaf: A diff --git a/src/extra/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, + }; + } + + fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize { + const self: *ShellEscapeWriter = @fieldParentPtr("writer", w); + + // 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); + + 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; } - - const Writer = std.io.Writer(*ShellEscapeWriter(T), error{Error}, write); - - pub fn init(child_writer: T) ShellEscapeWriter(T) { - return .{ .child_writer = child_writer }; - } - - pub fn writer(self: *ShellEscapeWriter(T)) Writer { - return .{ .context = self }; - } - }; -} + } +}; 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={}", .{