feature. See also
. The project being documented here (as the example) is the Zig library itself.
Render.renderArrayInit
fn renderArrayInit(
r: *Render,
array_init: Ast.full.ArrayInit,
space: Space,
) Error!void
File
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);
} 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);
ais.popIndent();
return renderToken(r, array_init.ast.lbrace + 1, space);
}
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) {
if (array_init.ast.elements.len == 1) {
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);
}
try ais.pushIndent(.normal);
try renderToken(r, array_init.ast.lbrace, .newline);
try ais.pushSpace(.comma);
const expr_widths = try gpa.alloc(enum(usize) {
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;
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;
}
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 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 {
// However, if there is a trailing comma, we put them each on their own line.
if (tree.tokenTag(rbrace - 1) == .comma)
row_size = 1;
}
const section_end = end: {
var line_start = row_size;
break :end for (line_start.., remaining_exprs[line_start..]) |i, e| {
const expr_first = tree.firstToken(e);
// 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..];
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;
}
}
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);
}