authorgravatar for tom@johnes.metjohnes <tom@johnes.me> 2021-10-25 09:59:45+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-24 18:45:09-08:00
loga130eac7857512db50320fb1c64079b0d696885d
treef886cd204f4e5d6657161f3d8f26cbfd18e1c4ca
parent36c8adf58922d8e357bbaf7ce914cfd4b7b56354

zig fmt: fix formatting for single-line containers with comments

* Fixes #8810. * Prevent a single-line container declaration if it contains a comment or multiline string. * If a container declaration cannot be single-line, ensure container fields are rendered with a trailing comma. * If `Space.comma` is passed to `renderExpressionComma` or `renderTokenComma`, and there already exists a comma in the source, then render one comma instead of two.

2 files changed, 67 insertions(+), 5 deletions(-)

lib/std/zig/parser_test.zig+44
......@@ -329,6 +329,50 @@ test "zig fmt: container declaration, transform trailing comma" {
329329 );
330330}
331331
332test "zig fmt: container declaration, comment, add trailing comma" {
333 try testTransform(
334 \\const X = struct {
335 \\ foo: i32, // foo
336 \\ bar: i8
337 \\};
338 ,
339 \\const X = struct {
340 \\ foo: i32, // foo
341 \\ bar: i8,
342 \\};
343 \\
344 );
345 try testTransform(
346 \\const X = struct {
347 \\ foo: i32 // foo
348 \\};
349 ,
350 \\const X = struct {
351 \\ foo: i32, // foo
352 \\};
353 \\
354 );
355}
356
357test "zig fmt: container declaration, multiline string, add trailing comma" {
358 try testTransform(
359 \\const X = struct {
360 \\ foo: []const u8 =
361 \\ \\ foo
362 \\ ,
363 \\ bar: i8
364 \\};
365 ,
366 \\const X = struct {
367 \\ foo: []const u8 =
368 \\ \\ foo
369 \\ ,
370 \\ bar: i8,
371 \\};
372 \\
373 );
374}
375
332376test "zig fmt: remove empty lines at start/end of container decl" {
333377 try testTransform(
334378 \\const X = struct {
lib/std/zig/render.zig+23-5
......@@ -1206,7 +1206,7 @@ fn renderContainerField(
12061206 ais.pushIndent();
12071207 try renderExpression(gpa, ais, tree, field.ast.value_expr, .none); // value
12081208 ais.popIndent();
1209 try renderToken(ais, tree, maybe_comma, space);
1209 try renderToken(ais, tree, maybe_comma, .newline);
12101210 } else {
12111211 ais.pushIndent();
12121212 try renderExpression(gpa, ais, tree, field.ast.value_expr, space); // value
......@@ -1894,7 +1894,11 @@ fn renderContainerDecl(
18941894
18951895 const src_has_trailing_comma = token_tags[rbrace - 1] == .comma;
18961896 if (!src_has_trailing_comma) one_line: {
1897 // We can only print all the members in-line if all the members are fields.
1897 // We can only print all the members in-line if there are no comments or multiline strings,
1898 // and all the members are fields.
1899 if (hasComment(tree, lbrace, rbrace) or hasMultilineString(tree, lbrace, rbrace)) {
1900 break :one_line;
1901 }
18981902 for (container_decl.ast.members) |member| {
18991903 if (!node_tags[member].isContainerField()) break :one_line;
19001904 }
......@@ -1912,7 +1916,18 @@ fn renderContainerDecl(
19121916 if (token_tags[lbrace + 1] == .container_doc_comment) {
19131917 try renderContainerDocComments(ais, tree, lbrace + 1);
19141918 }
1915 try renderMembers(gpa, ais, tree, container_decl.ast.members);
1919 for (container_decl.ast.members) |member, i| {
1920 if (i != 0) try renderExtraNewline(ais, tree, member);
1921 switch (tree.nodes.items(.tag)[member]) {
1922 // For container fields, ensure a trailing comma is added if necessary.
1923 .container_field_init,
1924 .container_field_align,
1925 .container_field,
1926 => try renderMember(gpa, ais, tree, member, .comma),
1927
1928 else => try renderMember(gpa, ais, tree, member, .newline),
1929 }
1930 }
19161931 ais.popIndent();
19171932
19181933 return renderToken(ais, tree, rbrace, space); // rbrace
......@@ -2200,10 +2215,11 @@ fn renderExpressionIndented(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Nod
22002215}
22012216
22022217/// Render an expression, and the comma that follows it, if it is present in the source.
2218/// If a comma is present, and `space` is `Space.comma`, render only a single comma.
22032219fn renderExpressionComma(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, space: Space) Error!void {
22042220 const token_tags = tree.tokens.items(.tag);
22052221 const maybe_comma = tree.lastToken(node) + 1;
2206 if (token_tags[maybe_comma] == .comma) {
2222 if (token_tags[maybe_comma] == .comma and space != .comma) {
22072223 try renderExpression(gpa, ais, tree, node, .none);
22082224 return renderToken(ais, tree, maybe_comma, space);
22092225 } else {
......@@ -2211,10 +2227,12 @@ fn renderExpressionComma(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.I
22112227 }
22122228}
22132229
2230/// Render a token, and the comma that follows it, if it is present in the source.
2231/// If a comma is present, and `space` is `Space.comma`, render only a single comma.
22142232fn renderTokenComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space) Error!void {
22152233 const token_tags = tree.tokens.items(.tag);
22162234 const maybe_comma = token + 1;
2217 if (token_tags[maybe_comma] == .comma) {
2235 if (token_tags[maybe_comma] == .comma and space != .comma) {
22182236 try renderToken(ais, tree, token, .none);
22192237 return renderToken(ais, tree, maybe_comma, space);
22202238 } else {