diff --git a/std/mem.zig b/std/mem.zig index 617c1de2f5da..70f2fe22ac19 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -177,6 +177,14 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool { return true; } +/// Returns true if all elements in a slice are equal to the scalar value provided +pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool { + for (slice) |item| { + if (item != scalar) return false; + } + return true; +} + /// Copies ::m to newly allocated memory. Caller is responsible to free it. pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T { const new_buf = try allocator.alloc(T, m.len); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 152056c6b459..909220a6f684 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,62 @@ +test "zig fmt: array literal with hint" { + try testTransform( + \\const a = []u8{ + \\ 1, 2, // + \\ 3, + \\ 4, + \\ 5, + \\ 6, + \\ 7 }; + \\const a = []u8{ + \\ 1, 2, // + \\ 3, + \\ 4, + \\ 5, + \\ 6, + \\ 7, 8 }; + \\const a = []u8{ + \\ 1, 2, // + \\ 3, + \\ 4, + \\ 5, + \\ 6, // blah + \\ 7, 8 }; + \\const a = []u8{ + \\ 1, 2, // + \\ 3, // + \\ 4, + \\ 5, + \\ 6, + \\ 7 }; + , + \\const a = []u8{ + \\ 1, 2, // + \\ 3, 4, // + \\ 5, 6, // + \\ 7, + \\}; + \\const a = []u8{ + \\ 1, 2, // + \\ 3, 4, // + \\ 5, 6, // + \\ 7, 8, // + \\}; + \\const a = []u8{ + \\ 1, 2, // + \\ 3, 4, // + \\ 5, 6, // blah + \\ 7, 8, // + \\}; + \\const a = []u8{ + \\ 1, 2, // + \\ 3, 4, // + \\ 5, 6, // + \\ 7, + \\}; + \\ + ); +} + test "zig fmt: multiline string with backslash at end of line" { try testCanonical( \\comptime { diff --git a/std/zig/render.zig b/std/zig/render.zig index 409828e0702a..fa3755b719b2 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -19,7 +19,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf( var tok_it = tree.tokens.iterator(0); while (tok_it.next()) |token| { if (token.id != Token.Id.LineComment) break; - try stream.print("{}\n", tree.tokenSlicePtr(token)); + try stream.print("{}\n", mem.trimRight(u8, tree.tokenSlicePtr(token), " ")); if (tok_it.peek()) |next_token| { const loc = tree.tokenLocationPtr(token.end, next_token); if (loc.line >= 2) { @@ -532,12 +532,73 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); + // scan to find row size + const maybe_row_size: ?usize = blk: { + var count: usize = 0; + var it = exprs.iterator(0); + var prev_token = (??it.peek()).*.lastToken() + 1; + while (it.next()) |expr| { + const expr_last_token = expr.*.lastToken() + 1; + const next_token = tree.tokens.at(expr_last_token + 1); + const loc = tree.tokenLocationPtr(tree.tokens.at(prev_token).end, next_token); + if (loc.line != 0) break :blk null; + if (next_token.id == Token.Id.LineComment) { + const trimmed = mem.trimRight(u8, tree.tokenSlicePtr(next_token), " "); + if (trimmed.len == 2) { + break :blk count; + } else { + break :blk null; + } + } + prev_token = expr_last_token; + count += 1; + } + break :blk null; + }; + + const new_indent = indent + indent_delta; try renderToken(tree, stream, lbrace, new_indent, Space.Newline); + try stream.writeByteNTimes(' ', new_indent); + + if (maybe_row_size) |row_size| { + var it = exprs.iterator(0); + var i: usize = 0; + while (it.next()) |expr| { + if (it.peek()) |next_expr| { + try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.None); + + const comma = tree.nextToken(expr.*.lastToken()); + + if (i != row_size) { + try renderToken(tree, stream, comma, new_indent, Space.IgnoreEmptyComment); // , + i += 1; + continue; + } + i = 0; + + try renderToken(tree, stream, comma, new_indent, Space.NoIndent); // , + + const next_token = tree.tokens.at(comma + 1); + if (next_token.id != Token.Id.LineComment) { + try stream.print(" //\n"); + } + + try renderExtraNewline(tree, stream, next_expr.*); + try stream.writeByteNTimes(' ', new_indent); + } else if (i == row_size) { + try renderTrailingCommaAndEmptyComment(allocator, stream, tree, new_indent, expr.*); // , // + } else { + try renderTrailingComma(allocator, stream, tree, new_indent, expr.*, Space.Newline); // , + } + } + try stream.writeByteNTimes(' ', indent); + try renderToken(tree, stream, suffix_op.rtoken, indent, space); + return; + } var it = exprs.iterator(0); while (it.next()) |expr| { - try stream.writeByteNTimes(' ', new_indent); if (it.peek()) |next_expr| { try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.None); @@ -546,6 +607,7 @@ fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, ind try renderToken(tree, stream, comma, new_indent, Space.Newline); // , try renderExtraNewline(tree, stream, next_expr.*); + try stream.writeByteNTimes(' ', new_indent); } else { try renderTrailingComma(allocator, stream, tree, new_indent, expr.*, Space.Newline); } @@ -1496,11 +1558,12 @@ const Space = enum { NoNewline, NoIndent, NoComment, + IgnoreEmptyComment, }; fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { var token = tree.tokens.at(token_index); - try stream.write(tree.tokenSlicePtr(token)); + try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(token), " ")); if (space == Space.NoComment) return; @@ -1515,15 +1578,19 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent return stream.write("\n"); } }, - Space.Space => return stream.writeByte(' '), + Space.Space, Space.IgnoreEmptyComment => return stream.writeByte(' '), Space.NoComment => unreachable, } } + if (space == Space.IgnoreEmptyComment and mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ").len == 2) { + return stream.writeByte(' '); + } + var loc = tree.tokenLocationPtr(token.end, next_token); var offset: usize = 1; if (loc.line == 0) { - try stream.print(" {}", tree.tokenSlicePtr(next_token)); + try stream.print(" {}", mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ")); offset = 2; token = next_token; next_token = tree.tokens.at(token_index + offset); @@ -1546,7 +1613,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } }, Space.NoNewline => {}, - Space.NoComment => unreachable, + Space.NoComment, Space.IgnoreEmptyComment => unreachable, } return; } @@ -1558,7 +1625,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent const newline_count = if (loc.line == 1) u8(1) else u8(2); try stream.writeByteNTimes('\n', newline_count); try stream.writeByteNTimes(' ', indent); - try stream.write(tree.tokenSlicePtr(next_token)); + try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ")); offset += 1; token = next_token; @@ -1583,7 +1650,7 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent try stream.writeByteNTimes(' ', next_line_indent); }, Space.NoNewline => {}, - Space.NoComment => unreachable, + Space.NoComment, Space.IgnoreEmptyComment => unreachable, } return; } @@ -1621,3 +1688,28 @@ fn renderTrailingComma(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, }, } } + +fn renderTrailingCommaAndEmptyComment(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void +{ + const end_token = base.lastToken() + 1; + switch (tree.tokens.at(end_token).id) { + Token.Id.Comma => { + try renderExpression(allocator, stream, tree, indent, base, Space.None); + try renderToken(tree, stream, end_token, indent, Space.Space); // , + + const next_token = tree.tokens.at(end_token + 1); + if (next_token.id != Token.Id.LineComment) { + try stream.print("//\n"); + } + }, + Token.Id.LineComment => { + try renderExpression(allocator, stream, tree, indent, base, Space.NoComment); + try stream.write(", "); + try renderToken(tree, stream, end_token, indent, Space.Newline); + }, + else => { + try renderExpression(allocator, stream, tree, indent, base, Space.None); + try stream.write(", //\n"); + }, + } +}