diff --git a/benchmarks/compiled/friends.zig b/benchmarks/compiled/friends.zig index f9a9ca2..f1c0216 100644 --- a/benchmarks/compiled/friends.zig +++ b/benchmarks/compiled/friends.zig @@ -4,7 +4,7 @@ const helpers = @import("helpers.zig"); pub const Data = struct {}; pub fn render(allocator: std.mem.Allocator, _: Data) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .{}; + var buf: std.ArrayList(u8) = .{}; defer buf.deinit(allocator); try buf.appendSlice(allocator, "

friend_name

friend_email

friend_about

tag_value
"); diff --git a/benchmarks/compiled/helpers.zig b/benchmarks/compiled/helpers.zig index a992b8e..5ed874a 100644 --- a/benchmarks/compiled/helpers.zig +++ b/benchmarks/compiled/helpers.zig @@ -4,7 +4,7 @@ const std = @import("std"); /// Append HTML-escaped string to buffer -pub fn appendEscaped(buf: *std.ArrayListUnmanaged(u8), allocator: std.mem.Allocator, str: []const u8) !void { +pub fn appendEscaped(buf: *std.ArrayList(u8), allocator: std.mem.Allocator, str: []const u8) !void { for (str) |c| { switch (c) { '&' => try buf.appendSlice(allocator, "&"), diff --git a/benchmarks/compiled/if-expression.zig b/benchmarks/compiled/if-expression.zig index 4c76bd7..417a271 100644 --- a/benchmarks/compiled/if-expression.zig +++ b/benchmarks/compiled/if-expression.zig @@ -4,7 +4,7 @@ const helpers = @import("helpers.zig"); pub const Data = struct {}; pub fn render(allocator: std.mem.Allocator, _: Data) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .{}; + var buf: std.ArrayList(u8) = .{}; defer buf.deinit(allocator); try buf.appendSlice(allocator, "

Active

Inactive

"); diff --git a/benchmarks/compiled/projects-escaped.zig b/benchmarks/compiled/projects-escaped.zig index 66003c3..dcc894b 100644 --- a/benchmarks/compiled/projects-escaped.zig +++ b/benchmarks/compiled/projects-escaped.zig @@ -4,7 +4,7 @@ const helpers = @import("helpers.zig"); pub const Data = struct {}; pub fn render(allocator: std.mem.Allocator, _: Data) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .{}; + var buf: std.ArrayList(u8) = .{}; defer buf.deinit(allocator); try buf.appendSlice(allocator, "
  • project_name: project_description
  • "); diff --git a/benchmarks/compiled/search-results.zig b/benchmarks/compiled/search-results.zig index 34a3e3a..59db454 100644 --- a/benchmarks/compiled/search-results.zig +++ b/benchmarks/compiled/search-results.zig @@ -4,7 +4,7 @@ const helpers = @import("helpers.zig"); pub const Data = struct {}; pub fn render(allocator: std.mem.Allocator, _: Data) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .{}; + var buf: std.ArrayList(u8) = .{}; defer buf.deinit(allocator); try buf.appendSlice(allocator, "

    result_title

    $result_price
    "); diff --git a/benchmarks/compiled/simple-0.zig b/benchmarks/compiled/simple-0.zig index a0a104d..b352d96 100644 --- a/benchmarks/compiled/simple-0.zig +++ b/benchmarks/compiled/simple-0.zig @@ -4,7 +4,7 @@ const helpers = @import("helpers.zig"); pub const Data = struct {}; pub fn render(allocator: std.mem.Allocator, _: Data) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .{}; + var buf: std.ArrayList(u8) = .{}; defer buf.deinit(allocator); try buf.appendSlice(allocator, "

    Hello World

    "); diff --git a/benchmarks/compiled/simple-1.zig b/benchmarks/compiled/simple-1.zig index bfcc524..14a2168 100644 --- a/benchmarks/compiled/simple-1.zig +++ b/benchmarks/compiled/simple-1.zig @@ -4,7 +4,7 @@ const helpers = @import("helpers.zig"); pub const Data = struct {}; pub fn render(allocator: std.mem.Allocator, _: Data) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .{}; + var buf: std.ArrayList(u8) = .{}; defer buf.deinit(allocator); try buf.appendSlice(allocator, "My Site

    Welcome

    This is a simple page

    "); diff --git a/benchmarks/compiled/simple-2.zig b/benchmarks/compiled/simple-2.zig index cd3aa53..5006b3a 100644 --- a/benchmarks/compiled/simple-2.zig +++ b/benchmarks/compiled/simple-2.zig @@ -4,7 +4,7 @@ const helpers = @import("helpers.zig"); pub const Data = struct {}; pub fn render(allocator: std.mem.Allocator, _: Data) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .{}; + var buf: std.ArrayList(u8) = .{}; defer buf.deinit(allocator); try buf.appendSlice(allocator, "

    Header

    Header2

    Header3

    Header4

    Header5
    Header6
    "); diff --git a/build.zig.zon b/build.zig.zon index 1e55c44..8354247 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .pugz, - .version = "0.3.7", + .version = "0.3.8", .fingerprint = 0x822db0790e17621d, // Changing this has security and trust implications. .minimum_zig_version = "0.15.2", .dependencies = .{}, diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md index e029853..c767502 100644 --- a/docs/CLAUDE.md +++ b/docs/CLAUDE.md @@ -12,6 +12,10 @@ Pugz is a Pug-like HTML template engine written in Zig 0.15.2. It compiles Pug t - When the user specifies a new rule, update this CLAUDE.md file to include it. - Code comments are required but must be meaningful, not bloated. Focus on explaining "why" not "what". Avoid obvious comments like "// increment counter" - instead explain complex logic, non-obvious decisions, or tricky edge cases. - **All documentation files (.md) must be saved to the `docs/` directory.** Do not create .md files in the root directory or examples directories - always place them in `docs/`. +- **Follow Zig standards for the version specified in `build.zig.zon`** (currently 0.15.2). This includes: + - Use `std.ArrayList(T)` instead of the deprecated `std.ArrayListUnmanaged(T)` (renamed in Zig 0.15) + - Pass allocator to method calls (`append`, `deinit`, etc.) as per the unmanaged pattern + - Check Zig release notes for API changes when updating the minimum Zig version - **Publish command**: Only when user explicitly says "publish", do the following: 1. Bump the fix version (patch version in build.zig.zon) 2. Git commit with appropriate message diff --git a/src/codegen.zig b/src/codegen.zig index e718066..a9d11d3 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -85,7 +85,7 @@ pub const CompilerError = error{ pub const Compiler = struct { allocator: Allocator, options: CompilerOptions, - output: std.ArrayListUnmanaged(u8), + output: std.ArrayList(u8), indent_level: usize = 0, has_doctype: bool = false, has_tag: bool = false, diff --git a/src/compile_tpls.zig b/src/compile_tpls.zig index f6a3839..6552c29 100644 --- a/src/compile_tpls.zig +++ b/src/compile_tpls.zig @@ -233,7 +233,7 @@ fn compileSingleFile( } fn findPugFiles(allocator: mem.Allocator, dir_path: []const u8) ![][]const u8 { - var results: std.ArrayListUnmanaged([]const u8) = .{}; + var results: std.ArrayList([]const u8) = .{}; errdefer { for (results.items) |item| allocator.free(item); results.deinit(allocator); @@ -243,7 +243,7 @@ fn findPugFiles(allocator: mem.Allocator, dir_path: []const u8) ![][]const u8 { return results.toOwnedSlice(allocator); } -fn findPugFilesRecursive(allocator: mem.Allocator, dir_path: []const u8, results: *std.ArrayListUnmanaged([]const u8)) !void { +fn findPugFilesRecursive(allocator: mem.Allocator, dir_path: []const u8, results: *std.ArrayList([]const u8)) !void { var dir = try fs.cwd().openDir(dir_path, .{ .iterate = true }); defer dir.close(); @@ -277,7 +277,7 @@ fn makeTemplateName(allocator: mem.Allocator, path: []const u8) ![]const u8 { else path; - var result: std.ArrayListUnmanaged(u8) = .{}; + var result: std.ArrayList(u8) = .{}; defer result.deinit(allocator); for (without_ext) |c| { @@ -298,7 +298,7 @@ fn makeFlatFileName(allocator: mem.Allocator, path: []const u8) ![]const u8 { else path; - var result: std.ArrayListUnmanaged(u8) = .{}; + var result: std.ArrayList(u8) = .{}; defer result.deinit(allocator); for (without_ext) |c| { @@ -315,14 +315,14 @@ fn makeFlatFileName(allocator: mem.Allocator, path: []const u8) ![]const u8 { } fn generateRootZig(allocator: mem.Allocator, output_dir: []const u8, template_map: *std.StringHashMap([]const u8)) !void { - var output: std.ArrayListUnmanaged(u8) = .{}; + var output: std.ArrayList(u8) = .{}; defer output.deinit(allocator); try output.appendSlice(allocator, "// Auto-generated by Pugz build step\n"); try output.appendSlice(allocator, "// This file exports all compiled templates\n\n"); // Sort template names - var names: std.ArrayListUnmanaged([]const u8) = .{}; + var names: std.ArrayList([]const u8) = .{}; defer names.deinit(allocator); var iter = template_map.keyIterator(); diff --git a/src/error.zig b/src/error.zig index 3138c6f..34e55fd 100644 --- a/src/error.zig +++ b/src/error.zig @@ -1,7 +1,7 @@ const std = @import("std"); const mem = std.mem; const Allocator = std.mem.Allocator; -const ArrayListUnmanaged = std.ArrayListUnmanaged; +const ArrayList = std.ArrayList; // ============================================================================ // Pug Error - Error formatting with source context @@ -47,7 +47,7 @@ pub const PugError = struct { /// Format as JSON-like structure for serialization pub fn toJson(self: *const PugError, allocator: Allocator) ![]const u8 { - var result: ArrayListUnmanaged(u8) = .{}; + var result: ArrayList(u8) = .{}; errdefer result.deinit(allocator); try result.appendSlice(allocator, "{\"code\":\""); @@ -76,7 +76,7 @@ pub const PugError = struct { }; /// Append JSON-escaped string to result -fn appendJsonEscaped(allocator: Allocator, result: *ArrayListUnmanaged(u8), s: []const u8) !void { +fn appendJsonEscaped(allocator: Allocator, result: *ArrayList(u8), s: []const u8) !void { for (s) |c| { switch (c) { '"' => try result.appendSlice(allocator, "\\\""), @@ -150,7 +150,7 @@ fn formatErrorMessage( ) ![]const u8 { _ = code; // Code is embedded in PugError struct - var result: ArrayListUnmanaged(u8) = .{}; + var result: ArrayList(u8) = .{}; errdefer result.deinit(allocator); // Header: filename:line:column or Pug:line:column @@ -231,7 +231,7 @@ fn formatErrorMessage( /// Split source into lines (handles \n, \r\n, \r) fn splitLines(allocator: Allocator, src: []const u8) ![][]const u8 { - var lines: ArrayListUnmanaged([]const u8) = .{}; + var lines: ArrayList([]const u8) = .{}; errdefer lines.deinit(allocator); var start: usize = 0; diff --git a/src/lexer.zig b/src/lexer.zig index 5640c02..eed3b66 100644 --- a/src/lexer.zig +++ b/src/lexer.zig @@ -180,13 +180,13 @@ pub const Token = struct { }; // ============================================================================ -// Character Parser State (simplified) - Zig 0.15 style with ArrayListUnmanaged +// Character Parser State (simplified) - Zig 0.15 style with ArrayList // ============================================================================ const BracketType = enum { paren, brace, bracket }; const CharParserState = struct { - nesting_stack: std.ArrayListUnmanaged(BracketType) = .{}, + nesting_stack: std.ArrayList(BracketType) = .{}, in_string: bool = false, string_char: ?u8 = null, in_template: bool = false, @@ -320,7 +320,7 @@ const BracketExpressionResult = struct { }; // ============================================================================ -// Lexer - Zig 0.15 style with ArrayListUnmanaged +// Lexer - Zig 0.15 style with ArrayList // ============================================================================ pub const Lexer = struct { @@ -332,10 +332,10 @@ pub const Lexer = struct { interpolated: bool, lineno: usize, colno: usize, - indent_stack: std.ArrayListUnmanaged(usize) = .{}, + indent_stack: std.ArrayList(usize) = .{}, indent_re_type: ?IndentType = null, interpolation_allowed: bool, - tokens: std.ArrayListUnmanaged(Token) = .{}, + tokens: std.ArrayList(Token) = .{}, ended: bool, last_error: ?LexerError = null, @@ -359,7 +359,7 @@ pub const Lexer = struct { } // Normalize line endings - var normalized: std.ArrayListUnmanaged(u8) = .{}; + var normalized: std.ArrayList(u8) = .{}; errdefer normalized.deinit(allocator); var i: usize = 0; @@ -378,7 +378,7 @@ pub const Lexer = struct { } } - var indent_stack: std.ArrayListUnmanaged(usize) = .{}; + var indent_stack: std.ArrayList(usize) = .{}; try indent_stack.append(allocator, 0); const input_slice = try normalized.toOwnedSlice(allocator); @@ -1349,7 +1349,7 @@ pub const Lexer = struct { /// Validates that brackets in an expression are balanced fn validateExpressionBrackets(self: *Lexer, expr: []const u8) bool { - var bracket_stack = std.ArrayListUnmanaged(u8){}; + var bracket_stack = std.ArrayList(u8){}; defer bracket_stack.deinit(self.allocator); var in_string: u8 = 0; @@ -2309,8 +2309,8 @@ pub const Lexer = struct { self.tokens.append(self.allocator, start_token) catch return false; var string_ptr: usize = 0; - var tokens_list: std.ArrayListUnmanaged([]const u8) = .{}; - var token_indent_list: std.ArrayListUnmanaged(bool) = .{}; + var tokens_list: std.ArrayList([]const u8) = .{}; + var token_indent_list: std.ArrayList(bool) = .{}; defer tokens_list.deinit(self.allocator); defer token_indent_list.deinit(self.allocator); @@ -2451,7 +2451,7 @@ pub const Lexer = struct { var in_string: u8 = 0; // Track bracket stack - inside #[...] you can have (...) and {...} for attrs/code - var bracket_stack = std.ArrayListUnmanaged(u8){}; + var bracket_stack = std.ArrayList(u8){}; defer bracket_stack.deinit(self.allocator); bracket_stack.append(self.allocator, '[') catch return; diff --git a/src/linker.zig b/src/linker.zig index 969d344..b0cdd55 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -102,10 +102,10 @@ pub fn link(allocator: Allocator, ast: *Node) LinkerError!LinkerResult { // Handle extends if (extends_node) |ext_node| { // Get mixins and expected blocks from current template - var mixins = std.ArrayListUnmanaged(*Node){}; + var mixins = std.ArrayList(*Node){}; defer mixins.deinit(allocator); - var expected_blocks = std.ArrayListUnmanaged(*Node){}; + var expected_blocks = std.ArrayList(*Node){}; defer expected_blocks.deinit(allocator); try collectMixinsAndBlocks(allocator, result.ast, &mixins, &expected_blocks); @@ -178,8 +178,8 @@ fn findDeclaredBlocks(allocator: Allocator, ast: *Node) LinkerError!BlockDefinit fn collectMixinsAndBlocks( allocator: Allocator, ast: *Node, - mixins: *std.ArrayListUnmanaged(*Node), - expected_blocks: *std.ArrayListUnmanaged(*Node), + mixins: *std.ArrayList(*Node), + expected_blocks: *std.ArrayList(*Node), ) LinkerError!void { for (ast.nodes.items) |node| { switch (node.type) { diff --git a/src/load.zig b/src/load.zig index ddd3c9a..d5ea0d8 100644 --- a/src/load.zig +++ b/src/load.zig @@ -357,7 +357,7 @@ pub fn pathJoin(allocator: Allocator, base: []const u8, relative: []const u8) ![ const base_dir = dirname(base); // Handle .. and . components - var result = std.ArrayListUnmanaged(u8){}; + var result = std.ArrayList(u8){}; errdefer result.deinit(allocator); try result.appendSlice(allocator, base_dir); diff --git a/src/mixin.zig b/src/mixin.zig index 69d7d46..46c2dfa 100644 --- a/src/mixin.zig +++ b/src/mixin.zig @@ -289,7 +289,7 @@ fn substituteArgs( } // Perform substitution - var result = std.ArrayListUnmanaged(u8){}; + var result = std.ArrayList(u8){}; errdefer result.deinit(allocator); var i: usize = 0; @@ -335,7 +335,7 @@ fn evaluateStringConcat(allocator: Allocator, expr: []const u8) ![]const u8 { // Check if there's a + operator (string concat) _ = mem.indexOf(u8, expr, " + ") orelse return expr; - var result = std.ArrayListUnmanaged(u8){}; + var result = std.ArrayList(u8){}; errdefer result.deinit(allocator); var remaining = expr; @@ -386,7 +386,7 @@ fn bindArguments( bindings: *std.StringHashMapUnmanaged([]const u8), ) MixinError!void { // Parse parameter names from definition: "text, type" or "text, type='primary'" - var param_names = std.ArrayListUnmanaged([]const u8){}; + var param_names = std.ArrayList([]const u8){}; defer param_names.deinit(allocator); var param_iter = mem.splitSequence(u8, params, ","); @@ -409,7 +409,7 @@ fn bindArguments( } // Parse argument values from call: "'Click', 'primary'" or "text='Click'" - var arg_values = std.ArrayListUnmanaged([]const u8){}; + var arg_values = std.ArrayList([]const u8){}; defer arg_values.deinit(allocator); // Simple argument parsing - split by comma but respect quotes @@ -589,12 +589,6 @@ test "bindArguments - with default value in param" { // This is how it appears: params have default, args are the call args try bindArguments(allocator, "text, type=\"primary\"", "\"Click Me\", \"primary\"", &bindings); - std.debug.print("\nBindings:\n", .{}); - var iter = bindings.iterator(); - while (iter.next()) |entry| { - std.debug.print(" {s} = '{s}'\n", .{ entry.key_ptr.*, entry.value_ptr.* }); - } - try std.testing.expectEqualStrings("Click Me", bindings.get("text").?); try std.testing.expectEqualStrings("primary", bindings.get("type").?); } diff --git a/src/parser.zig b/src/parser.zig index 4e54308..d3dce91 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -110,7 +110,7 @@ pub const Node = struct { filename: ?[]const u8 = null, // Block fields - nodes: std.ArrayListUnmanaged(*Node) = .{}, + nodes: std.ArrayList(*Node) = .{}, // NamedBlock additional fields name: ?[]const u8 = null, // Also used for Tag, Mixin, Filter @@ -118,8 +118,8 @@ pub const Node = struct { // Tag fields self_closing: bool = false, - attrs: std.ArrayListUnmanaged(Attribute) = .{}, - attribute_blocks: std.ArrayListUnmanaged(AttributeBlock) = .{}, + attrs: std.ArrayList(Attribute) = .{}, + attribute_blocks: std.ArrayList(AttributeBlock) = .{}, is_inline: bool = false, text_only: bool = false, self_closing_allowed: bool = false, @@ -150,7 +150,7 @@ pub const Node = struct { file: ?FileReference = null, // Include fields - filters: std.ArrayListUnmanaged(*Node) = .{}, + filters: std.ArrayList(*Node) = .{}, // InterpolatedTag fields expr: ?[]const u8 = null, @@ -247,7 +247,7 @@ pub const Parser = struct { allocator: Allocator, tokens: []const Token, pos: usize = 0, - deferred: std.ArrayListUnmanaged(Token) = .{}, + deferred: std.ArrayList(Token) = .{}, filename: ?[]const u8 = null, src: ?[]const u8 = null, in_mixin: usize = 0, @@ -475,7 +475,7 @@ pub const Parser = struct { fn parseText(self: *Parser, allow_block: bool) !*Node { const lineno = self.peek().loc.start.line; - var tags = std.ArrayListUnmanaged(*Node){}; + var tags = std.ArrayList(*Node){}; defer tags.deinit(self.allocator); while (true) { @@ -548,8 +548,8 @@ pub const Parser = struct { } } - fn parseTextHtml(self: *Parser) !std.ArrayListUnmanaged(*Node) { - var nodes = std.ArrayListUnmanaged(*Node){}; + fn parseTextHtml(self: *Parser) !std.ArrayList(*Node) { + var nodes = std.ArrayList(*Node){}; var current_node: ?*Node = null; while (true) { @@ -922,7 +922,7 @@ pub const Parser = struct { const line = tok.loc.start.line; const column = tok.loc.start.column; - var text = std.ArrayListUnmanaged(u8){}; + var text = std.ArrayList(u8){}; defer text.deinit(self.allocator); if (self.peek().type == .start_pipeless_text) { @@ -1095,7 +1095,7 @@ pub const Parser = struct { fn parseIncludeFilter(self: *Parser) !*Node { const tok = try self.expect(.filter); - var filter_attrs = std.ArrayListUnmanaged(Attribute){}; + var filter_attrs = std.ArrayList(Attribute){}; if (self.peek().type == .start_attributes) { filter_attrs = try self.attrs(null); @@ -1115,7 +1115,7 @@ pub const Parser = struct { fn parseFilter(self: *Parser) !*Node { const tok = try self.expect(.filter); - var filter_attrs = std.ArrayListUnmanaged(Attribute){}; + var filter_attrs = std.ArrayList(Attribute){}; if (self.peek().type == .start_attributes) { filter_attrs = try self.attrs(null); @@ -1483,11 +1483,11 @@ pub const Parser = struct { fn tag_(self: *Parser, tag: *Node, self_closing_allowed: bool) !void { var seen_attrs = false; - var attribute_names = std.ArrayListUnmanaged([]const u8){}; + var attribute_names = std.ArrayList([]const u8){}; defer attribute_names.deinit(self.allocator); // Collect class values to merge into single attribute - var class_values = std.ArrayListUnmanaged([]const u8){}; + var class_values = std.ArrayList([]const u8){}; defer class_values.deinit(self.allocator); var class_line: usize = 0; var class_column: usize = 0; @@ -1689,10 +1689,10 @@ pub const Parser = struct { } } - fn attrs(self: *Parser, attribute_names: ?*std.ArrayListUnmanaged([]const u8)) !std.ArrayListUnmanaged(Attribute) { + fn attrs(self: *Parser, attribute_names: ?*std.ArrayList([]const u8)) !std.ArrayList(Attribute) { _ = try self.expect(.start_attributes); - var result = std.ArrayListUnmanaged(Attribute){}; + var result = std.ArrayList(Attribute){}; var tok = self.advance(); while (tok.type == .attribute) { diff --git a/src/runtime.zig b/src/runtime.zig index a2d001d..84a73a7 100644 --- a/src/runtime.zig +++ b/src/runtime.zig @@ -1,7 +1,7 @@ const std = @import("std"); const mem = std.mem; const Allocator = std.mem.Allocator; -const ArrayListUnmanaged = std.ArrayListUnmanaged; +const ArrayList = std.ArrayList; // ============================================================================ // Pug Runtime - HTML generation utilities @@ -45,7 +45,7 @@ pub fn escape(allocator: Allocator, html: []const u8) ![]const u8 { return try allocator.dupe(u8, html); } - var result: ArrayListUnmanaged(u8) = .{}; + var result: ArrayList(u8) = .{}; errdefer result.deinit(allocator); for (html) |c| { @@ -84,7 +84,7 @@ pub fn style(allocator: Allocator, val: StyleValue) ![]const u8 { return try allocator.dupe(u8, s); }, .object => |props| { - var result: ArrayListUnmanaged(u8) = .{}; + var result: ArrayList(u8) = .{}; errdefer result.deinit(allocator); for (props) |prop| { @@ -111,7 +111,7 @@ pub const AttrValue = union(enum) { /// Returns empty string for false/null values. /// For true values, returns terse form " key" or full form " key="key"". pub fn attr(allocator: Allocator, key: []const u8, val: AttrValue, escaped: bool, terse: bool) ![]const u8 { - var result: ArrayListUnmanaged(u8) = .{}; + var result: ArrayList(u8) = .{}; errdefer result.deinit(allocator); try appendAttr(allocator, &result, key, val, escaped, terse); if (result.items.len == 0) { @@ -122,7 +122,7 @@ pub fn attr(allocator: Allocator, key: []const u8, val: AttrValue, escaped: bool /// Append attribute directly to output buffer - avoids intermediate allocations /// This is the preferred method for rendering attributes in hot paths -pub fn appendAttr(allocator: Allocator, output: *ArrayListUnmanaged(u8), key: []const u8, val: AttrValue, escaped: bool, terse: bool) !void { +pub fn appendAttr(allocator: Allocator, output: *ArrayList(u8), key: []const u8, val: AttrValue, escaped: bool, terse: bool) !void { switch (val) { .none => return, .boolean => |b| { @@ -186,7 +186,7 @@ pub const ClassCondition = struct { /// Arrays are flattened, objects include keys with truthy values. /// Optimized to minimize allocations by writing directly to result buffer. pub fn classes(allocator: Allocator, val: ClassValue, escaping: ?[]const bool) ![]const u8 { - var result: ArrayListUnmanaged(u8) = .{}; + var result: ArrayList(u8) = .{}; errdefer result.deinit(allocator); try classesInternal(allocator, val, escaping, &result, 0); @@ -204,7 +204,7 @@ fn classesInternal( allocator: Allocator, val: ClassValue, escaping: ?[]const bool, - result: *ArrayListUnmanaged(u8), + result: *ArrayList(u8), depth: usize, ) !void { switch (val) { @@ -236,7 +236,7 @@ fn classesInternal( const had_content = start_len > 0; // Temporarily collect the class string - var temp: ArrayListUnmanaged(u8) = .{}; + var temp: ArrayList(u8) = .{}; defer temp.deinit(allocator); try classesInternal(allocator, item, null, &temp, depth + 1); @@ -256,7 +256,7 @@ fn classesInternal( /// Append escaped HTML directly to result buffer (avoids intermediate allocation) /// Public for use by codegen and other modules -pub fn appendEscaped(allocator: Allocator, result: *ArrayListUnmanaged(u8), html: []const u8) !void { +pub fn appendEscaped(allocator: Allocator, result: *ArrayList(u8), html: []const u8) !void { for (html) |c| { if (escapeChar(c)) |escaped| { try result.appendSlice(allocator, escaped); @@ -350,7 +350,7 @@ pub fn isHtmlEntity(str: []const u8) bool { /// Escape for text content - escapes < > & (NOT quotes) /// Preserves existing HTML entities like ’ or & /// Shared across codegen.zig and template.zig. -pub fn appendTextEscaped(allocator: Allocator, output: *ArrayListUnmanaged(u8), str: []const u8) Allocator.Error!void { +pub fn appendTextEscaped(allocator: Allocator, output: *ArrayList(u8), str: []const u8) Allocator.Error!void { var i: usize = 0; while (i < str.len) { const c = str[i]; @@ -396,7 +396,7 @@ pub const AttrEntry = struct { /// Render multiple attributes. /// Class attributes are processed specially and placed first. pub fn attrs(allocator: Allocator, entries: []const AttrEntry, terse: bool) ![]const u8 { - var result: ArrayListUnmanaged(u8) = .{}; + var result: ArrayList(u8) = .{}; errdefer result.deinit(allocator); // First pass: find and render class attribute @@ -459,7 +459,7 @@ pub const MergedValue = struct { key: []const u8, value: MergeValue, allocator: Allocator, - owned_strings: ArrayListUnmanaged([]const u8), + owned_strings: ArrayList([]const u8), pub fn deinit(self: *MergedValue) void { for (self.owned_strings.items) |s| { @@ -474,7 +474,7 @@ fn ensureTrailingSemicolon(allocator: Allocator, s: []const u8) ![]const u8 { if (s.len == 0) return try allocator.dupe(u8, ""); if (s[s.len - 1] == ';') return try allocator.dupe(u8, s); - var result: ArrayListUnmanaged(u8) = .{}; + var result: ArrayList(u8) = .{}; errdefer result.deinit(allocator); try result.appendSlice(allocator, s); try result.append(allocator, ';'); @@ -495,9 +495,9 @@ fn styleToString(allocator: Allocator, val: StyleValue) ![]const u8 { /// Merged attributes result with O(1) lookups for class/style pub const MergedAttrs = struct { allocator: Allocator, - entries: ArrayListUnmanaged(MergedAttrEntry), - owned_strings: ArrayListUnmanaged([]const u8), - owned_class_arrays: ArrayListUnmanaged([][]const u8), + entries: ArrayList(MergedAttrEntry), + owned_strings: ArrayList([]const u8), + owned_class_arrays: ArrayList([][]const u8), // O(1) index tracking for special keys class_idx: ?usize = null, style_idx: ?usize = null, @@ -751,7 +751,7 @@ fn mergeStyleValue(result: *MergedAttrs, idx: usize, value: MergedAttrValue) !vo const s2 = try ensureTrailingSemicolon(allocator, s); defer allocator.free(s2); - var combined: ArrayListUnmanaged(u8) = .{}; + var combined: ArrayList(u8) = .{}; errdefer combined.deinit(allocator); try combined.appendSlice(allocator, s1); try combined.appendSlice(allocator, s2); @@ -842,7 +842,7 @@ pub const PugError = struct { }; fn formatErrorMessage(allocator: Allocator, err_message: []const u8, filename: ?[]const u8, line: usize, src: []const u8) ![]const u8 { - var result: ArrayListUnmanaged(u8) = .{}; + var result: ArrayList(u8) = .{}; errdefer result.deinit(allocator); // Add filename and line diff --git a/src/strip_comments.zig b/src/strip_comments.zig index 0d44e0c..2348e1e 100644 --- a/src/strip_comments.zig +++ b/src/strip_comments.zig @@ -44,7 +44,7 @@ pub const StripCommentsError = error{ // ============================================================================ pub const StripCommentsResult = struct { - tokens: std.ArrayListUnmanaged(Token), + tokens: std.ArrayList(Token), err: ?PugError = null, pub fn deinit(self: *StripCommentsResult, allocator: Allocator) void { diff --git a/src/template.zig b/src/template.zig index bfc0a83..ac7ccce 100644 --- a/src/template.zig +++ b/src/template.zig @@ -97,7 +97,7 @@ pub fn renderWithData(allocator: Allocator, source: []const u8, data: anytype) ! /// Render a pre-parsed AST with data. Use this for better performance when /// rendering the same template multiple times - parse once, render many. pub fn renderAst(allocator: Allocator, ast: *Node, data: anytype) ![]const u8 { - var output = std.ArrayListUnmanaged(u8){}; + var output = std.ArrayList(u8){}; errdefer output.deinit(allocator); // Detect doctype to set terse mode @@ -122,7 +122,7 @@ pub fn renderAstWithMixins(allocator: Allocator, ast: *Node, data: anytype, regi /// Render a pre-parsed AST with data, mixin registry, and render options. pub fn renderAstWithMixinsAndOptions(allocator: Allocator, ast: *Node, data: anytype, registry: *const MixinRegistry, options: RenderOptions) ![]const u8 { - var output = std.ArrayListUnmanaged(u8){}; + var output = std.ArrayList(u8){}; errdefer output.deinit(allocator); // Detect doctype to set terse mode @@ -213,14 +213,14 @@ fn detectDoctype(node: *Node, ctx: *RenderContext) void { const whitespace_sensitive_tags = runtime.whitespace_sensitive_tags; /// Write indentation (two spaces per level) -fn writeIndent(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), level: u32) Allocator.Error!void { +fn writeIndent(allocator: Allocator, output: *std.ArrayList(u8), level: u32) Allocator.Error!void { var i: u32 = 0; while (i < level) : (i += 1) { try output.appendSlice(allocator, " "); } } -fn renderNode(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), node: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { +fn renderNode(allocator: Allocator, output: *std.ArrayList(u8), node: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { switch (node.type) { .Block, .NamedBlock => { for (node.nodes.items) |child| { @@ -251,7 +251,7 @@ fn renderNode(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), node: * } } -fn renderTag(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), tag: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { +fn renderTag(allocator: Allocator, output: *std.ArrayList(u8), tag: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { const name = tag.name orelse "div"; const is_whitespace_sensitive = whitespace_sensitive_tags.has(name); @@ -393,13 +393,13 @@ fn evaluateAttrValue(allocator: Allocator, val: ?[]const u8, data: anytype) !run return .{ .string = v }; } -fn renderText(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), text: *Node, data: anytype) Allocator.Error!void { +fn renderText(allocator: Allocator, output: *std.ArrayList(u8), text: *Node, data: anytype) Allocator.Error!void { if (text.val) |val| { try processInterpolation(allocator, output, val, false, data); } } -fn renderCode(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), code: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { +fn renderCode(allocator: Allocator, output: *std.ArrayList(u8), code: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { if (code.buffer) { if (code.val) |val| { // Check if it's a string literal (quoted) @@ -441,7 +441,7 @@ fn renderCode(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), code: * } /// Render mixin definition or call -fn renderMixin(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), node: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { +fn renderMixin(allocator: Allocator, output: *std.ArrayList(u8), node: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { // Mixin definitions are skipped (only mixin calls render) if (!node.call) return; @@ -494,7 +494,7 @@ fn bindMixinArguments( bindings: *std.StringHashMapUnmanaged([]const u8), ) !void { // Parse parameter names from definition: "text, type" or "text, type='primary'" - var param_names = std.ArrayListUnmanaged([]const u8){}; + var param_names = std.ArrayList([]const u8){}; defer param_names.deinit(allocator); var param_iter = std.mem.splitSequence(u8, params, ","); @@ -517,7 +517,7 @@ fn bindMixinArguments( } // Parse argument values from call: "'Click', 'primary'" or "text='Click'" - var arg_values = std.ArrayListUnmanaged([]const u8){}; + var arg_values = std.ArrayList([]const u8){}; defer arg_values.deinit(allocator); // Simple argument parsing - split by comma but respect quotes @@ -572,7 +572,7 @@ fn stripQuotes(val: []const u8) []const u8 { return val; } -fn renderEach(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), each: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { +fn renderEach(allocator: Allocator, output: *std.ArrayList(u8), each: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { const collection_name = each.obj orelse return; const item_name = each.val orelse "item"; _ = item_name; @@ -623,7 +623,7 @@ fn renderEach(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), each: * } } -fn renderNodeWithItem(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), node: *Node, data: anytype, item: []const u8, ctx: *const RenderContext) Allocator.Error!void { +fn renderNodeWithItem(allocator: Allocator, output: *std.ArrayList(u8), node: *Node, data: anytype, item: []const u8, ctx: *const RenderContext) Allocator.Error!void { switch (node.type) { .Block, .NamedBlock => { for (node.nodes.items) |child| { @@ -649,7 +649,7 @@ fn renderNodeWithItem(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), } } -fn renderTagWithItem(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), tag: *Node, data: anytype, item: []const u8, ctx: *const RenderContext) Allocator.Error!void { +fn renderTagWithItem(allocator: Allocator, output: *std.ArrayList(u8), tag: *Node, data: anytype, item: []const u8, ctx: *const RenderContext) Allocator.Error!void { const name = tag.name orelse "div"; try output.appendSlice(allocator, "<"); @@ -698,14 +698,14 @@ fn renderTagWithItem(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), } } -fn renderTextWithItem(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), text: *Node, item: []const u8) Allocator.Error!void { +fn renderTextWithItem(allocator: Allocator, output: *std.ArrayList(u8), text: *Node, item: []const u8) Allocator.Error!void { if (text.val) |val| { try runtime.appendEscaped(allocator, output, val); _ = item; } } -fn processInterpolationWithItem(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), text: []const u8, escape: bool, data: anytype, item: []const u8) Allocator.Error!void { +fn processInterpolationWithItem(allocator: Allocator, output: *std.ArrayList(u8), text: []const u8, escape: bool, data: anytype, item: []const u8) Allocator.Error!void { _ = data; var i: usize = 0; while (i < text.len) { @@ -740,7 +740,7 @@ fn processInterpolationWithItem(allocator: Allocator, output: *std.ArrayListUnma } } -fn renderComment(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), comment: *Node) Allocator.Error!void { +fn renderComment(allocator: Allocator, output: *std.ArrayList(u8), comment: *Node) Allocator.Error!void { if (!comment.buffer) return; try output.appendSlice(allocator, ""); } -fn renderBlockComment(allocator: Allocator, output: *std.ArrayListUnmanaged(u8), comment: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { +fn renderBlockComment(allocator: Allocator, output: *std.ArrayList(u8), comment: *Node, data: anytype, ctx: *const RenderContext) Allocator.Error!void { if (!comment.buffer) return; try output.appendSlice(allocator, "