authorgravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2025-07-28 13:18:09-04:00
committergravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-03-12 17:22:46-04:00
logfcc54ca981d30af0a187c9d187b2a63ce0195f3c
treef428617b308f61c961e1bd02de0ad26646d5608b
parent476c3a1f379eadbd200d568340a2f479d5b6d260

zig fmt: fix many bugs with multiline string literals

Adds two new space modes maybe_space and comma_maybe_space which only render the space when the next token is not a multiline string literal. This is used in a lot of places to avoid a trailing space on lines caused by multiline string literals forcing newlines. Fixes places where the node tag is compared to be a multiline string literal to instead check if the first token is a multiline string literal line. This fixes cases like `\\ ++ text`. This commit also rewrites quite a bit of code to be clearer / deduplicated.

2 files changed, 586 insertions(+), 311 deletions(-)

lib/std/zig/Ast/Render.zig+273-311
......@@ -346,20 +346,27 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
346346 const next_token_tag = tree.tokenTag(next_token);
347347
348348 // dedent the next thing that comes after a multiline string literal
349 if (!ais.indentStackEmpty() and
350 next_token_tag != .colon and
351 ((next_token_tag != .semicolon and next_token_tag != .comma) or
352 ais.lastSpaceModeIndent() < ais.currentIndent()))
349 if (next_token_tag != .colon and
350 !ais.indentStackEmpty() and
351 ais.lastSpaceModeIndent() < ais.currentIndent())
353352 {
354 ais.popIndent();
355 try ais.pushIndent(.normal);
353 const indent_top = &ais.indent_stack.items[ais.indent_stack.items.len - 1];
354 if (indent_top.realized) {
355 indent_top.realized = false;
356 ais.indent_count -= 1;
357 }
356358 }
357359
358360 switch (space) {
359 .none, .space, .newline, .skip => {},
360 .semicolon => if (next_token_tag == .semicolon) try renderTokenOverrideSpaceMode(r, next_token, .newline, .semicolon),
361 .comma => if (next_token_tag == .comma) try renderTokenOverrideSpaceMode(r, next_token, .newline, .comma),
362 .comma_space => if (next_token_tag == .comma) try renderToken(r, next_token, .space),
361 .none, .space, .newline, .maybe_space, .skip => {},
362 .semicolon => if (next_token_tag == .semicolon)
363 try renderTokenOverrideSpaceMode(r, next_token, .newline, .semicolon),
364 .comma => if (next_token_tag == .comma)
365 try renderTokenOverrideSpaceMode(r, next_token, .newline, .comma),
366 .comma_space => if (next_token_tag == .comma)
367 try renderToken(r, next_token, .space),
368 .comma_maybe_space => if (next_token_tag == .comma)
369 try renderToken(r, next_token, .maybe_space),
363370 }
364371 },
365372
......@@ -384,11 +391,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
384391 const defer_token = tree.nodeMainToken(node);
385392 const maybe_payload_token, const expr = tree.nodeData(node).opt_token_and_node;
386393
387 try renderToken(r, defer_token, .space);
394 try renderToken(r, defer_token, .maybe_space);
388395 if (maybe_payload_token.unwrap()) |payload_token| {
389396 try renderToken(r, payload_token - 1, .none); // |
390397 try renderIdentifier(r, payload_token, .none, .preserve_when_shadowing); // identifier
391 try renderToken(r, payload_token + 1, .space); // |
398 try renderToken(r, payload_token + 1, .maybe_space); // |
392399 }
393400 return renderExpression(r, expr, space);
394401 },
......@@ -400,7 +407,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
400407 => {
401408 const main_token = tree.nodeMainToken(node);
402409 const item = tree.nodeData(node).node;
403 try renderToken(r, main_token, .space);
410 try renderToken(r, main_token, .maybe_space);
404411 return renderExpression(r, item, space);
405412 },
406413
......@@ -409,8 +416,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
409416 const lhs, const rhs = tree.nodeData(node).node_and_node;
410417 const fallback_first = tree.firstToken(rhs);
411418
412 const same_line = tree.tokensOnSameLine(main_token, fallback_first);
413 const after_op_space = if (same_line) Space.space else Space.newline;
419 const seperate_line = !tree.tokensOnSameLine(main_token, fallback_first) or
420 tree.tokenTag(fallback_first) == .multiline_string_literal_line;
421 const after_op_space: Space = if (seperate_line) .newline else .space;
414422
415423 try renderExpression(r, lhs, .space); // target
416424
......@@ -489,11 +497,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
489497 try renderExpression(r, lhs, .space);
490498 const op_token = tree.nodeMainToken(node);
491499 try ais.pushIndent(.after_equals);
492 if (tree.tokensOnSameLine(op_token, op_token + 1)) {
493 try renderToken(r, op_token, .space);
494 } else {
495 try renderToken(r, op_token, .newline);
496 }
500 const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or
501 tree.tokenTag(op_token + 1) == .multiline_string_literal_line;
502 try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space);
497503 try renderExpression(r, rhs, space);
498504 ais.popIndent();
499505 },
......@@ -532,11 +538,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
532538 try renderExpression(r, lhs, .space);
533539 const op_token = tree.nodeMainToken(node);
534540 try ais.pushIndent(.binop);
535 if (tree.tokensOnSameLine(op_token, op_token + 1)) {
536 try renderToken(r, op_token, .space);
537 } else {
538 try renderToken(r, op_token, .newline);
539 }
541 const rhs_seperate_line = !tree.tokensOnSameLine(op_token, op_token + 1) or
542 tree.tokenTag(op_token + 1) == .multiline_string_literal_line;
543 try renderToken(r, op_token, if (rhs_seperate_line) .newline else .space);
540544 try renderExpression(r, rhs, space);
541545 ais.popIndent();
542546 },
......@@ -544,11 +548,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
544548 .assign_destructure => {
545549 const full = tree.assignDestructure(node);
546550 if (full.comptime_token) |comptime_token| {
547 try renderToken(r, comptime_token, .space);
551 try renderToken(r, comptime_token, .maybe_space);
548552 }
549553
550554 for (full.ast.variables, 0..) |variable_node, i| {
551 const variable_space: Space = if (i == full.ast.variables.len - 1) .space else .comma_space;
555 const variable_space: Space = if (i == full.ast.variables.len - 1) .maybe_space else .comma_maybe_space;
552556 switch (tree.nodeTag(variable_node)) {
553557 .global_var_decl,
554558 .local_var_decl,
......@@ -561,11 +565,10 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
561565 }
562566 }
563567 try ais.pushIndent(.after_equals);
564 if (tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1)) {
565 try renderToken(r, full.ast.equal_token, .space);
566 } else {
567 try renderToken(r, full.ast.equal_token, .newline);
568 }
568 const expr_seperate_line =
569 !tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1) or
570 tree.tokenTag(full.ast.equal_token + 1) == .multiline_string_literal_line;
571 try renderToken(r, full.ast.equal_token, if (expr_seperate_line) .newline else .space);
569572 try renderExpression(r, full.ast.value_expr, space);
570573 ais.popIndent();
571574 },
......@@ -584,7 +587,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
584587 .@"try",
585588 .@"resume",
586589 => {
587 try renderToken(r, tree.nodeMainToken(node), .space);
590 try renderToken(r, tree.nodeMainToken(node), .maybe_space);
588591 return renderExpression(r, tree.nodeData(node).node, space);
589592 },
590593
......@@ -668,30 +671,23 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
668671 .@"break", .@"continue" => {
669672 const main_token = tree.nodeMainToken(node);
670673 const opt_label_token, const opt_target = tree.nodeData(node).opt_token_and_opt_node;
671 if (opt_label_token == .none and opt_target == .none) {
672 try renderToken(r, main_token, space); // break/continue
673 } else if (opt_label_token == .none and opt_target != .none) {
674 const target = opt_target.unwrap().?;
675 try renderToken(r, main_token, .space); // break/continue
676 try renderExpression(r, target, space);
677 } else if (opt_label_token != .none and opt_target == .none) {
678 const label_token = opt_label_token.unwrap().?;
679 try renderToken(r, main_token, .space); // break/continue
680 try renderToken(r, label_token - 1, .none); // :
681 try renderIdentifier(r, label_token, space, .eagerly_unquote); // identifier
682 } else if (opt_label_token != .none and opt_target != .none) {
683 const label_token = opt_label_token.unwrap().?;
684 const target = opt_target.unwrap().?;
685 try renderToken(r, main_token, .space); // break/continue
674
675 const before_target_space: Space = if (opt_target != .none) .maybe_space else space;
676 const before_label_space: Space = if (opt_label_token != .none) .space else before_target_space;
677
678 try renderToken(r, main_token, before_label_space);
679 if (opt_label_token.unwrap()) |label_token| {
686680 try renderToken(r, label_token - 1, .none); // :
687 try renderIdentifier(r, label_token, .space, .eagerly_unquote); // identifier
681 try renderIdentifier(r, label_token, before_target_space, .eagerly_unquote); // identifier
682 }
683 if (opt_target.unwrap()) |target| {
688684 try renderExpression(r, target, space);
689 } else unreachable;
685 }
690686 },
691687
692688 .@"return" => {
693689 if (tree.nodeData(node).opt_node.unwrap()) |expr| {
694 try renderToken(r, tree.nodeMainToken(node), .space);
690 try renderToken(r, tree.nodeMainToken(node), .maybe_space);
695691 try renderExpression(r, expr, space);
696692 } else {
697693 try renderToken(r, tree.nodeMainToken(node), space);
......@@ -951,6 +947,7 @@ fn renderArrayType(
951947fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!void {
952948 const tree = r.tree;
953949 const main_token = ptr_type.ast.main_token;
950
954951 switch (ptr_type.size) {
955952 .one => {
956953 // Since ** tokens exist and the same token is shared by two
......@@ -997,11 +994,36 @@ fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!voi
997994 },
998995 }
999996
997 // .maybe_space cannot be used at the end of each qualifier since they may be reordered
998 const final_qual: enum {
999 @"volatile",
1000 @"const",
1001 @"addrspace",
1002 @"align",
1003 @"allowzero",
1004 none,
1005 } = if (ptr_type.volatile_token != null)
1006 .@"volatile"
1007 else if (ptr_type.const_token != null)
1008 .@"const"
1009 else if (ptr_type.ast.addrspace_node != .none)
1010 .@"addrspace"
1011 else if (ptr_type.ast.align_node != .none)
1012 .@"align"
1013 else if (ptr_type.allowzero_token != null)
1014 .@"allowzero"
1015 else
1016 .none;
1017 const final_qual_space: Space = if (tree.tokenTag(tree.firstToken(ptr_type.ast.child_type)) !=
1018 .multiline_string_literal_line) .space else .none;
1019
10001020 if (ptr_type.allowzero_token) |allowzero_token| {
1001 try renderToken(r, allowzero_token, .space);
1021 const this_space: Space = if (final_qual == .@"allowzero") final_qual_space else .space;
1022 try renderToken(r, allowzero_token, this_space);
10021023 }
10031024
10041025 if (ptr_type.ast.align_node.unwrap()) |align_node| {
1026 const this_space: Space = if (final_qual == .@"align") final_qual_space else .space;
10051027 const align_first = tree.firstToken(align_node);
10061028 try renderToken(r, align_first - 2, .none); // align
10071029 try renderToken(r, align_first - 1, .none); // lparen
......@@ -1012,26 +1034,29 @@ fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!voi
10121034 try renderExpression(r, bit_range_start, .none);
10131035 try renderToken(r, tree.firstToken(bit_range_end) - 1, .none); // colon
10141036 try renderExpression(r, bit_range_end, .none);
1015 try renderToken(r, tree.lastToken(bit_range_end) + 1, .space); // rparen
1037 try renderToken(r, tree.lastToken(bit_range_end) + 1, this_space); // rparen
10161038 } else {
1017 try renderToken(r, tree.lastToken(align_node) + 1, .space); // rparen
1039 try renderToken(r, tree.lastToken(align_node) + 1, this_space); // rparen
10181040 }
10191041 }
10201042
10211043 if (ptr_type.ast.addrspace_node.unwrap()) |addrspace_node| {
1044 const this_space: Space = if (final_qual == .@"addrspace") final_qual_space else .space;
10221045 const addrspace_first = tree.firstToken(addrspace_node);
10231046 try renderToken(r, addrspace_first - 2, .none); // addrspace
10241047 try renderToken(r, addrspace_first - 1, .none); // lparen
10251048 try renderExpression(r, addrspace_node, .none);
1026 try renderToken(r, tree.lastToken(addrspace_node) + 1, .space); // rparen
1049 try renderToken(r, tree.lastToken(addrspace_node) + 1, this_space); // rparen
10271050 }
10281051
10291052 if (ptr_type.const_token) |const_token| {
1030 try renderToken(r, const_token, .space);
1053 const this_space: Space = if (final_qual == .@"const") final_qual_space else .space;
1054 try renderToken(r, const_token, this_space);
10311055 }
10321056
10331057 if (ptr_type.volatile_token) |volatile_token| {
1034 try renderToken(r, volatile_token, .space);
1058 const this_space: Space = if (final_qual == .@"volatile") final_qual_space else unreachable;
1059 try renderToken(r, volatile_token, this_space);
10351060 }
10361061
10371062 try renderExpression(r, ptr_type.ast.child_type, space);
......@@ -1044,12 +1069,14 @@ fn renderSlice(
10441069 space: Space,
10451070) Error!void {
10461071 const tree = r.tree;
1047 const after_start_space_bool = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or
1072 const space_around_dots = nodeCausesSliceOpSpace(tree.nodeTag(slice.ast.start)) or
10481073 if (slice.ast.end.unwrap()) |end| nodeCausesSliceOpSpace(tree.nodeTag(end)) else false;
1049 const after_start_space = if (after_start_space_bool) Space.space else Space.none;
1050 const after_dots_space = if (slice.ast.end != .none)
1051 after_start_space
1052 else if (slice.ast.sentinel != .none) Space.space else Space.none;
1074 const after_start_space: Space = if (space_around_dots) .space else .none;
1075 const before_sentinel_space: Space = if (slice.ast.sentinel != .none) .space else .none;
1076 const after_dots_space: Space = if (slice.ast.end != .none)
1077 if (space_around_dots) .maybe_space else .none
1078 else
1079 before_sentinel_space;
10531080
10541081 try renderExpression(r, slice.ast.sliced, .none);
10551082 try renderToken(r, slice.ast.lbracket, .none); // lbracket
......@@ -1059,8 +1086,7 @@ fn renderSlice(
10591086 try renderToken(r, start_last + 1, after_dots_space); // ellipsis2 ("..")
10601087
10611088 if (slice.ast.end.unwrap()) |end| {
1062 const after_end_space = if (slice.ast.sentinel != .none) Space.space else Space.none;
1063 try renderExpression(r, end, after_end_space);
1089 try renderExpression(r, end, before_sentinel_space);
10641090 }
10651091
10661092 if (slice.ast.sentinel.unwrap()) |sentinel| {
......@@ -1088,7 +1114,7 @@ fn renderAsmOutput(
10881114
10891115 if (tree.tokenTag(symbolic_name + 4) == .arrow) {
10901116 const type_expr, const rparen = tree.nodeData(asm_output).opt_node_and_token;
1091 try renderToken(r, symbolic_name + 4, .space); // ->
1117 try renderToken(r, symbolic_name + 4, .maybe_space); // ->
10921118 try renderExpression(r, type_expr.unwrap().?, Space.none);
10931119 return renderToken(r, rparen, space);
10941120 } else {
......@@ -1169,33 +1195,38 @@ fn renderVarDeclWithoutFixups(
11691195
11701196 try renderToken(r, var_decl.ast.mut_token, .space); // var
11711197
1172 if (var_decl.ast.type_node != .none or var_decl.ast.align_node != .none or
1173 var_decl.ast.addrspace_node != .none or var_decl.ast.section_node != .none or
1174 var_decl.ast.init_node != .none)
1175 {
1176 const name_space = if (var_decl.ast.type_node == .none and
1177 (var_decl.ast.align_node != .none or
1178 var_decl.ast.addrspace_node != .none or
1179 var_decl.ast.section_node != .none or
1180 var_decl.ast.init_node != .none))
1181 Space.space
1182 else
1183 Space.none;
1184
1185 try renderIdentifier(r, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name
1186 } else {
1187 return renderIdentifier(r, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing); // name
1198 const last_component: enum {
1199 value,
1200 @"linksection",
1201 @"addrspace",
1202 @"align",
1203 type,
1204 identifier,
1205 } = if (var_decl.ast.init_node != .none)
1206 .value
1207 else if (var_decl.ast.section_node != .none)
1208 .@"linksection"
1209 else if (var_decl.ast.addrspace_node != .none)
1210 .@"addrspace"
1211 else if (var_decl.ast.align_node != .none)
1212 .@"align"
1213 else if (var_decl.ast.type_node != .none)
1214 .type
1215 else
1216 .identifier;
1217
1218 if (last_component == .identifier) {
1219 return renderIdentifier(r, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing);
11881220 }
1221 const after_ident_space: Space = if (var_decl.ast.type_node != .none) .none else .space;
1222 try renderIdentifier(r, var_decl.ast.mut_token + 1, after_ident_space, .preserve_when_shadowing);
11891223
11901224 if (var_decl.ast.type_node.unwrap()) |type_node| {
1191 try renderToken(r, var_decl.ast.mut_token + 2, Space.space); // :
1192 if (var_decl.ast.align_node != .none or var_decl.ast.addrspace_node != .none or
1193 var_decl.ast.section_node != .none or var_decl.ast.init_node != .none)
1194 {
1195 try renderExpression(r, type_node, .space);
1196 } else {
1225 try renderToken(r, var_decl.ast.mut_token + 2, .maybe_space); // :
1226 if (last_component == .type) {
11971227 return renderExpression(r, type_node, space);
11981228 }
1229 try renderExpression(r, type_node, .space);
11991230 }
12001231
12011232 if (var_decl.ast.align_node.unwrap()) |align_node| {
......@@ -1205,13 +1236,10 @@ fn renderVarDeclWithoutFixups(
12051236 try renderToken(r, align_kw, Space.none); // align
12061237 try renderToken(r, lparen, Space.none); // (
12071238 try renderExpression(r, align_node, Space.none);
1208 if (var_decl.ast.addrspace_node != .none or var_decl.ast.section_node != .none or
1209 var_decl.ast.init_node != .none)
1210 {
1211 try renderToken(r, rparen, .space); // )
1212 } else {
1239 if (last_component == .@"align") {
12131240 return renderToken(r, rparen, space); // )
12141241 }
1242 try renderToken(r, rparen, .space); // )
12151243 }
12161244
12171245 if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| {
......@@ -1221,12 +1249,10 @@ fn renderVarDeclWithoutFixups(
12211249 try renderToken(r, addrspace_kw, Space.none); // addrspace
12221250 try renderToken(r, lparen, Space.none); // (
12231251 try renderExpression(r, addrspace_node, Space.none);
1224 if (var_decl.ast.section_node != .none or var_decl.ast.init_node != .none) {
1225 try renderToken(r, rparen, .space); // )
1226 } else {
1227 try renderToken(r, rparen, .none); // )
1228 return renderToken(r, rparen + 1, Space.newline); // ;
1252 if (last_component == .@"addrspace") {
1253 return renderToken(r, rparen, space); // )
12291254 }
1255 try renderToken(r, rparen, .space); // )
12301256 }
12311257
12321258 if (var_decl.ast.section_node.unwrap()) |section_node| {
......@@ -1236,17 +1262,19 @@ fn renderVarDeclWithoutFixups(
12361262 try renderToken(r, section_kw, Space.none); // linksection
12371263 try renderToken(r, lparen, Space.none); // (
12381264 try renderExpression(r, section_node, Space.none);
1239 if (var_decl.ast.init_node != .none) {
1240 try renderToken(r, rparen, .space); // )
1241 } else {
1265 if (last_component == .@"linksection") {
12421266 return renderToken(r, rparen, space); // )
12431267 }
1268 try renderToken(r, rparen, .space); // )
12441269 }
12451270
1271 assert(last_component == .value);
12461272 const init_node = var_decl.ast.init_node.unwrap().?;
12471273
12481274 const eq_token = tree.firstToken(init_node) - 1;
1249 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
1275 const rhs_seperate_line = !tree.tokensOnSameLine(eq_token, eq_token + 1) or
1276 tree.tokenTag(eq_token + 1) == .multiline_string_literal_line;
1277 const eq_space: Space = if (rhs_seperate_line) .newline else .space;
12501278 try ais.pushIndent(.after_equals);
12511279 try renderToken(r, eq_token, eq_space); // =
12521280 try renderExpression(r, init_node, space); // ;
......@@ -1346,8 +1374,10 @@ fn renderThenElse(
13461374 const tree = r.tree;
13471375 const ais = r.ais;
13481376 const then_expr_is_block = nodeIsBlock(tree.nodeTag(then_expr));
1377 const then_expr_first_token = tree.firstToken(then_expr);
13491378 const indent_then_expr = !then_expr_is_block and
1350 !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(then_expr));
1379 (!tree.tokensOnSameLine(last_prefix_token, then_expr_first_token) or
1380 tree.tokenTag(then_expr_first_token) == .multiline_string_literal_line);
13511381
13521382 if (indent_then_expr) try ais.pushIndent(.normal);
13531383
......@@ -1381,7 +1411,9 @@ fn renderThenElse(
13811411
13821412 const indent_else_expr = indent_then_expr and
13831413 !nodeIsBlock(tree.nodeTag(else_expr)) and
1384 !nodeIsIfForWhileSwitch(tree.nodeTag(else_expr));
1414 !nodeIsIfForWhileSwitch(tree.nodeTag(else_expr)) or
1415 tree.tokenTag(tree.firstToken(else_expr)) ==
1416 .multiline_string_literal_line;
13851417 if (indent_else_expr) {
13861418 try ais.pushIndent(.normal);
13871419 try renderToken(r, last_else_token, .newline);
......@@ -1417,44 +1449,29 @@ fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void {
14171449 try renderParamList(r, lparen, for_node.ast.inputs, .space);
14181450
14191451 var cur = for_node.payload_token;
1420 const pipe = std.mem.findScalarPos(std.zig.Token.Tag, token_tags, cur, .pipe).?;
1421 if (tree.tokenTag(@intCast(pipe - 1)) == .comma) {
1452 const pipe = std.mem.findScalarPos(Token.Tag, token_tags, cur, .pipe).?;
1453 const capture_trailing_comma = token_tags[@intCast(pipe - 1)] == .comma;
1454
1455 if (capture_trailing_comma)
14221456 try ais.pushIndent(.normal);
1423 try renderToken(r, cur - 1, .newline); // |
1424 while (true) {
1425 if (tree.tokenTag(cur) == .asterisk) {
1426 try renderToken(r, cur, .none); // *
1427 cur += 1;
1428 }
1429 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1457 try renderToken(r, cur - 1, if (capture_trailing_comma) .newline else .none); // |
1458 while (true) {
1459 if (token_tags[cur] == .asterisk) {
1460 try renderToken(r, cur, .none); // *
14301461 cur += 1;
1431 if (tree.tokenTag(cur) == .comma) {
1432 try renderToken(r, cur, .newline); // ,
1433 cur += 1;
1434 }
1435 if (tree.tokenTag(cur) == .pipe) {
1436 break;
1437 }
14381462 }
1439 ais.popIndent();
1440 } else {
1441 try renderToken(r, cur - 1, .none); // |
1442 while (true) {
1443 if (tree.tokenTag(cur) == .asterisk) {
1444 try renderToken(r, cur, .none); // *
1445 cur += 1;
1446 }
1447 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1463 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1464 cur += 1;
1465 if (token_tags[cur] == .comma) {
1466 try renderToken(r, cur, if (capture_trailing_comma) .newline else .space); // ,
14481467 cur += 1;
1449 if (tree.tokenTag(cur) == .comma) {
1450 try renderToken(r, cur, .space); // ,
1451 cur += 1;
1452 }
1453 if (tree.tokenTag(cur) == .pipe) {
1454 break;
1455 }
1468 }
1469 if (token_tags[cur] == .pipe) {
1470 break;
14561471 }
14571472 }
1473 if (capture_trailing_comma)
1474 ais.popIndent();
14581475
14591476 try renderThenElse(
14601477 r,
......@@ -1483,97 +1500,80 @@ fn renderContainerField(
14831500 };
14841501
14851502 if (field.comptime_token) |t| {
1486 try renderToken(r, t, .space); // comptime
1487 }
1488 if (field.ast.type_expr == .none and field.ast.value_expr == .none) {
1489 if (field.ast.align_expr.unwrap()) |align_expr| {
1490 try renderIdentifier(r, field.ast.main_token, .space, quote); // name
1491 const lparen_token = tree.firstToken(align_expr) - 1;
1492 const align_kw = lparen_token - 1;
1493 const rparen_token = tree.lastToken(align_expr) + 1;
1494 try renderToken(r, align_kw, .none); // align
1495 try renderToken(r, lparen_token, .none); // (
1496 try renderExpression(r, align_expr, .none); // alignment
1497 return renderToken(r, rparen_token, .space); // )
1498 }
1499 return renderIdentifierComma(r, field.ast.main_token, space, quote); // name
1500 }
1501 if (field.ast.type_expr != .none and field.ast.value_expr == .none) {
1502 const type_expr = field.ast.type_expr.unwrap().?;
1503 if (!field.ast.tuple_like) {
1504 try renderIdentifier(r, field.ast.main_token, .none, quote); // name
1505 try renderToken(r, field.ast.main_token + 1, .space); // :
1506 }
1503 try renderToken(r, t, .maybe_space); // comptime
1504 }
1505
1506 const last_component: enum {
1507 value,
1508 @"align",
1509 type,
1510 identifier,
1511 } = if (field.ast.value_expr != .none)
1512 .value
1513 else if (field.ast.align_expr != .none)
1514 .@"align"
1515 else if (field.ast.type_expr != .none)
1516 .type
1517 else if (!field.ast.tuple_like)
1518 .identifier
1519 else
1520 unreachable;
15071521
1508 if (field.ast.align_expr.unwrap()) |align_expr| {
1509 try renderExpression(r, type_expr, .space); // type
1510 const align_token = tree.firstToken(align_expr) - 2;
1511 try renderToken(r, align_token, .none); // align
1512 try renderToken(r, align_token + 1, .none); // (
1513 try renderExpression(r, align_expr, .none); // alignment
1514 const rparen = tree.lastToken(align_expr) + 1;
1515 return renderTokenComma(r, rparen, space); // )
1516 } else {
1517 return renderExpressionComma(r, type_expr, space); // type
1522 if (!field.ast.tuple_like) {
1523 if (last_component == .identifier) {
1524 return renderIdentifierComma(r, field.ast.main_token, space, quote); // name
15181525 }
1526 const this_space: Space = if (field.ast.type_expr != .none) .none else .space;
1527 try renderIdentifier(r, field.ast.main_token, this_space, quote); // name
15191528 }
1520 if (field.ast.type_expr == .none and field.ast.value_expr != .none) {
1521 const value_expr = field.ast.value_expr.unwrap().?;
15221529
1523 try renderIdentifier(r, field.ast.main_token, .space, quote); // name
1524 if (field.ast.align_expr.unwrap()) |align_expr| {
1525 const lparen_token = tree.firstToken(align_expr) - 1;
1526 const align_kw = lparen_token - 1;
1527 const rparen_token = tree.lastToken(align_expr) + 1;
1528 try renderToken(r, align_kw, .none); // align
1529 try renderToken(r, lparen_token, .none); // (
1530 try renderExpression(r, align_expr, .none); // alignment
1531 try renderToken(r, rparen_token, .space); // )
1530 if (field.ast.type_expr.unwrap()) |type_expr| {
1531 if (!field.ast.tuple_like) {
1532 try renderToken(r, field.ast.main_token + 1, .maybe_space); // :
15321533 }
1533 try renderToken(r, field.ast.main_token + 1, .space); // =
1534 return renderExpressionComma(r, value_expr, space); // value
1535 }
1536 if (!field.ast.tuple_like) {
1537 try renderIdentifier(r, field.ast.main_token, .none, quote); // name
1538 try renderToken(r, field.ast.main_token + 1, .space); // :
1539 }
15401534
1541 const type_expr = field.ast.type_expr.unwrap().?;
1542 const value_expr = field.ast.value_expr.unwrap().?;
1543
1544 try renderExpression(r, type_expr, .space); // type
1535 if (last_component == .type) {
1536 return renderExpressionComma(r, type_expr, space); // type
1537 }
1538 try renderExpression(r, type_expr, .space); // type
1539 }
15451540
15461541 if (field.ast.align_expr.unwrap()) |align_expr| {
1547 const lparen_token = tree.firstToken(align_expr) - 1;
1548 const align_kw = lparen_token - 1;
1549 const rparen_token = tree.lastToken(align_expr) + 1;
1550 try renderToken(r, align_kw, .none); // align
1551 try renderToken(r, lparen_token, .none); // (
1542 const align_token = tree.firstToken(align_expr) - 2;
1543 try renderToken(r, align_token, .none); // align
1544 try renderToken(r, align_token + 1, .none); // (
15521545 try renderExpression(r, align_expr, .none); // alignment
1553 try renderToken(r, rparen_token, .space); // )
1546 const rparen = tree.lastToken(align_expr) + 1;
1547 if (last_component == .@"align") {
1548 return renderTokenComma(r, rparen, space); // )
1549 }
1550 try renderToken(r, rparen, .space);
15541551 }
1555 const eq_token = tree.firstToken(value_expr) - 1;
1556 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
15571552
1558 try ais.pushIndent(.after_equals);
1559 try renderToken(r, eq_token, eq_space); // =
1553 if (field.ast.value_expr.unwrap()) |value_expr| {
1554 assert(last_component == .value);
1555 const eq_token = tree.firstToken(value_expr) - 1;
1556 const seperate_line = !tree.tokensOnSameLine(eq_token, eq_token + 1) or
1557 tree.tokenTag(eq_token + 1) == .multiline_string_literal_line;
1558 const eq_space: Space = if (seperate_line) .newline else .space;
15601559
1561 if (eq_space == .space) {
1562 ais.popIndent();
1563 try renderExpressionComma(r, value_expr, space); // value
1564 return;
1565 }
1566
1567 const maybe_comma = tree.lastToken(value_expr) + 1;
1560 try ais.pushIndent(.after_equals);
1561 try renderToken(r, eq_token, eq_space); // =
1562 if (eq_space == .space) {
1563 ais.popIndent();
1564 return renderExpressionComma(r, value_expr, space); // value
1565 }
15681566
1569 if (tree.tokenTag(maybe_comma) == .comma) {
1570 try renderExpression(r, value_expr, .none); // value
1571 ais.popIndent();
1572 try renderToken(r, maybe_comma, .newline);
1573 } else {
1574 try renderExpression(r, value_expr, space); // value
1575 ais.popIndent();
1576 }
1567 const maybe_comma = tree.lastToken(value_expr) + 1;
1568 if (tree.tokenTag(maybe_comma) == .comma) {
1569 try renderExpression(r, value_expr, .none); // value
1570 ais.popIndent();
1571 try renderToken(r, maybe_comma, .newline);
1572 } else {
1573 try renderExpression(r, value_expr, space); // value
1574 ais.popIndent();
1575 }
1576 } else unreachable;
15771577}
15781578
15791579fn renderBuiltinCall(
......@@ -1587,14 +1587,9 @@ fn renderBuiltinCall(
15871587
15881588 try renderToken(r, builtin_token, .none); // @name
15891589
1590 if (params.len == 0) {
1591 try renderToken(r, builtin_token + 1, .none); // (
1592 return renderToken(r, builtin_token + 2, space); // )
1593 }
1594
15951590 if (r.fixups.rebase_imported_paths) |prefix| {
15961591 const slice = tree.tokenSlice(builtin_token);
1597 if (mem.eql(u8, slice, "@import")) f: {
1592 if (params.len != 0 and mem.eql(u8, slice, "@import")) f: {
15981593 const param = params[0];
15991594 const str_lit_token = tree.nodeMainToken(param);
16001595 assert(tree.tokenTag(str_lit_token) == .string_literal);
......@@ -1613,45 +1608,7 @@ fn renderBuiltinCall(
16131608 }
16141609 }
16151610
1616 const last_param = params[params.len - 1];
1617 const after_last_param_token = tree.lastToken(last_param) + 1;
1618
1619 if (tree.tokenTag(after_last_param_token) != .comma) {
1620 // Render all on one line, no trailing comma.
1621 try renderToken(r, builtin_token + 1, .none); // (
1622
1623 for (params, 0..) |param_node, i| {
1624 const first_param_token = tree.firstToken(param_node);
1625 if (tree.tokenTag(first_param_token) == .multiline_string_literal_line or
1626 hasSameLineComment(tree, first_param_token - 1))
1627 {
1628 try ais.pushIndent(.normal);
1629 try renderExpression(r, param_node, .none);
1630 ais.popIndent();
1631 } else {
1632 try renderExpression(r, param_node, .none);
1633 }
1634
1635 if (i + 1 < params.len) {
1636 const comma_token = tree.lastToken(param_node) + 1;
1637 try renderToken(r, comma_token, .space); // ,
1638 }
1639 }
1640 return renderToken(r, after_last_param_token, space); // )
1641 } else {
1642 // Render one param per line.
1643 try ais.pushIndent(.normal);
1644 try renderToken(r, builtin_token + 1, Space.newline); // (
1645
1646 for (params) |param_node| {
1647 try ais.pushSpace(.comma);
1648 try renderExpression(r, param_node, .comma);
1649 ais.popSpace();
1650 }
1651 ais.popIndent();
1652
1653 return renderToken(r, after_last_param_token + 1, space); // )
1654 }
1611 return renderParamList(r, builtin_token + 1, params, space);
16551612}
16561613
16571614fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!void {
......@@ -1743,7 +1700,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
17431700 },
17441701 .r_paren => break,
17451702 .comma => {
1746 try renderToken(r, last_param_token, .space); // ,
1703 try renderToken(r, last_param_token, .maybe_space); // ,
17471704 continue;
17481705 },
17491706 else => {}, // Parameter type without a name.
......@@ -1753,7 +1710,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
17531710 {
17541711 try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name
17551712 last_param_token = last_param_token + 1;
1756 try renderToken(r, last_param_token, .space); // :
1713 try renderToken(r, last_param_token, .maybe_space); // :
17571714 last_param_token += 1;
17581715 }
17591716 if (tree.tokenTag(last_param_token) == .keyword_anytype) {
......@@ -1822,7 +1779,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
18221779 ais.popIndent();
18231780 }
18241781
1825 try renderToken(r, rparen, .space); // )
1782 try renderToken(r, rparen, .maybe_space); // )
18261783
18271784 if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
18281785 const align_lparen = tree.firstToken(align_expr) - 1;
......@@ -1831,7 +1788,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
18311788 try renderToken(r, align_lparen - 1, .none); // align
18321789 try renderToken(r, align_lparen, .none); // (
18331790 try renderExpression(r, align_expr, .none);
1834 try renderToken(r, align_rparen, .space); // )
1791 try renderToken(r, align_rparen, .maybe_space); // )
18351792 }
18361793
18371794 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
......@@ -1841,7 +1798,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
18411798 try renderToken(r, align_lparen - 1, .none); // addrspace
18421799 try renderToken(r, align_lparen, .none); // (
18431800 try renderExpression(r, addrspace_expr, .none);
1844 try renderToken(r, align_rparen, .space); // )
1801 try renderToken(r, align_rparen, .maybe_space); // )
18451802 }
18461803
18471804 if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
......@@ -1851,7 +1808,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
18511808 try renderToken(r, section_lparen - 1, .none); // section
18521809 try renderToken(r, section_lparen, .none); // (
18531810 try renderExpression(r, section_expr, .none);
1854 try renderToken(r, section_rparen, .space); // )
1811 try renderToken(r, section_rparen, .maybe_space); // )
18551812 }
18561813
18571814 if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {
......@@ -1865,7 +1822,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
18651822 try renderToken(r, callconv_lparen - 1, .none); // callconv
18661823 try renderToken(r, callconv_lparen, .none); // (
18671824 try renderExpression(r, callconv_expr, .none);
1868 try renderToken(r, callconv_rparen, .space); // )
1825 try renderToken(r, callconv_rparen, .maybe_space); // )
18691826 }
18701827 }
18711828
......@@ -1890,7 +1847,7 @@ fn renderSwitchCase(
18901847
18911848 // render inline keyword
18921849 if (switch_case.inline_token) |some| {
1893 try renderToken(r, some, .space);
1850 try renderToken(r, some, .maybe_space);
18941851 }
18951852
18961853 // Render everything before the arrow
......@@ -1904,33 +1861,26 @@ fn renderSwitchCase(
19041861 } else {
19051862 // Render on one line
19061863 for (switch_case.ast.values) |value_expr| {
1907 try renderExpression(r, value_expr, .comma_space);
1864 try renderExpression(r, value_expr, .comma_maybe_space);
19081865 }
19091866 }
19101867
1911 // Render the arrow and everything after it
1912 const pre_target_space = if (tree.nodeTag(switch_case.ast.target_expr) == .multiline_string_literal)
1913 // Newline gets inserted when rendering the target expr.
1914 Space.none
1915 else
1916 Space.space;
1917 const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space;
1918 try renderToken(r, switch_case.ast.arrow_token, after_arrow_space); // =>
1868 try renderToken(r, switch_case.ast.arrow_token, .maybe_space); // =>
19191869
19201870 if (switch_case.payload_token) |payload_token| {
19211871 try renderToken(r, payload_token - 1, .none); // pipe
1922 const ident = payload_token + @intFromBool(tree.tokenTag(payload_token) == .asterisk);
1923 if (tree.tokenTag(payload_token) == .asterisk) {
1872 var ident = payload_token;
1873 if (tree.tokenTag(ident) == .asterisk) {
19241874 try renderToken(r, payload_token, .none); // asterisk
1875 ident += 1;
19251876 }
19261877 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
19271878 if (tree.tokenTag(ident + 1) == .comma) {
1928 try renderToken(r, ident + 1, .space); // ,
1929 try renderIdentifier(r, ident + 2, .none, .preserve_when_shadowing); // identifier
1930 try renderToken(r, ident + 3, pre_target_space); // pipe
1931 } else {
1932 try renderToken(r, ident + 1, pre_target_space); // pipe
1879 ident += 2;
1880 try renderToken(r, ident - 1, .space); // ,
1881 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
19331882 }
1883 try renderToken(r, ident + 1, .maybe_space); // pipe
19341884 }
19351885
19361886 try renderExpression(r, switch_case.ast.target_expr, space);
......@@ -2018,26 +1968,13 @@ fn renderStructInit(
20181968 try ais.pushIndent(.normal);
20191969 try renderToken(r, struct_init.ast.lbrace, .newline);
20201970
2021 try renderToken(r, struct_init.ast.lbrace + 1, .none); // .
2022 try renderIdentifier(r, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name
2023 // Don't output a space after the = if expression is a multiline string,
2024 // since then it will start on the next line.
2025 const field_node = struct_init.ast.fields[0];
2026 const expr = tree.nodeTag(field_node);
2027 var space_after_equal: Space = if (expr == .multiline_string_literal) .none else .space;
2028 try renderToken(r, struct_init.ast.lbrace + 3, space_after_equal); // =
2029
2030 try ais.pushSpace(.comma);
2031 try renderExpressionFixup(r, field_node, .comma);
2032 ais.popSpace();
2033
2034 for (struct_init.ast.fields[1..]) |field_init| {
1971 for (0.., struct_init.ast.fields) |i, field_init| {
20351972 const init_token = tree.firstToken(field_init);
2036 try renderExtraNewlineToken(r, init_token - 3);
1973 if (i != 0)
1974 try renderExtraNewlineToken(r, init_token - 3);
20371975 try renderToken(r, init_token - 3, .none); // .
20381976 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
2039 space_after_equal = if (tree.nodeTag(field_init) == .multiline_string_literal) .none else .space;
2040 try renderToken(r, init_token - 1, space_after_equal); // =
1977 try renderToken(r, init_token - 1, .maybe_space); // =
20411978
20421979 try ais.pushSpace(.comma);
20431980 try renderExpressionFixup(r, field_init, .comma);
......@@ -2053,8 +1990,7 @@ fn renderStructInit(
20531990 const init_token = tree.firstToken(field_init);
20541991 try renderToken(r, init_token - 3, .none); // .
20551992 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
2056 const space_after_equal: Space = if (tree.nodeTag(field_init) == .multiline_string_literal) .none else .space;
2057 try renderToken(r, init_token - 1, space_after_equal); // =
1993 try renderToken(r, init_token - 1, .maybe_space); // =
20581994 try renderExpressionFixup(r, field_init, .comma_space);
20591995 }
20601996 }
......@@ -2436,7 +2372,7 @@ fn renderAsm(
24362372 const first_clobber = tree.firstToken(clobbers);
24372373 try renderToken(r, first_clobber - 3, .none);
24382374 try renderToken(r, first_clobber - 2, .none);
2439 try renderToken(r, first_clobber - 1, .space);
2375 try renderToken(r, first_clobber - 1, .maybe_space);
24402376 try renderExpression(r, clobbers, .none);
24412377 ais.popIndent();
24422378 return renderToken(r, asm_node.ast.rparen, space); // rparen
......@@ -2450,6 +2386,9 @@ fn renderAsm(
24502386
24512387 try ais.forcePushIndent(.normal);
24522388 try renderExpression(r, asm_node.ast.template, .newline);
2389 ais.forceLastIndent(); // Might have been dedented by a multiline string literal
2390 assert(ais.current_line_empty);
2391
24532392 ais.setIndentDelta(asm_indent_delta);
24542393 const colon1 = tree.lastToken(asm_node.ast.template) + 1;
24552394
......@@ -2527,9 +2466,10 @@ fn renderAsm(
25272466 unreachable;
25282467 };
25292468
2530 try renderToken(r, colon3, .space); // :
2469 try renderToken(r, colon3, .maybe_space); // :
25312470 const clobbers = asm_node.ast.clobbers.unwrap().?;
25322471 try renderExpression(r, clobbers, .none);
2472 ais.forceLastIndent(); // Might have been dedented by a multiline string literal
25332473 ais.setIndentDelta(indent_delta);
25342474 ais.popIndent();
25352475 return renderToken(r, asm_node.ast.rparen, space); // rparen
......@@ -2555,7 +2495,7 @@ fn renderParamList(
25552495
25562496 if (params.len == 0) {
25572497 try ais.pushIndent(.normal);
2558 try renderToken(r, lparen, .none);
2498 try renderToken(r, lparen, .none); // (
25592499 ais.popIndent();
25602500 return renderToken(r, lparen + 1, space); // )
25612501 }
......@@ -2590,10 +2530,7 @@ fn renderParamList(
25902530
25912531 if (i + 1 < params.len) {
25922532 const comma = tree.lastToken(param_node) + 1;
2593 const next_multiline_string =
2594 tree.tokenTag(tree.firstToken(params[i + 1])) == .multiline_string_literal_line;
2595 const comma_space: Space = if (next_multiline_string) .none else .space;
2596 try renderToken(r, comma, comma_space);
2533 try renderToken(r, comma, .maybe_space);
25972534 }
25982535 }
25992536 ais.popIndent();
......@@ -2655,6 +2592,13 @@ const Space = enum {
26552592 /// Additionally consume the next token if it is a semicolon.
26562593 /// In either case, a newline will be inserted afterwards.
26572594 semicolon,
2595 /// If the next token is not a multiline string literal, this acts as .space,
2596 /// otherwise this acts as .none.
2597 maybe_space,
2598 /// Additionally consume the next token if it is a comma.
2599 /// In either case, a space will be inserted afterwards
2600 /// if the following token is not a multiline string literal.
2601 comma_maybe_space,
26582602 /// Skip rendering whitespace and comments. If this is used, the caller
26592603 /// *must* handle whitespace and comments manually.
26602604 skip,
......@@ -2719,6 +2663,16 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
27192663 try ais.insertNewline();
27202664 },
27212665
2666 .maybe_space => if (!comment and next_token_tag != .multiline_string_literal_line) {
2667 try ais.writeByte(' ');
2668 },
2669
2670 .comma_maybe_space => if (next_token_tag == .comma) {
2671 try renderToken(r, token_index + 1, .maybe_space);
2672 } else if (!comment) {
2673 try ais.writeByte(' ');
2674 },
2675
27222676 .skip => unreachable,
27232677 }
27242678}
......@@ -2727,9 +2681,9 @@ fn renderOnlySpace(r: *Render, space: Space) Error!void {
27272681 const ais = r.ais;
27282682 switch (space) {
27292683 .none => {},
2730 .space => try ais.writeByte(' '),
2684 .space, .maybe_space => try ais.writeByte(' '),
27312685 .newline => try ais.insertNewline(),
2732 .comma => try ais.writeAll(",\n"),
2686 .comma, .comma_maybe_space => try ais.writeAll(",\n"),
27332687 .comma_space => try ais.writeAll(", "),
27342688 .semicolon => try ais.writeAll(";\n"),
27352689 .skip => unreachable,
......@@ -3477,11 +3431,19 @@ const AutoIndentingStream = struct {
34773431
34783432 pub fn popIndent(ais: *AutoIndentingStream) void {
34793433 if (ais.indent_stack.pop().?.realized) {
3480 assert(ais.indent_count > 0);
34813434 ais.indent_count -= 1;
34823435 }
34833436 }
34843437
3438 /// Forces the last pushed indent to be realized
3439 pub fn forceLastIndent(ais: *AutoIndentingStream) void {
3440 const top = &ais.indent_stack.items[ais.indent_stack.items.len - 1];
3441 if (!top.realized) {
3442 top.realized = true;
3443 ais.indent_count += 1;
3444 }
3445 }
3446
34853447 pub fn indentStackEmpty(ais: *AutoIndentingStream) bool {
34863448 return ais.indent_stack.items.len == 0;
34873449 }
lib/std/zig/parser_test.zig+313
......@@ -2543,6 +2543,19 @@ test "zig fmt: first line comment in struct initializer" {
25432543 );
25442544}
25452545
2546test "zig fmt: multiline string literals in struct initializer" {
2547 try testTransform(
2548 \\const a = .{ .a = \\
2549 \\+ 1};
2550 \\
2551 ,
2552 \\const a = .{ .a =
2553 \\ \\
2554 \\+ 1 };
2555 \\
2556 );
2557}
2558
25462559test "zig fmt: doc comments before struct field" {
25472560 try testCanonical(
25482561 \\pub const Allocator = struct {
......@@ -4005,6 +4018,18 @@ test "zig fmt: multiline string in array" {
40054018 \\}
40064019 \\
40074020 );
4021
4022 try testTransform(
4023 \\const a = .{ k, \\
4024 \\};
4025 \\
4026 ,
4027 \\const a = .{
4028 \\ k,
4029 \\ \\
4030 \\};
4031 \\
4032 );
40084033}
40094034
40104035test "zig fmt: if type expr" {
......@@ -6029,6 +6054,294 @@ test "zig fmt: extern addrspace in struct" {
60296054 );
60306055}
60316056
6057test "zig fmt: whitespace with multiline strings" {
6058 try testCanonical(
6059 \\const a = .{
6060 \\ .b =
6061 \\ \\
6062 \\ ++ "",
6063 \\};
6064 \\const b = switch (a) {
6065 \\ a =>
6066 \\ \\
6067 \\ ++ "",
6068 \\};
6069 \\
6070 );
6071
6072 try testTransform(
6073 \\test {
6074 \\ a = \\
6075 \\ ;
6076 \\ b = \\
6077 \\ ();
6078 \\ c = x ++ \\
6079 \\ ;
6080 \\ d = x catch \\
6081 \\ ;
6082 \\ comptime \\
6083 \\ , \\
6084 \\ , \\
6085 \\ = \\
6086 \\ ;
6087 \\ e = if (x) \\
6088 \\ else y;
6089 \\ f = if (x) y else
6090 \\ \\
6091 \\ ;
6092 \\ comptime \\
6093 \\ ;
6094 \\ errdefer \\
6095 \\ ;
6096 \\ try \\
6097 \\ ;
6098 \\ return \\
6099 \\ ;
6100 \\ const a = asm (\\
6101 \\ ++ "": [a] "" (-> \\
6102 \\ ) :: \\
6103 \\ );
6104 \\ const a2 = asm ("" ::: \\
6105 \\ );
6106 \\ const b = x[1 + 1 .. \\
6107 \\ ];
6108 \\}
6109 \\/// tuple type
6110 \\comptime \\
6111 \\,
6112 \\a: \\
6113 \\align(\\
6114 \\)
6115 \\= \\
6116 \\,
6117 \\const A = .{
6118 \\ *volatile \\
6119 \\ ,
6120 \\ *const \\
6121 \\ ,
6122 \\ *addrspace( \\
6123 \\ ) \\
6124 \\ ,
6125 \\ *align( \\
6126 \\ : \\
6127 \\ : \\
6128 \\ ) \\
6129 \\ ,
6130 \\ *allowzero \\
6131 \\ ,
6132 \\ *\\
6133 \\ ,
6134 \\ **\\
6135 \\ ,
6136 \\ [*]\\
6137 \\ ,
6138 \\ [*: \\
6139 \\ ]\\
6140 \\ ,
6141 \\ [*c]\\
6142 \\ ,
6143 \\ []\\
6144 \\ ,
6145 \\ [: \\
6146 \\ ]\\
6147 \\ ,
6148 \\ *addrspace(a) align(a) \\
6149 \\ ,
6150 \\};
6151 \\const a = blk: {
6152 \\ break \\
6153 \\ ;
6154 \\ break :blk \\
6155 \\ ;
6156 \\ continue \\
6157 \\ ;
6158 \\ continue :blk \\
6159 \\ ;
6160 \\};
6161 \\const b = a(a, \\
6162 \\++ "");
6163 \\const c = @a(a, \\
6164 \\++ "");
6165 \\extern fn a(T,\\
6166 \\) \\
6167 \\;
6168 \\extern fn b(a: \\
6169 \\) align(a) callconv(a) \\
6170 \\;
6171 \\const d = switch (a) { \\
6172 \\ , 1,
6173 \\ \\
6174 \\ => {},
6175 \\ inline \\
6176 \\ => {},
6177 \\};
6178 \\
6179 ,
6180 \\test {
6181 \\ a =
6182 \\ \\
6183 \\ ;
6184 \\ b =
6185 \\ \\
6186 \\ ();
6187 \\ c = x ++
6188 \\ \\
6189 \\ ;
6190 \\ d = x catch
6191 \\ \\
6192 \\ ;
6193 \\ comptime
6194 \\ \\
6195 \\ ,
6196 \\ \\
6197 \\ ,
6198 \\ \\
6199 \\ =
6200 \\ \\
6201 \\ ;
6202 \\ e = if (x)
6203 \\ \\
6204 \\ else
6205 \\ y;
6206 \\ f = if (x) y else
6207 \\ \\
6208 \\ ;
6209 \\ comptime
6210 \\ \\
6211 \\ ;
6212 \\ errdefer
6213 \\ \\
6214 \\ ;
6215 \\ try
6216 \\ \\
6217 \\ ;
6218 \\ return
6219 \\ \\
6220 \\ ;
6221 \\ const a = asm (
6222 \\ \\
6223 \\ ++ ""
6224 \\ : [a] "" (->
6225 \\ \\
6226 \\ ),
6227 \\ :
6228 \\ :
6229 \\ \\
6230 \\ );
6231 \\ const a2 = asm ("" :::
6232 \\ \\
6233 \\ );
6234 \\ const b = x[1 + 1 ..
6235 \\ \\
6236 \\ ];
6237 \\}
6238 \\/// tuple type
6239 \\comptime
6240 \\\\
6241 \\,
6242 \\a:
6243 \\\\
6244 \\align(
6245 \\\\
6246 \\) =
6247 \\ \\
6248 \\,
6249 \\const A = .{
6250 \\ *volatile
6251 \\ \\
6252 \\ ,
6253 \\ *const
6254 \\ \\
6255 \\ ,
6256 \\ *addrspace(
6257 \\ \\
6258 \\ )
6259 \\ \\
6260 \\ ,
6261 \\ *align(
6262 \\ \\
6263 \\ :
6264 \\ \\
6265 \\ :
6266 \\ \\
6267 \\ )
6268 \\ \\
6269 \\ ,
6270 \\ *allowzero
6271 \\ \\
6272 \\ ,
6273 \\ *
6274 \\ \\
6275 \\ ,
6276 \\ **
6277 \\ \\
6278 \\ ,
6279 \\ [*]
6280 \\ \\
6281 \\ ,
6282 \\ [*:
6283 \\ \\
6284 \\ ]
6285 \\ \\
6286 \\ ,
6287 \\ [*c]
6288 \\ \\
6289 \\ ,
6290 \\ []
6291 \\ \\
6292 \\ ,
6293 \\ [:
6294 \\ \\
6295 \\ ]
6296 \\ \\
6297 \\ ,
6298 \\ *align(a) addrspace(a)
6299 \\ \\
6300 \\ ,
6301 \\};
6302 \\const a = blk: {
6303 \\ break
6304 \\ \\
6305 \\ ;
6306 \\ break :blk
6307 \\ \\
6308 \\ ;
6309 \\ continue
6310 \\ \\
6311 \\ ;
6312 \\ continue :blk
6313 \\ \\
6314 \\ ;
6315 \\};
6316 \\const b = a(a,
6317 \\ \\
6318 \\++ "");
6319 \\const c = @a(a,
6320 \\ \\
6321 \\++ "");
6322 \\extern fn a(T,
6323 \\\\
6324 \\)
6325 \\\\
6326 \\;
6327 \\extern fn b(a:
6328 \\\\
6329 \\) align(a) callconv(a)
6330 \\\\
6331 \\;
6332 \\const d = switch (a) {
6333 \\ \\
6334 \\ , 1,
6335 \\ \\
6336 \\ => {},
6337 \\ inline
6338 \\ \\
6339 \\ => {},
6340 \\};
6341 \\
6342 );
6343}
6344
60326345test "recovery: top level" {
60336346 try testError(
60346347 \\test "" {inline}