| ... | @@ -2457,170 +2457,132 @@ fn renderArrayInit( | ... | @@ -2457,170 +2457,132 @@ fn renderArrayInit( |
| 2457 | | 2457 | |
| 2458 | try ais.pushIndent(.normal); | 2458 | try ais.pushIndent(.normal); |
| 2459 | try renderToken(r, array_init.ast.lbrace, .newline); | 2459 | try renderToken(r, array_init.ast.lbrace, .newline); |
| 2460 | | 2460 | try ais.pushSpace(.comma); |
| 2461 | var expr_index: usize = 0; | 2461 | |
| 2462 | while (true) { | 2462 | const expr_widths = try gpa.alloc(enum(usize) { |
| 2463 | const row_size = rowSize(tree, array_init.ast.elements[expr_index..], rbrace); | 2463 | /// The expression contains non-printable characters (e.g. unicode / newlines) |
| 2464 | const row_exprs = array_init.ast.elements[expr_index..]; | 2464 | /// or has formatting disabled at the start or end. |
| 2465 | // A place to store the width of each expression and its column's maximum | 2465 | nonprint = std.math.maxInt(usize), |
| 2466 | const widths = try gpa.alloc(usize, row_exprs.len + row_size); | 2466 | _, |
| 2467 | defer gpa.free(widths); | 2467 | }, array_init.ast.elements.len); |
| 2468 | @memset(widths, 0); | 2468 | defer gpa.free(expr_widths); |
| 2469 | | 2469 | { |
| 2470 | const expr_newlines = try gpa.alloc(bool, row_exprs.len); | 2470 | var buf: Writer.Allocating = .init(gpa); |
| 2471 | defer gpa.free(expr_newlines); | 2471 | defer buf.deinit(); |
| 2472 | @memset(expr_newlines, false); | 2472 | var sub_ais: AutoIndentingStream = .init(gpa, &buf.writer, indent_delta); |
| 2473 | | 2473 | sub_ais.disabled_offset = ais.disabled_offset; |
| 2474 | const expr_widths = widths[0..row_exprs.len]; | 2474 | defer sub_ais.deinit(); |
| 2475 | const column_widths = widths[row_exprs.len..]; | 2475 | var sub_r: Render = .{ |
| 2476 | | | |
| 2477 | // Find next row with trailing comment (if any) to end the current section. | | |
| 2478 | const section_end = sec_end: { | | |
| 2479 | var this_line_first_expr: usize = 0; | | |
| 2480 | var this_line_size = rowSize(tree, row_exprs, rbrace); | | |
| 2481 | for (row_exprs, 0..) |expr, i| { | | |
| 2482 | // Ignore comment on first line of this section. | | |
| 2483 | if (i == 0) continue; | | |
| 2484 | const expr_last_token = tree.lastToken(expr); | | |
| 2485 | if (tree.tokensOnSameLine(tree.firstToken(row_exprs[0]), expr_last_token)) | | |
| 2486 | continue; | | |
| 2487 | // Track start of line containing comment. | | |
| 2488 | if (!tree.tokensOnSameLine(tree.firstToken(row_exprs[this_line_first_expr]), expr_last_token)) { | | |
| 2489 | this_line_first_expr = i; | | |
| 2490 | this_line_size = rowSize(tree, row_exprs[this_line_first_expr..], rbrace); | | |
| 2491 | } | | |
| 2492 | | | |
| 2493 | const maybe_comma = expr_last_token + 1; | | |
| 2494 | if (tree.tokenTag(maybe_comma) == .comma) { | | |
| 2495 | if (hasSameLineComment(tree, maybe_comma)) | | |
| 2496 | break :sec_end i - this_line_size + 1; | | |
| 2497 | } | | |
| 2498 | } | | |
| 2499 | break :sec_end row_exprs.len; | | |
| 2500 | }; | | |
| 2501 | expr_index += section_end; | | |
| 2502 | | | |
| 2503 | const section_exprs = row_exprs[0..section_end]; | | |
| 2504 | | | |
| 2505 | var sub_expr_buffer: Writer.Allocating = .init(gpa); | | |
| 2506 | defer sub_expr_buffer.deinit(); | | |
| 2507 | | | |
| 2508 | const sub_expr_buffer_starts = try gpa.alloc(usize, section_exprs.len + 1); | | |
| 2509 | defer gpa.free(sub_expr_buffer_starts); | | |
| 2510 | | | |
| 2511 | var auto_indenting_stream: AutoIndentingStream = .init(gpa, &sub_expr_buffer.writer, indent_delta); | | |
| 2512 | defer auto_indenting_stream.deinit(); | | |
| 2513 | var sub_render: Render = .{ | | |
| 2514 | .gpa = r.gpa, | 2476 | .gpa = r.gpa, |
| 2515 | .ais = &auto_indenting_stream, | 2477 | .ais = &sub_ais, |
| 2516 | .tree = r.tree, | 2478 | .tree = r.tree, |
| 2517 | .fixups = r.fixups, | 2479 | .fixups = r.fixups, |
| 2518 | }; | 2480 | }; |
| 2519 | | 2481 | for (array_init.ast.elements, expr_widths) |e, *width| { |
| 2520 | // Calculate size of columns in current section | 2482 | const begin_disabled = sub_ais.disabled_offset != null; |
| 2521 | var column_counter: usize = 0; | 2483 | // `.skip` space so trailing commments aren't included |
| 2522 | var single_line = true; | 2484 | try renderExpressionComma(&sub_r, e, .skip); |
| 2523 | var contains_newline = false; | 2485 | if (!begin_disabled and sub_ais.disabled_offset == null) { |
| 2524 | for (section_exprs, 0..) |expr, i| { | 2486 | const w = buf.written(); |
| 2525 | const start = sub_expr_buffer.written().len; | 2487 | width.* = for (w) |c| { |
| 2526 | sub_expr_buffer_starts[i] = start; | 2488 | if (!std.ascii.isPrint(c)) |
| 2527 | | 2489 | break .nonprint; |
| 2528 | if (i + 1 < section_exprs.len) { | 2490 | } else @enumFromInt(w.len - @intFromBool(w[w.len - 1] == ',')); |
| 2529 | try renderExpression(&sub_render, expr, .none); | | |
| 2530 | const written = sub_expr_buffer.written(); | | |
| 2531 | const width = written.len - start; | | |
| 2532 | const this_contains_newline = mem.findScalar(u8, written[start..], '\n') != null; | | |
| 2533 | contains_newline = contains_newline or this_contains_newline; | | |
| 2534 | expr_widths[i] = width; | | |
| 2535 | expr_newlines[i] = this_contains_newline; | | |
| 2536 | | | |
| 2537 | if (!this_contains_newline) { | | |
| 2538 | const column = column_counter % row_size; | | |
| 2539 | column_widths[column] = @max(column_widths[column], width); | | |
| 2540 | | | |
| 2541 | const expr_last_token = tree.lastToken(expr) + 1; | | |
| 2542 | const next_expr = section_exprs[i + 1]; | | |
| 2543 | column_counter += 1; | | |
| 2544 | if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(next_expr))) single_line = false; | | |
| 2545 | } else { | | |
| 2546 | single_line = false; | | |
| 2547 | column_counter = 0; | | |
| 2548 | } | | |
| 2549 | } else { | 2491 | } else { |
| 2550 | try ais.pushSpace(.comma); | 2492 | width.* = .nonprint; |
| 2551 | try renderExpression(&sub_render, expr, .comma); | 2493 | } |
| 2552 | ais.popSpace(); | | |
| 2553 | | 2494 | |
| 2554 | const written = sub_expr_buffer.written(); | 2495 | // Write trailing comments since they may enable/disable zig fmt |
| 2555 | const width = written.len - start - 2; | 2496 | buf.clearRetainingCapacity(); |
| 2556 | const this_contains_newline = mem.findScalar(u8, written[start .. written.len - 1], '\n') != null; | 2497 | var after_expr = tree.lastToken(e); |
| 2557 | contains_newline = contains_newline or this_contains_newline; | 2498 | after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma); |
| 2558 | expr_widths[i] = width; | 2499 | try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none); |
| 2559 | expr_newlines[i] = contains_newline; | | |
| 2560 | | 2500 | |
| 2561 | if (!contains_newline) { | 2501 | buf.clearRetainingCapacity(); |
| 2562 | const column = column_counter % row_size; | | |
| 2563 | column_widths[column] = @max(column_widths[column], width); | | |
| 2564 | } | | |
| 2565 | } | | |
| 2566 | } | 2502 | } |
| 2567 | sub_expr_buffer_starts[section_exprs.len] = sub_expr_buffer.written().len; | 2503 | } |
| 2568 | | | |
| 2569 | // Render exprs in current section. | | |
| 2570 | column_counter = 0; | | |
| 2571 | for (section_exprs, 0..) |expr, i| { | | |
| 2572 | const start = sub_expr_buffer_starts[i]; | | |
| 2573 | const end = sub_expr_buffer_starts[i + 1]; | | |
| 2574 | const expr_text = sub_expr_buffer.written()[start..end]; | | |
| 2575 | if (!expr_newlines[i]) { | | |
| 2576 | try ais.writeAll(expr_text); | | |
| 2577 | } else { | | |
| 2578 | var by_line = std.mem.splitScalar(u8, expr_text, '\n'); | | |
| 2579 | var last_line_was_empty = false; | | |
| 2580 | try ais.writeAll(by_line.first()); | | |
| 2581 | while (by_line.next()) |line| { | | |
| 2582 | if (std.mem.startsWith(u8, line, "//") and last_line_was_empty) { | | |
| 2583 | try ais.insertNewline(); | | |
| 2584 | } else { | | |
| 2585 | try ais.maybeInsertNewline(); | | |
| 2586 | } | | |
| 2587 | last_line_was_empty = (line.len == 0); | | |
| 2588 | try ais.writeAll(line); | | |
| 2589 | } | | |
| 2590 | } | | |
| 2591 | | | |
| 2592 | if (i + 1 < section_exprs.len) { | | |
| 2593 | const next_expr = section_exprs[i + 1]; | | |
| 2594 | const comma = tree.lastToken(expr) + 1; | | |
| 2595 | | 2504 | |
| 2596 | if (column_counter != row_size - 1) { | 2505 | var remaining_exprs = array_init.ast.elements; |
| 2597 | if (!expr_newlines[i] and !expr_newlines[i + 1]) { | 2506 | var remaining_widths = expr_widths; |
| 2598 | // Neither the current or next expression is multiline | 2507 | while (remaining_exprs.len != 0) { |
| 2599 | try renderToken(r, comma, .space); // , | 2508 | var row_size: usize = 1; |
| 2600 | assert(column_widths[column_counter % row_size] >= expr_widths[i]); | 2509 | for (1.., remaining_exprs, remaining_widths) |len, e, w| { |
| 2601 | const padding = column_widths[column_counter % row_size] - expr_widths[i]; | 2510 | if (w == .nonprint) break; |
| 2602 | try ais.splatByteAll(' ', padding); | 2511 | row_size = len; |
| 2603 | | 2512 | |
| 2604 | column_counter += 1; | 2513 | var after_expr = tree.lastToken(e); |
| 2605 | continue; | 2514 | after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma); |
| 2606 | } | 2515 | assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace); |
| 2607 | } | 2516 | if (!tree.tokensOnSameLine(after_expr, after_expr + 1)) |
| | 2517 | break; |
| | 2518 | } else { |
| | 2519 | // All the expressions are on the same line. |
| | 2520 | // However, if there is a trailing comma, we put them each on their own line. |
| | 2521 | if (tree.tokenTag(rbrace - 1) == .comma) |
| | 2522 | row_size = 1; |
| | 2523 | } |
| 2608 | | 2524 | |
| 2609 | if (single_line and row_size != 1) { | 2525 | // Determine the size of this section |
| 2610 | try renderToken(r, comma, .space); // , | 2526 | const section_end = end: { |
| 2611 | continue; | 2527 | var line_start = row_size; // Start after the first row to ignore comments on it |
| | 2528 | break :end for (line_start.., remaining_exprs[line_start..]) |i, e| { |
| | 2529 | const expr_first = tree.firstToken(e); |
| | 2530 | // Any nonprint character terminates the line because they are always put on their |
| | 2531 | // own line, so they will not end up on the same line as the trailing comment. |
| | 2532 | if (expr_widths[i - 1] == .nonprint or !tree.tokensOnSameLine(expr_first - 1, expr_first)) { |
| | 2533 | line_start = i; |
| 2612 | } | 2534 | } |
| 2613 | | 2535 | |
| 2614 | column_counter = 0; | 2536 | var after_expr = tree.lastToken(e); |
| 2615 | try renderToken(r, comma, .newline); // , | 2537 | after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma); |
| 2616 | try renderExtraNewline(r, next_expr); | 2538 | assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace); |
| | 2539 | if (hasTrailingComment(tree, after_expr)) |
| | 2540 | break line_start; |
| | 2541 | } else remaining_exprs.len; |
| | 2542 | }; |
| | 2543 | const section_exprs = remaining_exprs[0..section_end]; |
| | 2544 | const section_widths = remaining_widths[0..section_end]; |
| | 2545 | remaining_exprs = remaining_exprs[section_end..]; |
| | 2546 | remaining_widths = remaining_widths[section_end..]; |
| | 2547 | |
| | 2548 | // Determine the width of each column |
| | 2549 | var col_widths = try gpa.alloc(usize, row_size); |
| | 2550 | defer gpa.free(col_widths); |
| | 2551 | @memset(col_widths, 0); |
| | 2552 | |
| | 2553 | var col: usize = 0; |
| | 2554 | for (section_widths) |w| { |
| | 2555 | if (w == .nonprint) { |
| | 2556 | col = 0; |
| | 2557 | continue; |
| | 2558 | } |
| | 2559 | col_widths[col] = @max(col_widths[col], @intFromEnum(w)); |
| | 2560 | col += 1; |
| | 2561 | if (col == row_size) { |
| | 2562 | col = 0; |
| 2617 | } | 2563 | } |
| 2618 | } | 2564 | } |
| 2619 | | 2565 | |
| 2620 | if (expr_index == array_init.ast.elements.len) | 2566 | // Render each expression |
| 2621 | break; | 2567 | col = 0; |
| | 2568 | for (0.., section_exprs, section_widths) |i, e, w| { |
| | 2569 | if (i + 1 == section_end or col + 1 == row_size or |
| | 2570 | w == .nonprint or section_widths[i + 1] == .nonprint) |
| | 2571 | { |
| | 2572 | try renderExpression(r, e, .comma); |
| | 2573 | col = 0; |
| | 2574 | if (i + 1 != section_end) { |
| | 2575 | try renderExtraNewline(r, section_exprs[i + 1]); |
| | 2576 | } |
| | 2577 | } else { |
| | 2578 | try renderExpression(r, e, .comma_space); |
| | 2579 | try ais.splatByteAll(' ', col_widths[col] - @intFromEnum(w)); |
| | 2580 | col += 1; |
| | 2581 | } |
| | 2582 | } |
| 2622 | } | 2583 | } |
| 2623 | | 2584 | |
| | 2585 | ais.popSpace(); |
| 2624 | ais.popIndent(); | 2586 | ais.popIndent(); |
| 2625 | return renderToken(r, rbrace, space); // rbrace | 2587 | return renderToken(r, rbrace, space); // rbrace |
| 2626 | } | 2588 | } |
| ... | @@ -3286,7 +3248,7 @@ fn hasComment(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) | ... | @@ -3286,7 +3248,7 @@ fn hasComment(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) |
| 3286 | const token: Ast.TokenIndex = @intCast(i); | 3248 | const token: Ast.TokenIndex = @intCast(i); |
| 3287 | const start = tree.tokenStart(token) + tree.tokenSlice(token).len; | 3249 | const start = tree.tokenStart(token) + tree.tokenSlice(token).len; |
| 3288 | const end = tree.tokenStart(token + 1); | 3250 | const end = tree.tokenStart(token + 1); |
| 3289 | if (mem.find(u8, tree.source[start..end], "//") != null) return true; | 3251 | if (mem.findScalar(u8, tree.source[start..end], '/') != null) return true; |
| 3290 | } | 3252 | } |
| 3291 | | 3253 | |
| 3292 | return false; | 3254 | return false; |
| ... | @@ -3497,9 +3459,10 @@ fn writeStringLiteralAsIdentifier(r: *Render, token_index: Ast.TokenIndex) !usiz | ... | @@ -3497,9 +3459,10 @@ fn writeStringLiteralAsIdentifier(r: *Render, token_index: Ast.TokenIndex) !usiz |
| 3497 | } | 3459 | } |
| 3498 | } | 3460 | } |
| 3499 | | 3461 | |
| 3500 | fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool { | 3462 | fn hasTrailingComment(tree: Ast, t: Ast.TokenIndex) bool { |
| 3501 | const between_source = tree.source[tree.tokenStart(token_index)..tree.tokenStart(token_index + 1)]; | 3463 | const start = tree.tokenStart(t) + tree.tokenSlice(t).len; |
| 3502 | for (between_source) |byte| switch (byte) { | 3464 | const between = tree.source[start..tree.tokenStart(t + 1)]; |
| | 3465 | for (between) |byte| switch (byte) { |
| 3503 | '\n' => return false, | 3466 | '\n' => return false, |
| 3504 | '/' => return true, | 3467 | '/' => return true, |
| 3505 | else => continue, | 3468 | else => continue, |
| ... | @@ -3511,12 +3474,7 @@ fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool { | ... | @@ -3511,12 +3474,7 @@ fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool { |
| 3511 | /// start_token and end_token. | 3474 | /// start_token and end_token. |
| 3512 | fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool { | 3475 | fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool { |
| 3513 | if (start_token + 1 != end_token) return true; | 3476 | if (start_token + 1 != end_token) return true; |
| 3514 | const between_source = tree.source[tree.tokenStart(start_token)..tree.tokenStart(start_token + 1)]; | 3477 | return hasComment(tree, start_token, end_token); |
| 3515 | for (between_source) |byte| switch (byte) { | | |
| 3516 | '/' => return true, | | |
| 3517 | else => continue, | | |
| 3518 | }; | | |
| 3519 | return false; | | |
| 3520 | } | 3478 | } |
| 3521 | | 3479 | |
| 3522 | fn writeFixingWhitespace(w: *Writer, slice: []const u8) Error!void { | 3480 | fn writeFixingWhitespace(w: *Writer, slice: []const u8) Error!void { |
| ... | @@ -3603,29 +3561,6 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool { | ... | @@ -3603,29 +3561,6 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool { |
| 3603 | }; | 3561 | }; |
| 3604 | } | 3562 | } |
| 3605 | | 3563 | |
| 3606 | // Returns the number of nodes in `exprs` that are on the same line as `rtoken`. | | |
| 3607 | fn rowSize(tree: Ast, exprs: []const Ast.Node.Index, rtoken: Ast.TokenIndex) usize { | | |
| 3608 | const first_token = tree.firstToken(exprs[0]); | | |
| 3609 | if (tree.tokensOnSameLine(first_token, rtoken)) { | | |
| 3610 | const maybe_comma = rtoken - 1; | | |
| 3611 | if (tree.tokenTag(maybe_comma) == .comma) | | |
| 3612 | return 1; | | |
| 3613 | return exprs.len; // no newlines | | |
| 3614 | } | | |
| 3615 | | | |
| 3616 | var count: usize = 1; | | |
| 3617 | for (exprs, 0..) |expr, i| { | | |
| 3618 | if (i + 1 < exprs.len) { | | |
| 3619 | const expr_last_token = tree.lastToken(expr) + 1; | | |
| 3620 | if (!tree.tokensOnSameLine(expr_last_token, tree.firstToken(exprs[i + 1]))) return count; | | |
| 3621 | count += 1; | | |
| 3622 | } else { | | |
| 3623 | return count; | | |
| 3624 | } | | |
| 3625 | } | | |
| 3626 | unreachable; | | |
| 3627 | } | | |
| 3628 | | | |
| 3629 | /// Automatically inserts indentation of written data by keeping | 3564 | /// Automatically inserts indentation of written data by keeping |
| 3630 | /// track of the current indentation level | 3565 | /// track of the current indentation level |
| 3631 | /// | 3566 | /// |