| ... | ... | @@ -2457,170 +2457,132 @@ fn renderArrayInit( |
| 2457 | 2457 | |
| 2458 | 2458 | try ais.pushIndent(.normal); |
| 2459 | 2459 | try renderToken(r, array_init.ast.lbrace, .newline); |
| 2460 | | |
| 2461 | | var expr_index: usize = 0; |
| 2462 | | while (true) { |
| 2463 | | const row_size = rowSize(tree, array_init.ast.elements[expr_index..], rbrace); |
| 2464 | | const row_exprs = array_init.ast.elements[expr_index..]; |
| 2465 | | // A place to store the width of each expression and its column's maximum |
| 2466 | | const widths = try gpa.alloc(usize, row_exprs.len + row_size); |
| 2467 | | defer gpa.free(widths); |
| 2468 | | @memset(widths, 0); |
| 2469 | | |
| 2470 | | const expr_newlines = try gpa.alloc(bool, row_exprs.len); |
| 2471 | | defer gpa.free(expr_newlines); |
| 2472 | | @memset(expr_newlines, false); |
| 2473 | | |
| 2474 | | const expr_widths = widths[0..row_exprs.len]; |
| 2475 | | const column_widths = widths[row_exprs.len..]; |
| 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 = .{ |
| 2460 | try ais.pushSpace(.comma); |
| 2461 | |
| 2462 | const expr_widths = try gpa.alloc(enum(usize) { |
| 2463 | /// The expression contains non-printable characters (e.g. unicode / newlines) |
| 2464 | /// or has formatting disabled at the start or end. |
| 2465 | nonprint = std.math.maxInt(usize), |
| 2466 | _, |
| 2467 | }, array_init.ast.elements.len); |
| 2468 | defer gpa.free(expr_widths); |
| 2469 | { |
| 2470 | var buf: Writer.Allocating = .init(gpa); |
| 2471 | defer buf.deinit(); |
| 2472 | var sub_ais: AutoIndentingStream = .init(gpa, &buf.writer, indent_delta); |
| 2473 | sub_ais.disabled_offset = ais.disabled_offset; |
| 2474 | defer sub_ais.deinit(); |
| 2475 | var sub_r: Render = .{ |
| 2514 | 2476 | .gpa = r.gpa, |
| 2515 | | .ais = &auto_indenting_stream, |
| 2477 | .ais = &sub_ais, |
| 2516 | 2478 | .tree = r.tree, |
| 2517 | 2479 | .fixups = r.fixups, |
| 2518 | 2480 | }; |
| 2519 | | |
| 2520 | | // Calculate size of columns in current section |
| 2521 | | var column_counter: usize = 0; |
| 2522 | | var single_line = true; |
| 2523 | | var contains_newline = false; |
| 2524 | | for (section_exprs, 0..) |expr, i| { |
| 2525 | | const start = sub_expr_buffer.written().len; |
| 2526 | | sub_expr_buffer_starts[i] = start; |
| 2527 | | |
| 2528 | | if (i + 1 < section_exprs.len) { |
| 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 | | } |
| 2481 | for (array_init.ast.elements, expr_widths) |e, *width| { |
| 2482 | const begin_disabled = sub_ais.disabled_offset != null; |
| 2483 | // `.skip` space so trailing commments aren't included |
| 2484 | try renderExpressionComma(&sub_r, e, .skip); |
| 2485 | if (!begin_disabled and sub_ais.disabled_offset == null) { |
| 2486 | const w = buf.written(); |
| 2487 | width.* = for (w) |c| { |
| 2488 | if (!std.ascii.isPrint(c)) |
| 2489 | break .nonprint; |
| 2490 | } else @enumFromInt(w.len - @intFromBool(w[w.len - 1] == ',')); |
| 2549 | 2491 | } else { |
| 2550 | | try ais.pushSpace(.comma); |
| 2551 | | try renderExpression(&sub_render, expr, .comma); |
| 2552 | | ais.popSpace(); |
| 2492 | width.* = .nonprint; |
| 2493 | } |
| 2553 | 2494 | |
| 2554 | | const written = sub_expr_buffer.written(); |
| 2555 | | const width = written.len - start - 2; |
| 2556 | | const this_contains_newline = mem.findScalar(u8, written[start .. written.len - 1], '\n') != null; |
| 2557 | | contains_newline = contains_newline or this_contains_newline; |
| 2558 | | expr_widths[i] = width; |
| 2559 | | expr_newlines[i] = contains_newline; |
| 2495 | // Write trailing comments since they may enable/disable zig fmt |
| 2496 | buf.clearRetainingCapacity(); |
| 2497 | var after_expr = tree.lastToken(e); |
| 2498 | after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma); |
| 2499 | try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none); |
| 2560 | 2500 | |
| 2561 | | if (!contains_newline) { |
| 2562 | | const column = column_counter % row_size; |
| 2563 | | column_widths[column] = @max(column_widths[column], width); |
| 2564 | | } |
| 2565 | | } |
| 2501 | buf.clearRetainingCapacity(); |
| 2566 | 2502 | } |
| 2567 | | sub_expr_buffer_starts[section_exprs.len] = sub_expr_buffer.written().len; |
| 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; |
| 2503 | } |
| 2595 | 2504 | |
| 2596 | | if (column_counter != row_size - 1) { |
| 2597 | | if (!expr_newlines[i] and !expr_newlines[i + 1]) { |
| 2598 | | // Neither the current or next expression is multiline |
| 2599 | | try renderToken(r, comma, .space); // , |
| 2600 | | assert(column_widths[column_counter % row_size] >= expr_widths[i]); |
| 2601 | | const padding = column_widths[column_counter % row_size] - expr_widths[i]; |
| 2602 | | try ais.splatByteAll(' ', padding); |
| 2505 | var remaining_exprs = array_init.ast.elements; |
| 2506 | var remaining_widths = expr_widths; |
| 2507 | while (remaining_exprs.len != 0) { |
| 2508 | var row_size: usize = 1; |
| 2509 | for (1.., remaining_exprs, remaining_widths) |len, e, w| { |
| 2510 | if (w == .nonprint) break; |
| 2511 | row_size = len; |
| 2603 | 2512 | |
| 2604 | | column_counter += 1; |
| 2605 | | continue; |
| 2606 | | } |
| 2607 | | } |
| 2513 | var after_expr = tree.lastToken(e); |
| 2514 | after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma); |
| 2515 | assert(tree.tokenTag(after_expr) == .comma or after_expr + 1 == rbrace); |
| 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) { |
| 2610 | | try renderToken(r, comma, .space); // , |
| 2611 | | continue; |
| 2525 | // Determine the size of this section |
| 2526 | const section_end = end: { |
| 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; |
| 2615 | | try renderToken(r, comma, .newline); // , |
| 2616 | | try renderExtraNewline(r, next_expr); |
| 2536 | var after_expr = tree.lastToken(e); |
| 2537 | after_expr += @intFromBool(tree.tokenTag(after_expr + 1) == .comma); |
| 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) |
| 2621 | | break; |
| 2566 | // Render each expression |
| 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 | 2586 | ais.popIndent(); |
| 2625 | 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 | 3248 | const token: Ast.TokenIndex = @intCast(i); |
| 3287 | 3249 | const start = tree.tokenStart(token) + tree.tokenSlice(token).len; |
| 3288 | 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 | 3254 | return false; |
| ... | ... | @@ -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 { |
| 3501 | | const between_source = tree.source[tree.tokenStart(token_index)..tree.tokenStart(token_index + 1)]; |
| 3502 | | for (between_source) |byte| switch (byte) { |
| 3462 | fn hasTrailingComment(tree: Ast, t: Ast.TokenIndex) bool { |
| 3463 | const start = tree.tokenStart(t) + tree.tokenSlice(t).len; |
| 3464 | const between = tree.source[start..tree.tokenStart(t + 1)]; |
| 3465 | for (between) |byte| switch (byte) { |
| 3503 | 3466 | '\n' => return false, |
| 3504 | 3467 | '/' => return true, |
| 3505 | 3468 | else => continue, |
| ... | ... | @@ -3511,12 +3474,7 @@ fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool { |
| 3511 | 3474 | /// start_token and end_token. |
| 3512 | 3475 | fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool { |
| 3513 | 3476 | if (start_token + 1 != end_token) return true; |
| 3514 | | const between_source = tree.source[tree.tokenStart(start_token)..tree.tokenStart(start_token + 1)]; |
| 3515 | | for (between_source) |byte| switch (byte) { |
| 3516 | | '/' => return true, |
| 3517 | | else => continue, |
| 3518 | | }; |
| 3519 | | return false; |
| 3477 | return hasComment(tree, start_token, end_token); |
| 3520 | 3478 | } |
| 3521 | 3479 | |
| 3522 | 3480 | fn writeFixingWhitespace(w: *Writer, slice: []const u8) Error!void { |
| ... | ... | @@ -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 | 3564 | /// Automatically inserts indentation of written data by keeping |
| 3630 | 3565 | /// track of the current indentation level |
| 3631 | 3566 | /// |