authorgravatar for 178735591+87flowers@users.noreply.github.com87flowers <178735591+87flowers@users.noreply.github.com> 2024-10-16 19:15:19+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-22 17:09:20-08:00
logec3e4cc14b004496d8d2ab0d15d03c0c22c3d95c
tree940863ed7c18f4a33771f1eb50be574937bb18cd
parent1b0584fd02a907d096265778044985d4c23fc05f

std/zig/render: Initial implementation of indentation


1 files changed, 119 insertions(+), 185 deletions(-)

lib/std/zig/render.zig+119-185
...@@ -82,6 +82,7 @@ const Render = struct {...@@ -82,6 +82,7 @@ const Render = struct {
82pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast, fixups: Fixups) Error!void {82pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast, fixups: Fixups) Error!void {
83 assert(tree.errors.len == 0); // Cannot render an invalid tree.83 assert(tree.errors.len == 0); // Cannot render an invalid tree.
84 var auto_indenting_stream = Ais.init(buffer, indent_delta);84 var auto_indenting_stream = Ais.init(buffer, indent_delta);
85 defer auto_indenting_stream.deinit();
85 var r: Render = .{86 var r: Render = .{
86 .gpa = buffer.allocator,87 .gpa = buffer.allocator,
87 .ais = &auto_indenting_stream,88 .ais = &auto_indenting_stream,
...@@ -195,7 +196,7 @@ fn renderMember(...@@ -195,7 +196,7 @@ fn renderMember(
195 try renderExpression(r, fn_proto, .space);196 try renderExpression(r, fn_proto, .space);
196 const body_node = datas[decl].rhs;197 const body_node = datas[decl].rhs;
197 if (r.fixups.gut_functions.contains(decl)) {198 if (r.fixups.gut_functions.contains(decl)) {
198 ais.pushIndent();199 try ais.pushIndent(.normal);
199 const lbrace = tree.nodes.items(.main_token)[body_node];200 const lbrace = tree.nodes.items(.main_token)[body_node];
200 try renderToken(r, lbrace, .newline);201 try renderToken(r, lbrace, .newline);
201 try discardAllParams(r, fn_proto);202 try discardAllParams(r, fn_proto);
...@@ -204,7 +205,7 @@ fn renderMember(...@@ -204,7 +205,7 @@ fn renderMember(
204 try ais.insertNewline();205 try ais.insertNewline();
205 try renderToken(r, tree.lastToken(body_node), space); // rbrace206 try renderToken(r, tree.lastToken(body_node), space); // rbrace
206 } else if (r.fixups.unused_var_decls.count() != 0) {207 } else if (r.fixups.unused_var_decls.count() != 0) {
207 ais.pushIndentNextLine();208 try ais.pushIndent(.normal);
208 const lbrace = tree.nodes.items(.main_token)[body_node];209 const lbrace = tree.nodes.items(.main_token)[body_node];
209 try renderToken(r, lbrace, .newline);210 try renderToken(r, lbrace, .newline);
210211
...@@ -358,13 +359,16 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -358,13 +359,16 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
358 => return renderToken(r, main_tokens[node], space),359 => return renderToken(r, main_tokens[node], space),
359360
360 .multiline_string_literal => {361 .multiline_string_literal => {
361 var locked_indents = ais.lockOneShotIndent();
362 try ais.maybeInsertNewline();362 try ais.maybeInsertNewline();
363363
364 var i = datas[node].lhs;364 var i = datas[node].lhs;
365 while (i <= datas[node].rhs) : (i += 1) try renderToken(r, i, .newline);365 while (i <= datas[node].rhs) : (i += 1) try renderToken(r, i, .newline);
366366
367 while (locked_indents > 0) : (locked_indents -= 1) ais.popIndent();367 // dedent the next thing that comes after a multiline string literal
368 if (!ais.indentStackEmpty()) {
369 ais.popIndent();
370 try ais.pushIndent(.normal);
371 }
368372
369 switch (space) {373 switch (space) {
370 .none, .space, .newline, .skip => {},374 .none, .space, .newline, .skip => {},
...@@ -442,6 +446,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -442,6 +446,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
442446
443 try renderExpression(r, datas[node].lhs, .space); // target447 try renderExpression(r, datas[node].lhs, .space); // target
444448
449 try ais.pushIndent(.normal);
445 if (token_tags[fallback_first - 1] == .pipe) {450 if (token_tags[fallback_first - 1] == .pipe) {
446 try renderToken(r, main_token, .space); // catch keyword451 try renderToken(r, main_token, .space); // catch keyword
447 try renderToken(r, main_token + 1, .none); // pipe452 try renderToken(r, main_token + 1, .none); // pipe
...@@ -451,38 +456,27 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -451,38 +456,27 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
451 assert(token_tags[fallback_first - 1] == .keyword_catch);456 assert(token_tags[fallback_first - 1] == .keyword_catch);
452 try renderToken(r, main_token, after_op_space); // catch keyword457 try renderToken(r, main_token, after_op_space); // catch keyword
453 }458 }
454
455 ais.pushIndentOneShot();
456 try renderExpression(r, datas[node].rhs, space); // fallback459 try renderExpression(r, datas[node].rhs, space); // fallback
460 ais.popIndent();
457 },461 },
458462
459 .field_access => {463 .field_access => {
460 const main_token = main_tokens[node];464 const main_token = main_tokens[node];
461 const field_access = datas[node];465 const field_access = datas[node];
462466
467 try ais.pushIndent(.normal);
463 try renderExpression(r, field_access.lhs, .none);468 try renderExpression(r, field_access.lhs, .none);
464469
465 // Allow a line break between the lhs and the dot if the lhs and rhs470 // Allow a line break between the lhs and the dot if the lhs and rhs
466 // are on different lines.471 // are on different lines.
467 const lhs_last_token = tree.lastToken(field_access.lhs);472 const lhs_last_token = tree.lastToken(field_access.lhs);
468 const same_line = tree.tokensOnSameLine(lhs_last_token, main_token + 1);473 const same_line = tree.tokensOnSameLine(lhs_last_token, main_token + 1);
469 if (!same_line) {474 if (!same_line and !hasComment(tree, lhs_last_token, main_token)) try ais.insertNewline();
470 if (!hasComment(tree, lhs_last_token, main_token)) try ais.insertNewline();
471 ais.pushIndentOneShot();
472 }
473475
474 try renderToken(r, main_token, .none); // .476 try renderToken(r, main_token, .none); // .
475477
476 // This check ensures that zag() is indented in the following example:478 try renderIdentifier(r, field_access.rhs, space, .eagerly_unquote); // field
477 // const x = foo479 ais.popIndent();
478 // .bar()
479 // . // comment
480 // zag();
481 if (!same_line and hasComment(tree, main_token, main_token + 1)) {
482 ais.pushIndentOneShot();
483 }
484
485 return renderIdentifier(r, field_access.rhs, space, .eagerly_unquote); // field
486 },480 },
487481
488 .error_union,482 .error_union,
...@@ -555,15 +549,14 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -555,15 +549,14 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
555 const infix = datas[node];549 const infix = datas[node];
556 try renderExpression(r, infix.lhs, .space);550 try renderExpression(r, infix.lhs, .space);
557 const op_token = main_tokens[node];551 const op_token = main_tokens[node];
552 try ais.pushIndent(.normal);
558 if (tree.tokensOnSameLine(op_token, op_token + 1)) {553 if (tree.tokensOnSameLine(op_token, op_token + 1)) {
559 try renderToken(r, op_token, .space);554 try renderToken(r, op_token, .space);
560 } else {555 } else {
561 ais.pushIndent();
562 try renderToken(r, op_token, .newline);556 try renderToken(r, op_token, .newline);
563 ais.popIndent();
564 }557 }
565 ais.pushIndentOneShot();558 try renderExpression(r, infix.rhs, space);
566 return renderExpression(r, infix.rhs, space);559 ais.popIndent();
567 },560 },
568561
569 .assign_destructure => {562 .assign_destructure => {
...@@ -585,15 +578,14 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -585,15 +578,14 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
585 else => try renderExpression(r, variable_node, variable_space),578 else => try renderExpression(r, variable_node, variable_space),
586 }579 }
587 }580 }
581 try ais.pushIndent(.normal);
588 if (tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1)) {582 if (tree.tokensOnSameLine(full.ast.equal_token, full.ast.equal_token + 1)) {
589 try renderToken(r, full.ast.equal_token, .space);583 try renderToken(r, full.ast.equal_token, .space);
590 } else {584 } else {
591 ais.pushIndent();
592 try renderToken(r, full.ast.equal_token, .newline);585 try renderToken(r, full.ast.equal_token, .newline);
593 ais.popIndent();
594 }586 }
595 ais.pushIndentOneShot();587 try renderExpression(r, full.ast.value_expr, space);
596 return renderExpression(r, full.ast.value_expr, space);588 ais.popIndent();
597 },589 },
598590
599 .bit_not,591 .bit_not,
...@@ -671,7 +663,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -671,7 +663,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
671 const one_line = tree.tokensOnSameLine(lbracket, rbracket);663 const one_line = tree.tokensOnSameLine(lbracket, rbracket);
672 const inner_space = if (one_line) Space.none else Space.newline;664 const inner_space = if (one_line) Space.none else Space.newline;
673 try renderExpression(r, suffix.lhs, .none);665 try renderExpression(r, suffix.lhs, .none);
674 ais.pushIndentNextLine();666 try ais.pushIndent(.normal);
675 try renderToken(r, lbracket, inner_space); // [667 try renderToken(r, lbracket, inner_space); // [
676 try renderExpression(r, suffix.rhs, inner_space);668 try renderExpression(r, suffix.rhs, inner_space);
677 ais.popIndent();669 ais.popIndent();
...@@ -723,8 +715,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -723,8 +715,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
723715
724 .grouped_expression => {716 .grouped_expression => {
725 try renderToken(r, main_tokens[node], .none); // lparen717 try renderToken(r, main_tokens[node], .none); // lparen
726 ais.pushIndentOneShot();718 try ais.pushIndent(.normal);
727 try renderExpression(r, datas[node].lhs, .none);719 try renderExpression(r, datas[node].lhs, .none);
720 ais.popIndent();
728 return renderToken(r, datas[node].rhs, space); // rparen721 return renderToken(r, datas[node].rhs, space); // rparen
729 },722 },
730723
...@@ -764,7 +757,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -764,7 +757,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
764 return renderToken(r, rbrace, space);757 return renderToken(r, rbrace, space);
765 } else if (token_tags[rbrace - 1] == .comma) {758 } else if (token_tags[rbrace - 1] == .comma) {
766 // There is a trailing comma so render each member on a new line.759 // There is a trailing comma so render each member on a new line.
767 ais.pushIndentNextLine();760 try ais.pushIndent(.normal);
768 try renderToken(r, lbrace, .newline);761 try renderToken(r, lbrace, .newline);
769 var i = lbrace + 1;762 var i = lbrace + 1;
770 while (i < rbrace) : (i += 1) {763 while (i < rbrace) : (i += 1) {
...@@ -845,7 +838,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -845,7 +838,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
845 try renderExpression(r, full.ast.condition, .none); // condition expression838 try renderExpression(r, full.ast.condition, .none); // condition expression
846 try renderToken(r, rparen, .space); // )839 try renderToken(r, rparen, .space); // )
847840
848 ais.pushIndentNextLine();841 try ais.pushIndent(.normal);
849 if (full.ast.cases.len == 0) {842 if (full.ast.cases.len == 0) {
850 try renderToken(r, rparen + 1, .none); // {843 try renderToken(r, rparen + 1, .none); // {
851 } else {844 } else {
...@@ -920,7 +913,7 @@ fn renderArrayType(...@@ -920,7 +913,7 @@ fn renderArrayType(
920 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;913 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
921 const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket);914 const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket);
922 const inner_space = if (one_line) Space.none else Space.newline;915 const inner_space = if (one_line) Space.none else Space.newline;
923 ais.pushIndentNextLine();916 try ais.pushIndent(.normal);
924 try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket917 try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket
925 try renderExpression(r, array_type.ast.elem_count, inner_space);918 try renderExpression(r, array_type.ast.elem_count, inner_space);
926 if (array_type.ast.sentinel != 0) {919 if (array_type.ast.sentinel != 0) {
...@@ -1236,13 +1229,10 @@ fn renderVarDeclWithoutFixups(...@@ -1236,13 +1229,10 @@ fn renderVarDeclWithoutFixups(
12361229
1237 const eq_token = tree.firstToken(var_decl.ast.init_node) - 1;1230 const eq_token = tree.firstToken(var_decl.ast.init_node) - 1;
1238 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;1231 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
1239 {1232 try ais.pushIndent(.normal);
1240 ais.pushIndent();1233 try renderToken(r, eq_token, eq_space); // =
1241 try renderToken(r, eq_token, eq_space); // =1234 try renderExpression(r, var_decl.ast.init_node, space); // ;
1242 ais.popIndent();1235 ais.popIndent();
1243 }
1244 ais.pushIndentOneShot();
1245 return renderExpression(r, var_decl.ast.init_node, space); // ;
1246}1236}
12471237
1248fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) Error!void {1238fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) Error!void {
...@@ -1342,23 +1332,28 @@ fn renderThenElse(...@@ -1342,23 +1332,28 @@ fn renderThenElse(
1342 const then_expr_is_block = nodeIsBlock(node_tags[then_expr]);1332 const then_expr_is_block = nodeIsBlock(node_tags[then_expr]);
1343 const indent_then_expr = !then_expr_is_block and1333 const indent_then_expr = !then_expr_is_block and
1344 !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(then_expr));1334 !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(then_expr));
1345 if (indent_then_expr or (then_expr_is_block and ais.isLineOverIndented())) {1335
1346 ais.pushIndentNextLine();1336 if (indent_then_expr) try ais.pushIndent(.normal);
1337
1338 if (then_expr_is_block and ais.isLineOverIndented()) {
1339 ais.disableIndentCommitting();
1340 try renderToken(r, last_prefix_token, .newline);
1341 ais.enableIndentCommitting();
1342 } else if (indent_then_expr) {
1347 try renderToken(r, last_prefix_token, .newline);1343 try renderToken(r, last_prefix_token, .newline);
1348 ais.popIndent();
1349 } else {1344 } else {
1350 try renderToken(r, last_prefix_token, .space);1345 try renderToken(r, last_prefix_token, .space);
1351 }1346 }
13521347
1353 if (else_expr != 0) {1348 if (else_expr != 0) {
1354 if (indent_then_expr) {1349 if (indent_then_expr) {
1355 ais.pushIndent();
1356 try renderExpression(r, then_expr, .newline);1350 try renderExpression(r, then_expr, .newline);
1357 ais.popIndent();
1358 } else {1351 } else {
1359 try renderExpression(r, then_expr, .space);1352 try renderExpression(r, then_expr, .space);
1360 }1353 }
13611354
1355 if (indent_then_expr) ais.popIndent();
1356
1362 var last_else_token = else_token;1357 var last_else_token = else_token;
13631358
1364 if (maybe_error_token) |error_token| {1359 if (maybe_error_token) |error_token| {
...@@ -1372,20 +1367,17 @@ fn renderThenElse(...@@ -1372,20 +1367,17 @@ fn renderThenElse(
1372 !nodeIsBlock(node_tags[else_expr]) and1367 !nodeIsBlock(node_tags[else_expr]) and
1373 !nodeIsIfForWhileSwitch(node_tags[else_expr]);1368 !nodeIsIfForWhileSwitch(node_tags[else_expr]);
1374 if (indent_else_expr) {1369 if (indent_else_expr) {
1375 ais.pushIndentNextLine();1370 try ais.pushIndent(.normal);
1376 try renderToken(r, last_else_token, .newline);1371 try renderToken(r, last_else_token, .newline);
1372 try renderExpression(r, else_expr, space);
1377 ais.popIndent();1373 ais.popIndent();
1378 try renderExpressionIndented(r, else_expr, space);
1379 } else {1374 } else {
1380 try renderToken(r, last_else_token, .space);1375 try renderToken(r, last_else_token, .space);
1381 try renderExpression(r, else_expr, space);1376 try renderExpression(r, else_expr, space);
1382 }1377 }
1383 } else {1378 } else {
1384 if (indent_then_expr) {1379 try renderExpression(r, then_expr, space);
1385 try renderExpressionIndented(r, then_expr, space);1380 if (indent_then_expr) ais.popIndent();
1386 } else {
1387 try renderExpression(r, then_expr, space);
1388 }
1389 }1381 }
1390}1382}
13911383
...@@ -1411,7 +1403,7 @@ fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void {...@@ -1411,7 +1403,7 @@ fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void {
1411 var cur = for_node.payload_token;1403 var cur = for_node.payload_token;
1412 const pipe = std.mem.indexOfScalarPos(std.zig.Token.Tag, token_tags, cur, .pipe).?;1404 const pipe = std.mem.indexOfScalarPos(std.zig.Token.Tag, token_tags, cur, .pipe).?;
1413 if (token_tags[pipe - 1] == .comma) {1405 if (token_tags[pipe - 1] == .comma) {
1414 ais.pushIndentNextLine();1406 try ais.pushIndent(.normal);
1415 try renderToken(r, cur - 1, .newline); // |1407 try renderToken(r, cur - 1, .newline); // |
1416 while (true) {1408 while (true) {
1417 if (token_tags[cur] == .asterisk) {1409 if (token_tags[cur] == .asterisk) {
...@@ -1540,7 +1532,7 @@ fn renderContainerField(...@@ -1540,7 +1532,7 @@ fn renderContainerField(
1540 const eq_token = tree.firstToken(field.ast.value_expr) - 1;1532 const eq_token = tree.firstToken(field.ast.value_expr) - 1;
1541 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;1533 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
1542 {1534 {
1543 ais.pushIndent();1535 try ais.pushIndent(.normal);
1544 try renderToken(r, eq_token, eq_space); // =1536 try renderToken(r, eq_token, eq_space); // =
1545 ais.popIndent();1537 ais.popIndent();
1546 }1538 }
...@@ -1552,12 +1544,12 @@ fn renderContainerField(...@@ -1552,12 +1544,12 @@ fn renderContainerField(
1552 const maybe_comma = tree.lastToken(field.ast.value_expr) + 1;1544 const maybe_comma = tree.lastToken(field.ast.value_expr) + 1;
15531545
1554 if (token_tags[maybe_comma] == .comma) {1546 if (token_tags[maybe_comma] == .comma) {
1555 ais.pushIndent();1547 try ais.pushIndent(.normal);
1556 try renderExpression(r, field.ast.value_expr, .none); // value1548 try renderExpression(r, field.ast.value_expr, .none); // value
1557 ais.popIndent();1549 ais.popIndent();
1558 try renderToken(r, maybe_comma, .newline);1550 try renderToken(r, maybe_comma, .newline);
1559 } else {1551 } else {
1560 ais.pushIndent();1552 try ais.pushIndent(.normal);
1561 try renderExpression(r, field.ast.value_expr, space); // value1553 try renderExpression(r, field.ast.value_expr, space); // value
1562 ais.popIndent();1554 ais.popIndent();
1563 }1555 }
...@@ -1614,9 +1606,12 @@ fn renderBuiltinCall(...@@ -1614,9 +1606,12 @@ fn renderBuiltinCall(
1614 if (token_tags[first_param_token] == .multiline_string_literal_line or1606 if (token_tags[first_param_token] == .multiline_string_literal_line or
1615 hasSameLineComment(tree, first_param_token - 1))1607 hasSameLineComment(tree, first_param_token - 1))
1616 {1608 {
1617 ais.pushIndentOneShot();1609 try ais.pushIndent(.normal);
1610 try renderExpression(r, param_node, .none);
1611 ais.popIndent();
1612 } else {
1613 try renderExpression(r, param_node, .none);
1618 }1614 }
1619 try renderExpression(r, param_node, .none);
16201615
1621 if (i + 1 < params.len) {1616 if (i + 1 < params.len) {
1622 const comma_token = tree.lastToken(param_node) + 1;1617 const comma_token = tree.lastToken(param_node) + 1;
...@@ -1626,7 +1621,7 @@ fn renderBuiltinCall(...@@ -1626,7 +1621,7 @@ fn renderBuiltinCall(
1626 return renderToken(r, after_last_param_token, space); // )1621 return renderToken(r, after_last_param_token, space); // )
1627 } else {1622 } else {
1628 // Render one param per line.1623 // Render one param per line.
1629 ais.pushIndent();1624 try ais.pushIndent(.normal);
1630 try renderToken(r, builtin_token + 1, Space.newline); // (1625 try renderToken(r, builtin_token + 1, Space.newline); // (
16311626
1632 for (params) |param_node| {1627 for (params) |param_node| {
...@@ -1752,7 +1747,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi...@@ -1752,7 +1747,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi
1752 }1747 }
1753 } else {1748 } else {
1754 // One param per line.1749 // One param per line.
1755 ais.pushIndent();1750 try ais.pushIndent(.normal);
1756 try renderToken(r, lparen, .newline); // (1751 try renderToken(r, lparen, .newline); // (
17571752
1758 var param_i: usize = 0;1753 var param_i: usize = 0;
...@@ -1933,7 +1928,7 @@ fn renderBlock(...@@ -1933,7 +1928,7 @@ fn renderBlock(
1933 try renderIdentifier(r, lbrace - 2, .none, .eagerly_unquote); // identifier1928 try renderIdentifier(r, lbrace - 2, .none, .eagerly_unquote); // identifier
1934 try renderToken(r, lbrace - 1, .space); // :1929 try renderToken(r, lbrace - 1, .space); // :
1935 }1930 }
1936 ais.pushIndentNextLine();1931 try ais.pushIndent(.normal);
1937 if (statements.len == 0) {1932 if (statements.len == 0) {
1938 try renderToken(r, lbrace, .none);1933 try renderToken(r, lbrace, .none);
1939 ais.popIndent();1934 ais.popIndent();
...@@ -1986,7 +1981,7 @@ fn renderStructInit(...@@ -1986,7 +1981,7 @@ fn renderStructInit(
1986 try renderExpression(r, struct_init.ast.type_expr, .none); // T1981 try renderExpression(r, struct_init.ast.type_expr, .none); // T
1987 }1982 }
1988 if (struct_init.ast.fields.len == 0) {1983 if (struct_init.ast.fields.len == 0) {
1989 ais.pushIndentNextLine();1984 try ais.pushIndent(.normal);
1990 try renderToken(r, struct_init.ast.lbrace, .none); // lbrace1985 try renderToken(r, struct_init.ast.lbrace, .none); // lbrace
1991 ais.popIndent();1986 ais.popIndent();
1992 return renderToken(r, struct_init.ast.lbrace + 1, space); // rbrace1987 return renderToken(r, struct_init.ast.lbrace + 1, space); // rbrace
...@@ -1996,7 +1991,7 @@ fn renderStructInit(...@@ -1996,7 +1991,7 @@ fn renderStructInit(
1996 const trailing_comma = token_tags[rbrace - 1] == .comma;1991 const trailing_comma = token_tags[rbrace - 1] == .comma;
1997 if (trailing_comma or hasComment(tree, struct_init.ast.lbrace, rbrace)) {1992 if (trailing_comma or hasComment(tree, struct_init.ast.lbrace, rbrace)) {
1998 // Render one field init per line.1993 // Render one field init per line.
1999 ais.pushIndentNextLine();1994 try ais.pushIndent(.normal);
2000 try renderToken(r, struct_init.ast.lbrace, .newline);1995 try renderToken(r, struct_init.ast.lbrace, .newline);
20011996
2002 try renderToken(r, struct_init.ast.lbrace + 1, .none); // .1997 try renderToken(r, struct_init.ast.lbrace + 1, .none); // .
...@@ -2054,7 +2049,7 @@ fn renderArrayInit(...@@ -2054,7 +2049,7 @@ fn renderArrayInit(
2054 }2049 }
20552050
2056 if (array_init.ast.elements.len == 0) {2051 if (array_init.ast.elements.len == 0) {
2057 ais.pushIndentNextLine();2052 try ais.pushIndent(.normal);
2058 try renderToken(r, array_init.ast.lbrace, .none); // lbrace2053 try renderToken(r, array_init.ast.lbrace, .none); // lbrace
2059 ais.popIndent();2054 ais.popIndent();
2060 return renderToken(r, array_init.ast.lbrace + 1, space); // rbrace2055 return renderToken(r, array_init.ast.lbrace + 1, space); // rbrace
...@@ -2096,7 +2091,7 @@ fn renderArrayInit(...@@ -2096,7 +2091,7 @@ fn renderArrayInit(
2096 return renderToken(r, last_elem_token + 1, space); // rbrace2091 return renderToken(r, last_elem_token + 1, space); // rbrace
2097 }2092 }
20982093
2099 ais.pushIndentNextLine();2094 try ais.pushIndent(.normal);
2100 try renderToken(r, array_init.ast.lbrace, .newline);2095 try renderToken(r, array_init.ast.lbrace, .newline);
21012096
2102 var expr_index: usize = 0;2097 var expr_index: usize = 0;
...@@ -2149,7 +2144,8 @@ fn renderArrayInit(...@@ -2149,7 +2144,8 @@ fn renderArrayInit(
2149 const sub_expr_buffer_starts = try gpa.alloc(usize, section_exprs.len + 1);2144 const sub_expr_buffer_starts = try gpa.alloc(usize, section_exprs.len + 1);
2150 defer gpa.free(sub_expr_buffer_starts);2145 defer gpa.free(sub_expr_buffer_starts);
21512146
2152 var auto_indenting_stream = Ais.init(sub_expr_buffer, indent_delta);2147 var auto_indenting_stream = Ais.init(&sub_expr_buffer, indent_delta);
2148 defer auto_indenting_stream.deinit();
2153 var sub_render: Render = .{2149 var sub_render: Render = .{
2154 .gpa = r.gpa,2150 .gpa = r.gpa,
2155 .ais = &auto_indenting_stream,2151 .ais = &auto_indenting_stream,
...@@ -2312,7 +2308,7 @@ fn renderContainerDecl(...@@ -2312,7 +2308,7 @@ fn renderContainerDecl(
23122308
2313 const rbrace = tree.lastToken(container_decl_node);2309 const rbrace = tree.lastToken(container_decl_node);
2314 if (container_decl.ast.members.len == 0) {2310 if (container_decl.ast.members.len == 0) {
2315 ais.pushIndentNextLine();2311 try ais.pushIndent(.normal);
2316 if (token_tags[lbrace + 1] == .container_doc_comment) {2312 if (token_tags[lbrace + 1] == .container_doc_comment) {
2317 try renderToken(r, lbrace, .newline); // lbrace2313 try renderToken(r, lbrace, .newline); // lbrace
2318 try renderContainerDocComments(r, lbrace + 1);2314 try renderContainerDocComments(r, lbrace + 1);
...@@ -2354,7 +2350,7 @@ fn renderContainerDecl(...@@ -2354,7 +2350,7 @@ fn renderContainerDecl(
2354 }2350 }
23552351
2356 // One member per line.2352 // One member per line.
2357 ais.pushIndentNextLine();2353 try ais.pushIndent(.normal);
2358 try renderToken(r, lbrace, .newline); // lbrace2354 try renderToken(r, lbrace, .newline); // lbrace
2359 if (token_tags[lbrace + 1] == .container_doc_comment) {2355 if (token_tags[lbrace + 1] == .container_doc_comment) {
2360 try renderContainerDocComments(r, lbrace + 1);2356 try renderContainerDocComments(r, lbrace + 1);
...@@ -2395,7 +2391,7 @@ fn renderAsm(...@@ -2395,7 +2391,7 @@ fn renderAsm(
2395 }2391 }
23962392
2397 if (asm_node.ast.items.len == 0) {2393 if (asm_node.ast.items.len == 0) {
2398 ais.pushIndent();2394 try ais.pushIndent(.normal);
2399 if (asm_node.first_clobber) |first_clobber| {2395 if (asm_node.first_clobber) |first_clobber| {
2400 // asm ("foo" ::: "a", "b")2396 // asm ("foo" ::: "a", "b")
2401 // asm ("foo" ::: "a", "b",)2397 // asm ("foo" ::: "a", "b",)
...@@ -2433,7 +2429,7 @@ fn renderAsm(...@@ -2433,7 +2429,7 @@ fn renderAsm(
2433 }2429 }
2434 }2430 }
24352431
2436 ais.pushIndent();2432 try ais.pushIndent(.normal);
2437 try renderExpression(r, asm_node.ast.template, .newline);2433 try renderExpression(r, asm_node.ast.template, .newline);
2438 ais.setIndentDelta(asm_indent_delta);2434 ais.setIndentDelta(asm_indent_delta);
2439 const colon1 = tree.lastToken(asm_node.ast.template) + 1;2435 const colon1 = tree.lastToken(asm_node.ast.template) + 1;
...@@ -2444,7 +2440,7 @@ fn renderAsm(...@@ -2444,7 +2440,7 @@ fn renderAsm(
2444 } else colon2: {2440 } else colon2: {
2445 try renderToken(r, colon1, .space); // :2441 try renderToken(r, colon1, .space); // :
24462442
2447 ais.pushIndent();2443 try ais.pushIndent(.normal);
2448 for (asm_node.outputs, 0..) |asm_output, i| {2444 for (asm_node.outputs, 0..) |asm_output, i| {
2449 if (i + 1 < asm_node.outputs.len) {2445 if (i + 1 < asm_node.outputs.len) {
2450 const next_asm_output = asm_node.outputs[i + 1];2446 const next_asm_output = asm_node.outputs[i + 1];
...@@ -2476,7 +2472,7 @@ fn renderAsm(...@@ -2476,7 +2472,7 @@ fn renderAsm(
2476 break :colon3 colon2 + 1;2472 break :colon3 colon2 + 1;
2477 } else colon3: {2473 } else colon3: {
2478 try renderToken(r, colon2, .space); // :2474 try renderToken(r, colon2, .space); // :
2479 ais.pushIndent();2475 try ais.pushIndent(.normal);
2480 for (asm_node.inputs, 0..) |asm_input, i| {2476 for (asm_node.inputs, 0..) |asm_input, i| {
2481 if (i + 1 < asm_node.inputs.len) {2477 if (i + 1 < asm_node.inputs.len) {
2482 const next_asm_input = asm_node.inputs[i + 1];2478 const next_asm_input = asm_node.inputs[i + 1];
...@@ -2558,7 +2554,7 @@ fn renderParamList(...@@ -2558,7 +2554,7 @@ fn renderParamList(
2558 const token_tags = tree.tokens.items(.tag);2554 const token_tags = tree.tokens.items(.tag);
25592555
2560 if (params.len == 0) {2556 if (params.len == 0) {
2561 ais.pushIndentNextLine();2557 try ais.pushIndent(.normal);
2562 try renderToken(r, lparen, .none);2558 try renderToken(r, lparen, .none);
2563 ais.popIndent();2559 ais.popIndent();
2564 return renderToken(r, lparen + 1, space); // )2560 return renderToken(r, lparen + 1, space); // )
...@@ -2567,22 +2563,15 @@ fn renderParamList(...@@ -2567,22 +2563,15 @@ fn renderParamList(
2567 const last_param = params[params.len - 1];2563 const last_param = params[params.len - 1];
2568 const after_last_param_tok = tree.lastToken(last_param) + 1;2564 const after_last_param_tok = tree.lastToken(last_param) + 1;
2569 if (token_tags[after_last_param_tok] == .comma) {2565 if (token_tags[after_last_param_tok] == .comma) {
2570 ais.pushIndentNextLine();2566 try ais.pushIndent(.normal);
2571 try renderToken(r, lparen, .newline); // (2567 try renderToken(r, lparen, .newline); // (
2572 for (params, 0..) |param_node, i| {2568 for (params, 0..) |param_node, i| {
2573 if (i + 1 < params.len) {2569 if (i + 1 < params.len) {
2574 try renderExpression(r, param_node, .none);2570 try renderExpression(r, param_node, .none);
25752571
2576 // Unindent the comma for multiline string literals.
2577 const is_multiline_string =
2578 token_tags[tree.firstToken(param_node)] == .multiline_string_literal_line;
2579 if (is_multiline_string) ais.popIndent();
2580
2581 const comma = tree.lastToken(param_node) + 1;2572 const comma = tree.lastToken(param_node) + 1;
2582 try renderToken(r, comma, .newline); // ,2573 try renderToken(r, comma, .newline); // ,
25832574
2584 if (is_multiline_string) ais.pushIndent();
2585
2586 try renderExtraNewline(r, params[i + 1]);2575 try renderExtraNewline(r, params[i + 1]);
2587 } else {2576 } else {
2588 try renderExpression(r, param_node, .comma);2577 try renderExpression(r, param_node, .comma);
...@@ -2599,9 +2588,12 @@ fn renderParamList(...@@ -2599,9 +2588,12 @@ fn renderParamList(
2599 if (token_tags[first_param_token] == .multiline_string_literal_line or2588 if (token_tags[first_param_token] == .multiline_string_literal_line or
2600 hasSameLineComment(tree, first_param_token - 1))2589 hasSameLineComment(tree, first_param_token - 1))
2601 {2590 {
2602 ais.pushIndentOneShot();2591 try ais.pushIndent(.normal);
2592 try renderExpression(r, param_node, .none);
2593 ais.popIndent();
2594 } else {
2595 try renderExpression(r, param_node, .none);
2603 }2596 }
2604 try renderExpression(r, param_node, .none);
26052597
2606 if (i + 1 < params.len) {2598 if (i + 1 < params.len) {
2607 const comma = tree.lastToken(param_node) + 1;2599 const comma = tree.lastToken(param_node) + 1;
...@@ -2615,66 +2607,6 @@ fn renderParamList(...@@ -2615,66 +2607,6 @@ fn renderParamList(
2615 return renderToken(r, after_last_param_tok, space); // )2607 return renderToken(r, after_last_param_tok, space); // )
2616}2608}
26172609
2618/// Renders the given expression indented, popping the indent before rendering
2619/// any following line comments
2620fn renderExpressionIndented(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
2621 const tree = r.tree;
2622 const ais = r.ais;
2623 const token_starts = tree.tokens.items(.start);
2624 const token_tags = tree.tokens.items(.tag);
2625
2626 ais.pushIndent();
2627
2628 var last_token = tree.lastToken(node);
2629 const punctuation = switch (space) {
2630 .none, .space, .newline, .skip => false,
2631 .comma => true,
2632 .comma_space => token_tags[last_token + 1] == .comma,
2633 .semicolon => token_tags[last_token + 1] == .semicolon,
2634 };
2635
2636 try renderExpression(r, node, if (punctuation) .none else .skip);
2637
2638 switch (space) {
2639 .none, .space, .newline, .skip => {},
2640 .comma => {
2641 if (token_tags[last_token + 1] == .comma) {
2642 try renderToken(r, last_token + 1, .skip);
2643 last_token += 1;
2644 } else {
2645 try ais.writer().writeByte(',');
2646 }
2647 },
2648 .comma_space => if (token_tags[last_token + 1] == .comma) {
2649 try renderToken(r, last_token + 1, .skip);
2650 last_token += 1;
2651 },
2652 .semicolon => if (token_tags[last_token + 1] == .semicolon) {
2653 try renderToken(r, last_token + 1, .skip);
2654 last_token += 1;
2655 },
2656 }
2657
2658 ais.popIndent();
2659
2660 if (space == .skip) return;
2661
2662 const comment_start = token_starts[last_token] + tokenSliceForRender(tree, last_token).len;
2663 const comment = try renderComments(r, comment_start, token_starts[last_token + 1]);
2664
2665 if (!comment) switch (space) {
2666 .none => {},
2667 .space,
2668 .comma_space,
2669 => try ais.writer().writeByte(' '),
2670 .newline,
2671 .comma,
2672 .semicolon,
2673 => try ais.insertNewline(),
2674 .skip => unreachable,
2675 };
2676}
2677
2678/// Render an expression, and the comma that follows it, if it is present in the source.2610/// Render an expression, and the comma that follows it, if it is present in the source.
2679/// If a comma is present, and `space` is `Space.comma`, render only a single comma.2611/// If a comma is present, and `space` is `Space.comma`, render only a single comma.
2680fn renderExpressionComma(r: *Render, node: Ast.Node.Index, space: Space) Error!void {2612fn renderExpressionComma(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
...@@ -3315,6 +3247,14 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {...@@ -3315,6 +3247,14 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
3315 pub const WriteError = UnderlyingWriter.Error;3247 pub const WriteError = UnderlyingWriter.Error;
3316 pub const Writer = std.io.Writer(*Self, WriteError, write);3248 pub const Writer = std.io.Writer(*Self, WriteError, write);
33173249
3250 pub const IndentType = enum {
3251 normal,
3252 };
3253 const StackElem = struct {
3254 indent_type: IndentType,
3255 realized: bool,
3256 };
3257
3318 underlying_writer: UnderlyingWriter,3258 underlying_writer: UnderlyingWriter,
33193259
3320 /// Offset into the source at which formatting has been disabled with3260 /// Offset into the source at which formatting has been disabled with
...@@ -3327,21 +3267,24 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {...@@ -3327,21 +3267,24 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
33273267
3328 indent_count: usize = 0,3268 indent_count: usize = 0,
3329 indent_delta: usize,3269 indent_delta: usize,
3270 indent_stack: std.ArrayList(StackElem),
3271 disable_indent_committing: usize = 0,
3330 current_line_empty: bool = true,3272 current_line_empty: bool = true,
3331 /// automatically popped when applied
3332 indent_one_shot_count: usize = 0,
3333 /// the most recently applied indent3273 /// the most recently applied indent
3334 applied_indent: usize = 0,3274 applied_indent: usize = 0,
3335 /// not used until the next line
3336 indent_next_line: usize = 0,
33373275
3338 pub fn init(buffer: *std.ArrayList(u8), indent_delta: usize) Self {3276 pub fn init(buffer: *std.ArrayList(u8), indent_delta_: usize) Self {
3339 return .{3277 return .{
3340 .underlying_writer = buffer.writer(),3278 .underlying_writer = buffer.writer(),
3341 .indent_delta = indent_delta,3279 .indent_delta = indent_delta_,
3280 .indent_stack = std.ArrayList(StackElem).init(buffer.allocator),
3342 };3281 };
3343 }3282 }
33443283
3284 pub fn deinit(self: *Self) void {
3285 self.indent_stack.deinit();
3286 }
3287
3345 pub fn writer(self: *Self) Writer {3288 pub fn writer(self: *Self) Writer {
3346 return .{ .context = self };3289 return .{ .context = self };
3347 }3290 }
...@@ -3385,7 +3328,23 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {...@@ -3385,7 +3328,23 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
33853328
3386 fn resetLine(self: *Self) void {3329 fn resetLine(self: *Self) void {
3387 self.current_line_empty = true;3330 self.current_line_empty = true;
3388 self.indent_next_line = 0;3331 if (self.disable_indent_committing > 0) return;
3332 if (self.indent_stack.items.len > 0) {
3333 // Only realize last pushed indent
3334 if (!self.indent_stack.items[self.indent_stack.items.len - 1].realized) {
3335 self.indent_stack.items[self.indent_stack.items.len - 1].realized = true;
3336 self.indent_count += 1;
3337 }
3338 }
3339 }
3340
3341 pub fn disableIndentCommitting(self: *Self) void {
3342 self.disable_indent_committing += 1;
3343 }
3344
3345 pub fn enableIndentCommitting(self: *Self) void {
3346 assert(self.disable_indent_committing > 0);
3347 self.disable_indent_committing -= 1;
3389 }3348 }
33903349
3391 /// Insert a newline unless the current line is blank3350 /// Insert a newline unless the current line is blank
...@@ -3397,36 +3356,19 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {...@@ -3397,36 +3356,19 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
3397 /// Push default indentation3356 /// Push default indentation
3398 /// Doesn't actually write any indentation.3357 /// Doesn't actually write any indentation.
3399 /// Just primes the stream to be able to write the correct indentation if it needs to.3358 /// Just primes the stream to be able to write the correct indentation if it needs to.
3400 pub fn pushIndent(self: *Self) void {3359 pub fn pushIndent(self: *Self, indent_type: IndentType) !void {
3401 self.indent_count += 1;3360 try self.indent_stack.append(.{ .indent_type = indent_type, .realized = false });
3402 }
3403
3404 /// Push an indent that is automatically popped after being applied
3405 pub fn pushIndentOneShot(self: *Self) void {
3406 self.indent_one_shot_count += 1;
3407 self.pushIndent();
3408 }
3409
3410 /// Turns all one-shot indents into regular indents
3411 /// Returns number of indents that must now be manually popped
3412 pub fn lockOneShotIndent(self: *Self) usize {
3413 const locked_count = self.indent_one_shot_count;
3414 self.indent_one_shot_count = 0;
3415 return locked_count;
3416 }
3417
3418 /// Push an indent that should not take effect until the next line
3419 pub fn pushIndentNextLine(self: *Self) void {
3420 self.indent_next_line += 1;
3421 self.pushIndent();
3422 }3361 }
34233362
3424 pub fn popIndent(self: *Self) void {3363 pub fn popIndent(self: *Self) void {
3425 assert(self.indent_count != 0);3364 if (self.indent_stack.pop().realized) {
3426 self.indent_count -= 1;3365 assert(self.indent_count > 0);
3366 self.indent_count -= 1;
3367 }
3368 }
34273369
3428 if (self.indent_next_line > 0)3370 pub fn indentStackEmpty(self: *Self) bool {
3429 self.indent_next_line -= 1;3371 return self.indent_stack.items.len == 0;
3430 }3372 }
34313373
3432 /// Writes ' ' bytes if the current line is empty3374 /// Writes ' ' bytes if the current line is empty
...@@ -3438,9 +3380,6 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {...@@ -3438,9 +3380,6 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
3438 }3380 }
3439 self.applied_indent = current_indent;3381 self.applied_indent = current_indent;
3440 }3382 }
3441
3442 self.indent_count -= self.indent_one_shot_count;
3443 self.indent_one_shot_count = 0;
3444 self.current_line_empty = false;3383 self.current_line_empty = false;
3445 }3384 }
34463385
...@@ -3451,12 +3390,7 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {...@@ -3451,12 +3390,7 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
3451 }3390 }
34523391
3453 fn currentIndent(self: *Self) usize {3392 fn currentIndent(self: *Self) usize {
3454 var indent_current: usize = 0;3393 return self.indent_count * self.indent_delta;
3455 if (self.indent_count > 0) {
3456 const indent_count = self.indent_count - self.indent_next_line;
3457 indent_current = indent_count * self.indent_delta;
3458 }
3459 return indent_current;
3460 }3394 }
3461 };3395 };
3462}3396}