authorgravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2025-07-28 14:25:35-04:00
committergravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-03-12 17:25:00-04:00
log99fea4431bdf927e2f17409d087243694e94918f
tree260c2ea7bcd0c7455b3f106e45157167965ab1ec
parentc477df98bbddb35e3abb7d88fbe7e250660458b8

zig fmt: rewrite renderArrayInit

There were just too many bugs. This new implementation supports zig fmt: on/off. It also puts expressions with unicode characters on their own line, which avoids issues with aligning them.

2 files changed, 233 insertions(+), 187 deletions(-)

lib/std/zig/Ast/Render.zig+114-179
...@@ -2457,170 +2457,132 @@ fn renderArrayInit(...@@ -2457,170 +2457,132 @@ fn renderArrayInit(
24572457
2458 try ais.pushIndent(.normal);2458 try ais.pushIndent(.normal);
2459 try renderToken(r, array_init.ast.lbrace, .newline);2459 try renderToken(r, array_init.ast.lbrace, .newline);
24602460 try ais.pushSpace(.comma);
2461 var expr_index: usize = 0;2461
2462 while (true) {2462 const expr_widths = try gpa.alloc(enum(usize) {
2463 const row_size = rowSize(tree, array_init.ast.elements[expr_index..], rbrace);2463 /// The expression contains non-printable characters (e.g. unicode / newlines)
2464 const row_exprs = array_init.ast.elements[expr_index..];2464 /// or has formatting disabled at the start or end.
2465 // A place to store the width of each expression and its column's maximum2465 nonprint = std.math.maxInt(usize),
2466 const widths = try gpa.alloc(usize, row_exprs.len + row_size);2466 _,
2467 defer gpa.free(widths);2467 }, array_init.ast.elements.len);
2468 @memset(widths, 0);2468 defer gpa.free(expr_widths);
24692469 {
2470 const expr_newlines = try gpa.alloc(bool, row_exprs.len);2470 var buf: Writer.Allocating = .init(gpa);
2471 defer gpa.free(expr_newlines);2471 defer buf.deinit();
2472 @memset(expr_newlines, false);2472 var sub_ais: AutoIndentingStream = .init(gpa, &buf.writer, indent_delta);
24732473 sub_ais.disabled_offset = ais.disabled_offset;
2474 const expr_widths = widths[0..row_exprs.len];2474 defer sub_ais.deinit();
2475 const column_widths = widths[row_exprs.len..];2475 var sub_r: Render = .{
2476
2477 // Find next row with trailing comment (if any) to end the current section.
2478 const section_end = sec_end: {
2479 var this_line_first_expr: usize = 0;
2480 var this_line_size = rowSize(tree, row_exprs, rbrace);
2481 for (row_exprs, 0..) |expr, i| {
2482 // Ignore comment on first line of this section.
2483 if (i == 0) continue;
2484 const expr_last_token = tree.lastToken(expr);
2485 if (tree.tokensOnSameLine(tree.firstToken(row_exprs[0]), expr_last_token))
2486 continue;
2487 // Track start of line containing comment.
2488 if (!tree.tokensOnSameLine(tree.firstToken(row_exprs[this_line_first_expr]), expr_last_token)) {
2489 this_line_first_expr = i;
2490 this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rbrace);
2491 }
2492
2493 const maybe_comma = expr_last_token + 1;
2494 if (tree.tokenTag(maybe_comma) == .comma) {
2495 if (hasSameLineComment(tree, maybe_comma))
2496 break :sec_end i - this_line_size + 1;
2497 }
2498 }
2499 break :sec_end row_exprs.len;
2500 };
2501 expr_index += section_end;
2502
2503 const section_exprs = row_exprs[0..section_end];
2504
2505 var sub_expr_buffer: Writer.Allocating = .init(gpa);
2506 defer sub_expr_buffer.deinit();
2507
2508 const sub_expr_buffer_starts = try gpa.alloc(usize, section_exprs.len + 1);
2509 defer gpa.free(sub_expr_buffer_starts);
2510
2511 var auto_indenting_stream: AutoIndentingStream = .init(gpa, &sub_expr_buffer.writer, indent_delta);
2512 defer auto_indenting_stream.deinit();
2513 var sub_render: Render = .{
2514 .gpa = r.gpa,2476 .gpa = r.gpa,
2515 .ais = &auto_indenting_stream,2477 .ais = &sub_ais,
2516 .tree = r.tree,2478 .tree = r.tree,
2517 .fixups = r.fixups,2479 .fixups = r.fixups,
2518 };2480 };
25192481 for (array_init.ast.elements, expr_widths) |e, *width| {
2520 // Calculate size of columns in current section2482 const begin_disabled = sub_ais.disabled_offset != null;
2521 var column_counter: usize = 0;2483 // `.skip` space so trailing commments aren't included
2522 var single_line = true;2484 try renderExpressionComma(&sub_r, e, .skip);
2523 var contains_newline = false;2485 if (!begin_disabled and sub_ais.disabled_offset == null) {
2524 for (section_exprs, 0..) |expr, i| {2486 const w = buf.written();
2525 const start = sub_expr_buffer.written().len;2487 width.* = for (w) |c| {
2526 sub_expr_buffer_starts[i] = start;2488 if (!std.ascii.isPrint(c))
25272489 break .nonprint;
2528 if (i + 1 < section_exprs.len) {2490 } else @enumFromInt(w.len - @intFromBool(w[w.len - 1] == ','));
2529 try renderExpression(&sub_render, expr, .none);
2530 const written = sub_expr_buffer.written();
2531 const width = written.len - start;
2532 const this_contains_newline = mem.findScalar(u8, written[start..], '\n') != null;
2533 contains_newline = contains_newline or this_contains_newline;
2534 expr_widths[i] = width;
2535 expr_newlines[i] = this_contains_newline;
2536
2537 if (!this_contains_newline) {
2538 const column = column_counter % row_size;
2539 column_widths[column] = @max(column_widths[column], width);
2540
2541 const expr_last_token = tree.lastToken(expr) + 1;
2542 const next_expr = section_exprs[i + 1];
2543 column_counter += 1;
2544 if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(next_expr))) single_line = false;
2545 } else {
2546 single_line = false;
2547 column_counter = 0;
2548 }
2549 } else {2491 } else {
2550 try ais.pushSpace(.comma);2492 width.* = .nonprint;
2551 try renderExpression(&sub_render, expr, .comma);2493 }
2552 ais.popSpace();
25532494
2554 const written = sub_expr_buffer.written();2495 // Write trailing comments since they may enable/disable zig fmt
2555 const width = written.len - start - 2;2496 buf.clearRetainingCapacity();
2556 const this_contains_newline = mem.findScalar(u8, written[start .. written.len - 1], '\n') != null;2497 var after_expr = tree.lastToken(e);
2557 contains_newline = contains_newline or this_contains_newline;2498 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
2558 expr_widths[i] = width;2499 try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none);
2559 expr_newlines[i] = contains_newline;
25602500
2561 if (!contains_newline) {2501 buf.clearRetainingCapacity();
2562 const column = column_counter % row_size;
2563 column_widths[column] = @max(column_widths[column], width);
2564 }
2565 }
2566 }2502 }
2567 sub_expr_buffer_starts[section_exprs.len] = sub_expr_buffer.written().len;2503 }
2568
2569 // Render exprs in current section.
2570 column_counter = 0;
2571 for (section_exprs, 0..) |expr, i| {
2572 const start = sub_expr_buffer_starts[i];
2573 const end = sub_expr_buffer_starts[i + 1];
2574 const expr_text = sub_expr_buffer.written()[start..end];
2575 if (!expr_newlines[i]) {
2576 try ais.writeAll(expr_text);
2577 } else {
2578 var by_line = std.mem.splitScalar(u8, expr_text, '\n');
2579 var last_line_was_empty = false;
2580 try ais.writeAll(by_line.first());
2581 while (by_line.next()) |line| {
2582 if (std.mem.startsWith(u8, line, "//") and last_line_was_empty) {
2583 try ais.insertNewline();
2584 } else {
2585 try ais.maybeInsertNewline();
2586 }
2587 last_line_was_empty = (line.len == 0);
2588 try ais.writeAll(line);
2589 }
2590 }
2591
2592 if (i + 1 < section_exprs.len) {
2593 const next_expr = section_exprs[i + 1];
2594 const comma = tree.lastToken(expr) + 1;
25952504
2596 if (column_counter != row_size - 1) {2505 var remaining_exprs = array_init.ast.elements;
2597 if (!expr_newlines[i] and !expr_newlines[i + 1]) {2506 var remaining_widths = expr_widths;
2598 // Neither the current or next expression is multiline2507 while (remaining_exprs.len != 0) {
2599 try renderToken(r, comma, .space); // ,2508 var row_size: usize = 1;
2600 assert(column_widths[column_counter % row_size] >= expr_widths[i]);2509 for (1.., remaining_exprs, remaining_widths) |len, e, w| {
2601 const padding = column_widths[column_counter % row_size] - expr_widths[i];2510 if (w == .nonprint) break;
2602 try ais.splatByteAll(' ', padding);2511 row_size = len;
26032512
2604 column_counter += 1;2513 var after_expr = tree.lastToken(e);
2605 continue;2514 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
2606 }2515 assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace);
2607 }2516 if (!tree.tokensOnSameLine(after_expr, after_expr + 1))
2517 break;
2518 } else {
2519 // All the expressions are on the same line.
2520 // However, if there is a trailing comma, we put them each on their own line.
2521 if (tree.tokenTag(rbrace - 1) == .comma)
2522 row_size = 1;
2523 }
26082524
2609 if (single_line and row_size != 1) {2525 // Determine the size of this section
2610 try renderToken(r, comma, .space); // ,2526 const section_end = end: {
2611 continue;2527 var line_start = row_size; // Start after the first row to ignore comments on it
2528 break :end for (line_start.., remaining_exprs[line_start..]) |i, e| {
2529 const expr_first = tree.firstToken(e);
2530 // Any nonprint character terminates the line because they are always put on their
2531 // own line, so they will not end up on the same line as the trailing comment.
2532 if (expr_widths[i - 1] == .nonprint or !tree.tokensOnSameLine(expr_first - 1, expr_first)) {
2533 line_start = i;
2612 }2534 }
26132535
2614 column_counter = 0;2536 var after_expr = tree.lastToken(e);
2615 try renderToken(r, comma, .newline); // ,2537 after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma);
2616 try renderExtraNewline(r, next_expr);2538 assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace);
2539 if (hasTrailingComment(tree, after_expr))
2540 break line_start;
2541 } else remaining_exprs.len;
2542 };
2543 const section_exprs = remaining_exprs[0..section_end];
2544 const section_widths = remaining_widths[0..section_end];
2545 remaining_exprs = remaining_exprs[section_end..];
2546 remaining_widths = remaining_widths[section_end..];
2547
2548 // Determine the width of each column
2549 var col_widths = try gpa.alloc(usize, row_size);
2550 defer gpa.free(col_widths);
2551 @memset(col_widths, 0);
2552
2553 var col: usize = 0;
2554 for (section_widths) |w| {
2555 if (w == .nonprint) {
2556 col = 0;
2557 continue;
2558 }
2559 col_widths[col] = @max(col_widths[col], @intFromEnum(w));
2560 col += 1;
2561 if (col == row_size) {
2562 col = 0;
2617 }2563 }
2618 }2564 }
26192565
2620 if (expr_index == array_init.ast.elements.len)2566 // Render each expression
2621 break;2567 col = 0;
2568 for (0.., section_exprs, section_widths) |i, e, w| {
2569 if (i + 1 == section_end or col + 1 == row_size or
2570 w == .nonprint or section_widths[i + 1] == .nonprint)
2571 {
2572 try renderExpression(r, e, .comma);
2573 col = 0;
2574 if (i + 1 != section_end) {
2575 try renderExtraNewline(r, section_exprs[i + 1]);
2576 }
2577 } else {
2578 try renderExpression(r, e, .comma_space);
2579 try ais.splatByteAll(' ', col_widths[col] - @intFromEnum(w));
2580 col += 1;
2581 }
2582 }
2622 }2583 }
26232584
2585 ais.popSpace();
2624 ais.popIndent();2586 ais.popIndent();
2625 return renderToken(r, rbrace, space); // rbrace2587 return renderToken(r, rbrace, space); // rbrace
2626}2588}
...@@ -3286,7 +3248,7 @@ fn hasComment(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex)...@@ -3286,7 +3248,7 @@ fn hasComment(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex)
3286 const token: Ast.TokenIndex = @intCast(i);3248 const token: Ast.TokenIndex = @intCast(i);
3287 const start = tree.tokenStart(token) + tree.tokenSlice(token).len;3249 const start = tree.tokenStart(token) + tree.tokenSlice(token).len;
3288 const end = tree.tokenStart(token + 1);3250 const end = tree.tokenStart(token + 1);
3289 if (mem.find(u8, tree.source[start..end], "//") != null) return true;3251 if (mem.findScalar(u8, tree.source[start..end], '/') != null) return true;
3290 }3252 }
32913253
3292 return false;3254 return false;
...@@ -3497,9 +3459,10 @@ fn writeStringLiteralAsIdentifier(r: *Render, token_index: Ast.TokenIndex) !usiz...@@ -3497,9 +3459,10 @@ fn writeStringLiteralAsIdentifier(r: *Render, token_index: Ast.TokenIndex) !usiz
3497 }3459 }
3498}3460}
34993461
3500fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool {3462fn hasTrailingComment(tree: Ast, t: Ast.TokenIndex) bool {
3501 const between_source = tree.source[tree.tokenStart(token_index)..tree.tokenStart(token_index + 1)];3463 const start = tree.tokenStart(t) + tree.tokenSlice(t).len;
3502 for (between_source) |byte| switch (byte) {3464 const between = tree.source[start..tree.tokenStart(t + 1)];
3465 for (between) |byte| switch (byte) {
3503 '\n' => return false,3466 '\n' => return false,
3504 '/' => return true,3467 '/' => return true,
3505 else => continue,3468 else => continue,
...@@ -3511,12 +3474,7 @@ fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool {...@@ -3511,12 +3474,7 @@ fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool {
3511/// start_token and end_token.3474/// start_token and end_token.
3512fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool {3475fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool {
3513 if (start_token + 1 != end_token) return true;3476 if (start_token + 1 != end_token) return true;
3514 const between_source = tree.source[tree.tokenStart(start_token)..tree.tokenStart(start_token + 1)];3477 return hasComment(tree, start_token, end_token);
3515 for (between_source) |byte| switch (byte) {
3516 '/' => return true,
3517 else => continue,
3518 };
3519 return false;
3520}3478}
35213479
3522fn writeFixingWhitespace(w: *Writer, slice: []const u8) Error!void {3480fn writeFixingWhitespace(w: *Writer, slice: []const u8) Error!void {
...@@ -3603,29 +3561,6 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {...@@ -3603,29 +3561,6 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
3603 };3561 };
3604}3562}
36053563
3606// Returns the number of nodes in `exprs` that are on the same line as `rtoken`.
3607fn rowSize(tree: Ast, exprs: []const Ast.Node.Index, rtoken: Ast.TokenIndex) usize {
3608 const first_token = tree.firstToken(exprs[0]);
3609 if (tree.tokensOnSameLine(first_token, rtoken)) {
3610 const maybe_comma = rtoken - 1;
3611 if (tree.tokenTag(maybe_comma) == .comma)
3612 return 1;
3613 return exprs.len; // no newlines
3614 }
3615
3616 var count: usize = 1;
3617 for (exprs, 0..) |expr, i| {
3618 if (i + 1 < exprs.len) {
3619 const expr_last_token = tree.lastToken(expr) + 1;
3620 if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(exprs[i + 1]))) return count;
3621 count += 1;
3622 } else {
3623 return count;
3624 }
3625 }
3626 unreachable;
3627}
3628
3629/// Automatically inserts indentation of written data by keeping3564/// Automatically inserts indentation of written data by keeping
3630/// track of the current indentation level3565/// track of the current indentation level
3631///3566///
lib/std/zig/parser_test.zig+119-8
...@@ -1330,12 +1330,30 @@ test "zig fmt: comment to disable/enable zig fmt" {...@@ -1330,12 +1330,30 @@ test "zig fmt: comment to disable/enable zig fmt" {
1330 \\const c = d;1330 \\const c = d;
1331 \\// zig fmt: on1331 \\// zig fmt: on
1332 \\const e = f;1332 \\const e = f;
1333 \\const g = .{
1334 \\ h, i,
1335 \\ // zig fmt: off
1336 \\ j,
1337 \\ k,
1338 \\ // zig fmt: on
1339 \\ l, m, n, o,
1340 \\};
1341 \\
1333 ,1342 ,
1334 \\const a = b;1343 \\const a = b;
1335 \\// zig fmt: off1344 \\// zig fmt: off
1336 \\const c = d;1345 \\const c = d;
1337 \\// zig fmt: on1346 \\// zig fmt: on
1338 \\const e = f;1347 \\const e = f;
1348 \\const g = .{
1349 \\ h, i,
1350 \\ // zig fmt: off
1351 \\ j,
1352 \\ k,
1353 \\ // zig fmt: on
1354 \\ l, m,
1355 \\ n, o,
1356 \\};
1339 \\1357 \\
1340 );1358 );
1341}1359}
...@@ -1987,6 +2005,38 @@ test "zig fmt: array literal vertical column alignment" {...@@ -1987,6 +2005,38 @@ test "zig fmt: array literal vertical column alignment" {
1987 \\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };2005 \\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
1988 \\const a = [12]u8{2006 \\const a = [12]u8{
1989 \\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, };2007 \\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, };
2008 \\const a = .{
2009 \\ 1, \\
2010 \\ , 2,
2011 \\ 3,
2012 \\};
2013 \\const a = .{
2014 \\ \\
2015 \\ , 1, 2,
2016 \\ 3,
2017 \\};
2018 \\const a = .{
2019 \\ {{}}, 1,
2020 \\ 2, 3,
2021 \\};
2022 \\const a = .{
2023 \\ a, bb //
2024 \\ , ccc, dddd,
2025 \\};
2026 \\const a = .{
2027 \\ "a", "b", "ä", "a", "123",
2028 \\};
2029 \\const a = .{
2030 \\ a, a, .{
2031 \\ // zig fmt: off
2032 \\ },
2033 \\ a*a, a,
2034 \\ .{
2035 \\ // zig fmt: on
2036 \\ }, aa,
2037 \\ // zig fmt: off
2038 \\ a*a,
2039 \\};
1990 \\2040 \\
1991 ,2041 ,
1992 \\const a = []u8{2042 \\const a = []u8{
...@@ -2015,6 +2065,53 @@ test "zig fmt: array literal vertical column alignment" {...@@ -2015,6 +2065,53 @@ test "zig fmt: array literal vertical column alignment" {
2015 \\ 30,2065 \\ 30,
2016 \\ 31,2066 \\ 31,
2017 \\};2067 \\};
2068 \\const a = .{
2069 \\ 1,
2070 \\ \\
2071 \\ ,
2072 \\ 2,
2073 \\ 3,
2074 \\};
2075 \\const a = .{
2076 \\ \\
2077 \\ ,
2078 \\ 1,
2079 \\ 2,
2080 \\ 3,
2081 \\};
2082 \\const a = .{
2083 \\ {
2084 \\ {}
2085 \\ },
2086 \\ 1,
2087 \\ 2,
2088 \\ 3,
2089 \\};
2090 \\const a = .{
2091 \\ a,
2092 \\ bb //
2093 \\ ,
2094 \\ ccc,
2095 \\ dddd,
2096 \\};
2097 \\const a = .{
2098 \\ "a", "b",
2099 \\ "ä",
2100 \\ "a", "123",
2101 \\};
2102 \\const a = .{
2103 \\ a, a,
2104 \\ .{
2105 \\ // zig fmt: off
2106 \\ },
2107 \\ a*a, a,
2108 \\ .{
2109 \\ // zig fmt: on
2110 \\ },
2111 \\ aa,
2112 \\ // zig fmt: off
2113 \\ a*a,
2114 \\};
2018 \\2115 \\
2019 );2116 );
2020}2117}
...@@ -4813,8 +4910,8 @@ test "zig fmt: multiline string literals should play nice with array initializer...@@ -4813,8 +4910,8 @@ test "zig fmt: multiline string literals should play nice with array initializer
4813 \\ 0,4910 \\ 0,
4814 \\ }}}}}}}};4911 \\ }}}}}}}};
4815 \\ myFunc(.{4912 \\ myFunc(.{
4816 \\ "aaaaaaa", "bbbbbb", "ccccc",4913 \\ "aaaaaaa", "bbbbbb", "ccccc",
4817 \\ "dddd", ("eee"), ("fff"),4914 \\ "dddd", ("eee"), ("fff"),
4818 \\ ("gggg"),4915 \\ ("gggg"),
4819 \\ // Line comment4916 \\ // Line comment
4820 \\ \\Multiline String Literals can be quite long4917 \\ \\Multiline String Literals can be quite long
...@@ -4843,11 +4940,9 @@ test "zig fmt: multiline string literals should play nice with array initializer...@@ -4843,11 +4940,9 @@ test "zig fmt: multiline string literals should play nice with array initializer
4843 \\ (4940 \\ (
4844 \\ \\ xxx4941 \\ \\ xxx
4845 \\ ),4942 \\ ),
4846 \\ "xxx",4943 \\ "xxx", "xxx",
4847 \\ "xxx",
4848 \\ },4944 \\ },
4849 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },4945 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, .{ "xxxxxxx", "xxx", "xxx", "xxx" },
4850 \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" },
4851 \\ "aaaaaaa", "bbbbbb", "ccccc", // -4946 \\ "aaaaaaa", "bbbbbb", "ccccc", // -
4852 \\ "dddd", ("eee"), ("fff"),4947 \\ "dddd", ("eee"), ("fff"),
4853 \\ .{4948 \\ .{
...@@ -4855,8 +4950,7 @@ test "zig fmt: multiline string literals should play nice with array initializer...@@ -4855,8 +4950,7 @@ test "zig fmt: multiline string literals should play nice with array initializer
4855 \\ (4950 \\ (
4856 \\ \\ xxx4951 \\ \\ xxx
4857 \\ ),4952 \\ ),
4858 \\ "xxxxxxxxxxxxxx",4953 \\ "xxxxxxxxxxxxxx", "xxx",
4859 \\ "xxx",
4860 \\ },4954 \\ },
4861 \\ .{4955 \\ .{
4862 \\ (4956 \\ (
...@@ -6676,6 +6770,23 @@ test "zig fmt: doc comments on fn parameters" {...@@ -6676,6 +6770,23 @@ test "zig fmt: doc comments on fn parameters" {
6676 );6770 );
6677}6771}
66786772
6773test "zig fmt: array literal formatting when element becomes multiline" {
6774 try testTransform(
6775 \\const a = .{a,{{}},
6776 \\ b,c,};
6777 ,
6778 \\const a = .{
6779 \\ a,
6780 \\ {
6781 \\ {}
6782 \\ },
6783 \\ b,
6784 \\ c,
6785 \\};
6786 \\
6787 );
6788}
6789
6679test "zig fmt: proper tracking of indentation" {6790test "zig fmt: proper tracking of indentation" {
6680 try testCanonical(6791 try testCanonical(
6681 \\const a = {6792 \\const a = {