| ... | @@ -523,11 +523,11 @@ fn renderExpression( | ... | @@ -523,11 +523,11 @@ fn renderExpression( |
| 523 | }; | 523 | }; |
| 524 | | 524 | |
| 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 | }, |
| 533 | | 533 | |
| ... | @@ -746,109 +746,130 @@ fn renderExpression( | ... | @@ -746,109 +746,130 @@ fn renderExpression( |
| 746 | } | 746 | } |
| 747 | | 747 | |
| 748 | // scan to find row size | 748 | // 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); |
| 804 | | 754 | |
| 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); |
| 810 | | 818 | |
| 811 | const comma = tree.nextToken(expr.*.lastToken()); | 819 | const comma = tree.nextToken(expr.*.lastToken()); |
| 812 | | 820 | |
| 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); // , |
| 815 | | 823 | |
| 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); |
| 818 | | 826 | |
| 819 | col += 1; | 827 | col += 1; |
| 820 | continue; | 828 | continue; |
| 821 | } | 829 | } |
| 822 | col = 1; | 830 | col = 1; |
| 823 | | 831 | |
| 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 | } |
| 829 | | 849 | |
| 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 | } | | |
| 849 | | 855 | |
| 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 | }, |
| 853 | | 874 | |
| 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; |
| 1880 | | 1901 | |
| 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 | |
| | 2592 | fn 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 | } |