Always write a multiline struct literal if a field expr is multiline

This commit is contained in:
hryx 2019-03-31 16:38:33 -07:00 committed by Andrew Kelley
parent aa794eb621
commit 0563e8e1d4
2 changed files with 107 additions and 16 deletions

View File

@ -393,6 +393,50 @@ test "zig fmt: struct literal no trailing comma" {
);
}
test "zig fmt: struct literal containing a multiline expression" {
try testTransform(
\\const a = A{ .x = if (f1()) 10 else 20 };
\\const a = A{ .x = if (f1()) 10 else 20, };
\\const a = A{ .x = if (f1())
\\ 10 else 20 };
\\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 };
\\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100, };
\\const a = A{ .x = if (f1())
\\ 10 else 20};
\\const a = A{ .x = switch(g) {0 => "ok", else => "no"} };
\\
,
\\const a = A{ .x = if (f1()) 10 else 20 };
\\const a = A{
\\ .x = if (f1()) 10 else 20,
\\};
\\const a = A{
\\ .x = if (f1())
\\ 10
\\ else
\\ 20,
\\};
\\const a = A{ .x = if (f1()) 10 else 20, .y = f2() + 100 };
\\const a = A{
\\ .x = if (f1()) 10 else 20,
\\ .y = f2() + 100,
\\};
\\const a = A{
\\ .x = if (f1())
\\ 10
\\ else
\\ 20,
\\};
\\const a = A{
\\ .x = switch (g) {
\\ 0 => "ok",
\\ else => "no",
\\ },
\\};
\\
);
}
test "zig fmt: array literal with hint" {
try testTransform(
\\const a = []u8{

View File

@ -596,21 +596,6 @@ fn renderExpression(
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
}
if (field_inits.len == 1) blk: {
const field_init = field_inits.at(0).*.cast(ast.Node.FieldInitializer).?;
if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| {
if (nested_suffix_op.op == ast.Node.SuffixOp.Op.StructInitializer) {
break :blk;
}
}
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
try renderExpression(allocator, stream, tree, indent, start_col, &field_init.base, Space.Space);
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
}
const src_has_trailing_comma = blk: {
const maybe_comma = tree.prevToken(suffix_op.rtoken);
break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma;
@ -621,7 +606,39 @@ fn renderExpression(
break :blk loc.line == 0;
};
if (!src_has_trailing_comma and src_same_line) {
const expr_outputs_one_line = blk: {
// render field expressions until a LF is found
var it = field_inits.iterator(0);
while (it.next()) |field_init| {
var find_stream = FindByteOutStream.init('\n');
var dummy_col: usize = 0;
try renderExpression(allocator, &find_stream.stream, tree, 0, &dummy_col, field_init.*, Space.None);
if (find_stream.byte_found) break :blk false;
}
break :blk true;
};
if (field_inits.len == 1) blk: {
const field_init = field_inits.at(0).*.cast(ast.Node.FieldInitializer).?;
if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| {
if (nested_suffix_op.op == ast.Node.SuffixOp.Op.StructInitializer) {
break :blk;
}
}
// if the expression outputs to multiline, make this struct multiline
if (!expr_outputs_one_line or src_has_trailing_comma) {
break :blk;
}
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
try renderExpression(allocator, stream, tree, indent, start_col, &field_init.base, Space.Space);
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
}
if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) {
// render all on one line, no trailing comma
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
@ -2092,3 +2109,33 @@ fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
else => true,
};
}
// An OutStream that returns whether the given character has been written to it.
// The contents are not written to anything.
const FindByteOutStream = struct {
const Self = FindByteOutStream;
pub const Error = error{};
pub const Stream = std.io.OutStream(Error);
pub stream: Stream,
pub byte_found: bool,
byte: u8,
pub fn init(byte: u8) Self {
return Self{
.stream = Stream{ .writeFn = writeFn },
.byte = byte,
.byte_found = false,
};
}
fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
const self = @fieldParentPtr(Self, "stream", out_stream);
if (self.byte_found) return;
self.byte_found = blk: {
for (bytes) |b|
if (b == self.byte) break :blk true;
break :blk false;
};
}
};