aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-02-09 22:25:49 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-02-09 22:26:21 -0700
commit36eee7bc6cbe6fcc388ebdc38fd3c5f06c9e9043 (patch)
treef52add396d55777d2f2cb510e5f2cc22c61efc31
parent25bcf4eb99d367965b0f699faff5d3fd1d991941 (diff)
downloadzig-36eee7bc6cbe6fcc388ebdc38fd3c5f06c9e9043.tar.gz
zig-36eee7bc6cbe6fcc388ebdc38fd3c5f06c9e9043.zip
zig fmt: anytype, fn calls with one param, trailing commas
and extra newlines between top level declarations
-rw-r--r--lib/std/zig/ast.zig124
-rw-r--r--lib/std/zig/parse.zig210
-rw-r--r--lib/std/zig/parser_test.zig58
-rw-r--r--lib/std/zig/render.zig173
4 files changed, 404 insertions, 161 deletions
diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig
index 8caafa73c8..35b10c6e2c 100644
--- a/lib/std/zig/ast.zig
+++ b/lib/std/zig/ast.zig
@@ -196,6 +196,7 @@ pub const Tree = struct {
const datas = tree.nodes.items(.data);
const main_tokens = tree.nodes.items(.main_token);
const token_tags = tree.tokens.items(.tag);
+ var end_offset: TokenIndex = 0;
var n = node;
while (true) switch (tags[n]) {
.Root => return 0,
@@ -251,7 +252,7 @@ pub const Tree = struct {
.ArrayType,
.ArrayTypeSentinel,
.ErrorValue,
- => return main_tokens[n],
+ => return main_tokens[n] - end_offset,
.ArrayInitDot,
.ArrayInitDotTwo,
@@ -260,7 +261,7 @@ pub const Tree = struct {
.StructInitDotTwo,
.StructInitDotTwoComma,
.EnumLiteral,
- => return main_tokens[n] - 1,
+ => return main_tokens[n] - 1 - end_offset,
.Catch,
.FieldAccess,
@@ -314,24 +315,32 @@ pub const Tree = struct {
.StructInitOne,
.StructInit,
.CallOne,
+ .CallOneComma,
.Call,
+ .CallComma,
.SwitchRange,
.FnDecl,
.ErrorUnion,
=> n = datas[n].lhs,
+ .AsyncCallOne,
+ .AsyncCallOneComma,
+ .AsyncCall,
+ .AsyncCallComma,
+ => {
+ end_offset += 1; // async token
+ n = datas[n].lhs;
+ },
+
.ContainerFieldInit,
.ContainerFieldAlign,
.ContainerField,
=> {
const name_token = main_tokens[n];
- if (name_token > 0 and
- token_tags[name_token - 1] == .Keyword_comptime)
- {
- return name_token - 1;
- } else {
- return name_token;
+ if (name_token > 0 and token_tags[name_token - 1] == .Keyword_comptime) {
+ end_offset += 1;
}
+ return name_token - end_offset;
},
.GlobalVarDecl,
@@ -351,10 +360,10 @@ pub const Tree = struct {
.StringLiteral,
=> continue,
- else => return i + 1,
+ else => return i + 1 - end_offset,
}
}
- return i;
+ return i - end_offset;
},
.Block,
@@ -365,10 +374,9 @@ pub const Tree = struct {
// Look for a label.
const lbrace = main_tokens[n];
if (token_tags[lbrace - 1] == .Colon) {
- return lbrace - 2;
- } else {
- return lbrace;
+ end_offset += 2;
}
+ return lbrace - end_offset;
},
.ContainerDecl,
@@ -386,9 +394,10 @@ pub const Tree = struct {
=> {
const main_token = main_tokens[n];
switch (token_tags[main_token - 1]) {
- .Keyword_packed, .Keyword_extern => return main_token - 1,
- else => return main_token,
+ .Keyword_packed, .Keyword_extern => end_offset += 1,
+ else => {},
}
+ return main_token - end_offset;
},
.PtrTypeAligned,
@@ -404,12 +413,12 @@ pub const Tree = struct {
},
.LBrace => main_token,
else => unreachable,
- };
+ } - end_offset;
},
.SwitchCaseOne => {
if (datas[n].lhs == 0) {
- return main_tokens[n] - 1; // else token
+ return main_tokens[n] - 1 - end_offset; // else token
} else {
n = datas[n].lhs;
}
@@ -422,7 +431,7 @@ pub const Tree = struct {
.AsmOutput, .AsmInput => {
assert(token_tags[main_tokens[n] - 1] == .LBracket);
- return main_tokens[n] - 1;
+ return main_tokens[n] - 1 - end_offset;
},
.WhileSimple,
@@ -435,7 +444,7 @@ pub const Tree = struct {
return switch (token_tags[main_token - 1]) {
.Keyword_inline => main_token - 1,
else => main_token,
- };
+ } - end_offset;
},
};
}
@@ -555,7 +564,7 @@ pub const Tree = struct {
return main_tokens[n] + end_offset;
},
- .Call => {
+ .Call, .AsyncCall => {
end_offset += 1; // for the rparen
const params = tree.extraData(datas[n].rhs, Node.SubRange);
if (params.end - params.start == 0) {
@@ -563,6 +572,12 @@ pub const Tree = struct {
}
n = tree.extra_data[params.end - 1]; // last parameter
},
+ .CallComma, .AsyncCallComma => {
+ end_offset += 2; // for the comma+rparen
+ const params = tree.extraData(datas[n].rhs, Node.SubRange);
+ assert(params.end > params.start);
+ n = tree.extra_data[params.end - 1]; // last parameter
+ },
.Switch => {
const cases = tree.extraData(datas[n].rhs, Node.SubRange);
if (cases.end - cases.start == 0) {
@@ -614,6 +629,7 @@ pub const Tree = struct {
n = tree.extra_data[datas[n].rhs - 1]; // last member
},
.CallOne,
+ .AsyncCallOne,
.ArrayAccess,
=> {
end_offset += 1; // for the rparen/rbracket
@@ -622,7 +638,6 @@ pub const Tree = struct {
}
n = datas[n].rhs;
},
-
.ArrayInitDotTwo,
.BlockTwo,
.StructInitDotTwo,
@@ -755,9 +770,10 @@ pub const Tree = struct {
}
},
- .SliceOpen => {
- end_offset += 2; // ellipsis2 and rbracket
+ .SliceOpen, .CallOneComma, .AsyncCallOneComma => {
+ end_offset += 2; // ellipsis2 + rbracket, or comma + rparen
n = datas[n].rhs;
+ assert(n != 0);
},
.Slice => {
const extra = tree.extraData(datas[n].rhs, Node.Slice);
@@ -1496,6 +1512,27 @@ pub const Tree = struct {
});
}
+ pub fn callOne(tree: Tree, buffer: *[1]Node.Index, node: Node.Index) full.Call {
+ const data = tree.nodes.items(.data)[node];
+ buffer.* = .{data.rhs};
+ const params = if (data.rhs != 0) buffer[0..1] else buffer[0..0];
+ return tree.fullCall(.{
+ .lparen = tree.nodes.items(.main_token)[node],
+ .fn_expr = data.lhs,
+ .params = params,
+ });
+ }
+
+ pub fn callFull(tree: Tree, node: Node.Index) full.Call {
+ const data = tree.nodes.items(.data)[node];
+ const extra = tree.extraData(data.rhs, Node.SubRange);
+ return tree.fullCall(.{
+ .lparen = tree.nodes.items(.main_token)[node],
+ .fn_expr = data.lhs,
+ .params = tree.extra_data[extra.start..extra.end],
+ });
+ }
+
fn fullVarDecl(tree: Tree, info: full.VarDecl.Ast) full.VarDecl {
const token_tags = tree.tokens.items(.tag);
var result: full.VarDecl = .{
@@ -1750,6 +1787,19 @@ pub const Tree = struct {
}
return result;
}
+
+ fn fullCall(tree: Tree, info: full.Call.Ast) full.Call {
+ const token_tags = tree.tokens.items(.tag);
+ var result: full.Call = .{
+ .ast = info,
+ .async_token = null,
+ };
+ const maybe_async_token = tree.firstToken(info.fn_expr) - 1;
+ if (token_tags[maybe_async_token] == .Keyword_async) {
+ result.async_token = maybe_async_token;
+ }
+ return result;
+ }
};
/// Fully assembled AST node information.
@@ -1942,6 +1992,17 @@ pub const full = struct {
rparen: TokenIndex,
};
};
+
+ pub const Call = struct {
+ ast: Ast,
+ async_token: ?TokenIndex,
+
+ pub const Ast = struct {
+ lparen: TokenIndex,
+ fn_expr: Node.Index,
+ params: []const Node.Index,
+ };
+ };
};
pub const Error = union(enum) {
@@ -2383,9 +2444,24 @@ pub const Node = struct {
StructInit,
/// `lhs(rhs)`. rhs can be omitted.
CallOne,
- /// `lhs(a, b, c)`. `sub_range_list[rhs]`.
+ /// `lhs(rhs,)`. rhs can be omitted.
+ CallOneComma,
+ /// `async lhs(rhs)`. rhs can be omitted.
+ AsyncCallOne,
+ /// `async lhs(rhs,)`.
+ AsyncCallOneComma,
+ /// `lhs(a, b, c)`. `SubRange[rhs]`.
/// main_token is the `(`.
Call,
+ /// `lhs(a, b, c,)`. `SubRange[rhs]`.
+ /// main_token is the `(`.
+ CallComma,
+ /// `async lhs(a, b, c)`. `SubRange[rhs]`.
+ /// main_token is the `(`.
+ AsyncCall,
+ /// `async lhs(a, b, c,)`. `SubRange[rhs]`.
+ /// main_token is the `(`.
+ AsyncCallComma,
/// `switch(lhs) {}`. `SubRange[rhs]`.
Switch,
/// Same as Switch except there is known to be a trailing comma
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
index eb15a29650..da45090ffd 100644
--- a/lib/std/zig/parse.zig
+++ b/lib/std/zig/parse.zig
@@ -2254,8 +2254,6 @@ const Parser = struct {
/// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
/// FnCallArguments <- LPAREN ExprList RPAREN
/// ExprList <- (Expr COMMA)* Expr?
- /// TODO detect when there is 1 or less parameter to the call and emit
- /// CallOne instead of Call.
fn parseSuffixExpr(p: *Parser) !Node.Index {
if (p.eatToken(.Keyword_async)) |async_token| {
var res = try p.expectPrimaryTypeExpr();
@@ -2269,20 +2267,95 @@ const Parser = struct {
try p.warn(.{ .ExpectedParamList = .{ .token = p.tok_i } });
return res;
};
- const params = try ListParseFn(parseExpr)(p);
- _ = try p.expectToken(.RParen);
+ if (p.eatToken(.RParen)) |_| {
+ return p.addNode(.{
+ .tag = .AsyncCallOne,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = 0,
+ },
+ });
+ }
+ const param_one = try p.expectExpr();
+ const comma_one = p.eatToken(.Comma);
+ if (p.eatToken(.RParen)) |_| {
+ return p.addNode(.{
+ .tag = if (comma_one == null) .AsyncCallOne else .AsyncCallOneComma,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = param_one,
+ },
+ });
+ }
+ if (comma_one == null) {
+ try p.warn(.{
+ .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma },
+ });
+ }
- return p.addNode(.{
- .tag = .Call,
- .main_token = lparen,
- .data = .{
- .lhs = res,
- .rhs = try p.addExtra(Node.SubRange{
- .start = params.start,
- .end = params.end,
- }),
- },
- });
+ var param_list = std.ArrayList(Node.Index).init(p.gpa);
+ defer param_list.deinit();
+
+ try param_list.append(param_one);
+
+ while (true) {
+ const next = try p.expectExpr();
+ try param_list.append(next);
+ switch (p.token_tags[p.nextToken()]) {
+ .Comma => {
+ if (p.eatToken(.RParen)) |_| {
+ const span = try p.listToSpan(param_list.items);
+ return p.addNode(.{
+ .tag = .AsyncCallComma,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ },
+ });
+ } else {
+ continue;
+ }
+ },
+ .RParen => {
+ const span = try p.listToSpan(param_list.items);
+ return p.addNode(.{
+ .tag = .AsyncCall,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ },
+ });
+ },
+ .Colon, .RBrace, .RBracket => {
+ p.tok_i -= 1;
+ return p.fail(.{
+ .ExpectedToken = .{
+ .token = p.tok_i,
+ .expected_id = .RParen,
+ },
+ });
+ },
+ else => {
+ p.tok_i -= 1;
+ try p.warn(.{
+ .ExpectedToken = .{
+ .token = p.tok_i,
+ .expected_id = .Comma,
+ },
+ });
+ },
+ }
+ }
}
var res = try p.parsePrimaryTypeExpr();
if (res == 0) return res;
@@ -2293,21 +2366,98 @@ const Parser = struct {
res = suffix_op;
continue;
}
- const lparen = p.eatToken(.LParen) orelse return res;
- const params = try ListParseFn(parseExpr)(p);
- _ = try p.expectToken(.RParen);
+ res = res: {
+ const lparen = p.eatToken(.LParen) orelse return res;
+ if (p.eatToken(.RParen)) |_| {
+ break :res try p.addNode(.{
+ .tag = .CallOne,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = 0,
+ },
+ });
+ }
+ const param_one = try p.expectExpr();
+ const comma_one = p.eatToken(.Comma);
+ if (p.eatToken(.RParen)) |_| {
+ break :res try p.addNode(.{
+ .tag = if (comma_one == null) .CallOne else .CallOneComma,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = param_one,
+ },
+ });
+ }
+ if (comma_one == null) {
+ try p.warn(.{
+ .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma },
+ });
+ }
- res = try p.addNode(.{
- .tag = .Call,
- .main_token = lparen,
- .data = .{
- .lhs = res,
- .rhs = try p.addExtra(Node.SubRange{
- .start = params.start,
- .end = params.end,
- }),
- },
- });
+ var param_list = std.ArrayList(Node.Index).init(p.gpa);
+ defer param_list.deinit();
+
+ try param_list.append(param_one);
+
+ while (true) {
+ const next = try p.expectExpr();
+ try param_list.append(next);
+ switch (p.token_tags[p.nextToken()]) {
+ .Comma => {
+ if (p.eatToken(.RParen)) |_| {
+ const span = try p.listToSpan(param_list.items);
+ break :res try p.addNode(.{
+ .tag = .CallComma,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ },
+ });
+ } else {
+ continue;
+ }
+ },
+ .RParen => {
+ const span = try p.listToSpan(param_list.items);
+ break :res try p.addNode(.{
+ .tag = .Call,
+ .main_token = lparen,
+ .data = .{
+ .lhs = res,
+ .rhs = try p.addExtra(Node.SubRange{
+ .start = span.start,
+ .end = span.end,
+ }),
+ },
+ });
+ },
+ .Colon, .RBrace, .RBracket => {
+ p.tok_i -= 1;
+ return p.fail(.{
+ .ExpectedToken = .{
+ .token = p.tok_i,
+ .expected_id = .RParen,
+ },
+ });
+ },
+ else => {
+ p.tok_i -= 1;
+ try p.warn(.{
+ .ExpectedToken = .{
+ .token = p.tok_i,
+ .expected_id = .Comma,
+ },
+ });
+ },
+ }
+ }
+ };
}
}
@@ -2588,7 +2738,7 @@ const Parser = struct {
while (true) {
const next = try p.expectFieldInit();
- if (next == 0) break;
+ assert(next != 0);
try init_list.append(next);
switch (p.token_tags[p.nextToken()]) {
.Comma => {
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
index 73c4458352..6b9f39a728 100644
--- a/lib/std/zig/parser_test.zig
+++ b/lib/std/zig/parser_test.zig
@@ -3046,35 +3046,35 @@ test "zig fmt: for" {
// \\
// );
//}
-//
-//test "zig fmt: async functions" {
-// try testCanonical(
-// \\fn simpleAsyncFn() void {
-// \\ const a = async a.b();
-// \\ x += 1;
-// \\ suspend;
-// \\ x += 1;
-// \\ suspend;
-// \\ const p: anyframe->void = async simpleAsyncFn() catch unreachable;
-// \\ await p;
-// \\}
-// \\
-// \\test "suspend, resume, await" {
-// \\ const p: anyframe = async testAsyncSeq();
-// \\ resume p;
-// \\ await p;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: nosuspend" {
-// try testCanonical(
-// \\const a = nosuspend foo();
-// \\
-// );
-//}
-//
+
+test "zig fmt: async functions" {
+ try testCanonical(
+ \\fn simpleAsyncFn() void {
+ \\ const a = async a.b();
+ \\ x += 1;
+ \\ suspend;
+ \\ x += 1;
+ \\ suspend;
+ \\ const p: anyframe->void = async simpleAsyncFn() catch unreachable;
+ \\ await p;
+ \\}
+ \\
+ \\test "suspend, resume, await" {
+ \\ const p: anyframe = async testAsyncSeq();
+ \\ resume p;
+ \\ await p;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: nosuspend" {
+ try testCanonical(
+ \\const a = nosuspend foo();
+ \\
+ );
+}
+
//test "zig fmt: Block after if" {
// try testCanonical(
// \\test "Block after if" {
diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig
index b1ff7ff64b..0f922ceb0a 100644
--- a/lib/std/zig/render.zig
+++ b/lib/std/zig/render.zig
@@ -73,8 +73,18 @@ fn renderRoot(ais: *Ais, tree: ast.Tree) Error!void {
const nodes_data = tree.nodes.items(.data);
const root_decls = tree.extra_data[nodes_data[0].lhs..nodes_data[0].rhs];
- for (root_decls) |decl| {
- try renderMember(ais, tree, decl, .Newline);
+ return renderAllMembers(ais, tree, root_decls);
+}
+
+fn renderAllMembers(ais: *Ais, tree: ast.Tree, members: []const ast.Node.Index) Error!void {
+ if (members.len == 0) return;
+
+ const first_member = members[0];
+ try renderMember(ais, tree, first_member, .Newline);
+
+ for (members[1..]) |member| {
+ try renderExtraNewline(ais, tree, member);
+ try renderMember(ais, tree, member, .Newline);
}
}
@@ -391,65 +401,17 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
.StructInitDot => return renderStructInit(ais, tree, tree.structInitDot(node), space),
.StructInit => return renderStructInit(ais, tree, tree.structInit(node), space),
- .CallOne => unreachable, // TODO
- .Call => {
- const call = datas[node];
- const params_range = tree.extraData(call.rhs, ast.Node.SubRange);
- const params = tree.extra_data[params_range.start..params_range.end];
- const async_token = tree.firstToken(call.lhs) - 1;
- if (token_tags[async_token] == .Keyword_async) {
- try renderToken(ais, tree, async_token, .Space);
- }
- try renderExpression(ais, tree, call.lhs, .None);
-
- const lparen = main_tokens[node];
-
- if (params.len == 0) {
- try renderToken(ais, tree, lparen, .None);
- return renderToken(ais, tree, lparen + 1, space); // )
- }
-
- const last_param = params[params.len - 1];
- const after_last_param_tok = tree.lastToken(last_param) + 1;
- if (token_tags[after_last_param_tok] == .Comma) {
- ais.pushIndent();
- try renderToken(ais, tree, lparen, Space.Newline); // (
- for (params) |param_node, i| {
- if (i + 1 < params.len) {
- try renderExpression(ais, tree, param_node, Space.None);
-
- // Unindent the comma for multiline string literals
- const is_multiline_string = node_tags[param_node] == .StringLiteral and
- token_tags[main_tokens[param_node]] == .MultilineStringLiteralLine;
- if (is_multiline_string) ais.popIndent();
-
- const comma = tree.lastToken(param_node) + 1;
- try renderToken(ais, tree, comma, Space.Newline); // ,
-
- if (is_multiline_string) ais.pushIndent();
-
- try renderExtraNewline(ais, tree, params[i + 1]);
- } else {
- try renderExpression(ais, tree, param_node, Space.Comma);
- }
- }
- ais.popIndent();
- return renderToken(ais, tree, after_last_param_tok + 1, space); // )
- }
-
- try renderToken(ais, tree, lparen, Space.None); // (
-
- for (params) |param_node, i| {
- try renderExpression(ais, tree, param_node, Space.None);
-
- if (i + 1 < params.len) {
- const comma = tree.lastToken(param_node) + 1;
- try renderToken(ais, tree, comma, Space.Space);
- }
- }
- return renderToken(ais, tree, after_last_param_tok, space); // )
+ .CallOne, .CallOneComma, .AsyncCallOne, .AsyncCallOneComma => {
+ var params: [1]ast.Node.Index = undefined;
+ return renderCall(ais, tree, tree.callOne(&params, node), space);
},
+ .Call,
+ .CallComma,
+ .AsyncCall,
+ .AsyncCallComma,
+ => return renderCall(ais, tree, tree.callFull(node), space),
+
.ArrayAccess => {
const suffix = datas[node];
const lbracket = tree.firstToken(suffix.rhs) - 1;
@@ -625,18 +587,16 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac
},
.FnProto => return renderFnProto(ais, tree, tree.fnProto(node), space),
- .AnyFrameType => unreachable, // TODO
- //.AnyFrameType => {
- // const anyframe_type = @fieldParentPtr(ast.Node.AnyFrameType, "base", base);
-
- // if (anyframe_type.result) |result| {
- // try renderToken(ais, tree, anyframe_type.anyframe_token, Space.None); // anyframe
- // try renderToken(ais, tree, result.arrow_token, Space.None); // ->
- // return renderExpression(ais, tree, result.return_type, space);
- // } else {
- // return renderToken(ais, tree, anyframe_type.anyframe_token, space); // anyframe
- // }
- //},
+ .AnyFrameType => {
+ const main_token = main_tokens[node];
+ if (datas[node].rhs != 0) {
+ try renderToken(ais, tree, main_token, .None); // anyframe
+ try renderToken(ais, tree, main_token + 1, .None); // ->
+ return renderExpression(ais, tree, datas[node].rhs, space);
+ } else {
+ return renderToken(ais, tree, main_token, space); // anyframe
+ }
+ },
.Switch,
.SwitchComma,
@@ -1730,13 +1690,7 @@ fn renderContainerDecl(
// One member per line.
ais.pushIndent();
try renderToken(ais, tree, lbrace, .Newline); // lbrace
- for (container_decl.ast.members) |member, i| {
- try renderMember(ais, tree, member, .Newline);
-
- if (i + 1 < container_decl.ast.members.len) {
- try renderExtraNewline(ais, tree, container_decl.ast.members[i + 1]);
- }
- }
+ try renderAllMembers(ais, tree, container_decl.ast.members);
ais.popIndent();
return renderToken(ais, tree, rbrace, space); // rbrace
@@ -1871,6 +1825,69 @@ fn renderAsm(
} else unreachable; // TODO shouldn't need this on while(true)
}
+fn renderCall(
+ ais: *Ais,
+ tree: ast.Tree,
+ call: ast.full.Call,
+ space: Space,
+) Error!void {
+ const token_tags = tree.tokens.items(.tag);
+ const node_tags = tree.nodes.items(.tag);
+ const main_tokens = tree.nodes.items(.main_token);
+
+ if (call.async_token) |async_token| {
+ try renderToken(ais, tree, async_token, .Space);
+ }
+ try renderExpression(ais, tree, call.ast.fn_expr, .None);
+
+ const lparen = call.ast.lparen;
+ const params = call.ast.params;
+ if (params.len == 0) {
+ try renderToken(ais, tree, lparen, .None);
+ return renderToken(ais, tree, lparen + 1, space); // )
+ }
+
+ const last_param = params[params.len - 1];
+ const after_last_param_tok = tree.lastToken(last_param) + 1;
+ if (token_tags[after_last_param_tok] == .Comma) {
+ ais.pushIndent();
+ try renderToken(ais, tree, lparen, Space.Newline); // (
+ for (params) |param_node, i| {
+ if (i + 1 < params.len) {
+ try renderExpression(ais, tree, param_node, Space.None);
+
+ // Unindent the comma for multiline string literals
+ const is_multiline_string = node_tags[param_node] == .StringLiteral and
+ token_tags[main_tokens[param_node]] == .MultilineStringLiteralLine;
+ if (is_multiline_string) ais.popIndent();
+
+ const comma = tree.lastToken(param_node) + 1;
+ try renderToken(ais, tree, comma, Space.Newline); // ,
+
+ if (is_multiline_string) ais.pushIndent();
+
+ try renderExtraNewline(ais, tree, params[i + 1]);
+ } else {
+ try renderExpression(ais, tree, param_node, Space.Comma);
+ }
+ }
+ ais.popIndent();
+ return renderToken(ais, tree, after_last_param_tok + 1, space); // )
+ }
+
+ try renderToken(ais, tree, lparen, Space.None); // (
+
+ for (params) |param_node, i| {
+ try renderExpression(ais, tree, param_node, Space.None);
+
+ if (i + 1 < params.len) {
+ const comma = tree.lastToken(param_node) + 1;
+ try renderToken(ais, tree, comma, Space.Space);
+ }
+ }
+ return renderToken(ais, tree, after_last_param_tok, space); // )
+}
+
/// Render an expression, and the comma that follows it, if it is present in the source.
fn renderExpressionComma(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
const token_tags = tree.tokens.items(.tag);