Skip to content

Commit a064321

Browse files
JacobCrabillclaude
andcommitted
Upgrade to Zig 0.16.0; Fix tree-sitter panic issues
Update all APIs and dependencies for Zig v0.16.0 Update tree-sitter to resolve panic issues: Disable address sanitizer for non-debug builds, and properly handle errors with `ts_stack_merge` to prevent runtime panics. perf: use smp_allocator instead of init.gpa When the binary links libc (due to tree-sitter), init.gpa resolves to std.heap.c_allocator rather than smp_allocator. The c_allocator is significantly slower for the many small, short-lived allocations made during markdown parsing, causing ~10-20% more CPU cycles. Explicitly using smp_allocator restores performance parity with the 0.15.x build. Also clean up the file-reading path: replace the Writer.Allocating + empty-buffer reader with a proper 4096-byte buffer + allocRemaining, which avoids the unbuffered zero-length read path. perf: cache compiled TreeSitter queries per language ts_query_new dominates render time (~97% of CPU). Previously it was called on every code block, even for the same language. Add a compiled_queries map to ts_queries that lazily compiles and caches each language's query on first use; subsequent renders retrieve the cached *Query in O(1). syntax.getHighlights() now calls ts_queries.getCompiledQuery() directly, eliminating the per-call Query.create()/destroy() pair. For documents with repeated same-language blocks this makes dev/zig-16 ~3% faster than main; for unique-language documents it avoids the overhead on any second pass through the same file. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent 8b6672a commit a064321

27 files changed

