| ... | ... | @@ -523,11 +523,11 @@ fn renderExpression( |
| 523 | 523 | }; |
| 524 | 524 | |
| 525 | 525 | { |
| 526 | | try ais.pushIndent(); |
| 526 | ais.pushIndent(); |
| 527 | 527 | defer ais.popIndent(); |
| 528 | 528 | try renderToken(tree, ais, infix_op_node.op_token, after_op_space); |
| 529 | 529 | } |
| 530 | | try ais.pushIndentOneShot(); |
| 530 | ais.pushIndentOneShot(); |
| 531 | 531 | return renderExpression(allocator, ais, tree, infix_op_node.rhs, space); |
| 532 | 532 | }, |
| 533 | 533 | |
| ... | ... | @@ -746,109 +746,130 @@ fn renderExpression( |
| 746 | 746 | } |
| 747 | 747 | |
| 748 | 748 | // scan to find row size |
| 749 | | const maybe_row_size: ?usize = blk: { |
| 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 | | |
| 749 | if (rowSize(tree, exprs, rtoken, false) != null) { |
| 800 | 750 | { |
| 801 | 751 | ais.pushIndentNextLine(); |
| 802 | 752 | defer ais.popIndent(); |
| 803 | 753 | try renderToken(tree, ais, lbrace, Space.Newline); |
| 804 | 754 | |
| 805 | | var col: usize = 1; |
| 806 | | for (exprs) |expr, i| { |
| 807 | | if (i + 1 < exprs.len) { |
| 808 | | const next_expr = exprs[i + 1]; |
| 809 | | try renderExpression(allocator, ais, tree, expr, Space.None); |
| 755 | var expr_index: usize = 0; |
| 756 | while (rowSize(tree, exprs[expr_index..], rtoken, true)) |row_size| { |
| 757 | const row_exprs = exprs[expr_index..]; |
| 758 | // A place to store the width of each expression and its column's maximum |
| 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) { |
| 814 | | try renderToken(tree, ais, comma, Space.Space); // , |
| 821 | if (col != row_size) { |
| 822 | try renderToken(tree, ais, comma, Space.Space); // , |
| 815 | 823 | |
| 816 | | const padding = column_widths[i % row_size] - expr_widths[i]; |
| 817 | | try ais.writer().writeByteNTimes(' ', padding); |
| 824 | const padding = column_widths[i % row_size] - expr_widths[i]; |
| 825 | try ais.writer().writeByteNTimes(' ', padding); |
| 818 | 826 | |
| 819 | | col += 1; |
| 820 | | continue; |
| 821 | | } |
| 822 | | col = 1; |
| 827 | col += 1; |
| 828 | continue; |
| 829 | } |
| 830 | col = 1; |
| 823 | 831 | |
| 824 | | if (tree.token_ids[tree.nextToken(comma)] != .MultilineStringLiteralLine) { |
| 825 | | try renderToken(tree, ais, comma, Space.Newline); // , |
| 832 | if (tree.token_ids[tree.nextToken(comma)] != .MultilineStringLiteralLine) { |
| 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 | 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); |
| 831 | | } else { |
| 832 | | try renderExpression(allocator, ais, tree, expr, Space.Comma); // , |
| 850 | if (expr_index == exprs.len) { |
| 851 | break; |
| 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 | 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 | 875 | .StructInitializer, .StructInitializerDot => { |
| ... | ... | @@ -1879,7 +1900,7 @@ fn renderExpression( |
| 1879 | 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 | 1904 | defer ais.popIndent(); |
| 1884 | 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 | 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 | } |