summaryrefslogtreecommitdiff
path: root/test/syntax.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-06 18:11:36 -0300
committerGabriel Schneider <[email protected]>2026-09-07 13:59:12 -0300
commit60367d8fe23f6af98ec28e3cf6c2094dfe332df0 (patch)
tree310fc734173cf771881f4691c71909135fadde97 /test/syntax.zig
parentfa82cac885cb4738fe36d1e49b4749b5a3e31a4a (diff)
downloadpardes-60367d8fe23f6af98ec28e3cf6c2094dfe332df0.tar.gz
pardes-60367d8fe23f6af98ec28e3cf6c2094dfe332df0.zip
Refactor panes and filesystem; replace FUSE with 9P
Consolidate pane, layout, memory and host code. Serve 9P by default over Unix sockets, with runtime mounts and optional TCP/QUIC transports. Remove FUSE and obsolete proof-of-concept examples. Fix highlighting and terminal-history performance, expand differential and stress-test infrastructure, sort navigation results while preserving the next occurrence, add syntax-colored Braille minimaps, remove SPC-k, and document 9P interaction as a repository skill.
Diffstat (limited to 'test/syntax.zig')
-rw-r--r--test/syntax.zig179
1 files changed, 179 insertions, 0 deletions
diff --git a/test/syntax.zig b/test/syntax.zig
new file mode 100644
index 00000000..deaad7de
--- /dev/null
+++ b/test/syntax.zig
@@ -0,0 +1,179 @@
+const std = @import("std");
+const syntax = @import("syntax");
+const gpa = std.heap.c_allocator;
+
+const Case = struct {
+ name: []const u8,
+ path: []const u8,
+ source: []const u8,
+ locations: bool = false,
+ start: usize = 0,
+};
+
+const cases = [_]Case{
+ .{ .name = "zig", .path = "a.zig", .source = "const answer: u32 = 42;\npub fn main() void { // note\n _ = \"hello\";\n}\n" },
+ .{ .name = "markdown", .path = "a.md", .source = "# Heading *slant*\n\n**bold** and `code`\n\n```zig\nconst n = 42;\n```\n" },
+ .{ .name = "markdown-nested", .path = "a.md", .source = "> # Heading *slant*\n>\n> Prose with **bold** and [a link](https://example.org).\n\n- `code` with <https://example.org>\n- plain text\n\n```zig\nfn main() void { return; }\n```\n" },
+ .{ .name = "c-comment-window", .path = "a.c", .source = "/* opening\nstill a comment 42\n*/\nint n = 7;\n", .start = 11 },
+ .{ .name = "markdown-indent", .path = "a.md", .source = " # code, not a heading\n" },
+ .{ .name = "result-markdown", .path = "+Grep", .source = "a.md:1 # Heading *slant*\na.md:3 **bold** and `code`\n", .locations = true },
+ .{ .name = "result-indent", .path = "+Grep", .source = "a.md:1 # code, not a heading\n", .locations = true },
+ .{ .name = "result-zig", .path = "+Grep", .source = "a.zig:1:1 const answer: u32 = 42;\na.zig:2:1 pub fn main() void { // note\na.zig:3:1 _ = \"hello\";\na.zig:4:1 }\n", .locations = true },
+ .{ .name = "prose", .path = "+Help", .source = "A document without code locations.\nRead a.md for help.\n", .locations = true },
+};
+
+fn highlight(case: Case) ![]u8 {
+ if (comptime @hasDecl(syntax, "highlightLocations")) {
+ if (case.locations) return syntax.highlightLocations(gpa, case.source, case.start, case.source.len);
+ }
+ return syntax.highlightFileRange(gpa, case.path, case.source, case.start, case.source.len);
+}
+
+fn out(comptime format: []const u8, args: anytype) !void {
+ var buffer: [4096]u8 = undefined;
+ const text = try std.fmt.bufPrint(&buffer, format, args);
+ var offset: usize = 0;
+ while (offset < text.len) {
+ const n = std.c.write(1, text.ptr + offset, text.len - offset);
+ if (n <= 0) return error.WriteFailed;
+ offset += @intCast(n);
+ }
+}
+
+fn now() u64 {
+ var time: std.c.timespec = undefined;
+ _ = std.c.clock_gettime(.MONOTONIC, &time);
+ return @as(u64, @intCast(time.sec)) * 1_000_000_000 + @as(u64, @intCast(time.nsec));
+}
+
+const Counter = struct {
+ calls: usize = 0,
+ bytes: usize = 0,
+ live: usize = 0,
+ peak: usize = 0,
+
+ fn allocator(self: *Counter) std.mem.Allocator {
+ return .{ .ptr = self, .vtable = &.{ .alloc = alloc, .resize = resize, .remap = remap, .free = free } };
+ }
+
+ fn alloc(ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, ret: usize) ?[*]u8 {
+ const self: *Counter = @ptrCast(@alignCast(ctx));
+ const ptr = gpa.rawAlloc(len, alignment, ret) orelse return null;
+ self.calls += 1;
+ self.bytes += len;
+ self.live += len;
+ self.peak = @max(self.peak, self.live);
+ return ptr;
+ }
+
+ fn resize(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, len: usize, ret: usize) bool {
+ const self: *Counter = @ptrCast(@alignCast(ctx));
+ if (!gpa.rawResize(memory, alignment, len, ret)) return false;
+ self.live = self.live - memory.len + len;
+ self.peak = @max(self.peak, self.live);
+ return true;
+ }
+
+ fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, len: usize, ret: usize) ?[*]u8 {
+ const self: *Counter = @ptrCast(@alignCast(ctx));
+ const ptr = gpa.rawRemap(memory, alignment, len, ret) orelse return null;
+ self.calls += 1;
+ self.bytes += len;
+ self.live = self.live - memory.len + len;
+ self.peak = @max(self.peak, self.live);
+ return ptr;
+ }
+
+ fn free(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, ret: usize) void {
+ const self: *Counter = @ptrCast(@alignCast(ctx));
+ self.live -= memory.len;
+ gpa.rawFree(memory, alignment, ret);
+ }
+};
+
+fn benchmark(case: Case, reps: usize) !void {
+ var counter: Counter = .{};
+ syntax.start(counter.allocator());
+ const cold_start = now();
+ gpa.free(try highlight(case));
+ const cold = now() - cold_start;
+ const retained = counter.live;
+ const calls = counter.calls;
+ const bytes = counter.bytes;
+ const samples = try gpa.alloc(u64, reps);
+ defer gpa.free(samples);
+ for (samples) |*sample| {
+ const before = now();
+ gpa.free(try highlight(case));
+ sample.* = now() - before;
+ }
+ std.mem.sort(u64, samples, {}, std.sort.asc(u64));
+ const leaked = counter.live - retained;
+ try out("{{\"case\":\"{s}\",\"bytes\":{d},\"cold_ns\":{d},\"median_ns\":{d},\"p95_ns\":{d},\"allocations\":{d},\"allocated_bytes\":{d},\"retained_growth\":{d}}}\n", .{
+ case.name, case.source.len, cold, samples[reps / 2], samples[@min(reps - 1, reps * 95 / 100)],
+ (counter.calls - calls) / reps, (counter.bytes - bytes) / reps, leaked,
+ });
+ syntax.stop();
+ if (counter.live != 0) return error.LeakedSyntaxMemory;
+}
+
+pub fn main(init: std.process.Init) !void {
+ const args = try init.minimal.args.toSlice(init.arena.allocator());
+ var bench = false;
+ var reps: usize = 21;
+ var i: usize = 1;
+ while (i < args.len) : (i += 1) {
+ if (std.mem.eql(u8, args[i], "--bench")) {
+ bench = true;
+ } else if (std.mem.eql(u8, args[i], "--reps") and i + 1 < args.len) {
+ i += 1;
+ reps = try std.fmt.parseInt(usize, args[i], 10);
+ if (reps == 0) return error.InvalidRepetitions;
+ } else return error.InvalidArgument;
+ }
+ if (!bench) {
+ syntax.start(gpa);
+ defer syntax.stop();
+ for (cases) |case| {
+ try out("{s}\n", .{case.name});
+ const styles = try highlight(case);
+ defer gpa.free(styles);
+ for (case.source[case.start..], 0..) |byte, at| {
+ if (byte == '\n') {
+ try out("\n", .{});
+ } else {
+ const style = if (styles.len == 0) 0 else styles[at];
+ try out("{c}", .{".ksnc"[style]});
+ }
+ }
+ }
+ return;
+ }
+ for (cases) |case| try benchmark(case, reps);
+ var results: std.ArrayList(u8) = .empty;
+ defer results.deinit(gpa);
+ for (0..512) |_| try results.appendSlice(gpa, "src/a.zig:12:1 const value: u32 = 42; // result\n");
+ try benchmark(.{ .name = "results-512", .path = "+Grep", .source = results.items, .locations = true }, reps);
+ results.clearRetainingCapacity();
+ for (0..128) |_| try results.appendSlice(gpa, "a.zig:1 const value = 42;\nb.c:1 int value = 42;\nc.md:1 **bold** and `code`\nd.rs:1 let value = 42;\n");
+ try benchmark(.{ .name = "results-mixed-512", .path = "+Grep", .source = results.items, .locations = true }, reps);
+ results.clearRetainingCapacity();
+ var row_buffer: [512]u8 = undefined;
+ for (0..128) |line| {
+ const rows = try std.fmt.bufPrint(&row_buffer, "a.zig:{d} const value_{d} = {d};\nb.c:{d} int value_{d} = {d};\nc.md:{d} **item {d}** and `code {d}`\nd.rs:{d} let value_{d} = {d};\n", .{ line + 1, line, line, line + 1, line, line, line + 1, line, line, line + 1, line, line });
+ try results.appendSlice(gpa, rows);
+ }
+ try benchmark(.{ .name = "results-varied-512", .path = "+Grep", .source = results.items, .locations = true }, reps);
+ results.clearRetainingCapacity();
+ for (0..512) |line| {
+ const row = try std.fmt.bufPrint(&row_buffer, "notes.md:{d} Ordinary prose for item {d}, without inline markup.\n", .{ line + 1, line });
+ try results.appendSlice(gpa, row);
+ }
+ try benchmark(.{ .name = "results-prose-512", .path = "+Grep", .source = results.items, .locations = true }, reps);
+ results.clearRetainingCapacity();
+ for (0..128) |line| {
+ const block = try std.fmt.bufPrint(&row_buffer, "# Heading {d}\n\nPlain paragraph {d}.\n\n**Bold {d}** and `code`.\n\n```zig\nconst value_{d} = {d};\n```\n\n", .{ line, line, line, line, line });
+ try results.appendSlice(gpa, block);
+ }
+ try benchmark(.{ .name = "markdown-blocks-128", .path = "notes.md", .source = results.items }, reps);
+}