diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index e4a958388733..66d1803d008b 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -3186,7 +3186,7 @@ test "zig fmt: for" { test "zig fmt: for if" { try testCanonical( - \\test "for if" { + \\test { \\ for (a) |x| if (x) f(x); \\ \\ for (a) |x| if (x) @@ -3209,6 +3209,131 @@ test "zig fmt: for if" { ); } +test "zig fmt: if for" { + try testCanonical( + \\test { + \\ if (a) for (x) |x| f(x); + \\ + \\ if (a) for (x) |x| + \\ f(x); + \\ + \\ if (a) for (x) |x| { + \\ f(x); + \\ }; + \\ + \\ if (a) + \\ for (x) |x| + \\ f(x); + \\ + \\ if (a) + \\ for (x) |x| { + \\ f(x); + \\ }; + \\} + \\ + ); +} + +test "zig fmt: while if" { + try testCanonical( + \\test { + \\ while (a) if (x) f(x); + \\ + \\ while (a) if (x) + \\ f(x); + \\ + \\ while (a) if (x) { + \\ f(x); + \\ }; + \\ + \\ while (a) + \\ if (x) + \\ f(x); + \\ + \\ while (a) + \\ if (x) { + \\ f(x); + \\ }; + \\} + \\ + ); +} + +test "zig fmt: if while" { + try testCanonical( + \\test { + \\ if (a) while (x) : (cont) f(x); + \\ + \\ if (a) while (x) : (cont) + \\ f(x); + \\ + \\ if (a) while (x) : (cont) { + \\ f(x); + \\ }; + \\ + \\ if (a) + \\ while (x) : (cont) + \\ f(x); + \\ + \\ if (a) + \\ while (x) : (cont) { + \\ f(x); + \\ }; + \\} + \\ + ); +} + +test "zig fmt: while for" { + try testCanonical( + \\test { + \\ while (a) for (x) |x| f(x); + \\ + \\ while (a) for (x) |x| + \\ f(x); + \\ + \\ while (a) for (x) |x| { + \\ f(x); + \\ }; + \\ + \\ while (a) + \\ for (x) |x| + \\ f(x); + \\ + \\ while (a) + \\ for (x) |x| { + \\ f(x); + \\ }; + \\} + \\ + ); +} + +test "zig fmt: for while" { + try testCanonical( + \\test { + \\ for (a) |a| while (x) |x| f(x); + \\ + \\ for (a) |a| while (x) |x| + \\ f(x); + \\ + \\ for (a) |a| while (x) |x| { + \\ f(x); + \\ }; + \\ + \\ for (a) |a| + \\ while (x) |x| + \\ f(x); + \\ + \\ for (a) |a| + \\ while (x) |x| { + \\ f(x); + \\ }; + \\} + \\ + ); +} + test "zig fmt: if" { try testCanonical( \\test "if" { @@ -3258,6 +3383,82 @@ test "zig fmt: if" { ); } +test "zig fmt: fix single statement if/for/while line breaks" { + try testTransform( + \\test { + \\ if (cond) a + \\ else b; + \\ + \\ if (cond) + \\ a + \\ else b; + \\ + \\ for (xs) |x| foo() + \\ else bar(); + \\ + \\ for (xs) |x| + \\ foo() + \\ else bar(); + \\ + \\ while (a) : (b) foo() + \\ else bar(); + \\ + \\ while (a) : (b) + \\ foo() + \\ else bar(); + \\} + \\ + , + \\test { + \\ if (cond) a else b; + \\ + \\ if (cond) + \\ a + \\ else + \\ b; + \\ + \\ for (xs) |x| foo() else bar(); + \\ + \\ for (xs) |x| + \\ foo() + \\ else + \\ bar(); + \\ + \\ while (a) : (b) foo() else bar(); + \\ + \\ while (a) : (b) + \\ foo() + \\ else + \\ bar(); + \\} + \\ + ); +} + +test "zig fmt: anon struct/array literal in if" { + try testCanonical( + \\test { + \\ const a = if (cond) .{ + \\ 1, 2, + \\ 3, 4, + \\ } else .{ + \\ 1, + \\ 2, + \\ 3, + \\ }; + \\ + \\ const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{ + \\ .rl = .ref, + \\ .tag = .switchbr_ref, + \\ } else .{ + \\ .rl = .none, + \\ .tag = .switchbr, + \\ }; + \\} + \\ + ); +} + test "zig fmt: defer" { try testCanonical( \\test "defer" { @@ -3845,6 +4046,7 @@ test "zig fmt: comments in ternary ifs" { \\ // Comment \\ 1 \\else + \\ // Comment \\ 0; \\ \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; @@ -3852,6 +4054,20 @@ test "zig fmt: comments in ternary ifs" { ); } +test "zig fmt: while statement in blockless if" { + try testCanonical( + \\pub fn main() void { + \\ const zoom_node = if (focused_node == layout_first) + \\ while (it.next()) |node| { + \\ if (!node.view.pending.float and !node.view.pending.fullscreen) break node; + \\ } else null + \\ else + \\ focused_node; + \\} + \\ + ); +} + test "zig fmt: test comments in field access chain" { try testCanonical( \\pub const str = struct { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 23e9e030857e..9fe9b96f00b3 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -1018,170 +1018,95 @@ fn renderWhile(gpa: *Allocator, ais: *Ais, tree: ast.Tree, while_node: ast.full. try renderToken(ais, tree, inline_token, .space); // inline } - try renderToken(ais, tree, while_node.ast.while_token, .space); // if + try renderToken(ais, tree, while_node.ast.while_token, .space); // if/for/while try renderToken(ais, tree, while_node.ast.while_token + 1, .none); // lparen try renderExpression(gpa, ais, tree, while_node.ast.cond_expr, .none); // condition - const then_tag = node_tags[while_node.ast.then_expr]; - if (nodeIsBlock(then_tag) and !nodeIsIf(then_tag)) { - if (while_node.payload_token) |payload_token| { - const brace_space = if (while_node.ast.cont_expr == 0 and ais.isLineOverIndented()) - Space.newline - else - Space.space; - try renderWhilePayload(gpa, ais, tree, payload_token, brace_space); - } else { - const rparen = tree.lastToken(while_node.ast.cond_expr) + 1; - const brace_space = if (while_node.ast.cont_expr == 0 and ais.isLineOverIndented()) - Space.newline - else - Space.space; - try renderToken(ais, tree, rparen, brace_space); // rparen - } - if (while_node.ast.cont_expr != 0) { - const rparen = tree.lastToken(while_node.ast.cont_expr) + 1; - const lparen = tree.firstToken(while_node.ast.cont_expr) - 1; - try renderToken(ais, tree, lparen - 1, .space); // : - try renderToken(ais, tree, lparen, .none); // lparen - try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none); - const brace_space: Space = if (ais.isLineOverIndented()) .newline else .space; - try renderToken(ais, tree, rparen, brace_space); // rparen - } - if (while_node.ast.else_expr != 0) { - try renderExpression(gpa, ais, tree, while_node.ast.then_expr, Space.space); - try renderToken(ais, tree, while_node.else_token, .space); // else - if (while_node.error_token) |error_token| { - try renderToken(ais, tree, error_token - 1, .none); // | - try renderToken(ais, tree, error_token, .none); // identifier - try renderToken(ais, tree, error_token + 1, .space); // | - } - return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space); - } else { - return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space); - } - } - - const rparen = tree.lastToken(while_node.ast.cond_expr) + 1; - const first_then_token = tree.firstToken(while_node.ast.then_expr); - const last_then_token = tree.lastToken(while_node.ast.then_expr); - const src_has_newline = !tree.tokensOnSameLine(rparen, last_then_token); + var last_prefix_token = tree.lastToken(while_node.ast.cond_expr) + 1; // rparen - if (src_has_newline) { - const newline_before_then_token = !tree.tokensOnSameLine(rparen, first_then_token); - const space_before_then_token: Space = if (newline_before_then_token) .newline else .space; - const indent_expression = !nodeIsIf(then_tag) or newline_before_then_token; - - if (while_node.payload_token) |payload_token| { - const after_space: Space = if (while_node.ast.cont_expr != 0) .space else space_before_then_token; - try renderWhilePayload(gpa, ais, tree, payload_token, after_space); - } else { - if (indent_expression) ais.pushIndent(); - const after_space: Space = if (while_node.ast.cont_expr != 0) .space else space_before_then_token; - try renderToken(ais, tree, rparen, after_space); // rparen - if (indent_expression) ais.popIndent(); - } - if (while_node.ast.cont_expr != 0) { - const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1; - const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1; - try renderToken(ais, tree, cont_lparen - 1, .space); // : - try renderToken(ais, tree, cont_lparen, .none); // lparen - try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none); - try renderToken(ais, tree, cont_rparen, space_before_then_token); // rparen - } - if (while_node.ast.else_expr != 0) { - if (indent_expression) ais.pushIndent(); - try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .newline); - if (indent_expression) ais.popIndent(); - const else_is_block = nodeIsBlock(node_tags[while_node.ast.else_expr]); - if (else_is_block) { - try renderToken(ais, tree, while_node.else_token, .space); // else - if (while_node.error_token) |error_token| { - try renderToken(ais, tree, error_token - 1, .none); // | - try renderToken(ais, tree, error_token, .none); // identifier - try renderToken(ais, tree, error_token + 1, .space); // | - } - return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space); + if (while_node.payload_token) |payload_token| { + try renderToken(ais, tree, last_prefix_token, .space); + try renderToken(ais, tree, payload_token - 1, .none); // | + const ident = blk: { + if (token_tags[payload_token] == .asterisk) { + try renderToken(ais, tree, payload_token, .none); // * + break :blk payload_token + 1; } else { - if (while_node.error_token) |error_token| { - try renderToken(ais, tree, while_node.else_token, .space); // else - try renderToken(ais, tree, error_token - 1, .none); // | - try renderToken(ais, tree, error_token, .none); // identifier - try renderToken(ais, tree, error_token + 1, .space); // | - } else { - try renderToken(ais, tree, while_node.else_token, .newline); // else - } - if (indent_expression) { - return renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space); - } else { - return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space); - } + break :blk payload_token; } - } else { - if (indent_expression) { - return renderExpressionIndented(gpa, ais, tree, while_node.ast.then_expr, space); + }; + try renderToken(ais, tree, ident, .none); // identifier + const pipe = blk: { + if (token_tags[ident + 1] == .comma) { + try renderToken(ais, tree, ident + 1, .space); // , + try renderToken(ais, tree, ident + 2, .none); // index + break :blk ident + 3; } else { - return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space); + break :blk ident + 1; } - } - } - - // Render everything on a single line. - - if (while_node.payload_token) |payload_token| { - assert(payload_token - 2 == rparen); - try renderWhilePayload(gpa, ais, tree, payload_token, .space); - } else { - try renderToken(ais, tree, rparen, .space); // ) + }; + last_prefix_token = pipe; } if (while_node.ast.cont_expr != 0) { - const cont_rparen = tree.lastToken(while_node.ast.cont_expr) + 1; - const cont_lparen = tree.firstToken(while_node.ast.cont_expr) - 1; - try renderToken(ais, tree, cont_lparen - 1, .space); // : - try renderToken(ais, tree, cont_lparen, .none); // lparen + try renderToken(ais, tree, last_prefix_token, .space); + const lparen = tree.firstToken(while_node.ast.cont_expr) - 1; + try renderToken(ais, tree, lparen - 1, .space); // : + try renderToken(ais, tree, lparen, .none); // lparen try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none); - try renderToken(ais, tree, cont_rparen, .space); // rparen + last_prefix_token = tree.lastToken(while_node.ast.cont_expr) + 1; // rparen + } + + const then_expr_is_block = nodeIsBlock(node_tags[while_node.ast.then_expr]); + const indent_then_expr = !then_expr_is_block and + !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(while_node.ast.then_expr)); + if (indent_then_expr or (then_expr_is_block and ais.isLineOverIndented())) { + ais.pushIndentNextLine(); + try renderToken(ais, tree, last_prefix_token, .newline); + ais.popIndent(); + } else { + try renderToken(ais, tree, last_prefix_token, .space); } if (while_node.ast.else_expr != 0) { - try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space); - try renderToken(ais, tree, while_node.else_token, .space); // else + const first_else_expr_tok = tree.firstToken(while_node.ast.else_expr); + + if (indent_then_expr) { + ais.pushIndent(); + try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .newline); + ais.popIndent(); + } else { + try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space); + } + + var last_else_token = while_node.else_token; if (while_node.error_token) |error_token| { + try renderToken(ais, tree, while_node.else_token, .space); // else try renderToken(ais, tree, error_token - 1, .none); // | try renderToken(ais, tree, error_token, .none); // identifier - try renderToken(ais, tree, error_token + 1, .space); // | + last_else_token = error_token + 1; // | } - return renderExpression(gpa, ais, tree, while_node.ast.else_expr, space); - } else { - return renderExpression(gpa, ais, tree, while_node.ast.then_expr, space); - } -} - -fn renderWhilePayload(gpa: *Allocator, ais: *Ais, tree: ast.Tree, payload_token: ast.TokenIndex, space: Space) Error!void { - const token_tags = tree.tokens.items(.tag); - try renderToken(ais, tree, payload_token - 2, .space); // rparen - try renderToken(ais, tree, payload_token - 1, .none); // | - const ident = blk: { - if (token_tags[payload_token] == .asterisk) { - try renderToken(ais, tree, payload_token, .none); // * - break :blk payload_token + 1; + const indent_else_expr = indent_then_expr and + !nodeIsBlock(node_tags[while_node.ast.else_expr]) and + !nodeIsIfForWhileSwitch(node_tags[while_node.ast.else_expr]); + if (indent_else_expr) { + ais.pushIndentNextLine(); + try renderToken(ais, tree, last_else_token, .newline); + ais.popIndent(); + try renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space); } else { - break :blk payload_token; + try renderToken(ais, tree, last_else_token, .space); + try renderExpression(gpa, ais, tree, while_node.ast.else_expr, space); } - }; - try renderToken(ais, tree, ident, .none); // identifier - const pipe = blk: { - if (token_tags[ident + 1] == .comma) { - try renderToken(ais, tree, ident + 1, .space); // , - try renderToken(ais, tree, ident + 2, .none); // index - break :blk ident + 3; + } else { + if (indent_then_expr) { + try renderExpressionIndented(gpa, ais, tree, while_node.ast.then_expr, space); } else { - break :blk ident + 1; + try renderExpression(gpa, ais, tree, while_node.ast.then_expr, space); } - }; - try renderToken(ais, tree, pipe, space); // | + } } fn renderContainerField( @@ -2469,6 +2394,21 @@ fn nodeIsBlock(tag: ast.Node.Tag) bool { .block_semicolon, .block_two, .block_two_semicolon, + .struct_init_dot, + .struct_init_dot_comma, + .struct_init_dot_two, + .struct_init_dot_two_comma, + .array_init_dot, + .array_init_dot_comma, + .array_init_dot_two, + .array_init_dot_two_comma, + => true, + else => false, + }; +} + +fn nodeIsIfForWhileSwitch(tag: ast.Node.Tag) bool { + return switch (tag) { .@"if", .if_simple, .@"for", @@ -2483,13 +2423,6 @@ fn nodeIsBlock(tag: ast.Node.Tag) bool { }; } -fn nodeIsIf(tag: ast.Node.Tag) bool { - return switch (tag) { - .@"if", .if_simple => true, - else => false, - }; -} - fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool { return switch (tag) { .@"catch",