authorgravatar for lachlan@lakebythewoods.xyzLachlan Easton <lachlan@lakebythewoods.xyz> 2020-08-30 18:25:04+10:00
committergravatar for lachlan@lakebythewoods.xyzLachlan Easton <lachlan@lakebythewoods.xyz> 2020-09-18 20:34:00+10:00
loge1bd27119220c59211509f65c39fbb89c69b939b
tree52799ff002a65bf1e1fd1404dfe4639b6fa476eb
parent9f0821e68836a495c726e0aae13e62c5235c5446

zig fmt: Allow trailing comments to do manual array formatting. close #5948


2 files changed, 180 insertions(+), 89 deletions(-)

lib/std/zig/parser_test.zig+42-2
...@@ -1301,8 +1301,10 @@ test "zig fmt: array literal with hint" {...@@ -1301,8 +1301,10 @@ test "zig fmt: array literal with hint" {
1301 \\const a = []u8{1301 \\const a = []u8{
1302 \\ 1, 2,1302 \\ 1, 2,
1303 \\ 3, 4,1303 \\ 3, 4,
1304 \\ 5, 6, // blah1304 \\ 5,
1305 \\ 7, 8,1305 \\ 6, // blah
1306 \\ 7,
1307 \\ 8,
1306 \\};1308 \\};
1307 \\const a = []u8{1309 \\const a = []u8{
1308 \\ 1, 2,1310 \\ 1, 2,
...@@ -3444,6 +3446,44 @@ test "zig fmt: " {...@@ -3444,6 +3446,44 @@ test "zig fmt: " {
3444 );3446 );
3445}3447}
34463448
3449test "zig fmt: allow trailing line comments to do manual array formatting" {
3450 try testCanonical(
3451 \\fn foo() void {
3452 \\ self.code.appendSliceAssumeCapacity(&[_]u8{
3453 \\ 0x55, // push rbp
3454 \\ 0x48, 0x89, 0xe5, // mov rbp, rsp
3455 \\ 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
3456 \\ });
3457 \\
3458 \\ di_buf.appendAssumeCapacity(&[_]u8{
3459 \\ 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header
3460 \\ DW.AT_stmt_list, DW_FORM_data4, // form value pairs
3461 \\ DW.AT_low_pc, DW_FORM_addr,
3462 \\ DW.AT_high_pc, DW_FORM_addr,
3463 \\ DW.AT_name, DW_FORM_strp,
3464 \\ DW.AT_comp_dir, DW_FORM_strp,
3465 \\ DW.AT_producer, DW_FORM_strp,
3466 \\ DW.AT_language, DW_FORM_data2,
3467 \\ 0, 0, // sentinel
3468 \\ });
3469 \\
3470 \\ self.code.appendSliceAssumeCapacity(&[_]u8{
3471 \\ 0x55, // push rbp
3472 \\ 0x48, 0x89, 0xe5, // mov rbp, rsp
3473 \\ // How do we handle this?
3474 \\ //0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
3475 \\ // Here's a blank line, should that be allowed?
3476 \\
3477 \\ 0x48, 0x89, 0xe5,
3478 \\ 0x33, 0x45,
3479 \\ // Now the comment breaks a single line -- how do we handle this?
3480 \\ 0x88,
3481 \\ });
3482 \\}
3483 \\
3484 );
3485}
3486
3447const std = @import("std");3487const std = @import("std");
3448const mem = std.mem;3488const mem = std.mem;
3449const warn = std.debug.warn;3489const warn = std.debug.warn;
lib/std/zig/render.zig+138-87
...@@ -523,11 +523,11 @@ fn renderExpression(...@@ -523,11 +523,11 @@ fn renderExpression(
523 };523 };
524524
525 {525 {
526 try ais.pushIndent();526 ais.pushIndent();
527 defer ais.popIndent();527 defer ais.popIndent();
528 try renderToken(tree, ais, infix_op_node.op_token, after_op_space);528 try renderToken(tree, ais, infix_op_node.op_token, after_op_space);
529 }529 }
530 try ais.pushIndentOneShot();530 ais.pushIndentOneShot();
531 return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);531 return renderExpression(allocator, ais, tree, infix_op_node.rhs, space);
532 },532 },
533533
...@@ -746,109 +746,130 @@ fn renderExpression(...@@ -746,109 +746,130 @@ fn renderExpression(
746 }746 }
747747
748 // scan to find row size748 // scan to find row size
749 const maybe_row_size: ?usize = blk: {749 if (rowSize(tree, exprs, rtoken, false) != null) {
750 var count: usize = 1;
751 for (exprs) |expr, i| {
752 if (i + 1 < exprs.len) {
753 const expr_last_token = expr.lastToken() + 1;
754 const loc = tree.tokenLocation(tree.token_locs[expr_last_token].end, exprs[i + 1].firstToken());
755 if (loc.line != 0) break :blk count;
756 count += 1;
757 } else {
758 const expr_last_token = expr.lastToken();
759 const loc = tree.tokenLocation(tree.token_locs[expr_last_token].end, rtoken);
760 if (loc.line == 0) {
761 // all on one line
762 const src_has_trailing_comma = trailblk: {
763 const maybe_comma = tree.prevToken(rtoken);
764 break :trailblk tree.token_ids[maybe_comma] == .Comma;
765 };
766 if (src_has_trailing_comma) {
767 break :blk 1; // force row size 1
768 } else {
769 break :blk null; // no newlines
770 }
771 }
772 break :blk count;
773 }
774 }
775 unreachable;
776 };
777
778 if (maybe_row_size) |row_size| {
779 // A place to store the width of each expression and its column's maximum
780 var widths = try allocator.alloc(usize, exprs.len + row_size);
781 defer allocator.free(widths);
782 mem.set(usize, widths, 0);
783
784 var expr_widths = widths[0 .. widths.len - row_size];
785 var column_widths = widths[widths.len - row_size ..];
786
787 // Null ais for counting the printed length of each expression
788 var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
789 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer());
790
791 for (exprs) |expr, i| {
792 counting_stream.bytes_written = 0;
793 try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
794 const width = @intCast(usize, counting_stream.bytes_written);
795 const col = i % row_size;
796 column_widths[col] = std.math.max(column_widths[col], width);
797 expr_widths[i] = width;
798 }
799
800 {750 {
801 ais.pushIndentNextLine();751 ais.pushIndentNextLine();
802 defer ais.popIndent();752 defer ais.popIndent();
803 try renderToken(tree, ais, lbrace, Space.Newline);753 try renderToken(tree, ais, lbrace, Space.Newline);
804754
805 var col: usize = 1;755 var expr_index: usize = 0;
806 for (exprs) |expr, i| {756 while (rowSize(tree, exprs[expr_index..], rtoken, true)) |row_size| {
807 if (i + 1 < exprs.len) {757 const row_exprs = exprs[expr_index..];
808 const next_expr = exprs[i + 1];758 // A place to store the width of each expression and its column's maximum
809 try renderExpression(allocator, ais, tree, expr, Space.None);759 var widths = try allocator.alloc(usize, row_exprs.len + row_size);
760 defer allocator.free(widths);
761 mem.set(usize, widths, 0);
762
763 var expr_widths = widths[0 .. widths.len - row_size];
764 var column_widths = widths[widths.len - row_size ..];
765
766 // Null stream for counting the printed length of each expression
767 var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
768 var auto_indenting_stream = std.io.autoIndentingStream(indent_delta, counting_stream.writer());
769
770 // Find next row with trailing comment (if any) to end the current section then
771 var section_end = sec_end: {
772 var this_line_first_expr: usize = 0;
773 var this_line_size = rowSize(tree, row_exprs, rtoken, true);
774 for (row_exprs) |expr, i| {
775 // Ignore comment on first line of this section
776 if (i == 0 or tree.tokensOnSameLine(row_exprs[0].firstToken(), expr.lastToken())) continue;
777 // Track start of line containing comment
778 if (!tree.tokensOnSameLine(row_exprs[this_line_first_expr].firstToken(), expr.lastToken())) {
779 this_line_first_expr = i;
780 this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rtoken, true);
781 }
782 if (expr.lastToken() + 2 < tree.token_ids.len) {
783 if (tree.token_ids[expr.lastToken() + 1] == .Comma and
784 tree.token_ids[expr.lastToken() + 2] == .LineComment and
785 tree.tokensOnSameLine(expr.lastToken(), expr.lastToken() + 2))
786 {
787 var comment_token_loc = tree.token_locs[expr.lastToken() + 2];
788 const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(comment_token_loc), " ").len == 2;
789 if (!comment_is_empty) {
790 // Found row ending in comment
791 break :sec_end i - this_line_size.? + 1;
792 }
793 }
794 }
795 }
796 break :sec_end row_exprs.len;
797 };
798 expr_index += section_end;
799
800 const section_exprs = row_exprs[0..section_end];
801
802 // Calculate size of columns in current section
803 for (section_exprs) |expr, i| {
804 counting_stream.bytes_written = 0;
805 try renderExpression(allocator, &auto_indenting_stream, tree, expr, Space.None);
806 const width = @intCast(usize, counting_stream.bytes_written);
807 const col = i % row_size;
808 column_widths[col] = std.math.max(column_widths[col], width);
809 expr_widths[i] = width;
810 }
811
812 // Render exprs in current section
813 var col: usize = 1;
814 for (section_exprs) |expr, i| {
815 if (i + 1 < section_exprs.len) {
816 const next_expr = section_exprs[i + 1];
817 try renderExpression(allocator, ais, tree, expr, Space.None);
810818
811 const comma = tree.nextToken(expr.*.lastToken());819 const comma = tree.nextToken(expr.*.lastToken());
812820
813 if (col != row_size) {821 if (col != row_size) {
814 try renderToken(tree, ais, comma, Space.Space); // ,822 try renderToken(tree, ais, comma, Space.Space); // ,
815823
816 const padding = column_widths[i % row_size] - expr_widths[i];824 const padding = column_widths[i % row_size] - expr_widths[i];
817 try ais.writer().writeByteNTimes(' ', padding);825 try ais.writer().writeByteNTimes(' ', padding);
818826
819 col += 1;827 col += 1;
820 continue;828 continue;
821 }829 }
822 col = 1;830 col = 1;
823831
824 if (tree.token_ids[tree.nextToken(comma)] != .MultilineStringLiteralLine) {832 if (tree.token_ids[tree.nextToken(comma)] != .MultilineStringLiteralLine) {
825 try renderToken(tree, ais, comma, Space.Newline); // ,833 try renderToken(tree, ais, comma, Space.Newline); // ,
834 } else {
835 try renderToken(tree, ais, comma, Space.None); // ,
836 }
837
838 try renderExtraNewline(tree, ais, next_expr);
826 } else {839 } else {
827 try renderToken(tree, ais, comma, Space.None); // ,840 const maybe_comma = tree.nextToken(expr.*.lastToken());
841 if (tree.token_ids[maybe_comma] == .Comma) {
842 try renderExpression(allocator, ais, tree, expr, Space.None); // ,
843 try renderToken(tree, ais, maybe_comma, Space.Newline); // ,
844 } else {
845 try renderExpression(allocator, ais, tree, expr, Space.Comma); // ,
846 }
828 }847 }
848 }
829849
830 try renderExtraNewline(tree, ais, next_expr);850 if (expr_index == exprs.len) {
831 } else {851 break;
832 try renderExpression(allocator, ais, tree, expr, Space.Comma); // ,
833 }852 }
834 }853 }
835 }854 }
836 return renderToken(tree, ais, rtoken, space);
837 } else {
838 try renderToken(tree, ais, lbrace, Space.Space);
839 for (exprs) |expr, i| {
840 if (i + 1 < exprs.len) {
841 const next_expr = exprs[i + 1];
842 try renderExpression(allocator, ais, tree, expr, Space.None);
843 const comma = tree.nextToken(expr.*.lastToken());
844 try renderToken(tree, ais, comma, Space.Space); // ,
845 } else {
846 try renderExpression(allocator, ais, tree, expr, Space.Space);
847 }
848 }
849855
850 return renderToken(tree, ais, rtoken, space);856 return renderToken(tree, ais, rtoken, space);
851 }857 }
858
859 // Single line
860 try renderToken(tree, ais, lbrace, Space.Space);
861 for (exprs) |expr, i| {
862 if (i + 1 < exprs.len) {
863 const next_expr = exprs[i + 1];
864 try renderExpression(allocator, ais, tree, expr, Space.None);
865 const comma = tree.nextToken(expr.*.lastToken());
866 try renderToken(tree, ais, comma, Space.Space); // ,
867 } else {
868 try renderExpression(allocator, ais, tree, expr, Space.Space);
869 }
870 }
871
872 return renderToken(tree, ais, rtoken, space);
852 },873 },
853874
854 .StructInitializer, .StructInitializerDot => {875 .StructInitializer, .StructInitializerDot => {
...@@ -1879,7 +1900,7 @@ fn renderExpression(...@@ -1879,7 +1900,7 @@ fn renderExpression(
1879 const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space;1900 const after_rparen_space = if (if_node.payload == null) Space.Newline else Space.Space;
18801901
1881 {1902 {
1882 try ais.pushIndent();1903 ais.pushIndent();
1883 defer ais.popIndent();1904 defer ais.popIndent();
1884 try renderToken(tree, ais, rparen, after_rparen_space); // )1905 try renderToken(tree, ais, rparen, after_rparen_space); // )
1885 }1906 }
...@@ -2567,3 +2588,33 @@ fn copyFixingWhitespace(ais: anytype, slice: []const u8) @TypeOf(ais.*).Error!vo...@@ -2567,3 +2588,33 @@ fn copyFixingWhitespace(ais: anytype, slice: []const u8) @TypeOf(ais.*).Error!vo
2567 else => try ais.writer().writeByte(byte),2588 else => try ais.writer().writeByte(byte),
2568 };2589 };
2569}2590}
2591
2592fn rowSize(tree: *ast.Tree, exprs: []*ast.Node, rtoken: ast.TokenIndex, force: bool) ?usize {
2593 var count: usize = 1;
2594 for (exprs) |expr, i| {
2595 if (i + 1 < exprs.len) {
2596 const expr_last_token = expr.lastToken() + 1;
2597 const loc = tree.tokenLocation(tree.token_locs[expr_last_token].end, exprs[i + 1].firstToken());
2598 if (loc.line != 0) return count;
2599 count += 1;
2600 } else {
2601 if (force) return count;
2602 const expr_last_token = expr.lastToken();
2603 const loc = tree.tokenLocation(tree.token_locs[expr_last_token].end, rtoken);
2604 if (loc.line == 0) {
2605 // all on one line
2606 const src_has_trailing_comma = trailblk: {
2607 const maybe_comma = tree.prevToken(rtoken);
2608 break :trailblk tree.token_ids[maybe_comma] == .Comma;
2609 };
2610 if (src_has_trailing_comma) {
2611 return 1; // force row size 1
2612 } else {
2613 return null; // no newlines
2614 }
2615 }
2616 return count;
2617 }
2618 }
2619 unreachable;
2620}