Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

renderArrayInit

Render.renderArrayInit
fn renderArrayInit(
    r: *Render,
    array_init: Ast.full.ArrayInit,
    space: Space,
) Error!void

File

lib/std/zig/Ast/Render.zig:2104

Code

fn renderArrayInit(
    r: *Render,
    array_init: Ast.full.ArrayInit,
    space: Space,
) Error!void {
    const tree = r.tree;
    const ais = r.ais;
    const gpa = r.gpa;

    if (array_init.ast.type_expr.unwrap()) |type_expr| {
        try renderExpression(r, type_expr, .none); // T
    } else {
        try renderToken(r, array_init.ast.lbrace - 1, .none); // .
    }

    if (array_init.ast.elements.len == 0) {
        try ais.pushIndent(.normal);
        try renderToken(r, array_init.ast.lbrace, .none); // lbrace
        ais.popIndent();
        return renderToken(r, array_init.ast.lbrace + 1, space); // rbrace
    }

    const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1];
    const last_elem_token = tree.lastToken(last_elem);
    const trailing_comma = tree.tokenTag(last_elem_token + 1) == .comma;
    const rbrace = if (trailing_comma) last_elem_token + 2 else last_elem_token + 1;
    assert(tree.tokenTag(rbrace) == .r_brace);

    if (array_init.ast.elements.len == 1) {
        const only_elem = array_init.ast.elements[0];
        const first_token = tree.firstToken(only_elem);
        if (tree.tokenTag(first_token) != .multiline_string_literal_line and
            !anythingBetween(tree, last_elem_token, rbrace))
        {
            try renderToken(r, array_init.ast.lbrace, .none);
            try renderExpression(r, only_elem, .none);
            return renderToken(r, rbrace, space);
        }
    }

    const contains_comment = hasComment(tree, array_init.ast.lbrace, rbrace);
    const contains_multiline_string = hasMultilineString(tree, array_init.ast.lbrace, rbrace);

    if (!trailing_comma and !contains_comment and !contains_multiline_string) {
        // Render all on one line, no trailing comma.
        if (array_init.ast.elements.len == 1) {
            // If there is only one element, we don't use spaces
            try renderToken(r, array_init.ast.lbrace, .none);
            try renderExpression(r, array_init.ast.elements[0], .none);
        } else {
            try renderToken(r, array_init.ast.lbrace, .space);
            for (array_init.ast.elements) |elem| {
                try renderExpression(r, elem, .comma_space);
            }
        }
        return renderToken(r, last_elem_token + 1, space); // rbrace
    }

    try ais.pushIndent(.normal);
    try renderToken(r, array_init.ast.lbrace, .newline);
    try ais.pushSpace(.comma);

    const expr_widths = try gpa.alloc(enum(usize) {
        /// The expression contains non-printable characters (e.g. unicode / newlines)
        /// or has formatting disabled at the start or end.
        nonprint = std.math.maxInt(usize),
        _,
    }, array_init.ast.elements.len);
    defer gpa.free(expr_widths);
    {
        var buf: Writer.Allocating = .init(gpa);
        defer buf.deinit();
        var sub_ais: AutoIndentingStream = .init(gpa, &buf.writer, indent_delta);
        sub_ais.disabled_offset = ais.disabled_offset;
        defer sub_ais.deinit();
        var sub_r: Render = .{
            .gpa = r.gpa,
            .ais = &sub_ais,
            .tree = r.tree,
            .fixups = r.fixups,
        };
        for (array_init.ast.elements, expr_widths) |e, *width| {
            const begin_disabled = sub_ais.disabled_offset != null;
            // `.skip` space so trailing commments aren't included
            try renderExpressionComma(&sub_r, e, .skip);
            if (!begin_disabled and sub_ais.disabled_offset == null) {
                const w = buf.written();
                width.* = for (w) |c| {
                    if (!std.ascii.isPrint(c))
                        break .nonprint;
                } else @fromBackingInt(@intCast(w.len - @intFromBool(w[w.len - 1] == ',')));
            } else {
                width.* = .nonprint;
            }

            // Write trailing comments since they may enable/disable zig fmt
            buf.clearRetainingCapacity();
            var after_expr = tree.lastToken(e);
            after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
            try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none);

            buf.clearRetainingCapacity();
            // The following are needed to make sure isLineOverIndented is not influenced by
            // the previous element.
            sub_ais.indent_count = 0;
            sub_ais.applied_indent = 0;
        }
    }

    var remaining_exprs = array_init.ast.elements;
    var remaining_widths = expr_widths;
    while (remaining_exprs.len != 0) {
        var row_size: usize = 1;
        for (1.., remaining_exprs, remaining_widths) |len, e, w| {
            if (w == .nonprint) break;
            row_size = len;

            var after_expr = tree.lastToken(e);
            after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
            assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace);
            if (!tree.tokensOnSameLine(after_expr, after_expr + 1))
                break;
        } else {
            // All the expressions are on the same line.
            // However, if there is a trailing comma, we put them each on their own line.
            if (tree.tokenTag(rbrace - 1) == .comma)
                row_size = 1;
        }

        // Determine the size of this section
        const section_end = end: {
            var line_start = row_size; // Start after the first row to ignore comments on it
            break :end for (line_start.., remaining_exprs[line_start..]) |i, e| {
                const expr_first = tree.firstToken(e);
                // Any nonprint character terminates the line because they are always put on their
                // own line, so they will not end up on the same line as the trailing comment.
                if (expr_widths[i - 1] == .nonprint or !tree.tokensOnSameLine(expr_first - 1, expr_first)) {
                    line_start = i;
                }

                var after_expr = tree.lastToken(e);
                after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
                assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace);
                if (hasTrailingComment(tree, after_expr))
                    break line_start;
            } else remaining_exprs.len;
        };
        const section_exprs = remaining_exprs[0..section_end];
        const section_widths = remaining_widths[0..section_end];
        remaining_exprs = remaining_exprs[section_end..];
        remaining_widths = remaining_widths[section_end..];

        // Determine the width of each column
        var col_widths = try gpa.alloc(usize, row_size);
        defer gpa.free(col_widths);
        @memset(col_widths, 0);

        var col: usize = 0;
        for (section_widths) |w| {
            if (w == .nonprint) {
                col = 0;
                continue;
            }
            col_widths[col] = @max(col_widths[col], @backingInt(w));
            col += 1;
            if (col == row_size) {
                col = 0;
            }
        }

        // Render each expression
        col = 0;
        for (0.., section_exprs, section_widths) |i, e, w| {
            if (i + 1 == section_end or col + 1 == row_size or
                w == .nonprint or section_widths[i + 1] == .nonprint)
            {
                try renderExpression(r, e, .comma);
                col = 0;
                if (i + 1 != section_end) {
                    try renderExtraNewline(r, section_exprs[i + 1]);
                }
            } else {
                try renderExpression(r, e, .comma_space);
                try ais.splatByteAll(' ', col_widths[col] - @backingInt(w));
                col += 1;
            }
        }
    }

    ais.popSpace();
    ais.popIndent();
    return renderToken(r, rbrace, space); // rbrace
}