zig fmt: while loops

This commit is contained in:
Andrew Kelley 2021-02-09 17:23:57 -07:00
parent bcafc51e58
commit 1c79eea125
4 changed files with 272 additions and 183 deletions

View File

@ -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,
};

View File

@ -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,
}),

View File

@ -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)) {

View File

@ -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,