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" {...@@ -329,6 +329,50 @@ test "zig fmt: container declaration, transform trailing comma" {
329 );329 );
330}330}
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
332test "zig fmt: remove empty lines at start/end of container decl" {376test "zig fmt: remove empty lines at start/end of container decl" {
333 try testTransform(377 try testTransform(
334 \\const X = struct {378 \\const X = struct {
lib/std/zig/render.zig+23-5
...@@ -1206,7 +1206,7 @@ fn renderContainerField(...@@ -1206,7 +1206,7 @@ fn renderContainerField(
1206 ais.pushIndent();1206 ais.pushIndent();
1207 try renderExpression(gpa, ais, tree, field.ast.value_expr, .none); // value1207 try renderExpression(gpa, ais, tree, field.ast.value_expr, .none); // value
1208 ais.popIndent();1208 ais.popIndent();
1209 try renderToken(ais, tree, maybe_comma, space);1209 try renderToken(ais, tree, maybe_comma, .newline);
1210 } else {1210 } else {
1211 ais.pushIndent();1211 ais.pushIndent();
1212 try renderExpression(gpa, ais, tree, field.ast.value_expr, space); // value1212 try renderExpression(gpa, ais, tree, field.ast.value_expr, space); // value
...@@ -1894,7 +1894,11 @@ fn renderContainerDecl(...@@ -1894,7 +1894,11 @@ fn renderContainerDecl(
18941894
1895 const src_has_trailing_comma = token_tags[rbrace - 1] == .comma;1895 const src_has_trailing_comma = token_tags[rbrace - 1] == .comma;
1896 if (!src_has_trailing_comma) one_line: {1896 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 }
1898 for (container_decl.ast.members) |member| {1902 for (container_decl.ast.members) |member| {
1899 if (!node_tags[member].isContainerField()) break :one_line;1903 if (!node_tags[member].isContainerField()) break :one_line;
1900 }1904 }
...@@ -1912,7 +1916,18 @@ fn renderContainerDecl(...@@ -1912,7 +1916,18 @@ fn renderContainerDecl(
1912 if (token_tags[lbrace + 1] == .container_doc_comment) {1916 if (token_tags[lbrace + 1] == .container_doc_comment) {
1913 try renderContainerDocComments(ais, tree, lbrace + 1);1917 try renderContainerDocComments(ais, tree, lbrace + 1);
1914 }1918 }
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 }
1916 ais.popIndent();1931 ais.popIndent();
19171932
1918 return renderToken(ais, tree, rbrace, space); // rbrace1933 return renderToken(ais, tree, rbrace, space); // rbrace
...@@ -2200,10 +2215,11 @@ fn renderExpressionIndented(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Nod...@@ -2200,10 +2215,11 @@ fn renderExpressionIndented(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Nod
2200}2215}
22012216
2202/// Render an expression, and the comma that follows it, if it is present in the source.2217/// 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.
2203fn renderExpressionComma(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, space: Space) Error!void {2219fn renderExpressionComma(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, space: Space) Error!void {
2204 const token_tags = tree.tokens.items(.tag);2220 const token_tags = tree.tokens.items(.tag);
2205 const maybe_comma = tree.lastToken(node) + 1;2221 const maybe_comma = tree.lastToken(node) + 1;
2206 if (token_tags[maybe_comma] == .comma) {2222 if (token_tags[maybe_comma] == .comma and space != .comma) {
2207 try renderExpression(gpa, ais, tree, node, .none);2223 try renderExpression(gpa, ais, tree, node, .none);
2208 return renderToken(ais, tree, maybe_comma, space);2224 return renderToken(ais, tree, maybe_comma, space);
2209 } else {2225 } else {
...@@ -2211,10 +2227,12 @@ fn renderExpressionComma(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.I...@@ -2211,10 +2227,12 @@ fn renderExpressionComma(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.I
2211 }2227 }
2212}2228}
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.
2214fn renderTokenComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space) Error!void {2232fn renderTokenComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space) Error!void {
2215 const token_tags = tree.tokens.items(.tag);2233 const token_tags = tree.tokens.items(.tag);
2216 const maybe_comma = token + 1;2234 const maybe_comma = token + 1;
2217 if (token_tags[maybe_comma] == .comma) {2235 if (token_tags[maybe_comma] == .comma and space != .comma) {
2218 try renderToken(ais, tree, token, .none);2236 try renderToken(ais, tree, token, .none);
2219 return renderToken(ais, tree, maybe_comma, space);2237 return renderToken(ais, tree, maybe_comma, space);
2220 } else {2238 } else {