authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-02-05 13:15:42+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-05 10:51:45-08:00
log6f3b93e2e8e5cc8c98d473bef4804c61abc4a196
tree457433ec0b0b4a893b284d2cb99d944b5a9359db
parent3e960cfffea8083b1948ba295d1a85964b5afa25

zig fmt: struct and anon array initialization


4 files changed, 302 insertions(+), 247 deletions(-)

lib/std/zig/ast.zig+86-11
......@@ -212,8 +212,6 @@ pub const Tree = struct {
212212 .Try,
213213 .Await,
214214 .OptionalType,
215 .ArrayInitDotTwo,
216 .ArrayInitDot,
217215 .Switch,
218216 .IfSimple,
219217 .If,
......@@ -250,9 +248,12 @@ pub const Tree = struct {
250248 .FnProto,
251249 => return main_tokens[n],
252250
251 .ArrayInitDot,
252 .ArrayInitDotTwo,
253 .ArrayInitDotTwoComma,
254 .StructInitDot,
253255 .StructInitDotTwo,
254256 .StructInitDotTwoComma,
255 .StructInitDot,
256257 => return main_tokens[n] - 1,
257258
258259 .Catch,
......@@ -304,6 +305,7 @@ pub const Tree = struct {
304305 .ArrayInitOne,
305306 .ArrayInit,
306307 .StructInitOne,
308 .StructInit,
307309 .CallOne,
308310 .Call,
309311 .SwitchCaseOne,
......@@ -367,7 +369,6 @@ pub const Tree = struct {
367369 .PtrTypeSentinel => unreachable, // TODO
368370 .PtrType => unreachable, // TODO
369371 .SliceType => unreachable, // TODO
370 .StructInit => unreachable, // TODO
371372 .SwitchCaseMulti => unreachable, // TODO
372373 .WhileSimple => unreachable, // TODO
373374 .WhileCont => unreachable, // TODO
......@@ -506,6 +507,7 @@ pub const Tree = struct {
506507 n = datas[n].rhs;
507508 },
508509
510 .ArrayInitDotTwo,
509511 .BuiltinCallTwo,
510512 .BlockTwo,
511513 .StructInitDotTwo,
......@@ -519,7 +521,9 @@ pub const Tree = struct {
519521 return main_tokens[n] + end_offset;
520522 }
521523 },
522 .StructInitDotTwoComma => {
524 .ArrayInitDotTwoComma,
525 .StructInitDotTwoComma,
526 => {
523527 end_offset += 2; // for the comma + rbrace
524528 if (datas[n].rhs != 0) {
525529 n = datas[n].rhs;
......@@ -590,13 +594,16 @@ pub const Tree = struct {
590594 // require recursion due to the optional comma followed by rbrace.
591595 // TODO follow the pattern set by StructInitDotTwoComma which will allow
592596 // lastToken to work for all of these.
597 .ArrayInit => unreachable,
598 .ArrayInitOne => unreachable,
599 .ArrayInitDot => unreachable,
600 .StructInit => unreachable,
601 .StructInitOne => unreachable,
593602 .StructInitDot => unreachable,
594603 .ContainerFieldInit => unreachable,
595604 .ContainerFieldAlign => unreachable,
596605 .ContainerField => unreachable,
597606
598 .ArrayInitDotTwo => unreachable, // TODO
599 .ArrayInitDot => unreachable, // TODO
600607 .Switch => unreachable, // TODO
601608 .If => unreachable, // TODO
602609 .Continue => unreachable, // TODO
......@@ -606,9 +613,6 @@ pub const Tree = struct {
606613 .Asm => unreachable, // TODO
607614 .SliceOpen => unreachable, // TODO
608615 .Slice => unreachable, // TODO
609 .ArrayInitOne => unreachable, // TODO
610 .ArrayInit => unreachable, // TODO
611 .StructInitOne => unreachable, // TODO
612616 .SwitchCaseOne => unreachable, // TODO
613617 .SwitchRange => unreachable, // TODO
614618 .FnDecl => unreachable, // TODO
......@@ -618,7 +622,6 @@ pub const Tree = struct {
618622 .PtrTypeSentinel => unreachable, // TODO
619623 .PtrType => unreachable, // TODO
620624 .SliceType => unreachable, // TODO
621 .StructInit => unreachable, // TODO
622625 .SwitchCaseMulti => unreachable, // TODO
623626 .WhileCont => unreachable, // TODO
624627 .While => unreachable, // TODO
......@@ -863,6 +866,65 @@ pub const Tree = struct {
863866 });
864867 }
865868
869 pub fn arrayInitOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) Full.ArrayInit {
870 assert(tree.nodes.items(.tag)[node] == .ArrayInitOne);
871 const data = tree.nodes.items(.data)[node];
872 buffer[0] = data.rhs;
873 const elements = if (data.rhs == 0) buffer[0..0] else buffer[0..1];
874 return .{
875 .ast = .{
876 .lbrace = tree.nodes.items(.main_token)[node],
877 .elements = elements,
878 .type_expr = data.lhs,
879 },
880 };
881 }
882
883 pub fn arrayInitDotTwo(tree: Tree, buffer: *[2]Node.Index, node: Node.Index) Full.ArrayInit {
884 assert(tree.nodes.items(.tag)[node] == .ArrayInitDotTwo or
885 tree.nodes.items(.tag)[node] == .ArrayInitDotTwoComma);
886 const data = tree.nodes.items(.data)[node];
887 buffer.* = .{ data.lhs, data.rhs };
888 const elements = if (data.rhs != 0)
889 buffer[0..2]
890 else if (data.lhs != 0)
891 buffer[0..1]
892 else
893 buffer[0..0];
894 return .{
895 .ast = .{
896 .lbrace = tree.nodes.items(.main_token)[node],
897 .elements = elements,
898 .type_expr = 0,
899 },
900 };
901 }
902
903 pub fn arrayInitDot(tree: Tree, node: Node.Index) Full.ArrayInit {
904 assert(tree.nodes.items(.tag)[node] == .ArrayInitDot);
905 const data = tree.nodes.items(.data)[node];
906 return .{
907 .ast = .{
908 .lbrace = tree.nodes.items(.main_token)[node],
909 .elements = tree.extra_data[data.lhs..data.rhs],
910 .type_expr = 0,
911 },
912 };
913 }
914
915 pub fn arrayInit(tree: Tree, node: Node.Index) Full.ArrayInit {
916 assert(tree.nodes.items(.tag)[node] == .ArrayInit);
917 const data = tree.nodes.items(.data)[node];
918 const elem_range = tree.extraData(data.rhs, Node.SubRange);
919 return .{
920 .ast = .{
921 .lbrace = tree.nodes.items(.main_token)[node],
922 .elements = tree.extra_data[elem_range.start..elem_range.end],
923 .type_expr = data.lhs,
924 },
925 };
926 }
927
866928 fn fullVarDecl(tree: Tree, info: Full.VarDecl.Ast) Full.VarDecl {
867929 const token_tags = tree.tokens.items(.tag);
868930 var result: Full.VarDecl = .{
......@@ -1015,6 +1077,16 @@ pub const Full = struct {
10151077 type_expr: Node.Index,
10161078 };
10171079 };
1080
1081 pub const ArrayInit = struct {
1082 ast: Ast,
1083
1084 pub const Ast = struct {
1085 lbrace: TokenIndex,
1086 elements: []const Node.Index,
1087 type_expr: Node.Index,
1088 };
1089 };
10181090};
10191091
10201092pub const Error = union(enum) {
......@@ -1421,6 +1493,9 @@ pub const Node = struct {
14211493 ArrayInitOne,
14221494 /// `.{lhs, rhs}`. lhs and rhs can be omitted.
14231495 ArrayInitDotTwo,
1496 /// Same as `ArrayInitDotTwo` except there is known to be a trailing comma
1497 /// before the final rbrace.
1498 ArrayInitDotTwoComma,
14241499 /// `.{a, b}`. `sub_list[lhs..rhs]`.
14251500 ArrayInitDot,
14261501 /// `lhs{a, b}`. `sub_range_list[rhs]`. lhs can be omitted which means `.{a, b}`.
lib/std/zig/parse.zig+6-3
......@@ -2536,7 +2536,7 @@ const Parser = struct {
25362536 const comma_one = p.eatToken(.Comma);
25372537 if (p.eatToken(.RBrace)) |_| {
25382538 return p.addNode(.{
2539 .tag = .ArrayInitDotTwo,
2539 .tag = if (comma_one != null) .ArrayInitDotTwoComma else .ArrayInitDotTwo,
25402540 .main_token = lbrace,
25412541 .data = .{
25422542 .lhs = elem_init_one,
......@@ -2553,7 +2553,7 @@ const Parser = struct {
25532553 const comma_two = p.eatToken(.Comma);
25542554 if (p.eatToken(.RBrace)) |_| {
25552555 return p.addNode(.{
2556 .tag = .ArrayInitDotTwo,
2556 .tag = if (comma_one != null) .ArrayInitDotTwoComma else .ArrayInitDotTwo,
25572557 .main_token = lbrace,
25582558 .data = .{
25592559 .lhs = elem_init_one,
......@@ -2576,7 +2576,10 @@ const Parser = struct {
25762576 if (next == 0) break;
25772577 try init_list.append(next);
25782578 switch (p.token_tags[p.nextToken()]) {
2579 .Comma => continue,
2579 .Comma => {
2580 if (p.eatToken(.RBrace)) |_| break;
2581 continue;
2582 },
25802583 .RBrace => break,
25812584 .Colon, .RParen, .RBracket => {
25822585 p.tok_i -= 1;
lib/std/zig/parser_test.zig+154-18
......@@ -336,24 +336,160 @@ test "zig fmt: nosuspend block" {
336336// \\
337337// );
338338//}
339//
340//test "zig fmt: anon struct literal syntax" {
341// try testCanonical(
342// \\const x = .{
343// \\ .a = b,
344// \\ .c = d,
345// \\};
346// \\
347// );
348//}
349//
350//test "zig fmt: anon list literal syntax" {
351// try testCanonical(
352// \\const x = .{ a, b, c };
353// \\
354// );
355//}
356//
339
340test "zig fmt: anon struct literal 1 element" {
341 try testCanonical(
342 \\const x = .{ .a = b };
343 \\
344 );
345}
346
347test "zig fmt: anon struct literal 1 element comma" {
348 try testCanonical(
349 \\const x = .{
350 \\ .a = b,
351 \\};
352 \\
353 );
354}
355
356test "zig fmt: anon struct literal 2 element" {
357 try testCanonical(
358 \\const x = .{ .a = b, .c = d };
359 \\
360 );
361}
362
363test "zig fmt: anon struct literal 2 element comma" {
364 try testCanonical(
365 \\const x = .{
366 \\ .a = b,
367 \\ .c = d,
368 \\};
369 \\
370 );
371}
372
373test "zig fmt: anon struct literal 3 element" {
374 try testCanonical(
375 \\const x = .{ .a = b, .c = d, .e = f };
376 \\
377 );
378}
379
380test "zig fmt: anon struct literal 3 element comma" {
381 try testCanonical(
382 \\const x = .{
383 \\ .a = b,
384 \\ .c = d,
385 \\ .e = f,
386 \\};
387 \\
388 );
389}
390
391test "zig fmt: struct literal 1 element" {
392 try testCanonical(
393 \\const x = X{ .a = b };
394 \\
395 );
396}
397
398test "zig fmt: struct literal 1 element comma" {
399 try testCanonical(
400 \\const x = X{
401 \\ .a = b,
402 \\};
403 \\
404 );
405}
406
407test "zig fmt: struct literal 2 element" {
408 try testCanonical(
409 \\const x = X{ .a = b, .c = d };
410 \\
411 );
412}
413
414test "zig fmt: struct literal 2 element comma" {
415 try testCanonical(
416 \\const x = X{
417 \\ .a = b,
418 \\ .c = d,
419 \\};
420 \\
421 );
422}
423
424test "zig fmt: struct literal 3 element" {
425 try testCanonical(
426 \\const x = X{ .a = b, .c = d, .e = f };
427 \\
428 );
429}
430
431test "zig fmt: struct literal 3 element comma" {
432 try testCanonical(
433 \\const x = X{
434 \\ .a = b,
435 \\ .c = d,
436 \\ .e = f,
437 \\};
438 \\
439 );
440}
441
442test "zig fmt: anon list literal 1 element" {
443 try testCanonical(
444 \\const x = .{a};
445 \\
446 );
447}
448
449test "zig fmt: anon list literal 1 element comma" {
450 try testCanonical(
451 \\const x = .{
452 \\ a,
453 \\};
454 \\
455 );
456}
457
458test "zig fmt: anon list literal 2 element" {
459 try testCanonical(
460 \\const x = .{ a, b };
461 \\
462 );
463}
464
465test "zig fmt: anon list literal 2 element comma" {
466 try testCanonical(
467 \\const x = .{
468 \\ a,
469 \\ b,
470 \\};
471 \\
472 );
473}
474
475test "zig fmt: anon list literal 3 element" {
476 try testCanonical(
477 \\const x = .{ a, b, c };
478 \\
479 );
480}
481
482test "zig fmt: anon list literal 3 element comma" {
483 try testCanonical(
484 \\const x = .{
485 \\ a,
486 \\ b,
487 \\ c,
488 \\};
489 \\
490 );
491}
492
357493//test "zig fmt: async function" {
358494// try testCanonical(
359495// \\pub const Server = struct {
lib/std/zig/render.zig+56-215
......@@ -503,221 +503,16 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
503503 // return renderExpression(ais, tree, slice_type.rhs, space);
504504 //},
505505
506 .ArrayInitOne => unreachable, // TODO
507 .ArrayInitDotTwo => unreachable, // TODO
508 .ArrayInitDot => unreachable, // TODO
509 .ArrayInit => unreachable, // TODO
510 //.ArrayInitializer, .ArrayInitializerDot => {
511 // var rtoken: ast.TokenIndex = undefined;
512 // var exprs: []ast.Node.Index = undefined;
513 // const lhs: union(enum) { dot: ast.TokenIndex, node: ast.Node.Index } = switch (base.tag) {
514 // .ArrayInitializerDot => blk: {
515 // const casted = @fieldParentPtr(ast.Node.ArrayInitializerDot, "base", base);
516 // rtoken = casted.rtoken;
517 // exprs = casted.list();
518 // break :blk .{ .dot = casted.dot };
519 // },
520 // .ArrayInitializer => blk: {
521 // const casted = @fieldParentPtr(ast.Node.ArrayInitializer, "base", base);
522 // rtoken = casted.rtoken;
523 // exprs = casted.list();
524 // break :blk .{ .node = casted.lhs };
525 // },
526 // else => unreachable,
527 // };
528
529 // const lbrace = switch (lhs) {
530 // .dot => |dot| tree.nextToken(dot),
531 // .node => |node| tree.nextToken(node.lastToken()),
532 // };
533
534 // switch (lhs) {
535 // .dot => |dot| try renderToken(ais, tree, dot, Space.None),
536 // .node => |node| try renderExpression(ais, tree, node, Space.None),
537 // }
538
539 // if (exprs.len == 0) {
540 // try renderToken(ais, tree, lbrace, Space.None);
541 // return renderToken(ais, tree, rtoken, space);
542 // }
543
544 // if (exprs.len == 1 and exprs[0].tag != .MultilineStringLiteral and tree.token_tags[exprs[0].*.lastToken() + 1] == .RBrace) {
545 // const expr = exprs[0];
546
547 // try renderToken(ais, tree, lbrace, Space.None);
548 // try renderExpression(ais, tree, expr, Space.None);
549 // return renderToken(ais, tree, rtoken, space);
550 // }
551
552 // // scan to find row size
553 // if (rowSize(tree, exprs, rtoken) != null) {
554 // {
555 // ais.pushIndentNextLine();
556 // defer ais.popIndent();
557 // try renderToken(ais, tree, lbrace, Space.Newline);
558
559 // var expr_index: usize = 0;
560 // while (rowSize(tree, exprs[expr_index..], rtoken)) |row_size| {
561 // const row_exprs = exprs[expr_index..];
562 // // A place to store the width of each expression and its column's maximum
563 // var widths = try allocator.alloc(usize, row_exprs.len + row_size);
564 // defer allocator.free(widths);
565 // mem.set(usize, widths, 0);
566
567 // var expr_newlines = try allocator.alloc(bool, row_exprs.len);
568 // defer allocator.free(expr_newlines);
569 // mem.set(bool, expr_newlines, false);
570
571 // var expr_widths = widths[0 .. widths.len - row_size];
572 // var column_widths = widths[widths.len - row_size ..];
573
574 // // Find next row with trailing comment (if any) to end the current section
575 // var section_end = sec_end: {
576 // var this_line_first_expr: usize = 0;
577 // var this_line_size = rowSize(tree, row_exprs, rtoken);
578 // for (row_exprs) |expr, i| {
579 // // Ignore comment on first line of this section
580 // if (i == 0 or tree.tokensOnSameLine(row_exprs[0].firstToken(), expr.lastToken())) continue;
581 // // Track start of line containing comment
582 // if (!tree.tokensOnSameLine(row_exprs[this_line_first_expr].firstToken(), expr.lastToken())) {
583 // this_line_first_expr = i;
584 // this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rtoken);
585 // }
586
587 // const maybe_comma = expr.lastToken() + 1;
588 // const maybe_comment = expr.lastToken() + 2;
589 // if (maybe_comment < tree.token_tags.len) {
590 // if (tree.token_tags[maybe_comma] == .Comma and
591 // tree.token_tags[maybe_comment] == .LineComment and
592 // tree.tokensOnSameLine(expr.lastToken(), maybe_comment))
593 // {
594 // var comment_token_loc = tree.token_locs[maybe_comment];
595 // const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(comment_token_loc), " ").len == 2;
596 // if (!comment_is_empty) {
597 // // Found row ending in comment
598 // break :sec_end i - this_line_size.? + 1;
599 // }
600 // }
601 // }
602 // }
603 // break :sec_end row_exprs.len;
604 // };
605 // expr_index += section_end;
606
607 // const section_exprs = row_exprs[0..section_end];
608
609 // // Null stream for counting the printed length of each expression
610 // var line_find_stream = std.io.findByteWriter('\n', std.io.null_writer);
611 // var counting_stream = std.io.countingWriter(line_find_stream.writer());
612 // var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer());
613
614 // // Calculate size of columns in current section
615 // var column_counter: usize = 0;
616 // var single_line = true;
617 // for (section_exprs) |expr, i| {
618 // if (i + 1 < section_exprs.len) {
619 // counting_stream.bytes_written = 0;
620 // line_find_stream.byte_found = false;
621 // try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
622 // const width = @intCast(usize, counting_stream.bytes_written);
623 // expr_widths[i] = width;
624 // expr_newlines[i] = line_find_stream.byte_found;
625
626 // if (!line_find_stream.byte_found) {
627 // const column = column_counter % row_size;
628 // column_widths[column] = std.math.max(column_widths[column], width);
629
630 // const expr_last_token = expr.*.lastToken() + 1;
631 // const next_expr = section_exprs[i + 1];
632 // const loc = tree.tokenLocation(tree.token_locs[expr_last_token].start, next_expr.*.firstToken());
633
634 // column_counter += 1;
635
636 // if (loc.line != 0) single_line = false;
637 // } else {
638 // single_line = false;
639 // column_counter = 0;
640 // }
641 // } else {
642 // counting_stream.bytes_written = 0;
643 // try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
644 // const width = @intCast(usize, counting_stream.bytes_written);
645 // expr_widths[i] = width;
646 // expr_newlines[i] = line_find_stream.byte_found;
647
648 // if (!line_find_stream.byte_found) {
649 // const column = column_counter % row_size;
650 // column_widths[column] = std.math.max(column_widths[column], width);
651 // }
652 // break;
653 // }
654 // }
655
656 // // Render exprs in current section
657 // column_counter = 0;
658 // var last_col_index: usize = row_size - 1;
659 // for (section_exprs) |expr, i| {
660 // if (i + 1 < section_exprs.len) {
661 // const next_expr = section_exprs[i + 1];
662 // try renderExpression(ais, tree, expr, Space.None);
663
664 // const comma = tree.nextToken(expr.*.lastToken());
665
666 // if (column_counter != last_col_index) {
667 // if (!expr_newlines[i] and !expr_newlines[i + 1]) {
668 // // Neither the current or next expression is multiline
669 // try renderToken(ais, tree, comma, Space.Space); // ,
670 // assert(column_widths[column_counter % row_size] >= expr_widths[i]);
671 // const padding = column_widths[column_counter % row_size] - expr_widths[i];
672 // try ais.writer().writeByteNTimes(' ', padding);
673
674 // column_counter += 1;
675 // continue;
676 // }
677 // }
678 // if (single_line and row_size != 1) {
679 // try renderToken(ais, tree, comma, Space.Space); // ,
680 // continue;
681 // }
682
683 // column_counter = 0;
684 // try renderToken(ais, tree, comma, Space.Newline); // ,
685 // try renderExtraNewline(ais, tree, next_expr);
686 // } else {
687 // const maybe_comma = tree.nextToken(expr.*.lastToken());
688 // if (tree.token_tags[maybe_comma] == .Comma) {
689 // try renderExpression(ais, tree, expr, Space.None); // ,
690 // try renderToken(ais, tree, maybe_comma, Space.Newline); // ,
691 // } else {
692 // try renderExpression(ais, tree, expr, Space.Comma); // ,
693 // }
694 // }
695 // }
696
697 // if (expr_index == exprs.len) {
698 // break;
699 // }
700 // }
701 // }
702
703 // return renderToken(ais, tree, rtoken, space);
704 // }
705
706 // // Single line
707 // try renderToken(ais, tree, lbrace, Space.Space);
708 // for (exprs) |expr, i| {
709 // if (i + 1 < exprs.len) {
710 // const next_expr = exprs[i + 1];
711 // try renderExpression(ais, tree, expr, Space.None);
712 // const comma = tree.nextToken(expr.*.lastToken());
713 // try renderToken(ais, tree, comma, Space.Space); // ,
714 // } else {
715 // try renderExpression(ais, tree, expr, Space.Space);
716 // }
717 // }
718
719 // return renderToken(ais, tree, rtoken, space);
720 //},
506 .ArrayInitOne => {
507 var elements: [1]ast.Node.Index = undefined;
508 return renderArrayInit(ais, tree, tree.arrayInitOne(&elements, node), space);
509 },
510 .ArrayInitDotTwo, .ArrayInitDotTwoComma => {
511 var elements: [2]ast.Node.Index = undefined;
512 return renderArrayInit(ais, tree, tree.arrayInitDotTwo(&elements, node), space);
513 },
514 .ArrayInitDot => return renderArrayInit(ais, tree, tree.arrayInitDot(node), space),
515 .ArrayInit => return renderArrayInit(ais, tree, tree.arrayInit(node), space),
721516
722517 .StructInitOne => {
723518 var fields: [1]ast.Node.Index = undefined;
......@@ -2158,6 +1953,52 @@ fn renderStructInit(
21581953 }
21591954}
21601955
1956fn renderArrayInit(
1957 ais: *Ais,
1958 tree: ast.Tree,
1959 array_init: ast.Full.ArrayInit,
1960 space: Space,
1961) Error!void {
1962 const token_tags = tree.tokens.items(.tag);
1963 if (array_init.ast.type_expr == 0) {
1964 try renderToken(ais, tree, array_init.ast.lbrace - 1, .None); // .
1965 } else {
1966 try renderExpression(ais, tree, array_init.ast.type_expr, .None); // T
1967 }
1968 if (array_init.ast.elements.len == 0) {
1969 try renderToken(ais, tree, array_init.ast.lbrace, .None); // lbrace
1970 return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace
1971 }
1972 const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1];
1973 const last_elem_token = tree.lastToken(last_elem);
1974 if (token_tags[last_elem_token + 1] == .Comma) {
1975 // Render one element per line.
1976 ais.pushIndent();
1977 try renderToken(ais, tree, array_init.ast.lbrace, .Newline);
1978
1979 try renderExpression(ais, tree, array_init.ast.elements[0], .Comma);
1980 for (array_init.ast.elements[1..]) |elem| {
1981 try renderExpressionNewlined(ais, tree, elem, .Comma);
1982 }
1983
1984 ais.popIndent();
1985 return renderToken(ais, tree, last_elem_token + 2, space); // rbrace
1986 } else {
1987 // Render all on one line, no trailing comma.
1988 if (array_init.ast.elements.len == 1) {
1989 // If there is only one element, we don't use spaces
1990 try renderToken(ais, tree, array_init.ast.lbrace, .None);
1991 try renderExpression(ais, tree, array_init.ast.elements[0], .None);
1992 } else {
1993 try renderToken(ais, tree, array_init.ast.lbrace, .Space);
1994 for (array_init.ast.elements) |elem| {
1995 try renderExpression(ais, tree, elem, .CommaSpace);
1996 }
1997 }
1998 return renderToken(ais, tree, last_elem_token + 1, space); // rbrace
1999 }
2000}
2001
21612002/// Render an expression, and the comma that follows it, if it is present in the source.
21622003fn renderExpressionComma(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
21632004 const token_tags = tree.tokens.items(.tag);