From 1c79eea125c7e21b00a7abeffdc3b64548c54257 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 9 Feb 2021 17:23:57 -0700 Subject: [PATCH] zig fmt: while loops --- lib/std/zig/ast.zig | 132 ++++++++++++++++-- lib/std/zig/parse.zig | 28 ++-- lib/std/zig/parser_test.zig | 26 ++-- lib/std/zig/render.zig | 269 +++++++++++++++++------------------- 4 files changed, 272 insertions(+), 183 deletions(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 32c7e0ac7f..9bad013dea 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -760,6 +760,13 @@ pub const Tree = struct { n = extra.sentinel; }, + .Continue => { + if (datas[n].lhs != 0) { + return datas[n].lhs + end_offset; + } else { + return main_tokens[n] + end_offset; + } + }, .Break => { if (datas[n].rhs != 0) { n = datas[n].rhs; @@ -837,6 +844,21 @@ pub const Tree = struct { n = max_node; end_offset += max_offset; }, + .WhileCont => { + const extra = tree.extraData(datas[n].rhs, Node.WhileCont); + assert(extra.then_expr != 0); + n = extra.then_expr; + }, + .While => { + const extra = tree.extraData(datas[n].rhs, Node.While); + assert(extra.else_expr != 0); + n = extra.else_expr; + }, + .If => { + const extra = tree.extraData(datas[n].rhs, Node.If); + assert(extra.else_expr != 0); + n = extra.else_expr; + }, // These are not supported by lastToken() because implementation would // require recursion due to the optional comma followed by rbrace. @@ -851,13 +873,9 @@ pub const Tree = struct { .TaggedUnionEnumTag => unreachable, // TODO .TaggedUnionEnumTagComma => unreachable, // TODO - .If => unreachable, // TODO - .Continue => unreachable, // TODO .SwitchRange => unreachable, // TODO .ArrayType => unreachable, // TODO .ArrayTypeSentinel => unreachable, // TODO - .WhileCont => unreachable, // TODO - .While => unreachable, // TODO .ForSimple => unreachable, // TODO .For => unreachable, // TODO .ErrorValue => unreachable, // TODO @@ -1404,6 +1422,41 @@ pub const Tree = struct { }); } + pub fn whileSimple(tree: Tree, node: Node.Index) full.While { + const data = tree.nodes.items(.data)[node]; + return tree.fullWhile(.{ + .while_token = tree.nodes.items(.main_token)[node], + .cond_expr = data.lhs, + .cont_expr = 0, + .then_expr = data.rhs, + .else_expr = 0, + }); + } + + pub fn whileCont(tree: Tree, node: Node.Index) full.While { + const data = tree.nodes.items(.data)[node]; + const extra = tree.extraData(data.rhs, Node.WhileCont); + return tree.fullWhile(.{ + .while_token = tree.nodes.items(.main_token)[node], + .cond_expr = data.lhs, + .cont_expr = extra.cont_expr, + .then_expr = extra.then_expr, + .else_expr = 0, + }); + } + + pub fn whileFull(tree: Tree, node: Node.Index) full.While { + const data = tree.nodes.items(.data)[node]; + const extra = tree.extraData(data.rhs, Node.While); + return tree.fullWhile(.{ + .while_token = tree.nodes.items(.main_token)[node], + .cond_expr = data.lhs, + .cont_expr = extra.cont_expr, + .then_expr = extra.then_expr, + .else_expr = extra.else_expr, + }); + } + fn fullVarDecl(tree: Tree, info: full.VarDecl.Ast) full.VarDecl { const token_tags = tree.tokens.items(.tag); var result: full.VarDecl = .{ @@ -1623,6 +1676,41 @@ pub const Tree = struct { return result; } + + fn fullWhile(tree: Tree, info: full.While.Ast) full.While { + const token_tags = tree.tokens.items(.tag); + var result: full.While = .{ + .ast = info, + .inline_token = null, + .label_token = null, + .payload_token = null, + .else_token = undefined, + .error_token = null, + }; + var tok_i = info.while_token - 1; + if (token_tags[tok_i] == .Keyword_inline) { + result.inline_token = tok_i; + tok_i -= 1; + } + if (token_tags[tok_i] == .Colon and + token_tags[tok_i - 1] == .Identifier) + { + result.label_token = tok_i - 1; + } + const last_cond_token = tree.lastToken(info.cond_expr); + if (token_tags[last_cond_token + 2] == .Pipe) { + result.payload_token = last_cond_token + 3; + } + if (info.else_expr != 0) { + // then_expr else |x| + // ^ ^ + result.else_token = tree.lastToken(info.then_expr) + 1; + if (token_tags[result.else_token + 1] == .Pipe) { + result.error_token = result.else_token + 2; + } + } + return result; + } }; /// Fully assembled AST node information. @@ -1645,12 +1733,12 @@ pub const full = struct { }; pub const If = struct { - // Points to the first token after the `|`. Will either be an identifier or - // a `*` (with an identifier immediately after it). + /// Points to the first token after the `|`. Will either be an identifier or + /// a `*` (with an identifier immediately after it). payload_token: ?TokenIndex, - // Points to the identifier after the `|`. + /// Points to the identifier after the `|`. error_token: ?TokenIndex, - // Populated only if else_expr != 0. + /// Populated only if else_expr != 0. else_token: TokenIndex, ast: Ast, @@ -1662,6 +1750,24 @@ pub const full = struct { }; }; + pub const While = struct { + ast: Ast, + inline_token: ?TokenIndex, + label_token: ?TokenIndex, + payload_token: ?TokenIndex, + error_token: ?TokenIndex, + /// Populated only if else_expr != 0. + else_token: TokenIndex, + + pub const Ast = struct { + while_token: TokenIndex, + cond_expr: Node.Index, + cont_expr: Node.Index, + then_expr: Node.Index, + else_expr: Node.Index, + }; + }; + pub const ContainerField = struct { comptime_token: ?TokenIndex, ast: Ast, @@ -2270,9 +2376,9 @@ pub const Node = struct { /// `if (lhs) rhs`. /// `if (lhs) |a| rhs`. IfSimple, - /// `if (lhs) a else b`. `if_list[rhs]`. - /// `if (lhs) |x| a else b`. `if_list[rhs]`. - /// `if (lhs) |x| a else |y| b`. `if_list[rhs]`. + /// `if (lhs) a else b`. `If[rhs]`. + /// `if (lhs) |x| a else b`. `If[rhs]`. + /// `if (lhs) |x| a else |y| b`. `If[rhs]`. If, /// `suspend lhs`. lhs can be omitted. rhs is unused. Suspend, @@ -2497,13 +2603,13 @@ pub const Node = struct { }; pub const While = struct { - continue_expr: Index, + cont_expr: Index, then_expr: Index, else_expr: Index, }; pub const WhileCont = struct { - continue_expr: Index, + cont_expr: Index, then_expr: Index, }; diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 3ca5bb0049..7e2da72d00 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1085,7 +1085,7 @@ const Parser = struct { const condition = try p.expectExpr(); _ = try p.expectToken(.RParen); const then_payload = try p.parsePtrPayload(); - const continue_expr = try p.parseWhileContinueExpr(); + const cont_expr = try p.parseWhileContinueExpr(); // TODO propose to change the syntax so that semicolons are always required // inside while statements, even if there is an `else`. @@ -1098,7 +1098,7 @@ const Parser = struct { return p.fail(.{ .ExpectedBlockOrAssignment = .{ .token = p.tok_i } }); } if (p.eatToken(.Semicolon)) |_| { - if (continue_expr == 0) { + if (cont_expr == 0) { return p.addNode(.{ .tag = .WhileSimple, .main_token = while_token, @@ -1114,7 +1114,7 @@ const Parser = struct { .data = .{ .lhs = condition, .rhs = try p.addExtra(Node.WhileCont{ - .continue_expr = continue_expr, + .cont_expr = cont_expr, .then_expr = assign_expr, }), }, @@ -1128,7 +1128,7 @@ const Parser = struct { if (else_required) { return p.fail(.{ .ExpectedSemiOrElse = .{ .token = p.tok_i } }); } - if (continue_expr == 0) { + if (cont_expr == 0) { return p.addNode(.{ .tag = .WhileSimple, .main_token = while_token, @@ -1144,7 +1144,7 @@ const Parser = struct { .data = .{ .lhs = condition, .rhs = try p.addExtra(Node.WhileCont{ - .continue_expr = continue_expr, + .cont_expr = cont_expr, .then_expr = then_expr, }), }, @@ -1159,7 +1159,7 @@ const Parser = struct { .data = .{ .lhs = condition, .rhs = try p.addExtra(Node.While{ - .continue_expr = continue_expr, + .cont_expr = cont_expr, .then_expr = then_expr, .else_expr = else_expr, }), @@ -2073,11 +2073,11 @@ const Parser = struct { const condition = try p.expectExpr(); _ = try p.expectToken(.RParen); const then_payload = try p.parsePtrPayload(); - const continue_expr = try p.parseWhileContinueExpr(); + const cont_expr = try p.parseWhileContinueExpr(); const then_expr = try p.expectExpr(); const else_token = p.eatToken(.Keyword_else) orelse { - if (continue_expr == 0) { + if (cont_expr == 0) { return p.addNode(.{ .tag = .WhileSimple, .main_token = while_token, @@ -2093,7 +2093,7 @@ const Parser = struct { .data = .{ .lhs = condition, .rhs = try p.addExtra(Node.WhileCont{ - .continue_expr = continue_expr, + .cont_expr = cont_expr, .then_expr = then_expr, }), }, @@ -2108,7 +2108,7 @@ const Parser = struct { .data = .{ .lhs = condition, .rhs = try p.addExtra(Node.While{ - .continue_expr = continue_expr, + .cont_expr = cont_expr, .then_expr = then_expr, .else_expr = else_expr, }), @@ -2836,11 +2836,11 @@ const Parser = struct { const condition = try p.expectExpr(); _ = try p.expectToken(.RParen); const then_payload = try p.parsePtrPayload(); - const continue_expr = try p.parseWhileContinueExpr(); + const cont_expr = try p.parseWhileContinueExpr(); const then_expr = try p.expectTypeExpr(); const else_token = p.eatToken(.Keyword_else) orelse { - if (continue_expr == 0) { + if (cont_expr == 0) { return p.addNode(.{ .tag = .WhileSimple, .main_token = while_token, @@ -2856,7 +2856,7 @@ const Parser = struct { .data = .{ .lhs = condition, .rhs = try p.addExtra(Node.WhileCont{ - .continue_expr = continue_expr, + .cont_expr = cont_expr, .then_expr = then_expr, }), }, @@ -2871,7 +2871,7 @@ const Parser = struct { .data = .{ .lhs = condition, .rhs = try p.addExtra(Node.While{ - .continue_expr = continue_expr, + .cont_expr = cont_expr, .then_expr = then_expr, .else_expr = else_expr, }), diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index da6671177a..0d5775fd61 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -714,19 +714,19 @@ test "zig fmt: async function" { // \\ // ); //} -// -//test "zig fmt: while else err prong with no block" { -// try testCanonical( -// \\test "" { -// \\ const result = while (returnError()) |value| { -// \\ break value; -// \\ } else |err| @as(i32, 2); -// \\ expect(result == 2); -// \\} -// \\ -// ); -//} -// + +test "zig fmt: while else err prong with no block" { + try testCanonical( + \\test "" { + \\ const result = while (returnError()) |value| { + \\ break value; + \\ } else |err| @as(i32, 2); + \\ expect(result == 2); + \\} + \\ + ); +} + //test "zig fmt: tagged union with enum values" { // try testCanonical( // \\const MultipleChoice2 = union(enum(u32)) { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index df17b313f4..8ce7d8bb7b 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -567,13 +567,13 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac if (lbrace + 1 == rbrace) { // There is nothing between the braces so render condensed: `error{}` try renderToken(ais, tree, lbrace, .None); - try renderToken(ais, tree, rbrace, space); + return renderToken(ais, tree, rbrace, space); } else if (lbrace + 2 == rbrace and token_tags[lbrace + 1] == .Identifier) { // There is exactly one member and no trailing comma or // comments, so render without surrounding spaces: `error{Foo}` try renderToken(ais, tree, lbrace, .None); try renderToken(ais, tree, lbrace + 1, .None); // identifier - try renderToken(ais, tree, rbrace, space); + return renderToken(ais, tree, rbrace, space); } else if (token_tags[rbrace - 1] == .Comma) { // There is a trailing comma so render each member on a new line. try renderToken(ais, tree, lbrace, .Newline); @@ -589,7 +589,7 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac } } ais.popIndent(); - try renderToken(ais, tree, rbrace, space); + return renderToken(ais, tree, rbrace, space); } else { // There is no trailing comma so render everything on one line. try renderToken(ais, tree, lbrace, .Space); @@ -602,7 +602,7 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac else => unreachable, } } - try renderToken(ais, tree, rbrace, space); + return renderToken(ais, tree, rbrace, space); } }, @@ -663,7 +663,7 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac if (cases.len == 0) { try renderToken(ais, tree, rparen + 1, .None); // lbrace - try renderToken(ais, tree, rparen + 2, space); // rbrace + return renderToken(ais, tree, rparen + 2, space); // rbrace } else { try renderToken(ais, tree, rparen + 1, .Newline); // lbrace ais.pushIndent(); @@ -673,83 +673,16 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac try renderExpression(ais, tree, case, .Comma); } ais.popIndent(); - try renderToken(ais, tree, tree.lastToken(node), space); // rbrace + return renderToken(ais, tree, tree.lastToken(node), space); // rbrace } }, - .SwitchCaseOne => try renderSwitchCase(ais, tree, tree.switchCaseOne(node), space), - .SwitchCase => try renderSwitchCase(ais, tree, tree.switchCase(node), space), + .SwitchCaseOne => return renderSwitchCase(ais, tree, tree.switchCaseOne(node), space), + .SwitchCase => return renderSwitchCase(ais, tree, tree.switchCase(node), space), - .WhileSimple => unreachable, // TODO - .WhileCont => unreachable, // TODO - .While => unreachable, // TODO - //.While => { - // const while_node = @fieldParentPtr(ast.Node.While, "base", base); - - // if (while_node.label) |label| { - // try renderToken(ais, tree, label, Space.None); // label - // try renderToken(ais, tree, tree.nextToken(label), Space.Space); // : - // } - - // if (while_node.inline_token) |inline_token| { - // try renderToken(ais, tree, inline_token, Space.Space); // inline - // } - - // try renderToken(ais, tree, while_node.while_token, Space.Space); // while - // try renderToken(ais, tree, tree.nextToken(while_node.while_token), Space.None); // ( - // try renderExpression(ais, tree, while_node.condition, Space.None); - - // const cond_rparen = tree.nextToken(while_node.condition.lastToken()); - - // const body_is_block = nodeIsBlock(while_node.body); - - // var block_start_space: Space = undefined; - // var after_body_space: Space = undefined; - - // if (body_is_block) { - // block_start_space = Space.BlockStart; - // after_body_space = if (while_node.@"else" == null) space else Space.Space; - // } else if (tree.tokensOnSameLine(cond_rparen, while_node.body.lastToken())) { - // block_start_space = Space.Space; - // after_body_space = if (while_node.@"else" == null) space else Space.Space; - // } else { - // block_start_space = Space.Newline; - // after_body_space = if (while_node.@"else" == null) space else Space.Newline; - // } - - // { - // const rparen_space = if (while_node.payload != null or while_node.continue_expr != null) Space.Space else block_start_space; - // try renderToken(ais, tree, cond_rparen, rparen_space); // ) - // } - - // if (while_node.payload) |payload| { - // const payload_space = if (while_node.continue_expr != null) Space.Space else block_start_space; - // try renderExpression(ais, tree, payload, payload_space); - // } - - // if (while_node.continue_expr) |continue_expr| { - // const rparen = tree.nextToken(continue_expr.lastToken()); - // const lparen = tree.prevToken(continue_expr.firstToken()); - // const colon = tree.prevToken(lparen); - - // try renderToken(ais, tree, colon, Space.Space); // : - // try renderToken(ais, tree, lparen, Space.None); // ( - - // try renderExpression(ais, tree, continue_expr, Space.None); - - // try renderToken(ais, tree, rparen, block_start_space); // ) - // } - - // { - // if (!body_is_block) ais.pushIndent(); - // defer if (!body_is_block) ais.popIndent(); - // try renderExpression(ais, tree, while_node.body, after_body_space); - // } - - // if (while_node.@"else") |@"else"| { - // return renderExpression(ais, tree, &@"else".base, space); - // } - //}, + .WhileSimple => return renderWhile(ais, tree, tree.whileSimple(node), space), + .WhileCont => return renderWhile(ais, tree, tree.whileCont(node), space), + .While => return renderWhile(ais, tree, tree.whileFull(node), space), .ForSimple => unreachable, // TODO .For => unreachable, // TODO @@ -1092,105 +1025,142 @@ fn renderVarDecl(ais: *Ais, tree: ast.Tree, var_decl: ast.full.VarDecl) Error!vo } fn renderIf(ais: *Ais, tree: ast.Tree, if_node: ast.full.If, space: Space) Error!void { + return renderWhile(ais, tree, .{ + .ast = .{ + .while_token = if_node.ast.if_token, + .cond_expr = if_node.ast.cond_expr, + .cont_expr = 0, + .then_expr = if_node.ast.then_expr, + .else_expr = if_node.ast.else_expr, + }, + .inline_token = null, + .label_token = null, + .payload_token = if_node.payload_token, + .else_token = if_node.else_token, + .error_token = if_node.error_token, + }, space); +} + +/// Note that this function is additionally used to render if expressions, with +/// respective values set to null. +fn renderWhile(ais: *Ais, tree: ast.Tree, while_node: ast.full.While, space: Space) Error!void { const node_tags = tree.nodes.items(.tag); const token_tags = tree.tokens.items(.tag); - try renderToken(ais, tree, if_node.ast.if_token, .Space); // if - - const lparen = if_node.ast.if_token + 1; - - try renderToken(ais, tree, lparen, .None); // ( - try renderExpression(ais, tree, if_node.ast.cond_expr, .None); // condition - - switch (node_tags[if_node.ast.then_expr]) { - .If, .IfSimple => { - try renderExtraNewline(ais, tree, if_node.ast.then_expr); - }, - .Block, .For, .ForSimple, .While, .WhileSimple, .Switch => { - if (if_node.payload_token) |payload_token| { - try renderToken(ais, tree, payload_token - 2, .Space); // ) - try renderToken(ais, tree, payload_token - 1, .None); // | - if (token_tags[payload_token] == .Asterisk) { - try renderToken(ais, tree, payload_token, .None); // * - try renderToken(ais, tree, payload_token + 1, .None); // identifier - try renderToken(ais, tree, payload_token + 2, .BlockStart); // | - } else { - try renderToken(ais, tree, payload_token, .None); // identifier - try renderToken(ais, tree, payload_token + 1, .BlockStart); // | - } - } else { - const rparen = tree.lastToken(if_node.ast.cond_expr) + 1; - try renderToken(ais, tree, rparen, .BlockStart); // ) - } - if (if_node.ast.else_expr != 0) { - try renderExpression(ais, tree, if_node.ast.then_expr, Space.Space); - try renderToken(ais, tree, if_node.else_token, .Space); // else - if (if_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(ais, tree, if_node.ast.else_expr, space); - } else { - return renderExpression(ais, tree, if_node.ast.then_expr, space); - } - }, - else => {}, + if (while_node.label_token) |label| { + try renderToken(ais, tree, label, .None); // label + try renderToken(ais, tree, label + 1, .Space); // : } - const rparen = tree.lastToken(if_node.ast.cond_expr) + 1; - const last_then_token = tree.lastToken(if_node.ast.then_expr); + if (while_node.inline_token) |inline_token| { + 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 + 1, .None); // ( + try renderExpression(ais, tree, while_node.ast.cond_expr, .None); // condition + + if (nodeIsBlock(node_tags[while_node.ast.then_expr])) { + const payload_space: Space = if (while_node.ast.cont_expr != 0) .Space else .BlockStart; + if (while_node.payload_token) |payload_token| { + try renderToken(ais, tree, payload_token - 2, .Space); // ) + try renderToken(ais, tree, payload_token - 1, .None); // | + if (token_tags[payload_token] == .Asterisk) { + try renderToken(ais, tree, payload_token, .None); // * + try renderToken(ais, tree, payload_token + 1, .None); // identifier + try renderToken(ais, tree, payload_token + 2, payload_space); // | + } else { + try renderToken(ais, tree, payload_token, .None); // identifier + try renderToken(ais, tree, payload_token + 1, payload_space); // | + } + } else { + const rparen = tree.lastToken(while_node.ast.cond_expr) + 1; + try renderToken(ais, tree, rparen, payload_space); // ) + } + 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(ais, tree, while_node.ast.cont_expr, .None); + try renderToken(ais, tree, rparen, .BlockStart); // rparen + } + if (while_node.ast.else_expr != 0) { + try renderExpression(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(ais, tree, while_node.ast.else_expr, space); + } else { + return renderExpression(ais, tree, while_node.ast.then_expr, space); + } + } + + const rparen = tree.lastToken(while_node.ast.cond_expr) + 1; + const last_then_token = tree.lastToken(while_node.ast.then_expr); const src_has_newline = !tree.tokensOnSameLine(rparen, last_then_token); if (src_has_newline) { - if (if_node.payload_token) |payload_token| { + const payload_space: Space = if (while_node.ast.cont_expr != 0) .Space else .Newline; + if (while_node.payload_token) |payload_token| { try renderToken(ais, tree, payload_token - 2, .Space); // ) try renderToken(ais, tree, payload_token - 1, .None); // | try renderToken(ais, tree, payload_token, .None); // identifier - try renderToken(ais, tree, payload_token + 1, .Newline); // | + try renderToken(ais, tree, payload_token + 1, payload_space); // | } else { ais.pushIndent(); - try renderToken(ais, tree, rparen, .Newline); // ) + try renderToken(ais, tree, rparen, payload_space); // ) ais.popIndent(); } - if (if_node.ast.else_expr != 0) { + 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(ais, tree, while_node.ast.cont_expr, .None); + try renderToken(ais, tree, cont_rparen, .Newline); // rparen + } + if (while_node.ast.else_expr != 0) { ais.pushIndent(); - try renderExpression(ais, tree, if_node.ast.then_expr, Space.Newline); + try renderExpression(ais, tree, while_node.ast.then_expr, Space.Newline); ais.popIndent(); - const else_is_block = nodeIsBlock(node_tags[if_node.ast.else_expr]); + const else_is_block = nodeIsBlock(node_tags[while_node.ast.else_expr]); if (else_is_block) { - try renderToken(ais, tree, if_node.else_token, .Space); // else - if (if_node.error_token) |error_token| { + 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(ais, tree, if_node.ast.else_expr, space); + return renderExpression(ais, tree, while_node.ast.else_expr, space); } else { - if (if_node.error_token) |error_token| { - try renderToken(ais, tree, if_node.else_token, .Space); // 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, if_node.else_token, .Newline); // else + try renderToken(ais, tree, while_node.else_token, .Newline); // else } ais.pushIndent(); - try renderExpression(ais, tree, if_node.ast.else_expr, space); + try renderExpression(ais, tree, while_node.ast.else_expr, space); ais.popIndent(); return; } } else { ais.pushIndent(); - try renderExpression(ais, tree, if_node.ast.then_expr, space); + try renderExpression(ais, tree, while_node.ast.then_expr, space); ais.popIndent(); return; } } - // Single line if statement. + // Render everything on a single line. - if (if_node.payload_token) |payload_token| { + if (while_node.payload_token) |payload_token| { assert(payload_token - 2 == rparen); try renderToken(ais, tree, payload_token - 2, .Space); // ) try renderToken(ais, tree, payload_token - 1, .None); // | @@ -1206,19 +1176,28 @@ fn renderIf(ais: *Ais, tree: ast.Tree, if_node: ast.full.If, space: Space) Error try renderToken(ais, tree, rparen, .Space); // ) } - if (if_node.ast.else_expr != 0) { - try renderExpression(ais, tree, if_node.ast.then_expr, .Space); - try renderToken(ais, tree, if_node.else_token, .Space); // else + 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(ais, tree, while_node.ast.cont_expr, .None); + try renderToken(ais, tree, cont_rparen, .Space); // rparen + } - if (if_node.error_token) |error_token| { + if (while_node.ast.else_expr != 0) { + try renderExpression(ais, tree, while_node.ast.then_expr, .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(ais, tree, if_node.ast.else_expr, space); + return renderExpression(ais, tree, while_node.ast.else_expr, space); } else { - return renderExpression(ais, tree, if_node.ast.then_expr, space); + return renderExpression(ais, tree, while_node.ast.then_expr, space); } } @@ -2079,12 +2058,16 @@ fn renderDocComments(ais: *Ais, tree: ast.Tree, end_token: ast.TokenIndex) Error fn nodeIsBlock(tag: ast.Node.Tag) bool { return switch (tag) { .Block, + .BlockSemicolon, + .BlockTwo, + .BlockTwoSemicolon, .If, .IfSimple, .For, .ForSimple, .While, .WhileSimple, + .WhileCont, .Switch, => true, else => false,