Lines changed: 475 additions & 381 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*.a
66
zig-out/
77
.zig-cache/
8+
zig-pkg/
89
.ccls-cache/
910
docs/
1011
.worktrees/

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
![CI Status](https://github.com/JacobCrabill/zigdown/actions/workflows/main.yml/badge.svg)
66

77
> [!TIP]
8-
> Zig 0.15.x Required
8+
> Zig 0.16.x Required
99
1010
- [Tools & Features](#tools-&-features)
1111
- [Command-Line Tools](#command-line-tools)
@@ -120,7 +120,7 @@ subset of all Markdown syntax, and ignoring anything I personally find useless o
120120
## Usage
121121

122122
The current version of Zig this code compiles with is
123-
[0.15.2](https://ziglang.org/download/0.15.2/zig-x86_64-linux-0.15.2.tar.xz). I highly recommend
123+
[0.16.0](https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz). I highly recommend
124124
using the [Zig version manager](https://github.com/tristanisham/zvm) to install and manage various
125125
Zig versions.
126126

build.zig

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const builtin = @import("builtin");
33
const Build = std.Build;
44
const Allocator = std.mem.Allocator;
55

6-
const ArrayList = std.array_list.Managed;
76
const Target = std.Build.ResolvedTarget;
87
const OptimizeMode = std.builtin.OptimizeMode;
98
const Options = std.Build.Step.Options;
@@ -37,6 +36,7 @@ pub fn build(b: *std.Build) !void {
3736
const optimize: OptimizeMode = b.standardOptimizeOption(.{});
3837

3938
const wasm_optimize = b.option(OptimizeMode, "wasm-optimize", "Optimization mode for WASM targets") orelse .ReleaseSmall;
39+
const no_wasm = b.option(bool, "no-wasm", "Skip building the WASM target") orelse true;
4040

4141
const build_lua = b.option(bool, "lua", "[WIP] Build Zigdown as a Lua module") orelse false;
4242
const build_test_exes = b.option(bool, "build-test-exes", "Build the custom test executables") orelse false;
@@ -62,10 +62,10 @@ pub fn build(b: *std.Build) !void {
6262
options.addOption(bool, "extra_tests", do_extra_tests);
6363

6464
// Split the comma-separated list of builtin languages to a list of languages for our config struct
65-
var ts_language_list = ArrayList([]const u8).init(b.allocator);
65+
var ts_language_list: std.ArrayList([]const u8) = .empty;
6666
var iter = std.mem.tokenizeScalar(u8, ts_parser_list, ',');
6767
while (iter.next()) |name| {
68-
try ts_language_list.append(name);
68+
try ts_language_list.append(b.allocator, name);
6969
}
7070
options.addOption([]const []const u8, builtin_ts_option, ts_language_list.items);
7171

@@ -80,13 +80,13 @@ pub fn build(b: *std.Build) !void {
8080
mod.addOptions("config", options);
8181

8282
// Get all of our dependencies, both from build.zig.zon and our TreeSitter module
83-
var deps: ArrayList(Dependency) = try getDependencies(b, target, optimize, ts_language_list.items);
83+
var deps: std.ArrayList(Dependency) = try getDependencies(b, target, optimize, ts_language_list.items);
8484

8585
// Add all dependencies to our "root" Zigdown module, then add that to the dependencies list
8686
for (deps.items) |dep| {
8787
mod.addImport(dep.name, dep.module);
8888
}
89-
try deps.append(mod_dep);
89+
try deps.append(b.allocator, mod_dep);
9090

9191
const exe_opts = BuildOpts{
9292
.target = target,
@@ -159,7 +159,7 @@ pub fn build(b: *std.Build) !void {
159159
lua_mod.root_module.addImport(ziglua_dep.name, ziglua_dep.module);
160160

161161
const luajit_lib = ziglua.artifact("lua");
162-
lua_mod.linkLibrary(luajit_lib);
162+
lua_mod.root_module.linkLibrary(luajit_lib);
163163

164164
b.installArtifact(lua_mod);
165165

@@ -208,14 +208,17 @@ pub fn build(b: *std.Build) !void {
208208
// TODO: Still requires some implementation of much of libC to link.
209209
// See: https://github.com/floooh/pacman.zig/blob/main/build.zig for an
210210
// example of using Emscripten as the linker
211+
// NOTE: Currently broken due to std.Io.Threaded stdlib issue on this host.
212+
// Use `-Dno-wasm` to skip.
211213
////////////////////////////////////////////////////////////////////////////
214+
if (!no_wasm) {
212215
const wasm_target = b.resolveTargetQuery(.{
213216
.cpu_arch = .wasm32,
214217
.os_tag = .freestanding,
215218
.abi = .musl,
216219
});
217220

218-
var wasm_deps: ArrayList(Dependency) = try getDependencies(b, wasm_target, wasm_optimize, ts_language_list.items);
221+
var wasm_deps: std.ArrayList(Dependency) = try getDependencies(b, wasm_target, wasm_optimize, ts_language_list.items);
219222

220223
const wasm_mod = b.addModule("zigdown_wasm", .{
221224
.root_source_file = b.path("src/lib/zigdown.zig"),
@@ -229,7 +232,7 @@ pub fn build(b: *std.Build) !void {
229232
for (wasm_deps.items) |dep| {
230233
wasm_mod.addImport(dep.name, dep.module);
231234
}
232-
try wasm_deps.append(wasm_mod_dep);
235+
try wasm_deps.append(b.allocator, wasm_mod_dep);
233236

234237
const wasm = b.addExecutable(.{
235238
.name = "zigdown-wasm",
@@ -269,6 +272,7 @@ pub fn build(b: *std.Build) !void {
269272
b.installArtifact(wasm);
270273
b.getInstallStep().dependOn(wasm_step);
271274
}
275+
} // end if (!no_wasm)
272276

273277
////////////////////////////////////////////////////////////////////////////
274278
// Add unit tests
@@ -386,8 +390,8 @@ fn isWasm(target: std.Build.ResolvedTarget) bool {
386390
return false;
387391
}
388392

389-
fn getDependencies(b: *std.Build, target: Target, optimize: OptimizeMode, ts_language_list: []const []const u8) !ArrayList(Dependency) {
390-
var dependencies = ArrayList(Dependency).init(b.allocator);
393+
fn getDependencies(b: *std.Build, target: Target, optimize: OptimizeMode, ts_language_list: []const []const u8) !std.ArrayList(Dependency) {
394+
var dependencies: std.ArrayList(Dependency) = .empty;
391395

392396
// Module for baked-in data and files
393397
const asset_mod = b.addModule("assets", .{
@@ -433,7 +437,7 @@ fn getDependencies(b: *std.Build, target: Target, optimize: OptimizeMode, ts_lan
433437
}
434438
}
435439

436-
try dependencies.append(asset_dep);
440+
try dependencies.append(b.allocator, asset_dep);
437441

438442
if (!isWasm(target)) {
439443
///////////////////////////////////////////////////////////////////////////
@@ -442,30 +446,30 @@ fn getDependencies(b: *std.Build, target: Target, optimize: OptimizeMode, ts_lan
442446
// STB-Image
443447
const stbi = b.dependency("stbi", .{ .optimize = optimize, .target = target });
444448
const stbi_dep = Dependency{ .name = "stb_image", .module = stbi.module("stb_image") };
445-
try dependencies.append(stbi_dep);
449+
try dependencies.append(b.allocator, stbi_dep);
446450

447451
const target_os: std.Target.Os.Tag = target.query.os_tag orelse builtin.os.tag;
448452
if (target_os != .windows) {
449453
// PlutoSVG
450454
const plutosvg = b.dependency("plutosvg", .{ .optimize = optimize, .target = target });
451455
const plutosvg_dep = Dependency{ .name = "plutosvg", .module = plutosvg.module("plutosvg") };
452-
try dependencies.append(plutosvg_dep);
456+
try dependencies.append(b.allocator, plutosvg_dep);
453457
}
454458

455459
// Flags
456460
const flags = b.dependency("flags", .{ .optimize = optimize, .target = target });
457461
const flags_dep = Dependency{ .name = "flags", .module = flags.module("flags") };
458-
try dependencies.append(flags_dep);
462+
try dependencies.append(b.allocator, flags_dep);
459463

460464
// Treez (TreeSitter wrapper library)
461465
const treez = b.dependency("treez", .{ .optimize = optimize, .target = target });
462466
const treez_dep = Dependency{ .name = "treez", .module = treez.module("treez") };
463-
try dependencies.append(treez_dep);
467+
try dependencies.append(b.allocator, treez_dep);
464468
asset_mod.addImport(treez_dep.name, treez_dep.module);
465469

466470
const known_folders = b.dependency("known_folders", .{ .optimize = optimize, .target = target });
467471
const known_folders_dep = Dependency{ .name = "known-folders", .module = known_folders.module("known-folders") };
468-
try dependencies.append(known_folders_dep);
472+
try dependencies.append(b.allocator, known_folders_dep);
469473
}
470474

471475
return dependencies;

build.zig.zon

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
.{
22
.name = .zigdown,
33
.fingerprint = 0xec26eaf34f894e33,
4-
.version = "1.2.2",
5-
.minimum_zig_version = "0.15.1",
4+
.version = "1.3.0",
5+
.minimum_zig_version = "0.16.0",
66
.paths = .{
77
"lua",
88
"plugin",
@@ -17,24 +17,26 @@
1717
.hash = "zig_stb_image-2.28.0-2-9dD1dfoYBQCzF7IA9_-tSbsF1PgN7HGfzDzGVRG2BoUY",
1818
},
1919
.plutosvg = .{
20-
.url = "https://github.com/JacobCrabill/zig-plutosvg/archive/b2dcac090852c9b26f16798dbbb35c3da1f65773.tar.gz",
21-
.hash = "plutosvg-0.0.6-1-LyMxgYguAABq-4Xf7BWKdsl27x3fq5EUgsYS1LmGq9bW",
20+
.url = "https://github.com/JacobCrabill/zig-plutosvg/archive/7704671d5130040fb8a25e45c8112809aa89ae6c.tar.gz",
21+
.hash = "plutosvg-0.0.6-2-LyMxgdAuAACyA7po0YLhr75Net3boHUgEf3SP2k7DY-1",
2222
},
2323
.flags = .{
24-
.url = "https://github.com/JacobCrabill/zig-flags/archive/8f65f0d5bf0d92b950ddf16afb34533491831c7d.tar.gz",
25-
.hash = "flags-0.11.0-a_9h3gR2AACtwEzEcKhIsYPEgNPbBeGYKhbYL4lsmJ1N",
24+
.url = "https://github.com/JacobCrabill/zig-flags/archive/de90d081cd4ef7907fbdd8e1e3853fcee9bcf1b9.tar.gz",
25+
.hash = "flags-0.12.0-a_9h3p94AACZ_64EtswSKUGy_mvh8IVL8CIiu0SVebvS",
2626
},
2727
.treez = .{
28-
.url = "https://github.com/JacobCrabill/treez/archive/980316cb4fd0da6075a6a8f86bd1a3927bf68ad3.tar.gz",
29-
.hash = "treez-0.2.0-4JX9hV6mAAA-T-vae75m4eZSgIxKsv6uUae21-Dzhh8l",
28+
.url = "https://github.com/JacobCrabill/treez/archive/2d1f27c92a2d4a2179740a8ba4bd866b328dd24f.tar.gz",
29+
.hash = "treez-0.2.0-4JX9hYSkAABbotzNQsOTNzU8wcDglGCo9Ffik-qi1o_a",
3030
},
3131
.known_folders = .{
32-
.url = "git+https://github.com/ziglibs/known-folders.git#ab5cf5feb936fa3b72c95d3ad0c0c67791937ba1",
33-
.hash = "known_folders-0.0.0-Fy-PJtTTAADUOhGKM0sxzG4eMkNQxRvx9e5dfHVyaeA3",
32+
.url = "https://github.com/ziglibs/known-folders/archive/5865fd0f1bc5241de8355fcf2066692bf1b0ea02.tar.gz",
33+
.hash = "known_folders-0.0.0-Fy-PJpLKAAAmMEnwre7fFcfsxBGULd24N8Bp2pWEu3sf",
3434
},
3535
.ziglua = .{
36-
.url = "https://github.com/JacobCrabill/ziglua/archive/011758bab2c2b5a4b6e678e33a8dbb24570d4394.tar.gz",
37-
.hash = "zlua-0.2.0-hGRpC2sYBQCawPfqP3rxM95KUN3IYdRNKTAeSussl7kM",
36+
// .url = "https://github.com/JacobCrabill/ziglua/archive/011758bab2c2b5a4b6e678e33a8dbb24570d4394.tar.gz",
37+
// .hash = "zlua-0.2.0-hGRpC2sYBQCawPfqP3rxM95KUN3IYdRNKTAeSussl7kM",
38+
.url = "https://github.com/natecraddock/ziglua/archive/22cad3abc50bca7c73145e07ed2835926e2dda44.tar.gz",
39+
.hash = "zlua-0.1.0-hGRpC5dqBQCm1fzCu23-RM5xE9_6JSnUitX9HFi9jSYA",
3840
.lazy = true,
3941
},
4042
// Needed in order for ziglua to build luajit.
@@ -47,44 +49,44 @@
4749

4850
// Individual TreeSitter language parsers
4951
.tree_sitter_bash = .{
50-
.url = "https://github.com/tree-sitter/tree-sitter-bash/archive/49c31006d8307dcb12bc5770f35b6d5b9e2be68e.tar.gz",
51-
.hash = "N-V-__8AAG_cogDl9X3jxLCGR6dsBzkjlX8TYhtdCCvonOUr",
52+
.url = "https://github.com/tree-sitter/tree-sitter-bash/archive/a06c2e4415e9bc0346c6b86d401879ffb44058f7.tar.gz",
53+
.hash = "N-V-__8AANjenwCy_cXlIbU8nqBzGxMlEHVokLZJYrmIXtoz",
5254
},
5355
.tree_sitter_c = .{
54-
.url = "https://github.com/tree-sitter/tree-sitter-c/archive/e8841a6a9431b7365ac9055688429e1deb8db90f.tar.gz",
55-
.hash = "N-V-__8AALWcSAD2F6Vd1LSqa0js-e6kawfanpXMUnMhQeq6",
56+
.url = "https://github.com/tree-sitter/tree-sitter-c/archive/b780e47fc780ddc8da13afa35a3f4ed5c157823d.tar.gz",
57+
.hash = "tree_sitter_c-0.24.2-y5boS-ptQADHoCoVfjGT_nFtFQ5LbomIkW0fxG3_cmdB",
5658
},
5759
.tree_sitter_cmake = .{
5860
.url = "https://github.com/uyha/tree-sitter-cmake/archive/fe48221d4d9842d916d66b5e71ab3c6307ec28b3.tar.gz",
5961
.hash = "N-V-__8AACLOCQCV8GF5uI_qg4S3tygmCZMpxB9qk4L2evjb",
6062
},
6163
.tree_sitter_cpp = .{
62-
.url = "https://github.com/tree-sitter/tree-sitter-cpp/archive/f41b4f66a42100be405f96bdc4ebc4a61095d3e8.tar.gz",
63-
.hash = "N-V-__8AABV0FwF4Ttn0MFCCLOxdUUKUC07SL-wqYa-2ySus",
64+
.url = "https://github.com/tree-sitter/tree-sitter-cpp/archive/8b5b49eb196bec7040441bee33b2c9a4838d6967.tar.gz",
65+
.hash = "N-V-__8AAMePmwGeojPhBaBC10aGwaQIOU4vg1H17IWakWs-",
6466
},
6567
.tree_sitter_json = .{
6668
.url = "https://github.com/tree-sitter/tree-sitter-json/archive/4d770d31f732d50d3ec373865822fbe659e47c75.tar.gz",
6769
.hash = "N-V-__8AACVXAgCEl--zqcqoXlvXpEMk5BTS748bsLVslERc",
6870
},
6971
.tree_sitter_lua = .{
70-
.url = "https://github.com/tree-sitter-grammars/tree-sitter-lua/archive/7040a79f95b0bf7517a9809a8d0ecd41197027ac.tar.gz",
71-
.hash = "N-V-__8AAExZCQBbT7NCZKdW4KxnVtSRvSVqTtJt0FEqfSa9",
72+
.url = "https://github.com/tree-sitter-grammars/tree-sitter-lua/archive/10fe0054734eec83049514ea2e718b2a56acd0c9.tar.gz",
73+
.hash = "N-V-__8AAE5ZCQA-BW5BOioWVkGcPTjhC5x1Qv07BH3Xt3dR",
7274
},
7375
.tree_sitter_make = .{
74-
.url = "https://github.com/tree-sitter-grammars/tree-sitter-make/archive/5e9e8f8ff3387b0edcaa90f46ddf3629f4cfeb1d.tar.gz",
75-
.hash = "N-V-__8AAD6dEwA9hwePYcn8FdDAJEeYqCBBlYI2Oey6O5g2",
76+
.url = "https://github.com/tree-sitter-grammars/tree-sitter-make/archive/70613f3d812cbabbd7f38d104d60a409c4008b43.tar.gz",
77+
.hash = "N-V-__8AAMVsEgC0rug9JAAca82MW3tyFJrFdRA3ou6VEipn",
7678
},
7779
.tree_sitter_python = .{
78-
.url = "https://github.com/tree-sitter/tree-sitter-python/archive/de0c01e7102e755f6c2e1b3055ae6ca85f261a10.tar.gz",
79-
.hash = "N-V-__8AANVLPgC-yBA9l867GoeMn95l0Y_Q9CAzcizA7qlT",
80+
.url = "https://github.com/tree-sitter/tree-sitter-python/archive/26855eabccb19c6abf499fbc5b8dc7cc9ab8bc64.tar.gz",
81+
.hash = "N-V-__8AAOiqPgC9tH5tYSAYNsMWLoPiSxcmkHvIhMnezomx",
8082
},
8183
.tree_sitter_rust = .{
82-
.url = "https://github.com/tree-sitter/tree-sitter-rust/archive/c447dcce961ac438aaeaf117347749fe7d1e8365.tar.gz",
83-
.hash = "N-V-__8AAJ3uaABprqh1hqE6xstGrAJWc4xL3u1HfJ6Plj6E",
84+
.url = "https://github.com/tree-sitter/tree-sitter-rust/archive/77a3747266f4d621d0757825e6b11edcbf991ca5.tar.gz",
85+
.hash = "N-V-__8AAHJYbgAipcanYj2A6rnj9wtGzjGzANkrOsqiFAxm",
8486
},
8587
.tree_sitter_yaml = .{
86-
.url = "https://github.com/tree-sitter-grammars/tree-sitter-yaml/archive/1805917414a9a8ba2473717fd69447277a175fae.tar.gz",
87-
.hash = "N-V-__8AABBcGwBNXXnYqPiNmGDO5T2k7__Tglt1pwNFI_YN",
88+
.url = "https://github.com/tree-sitter-grammars/tree-sitter-yaml/archive/a1c4812a73ec5e089de8e441fdea3a921e8d5079.tar.gz",
89+
.hash = "N-V-__8AAEkcHQB61IN4Zp5o83gmDa69AUnJUBSFIVM9lGg-",
8890
},
8991
.tree_sitter_zig = .{
9092
.url = "https://github.com/tree-sitter-grammars/tree-sitter-zig/archive/b670c8df85a1568f498aa5c8cae42f51a90473c0.tar.gz",

src/app/lua_api.zig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export fn render_markdown(lua: ?*LuaState) callconv(.c) c_int {
4848
const input_a: [*c]const u8 = c.lua_tolstring(lua, 1, &len);
4949
const input: []const u8 = input_a[0..len];
5050
const alloc = std.heap.page_allocator;
51+
var threaded_io: std.Io.Threaded = .init(alloc, .{});
52+
const io = threaded_io.io();
5153

5254
// Number of columns to render (output width).
5355
// We handle the case of the user not providing the argument by defaulting to 100.
@@ -80,7 +82,7 @@ export fn render_markdown(lua: ?*LuaState) callconv(.c) c_int {
8082
.max_image_cols = if (columns > 4) @intCast(columns - 4) else @intCast(columns),
8183
.termsize = tsize,
8284
};
83-
var r_renderer = zd.render.RangeRenderer.init(&alloc_writer.writer, alloc, render_opts);
85+
var r_renderer = zd.render.RangeRenderer.init(io, alloc, &alloc_writer.writer, render_opts);
8486
defer r_renderer.deinit();
8587
r_renderer.renderBlock(md) catch @panic("Render error!");
8688

0 commit comments

Comments
 (0)