| ... | @@ -24,51 +24,21 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void { | ... | @@ -24,51 +24,21 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void { |
| 24 | const ais = &auto_indenting_stream; | 24 | const ais = &auto_indenting_stream; |
| 25 | | 25 | |
| 26 | // Render all the line comments at the beginning of the file. | 26 | // Render all the line comments at the beginning of the file. |
| 27 | const src_start: usize = if (mem.startsWith(u8, tree.source, "\xEF\xBB\xBF")) 3 else 0; | | |
| 28 | const comment_end_loc: usize = tree.tokens.items(.start)[0]; | 27 | const comment_end_loc: usize = tree.tokens.items(.start)[0]; |
| 29 | _ = try renderCommentsAndNewlines(ais, tree, src_start, comment_end_loc); | 28 | _ = try renderComments(ais, tree, 0, comment_end_loc); |
| 30 | | 29 | |
| 31 | for (tree.rootDecls()) |decl| { | 30 | try renderMembers(ais, tree, tree.rootDecls()); |
| 32 | try renderMember(ais, tree, decl, .newline); | | |
| 33 | } | | |
| 34 | } | 31 | } |
| 35 | | 32 | |
| 36 | /// Assumes that start is the first byte past the previous token and | 33 | /// Render all members in the given slice, keeping empty lines where appropriate |
| 37 | /// that end is the last byte before the next token. | 34 | fn renderMembers(ais: *Ais, tree: ast.Tree, members: []const ast.Node.Index) Error!void { |
| 38 | fn renderCommentsAndNewlines(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!bool { | 35 | if (members.len == 0) return; |
| 39 | var index: usize = start; | 36 | //try renderExtraNewline(ais, tree, members[0]); |
| 40 | while (mem.indexOf(u8, tree.source[index..end], "//")) |offset| { | 37 | try renderMember(ais, tree, members[0], .newline); |
| 41 | const comment_start = index + offset; | 38 | for (members[1..]) |member| { |
| 42 | const newline = comment_start + | 39 | try renderExtraNewline(ais, tree, member); |
| 43 | mem.indexOfScalar(u8, tree.source[comment_start..end], '\n').?; | 40 | try renderMember(ais, tree, member, .newline); |
| 44 | const untrimmed_comment = tree.source[comment_start..newline]; | | |
| 45 | const trimmed_comment = mem.trimRight(u8, untrimmed_comment, " \r\t"); | | |
| 46 | | | |
| 47 | // Leave up to one empty line before the comment | | |
| 48 | if (index == start and mem.containsAtLeast(u8, tree.source[index..comment_start], 2, "\n")) { | | |
| 49 | try ais.insertNewline(); | | |
| 50 | try ais.insertNewline(); | | |
| 51 | } else if (mem.indexOfScalar(u8, tree.source[index..comment_start], '\n') != null) { | | |
| 52 | // Respect the newline directly before the comment. This allows an | | |
| 53 | // empty line between comments | | |
| 54 | try ais.insertNewline(); | | |
| 55 | } else if (index == start and start != 0) { | | |
| 56 | // If the comment is on the same line as the token before it, | | |
| 57 | // prefix it with a single space | | |
| 58 | try ais.writer().writeByte(' '); | | |
| 59 | } | | |
| 60 | | | |
| 61 | try ais.writer().print("{s}\n", .{trimmed_comment}); | | |
| 62 | index = newline + 1; | | |
| 63 | } | | |
| 64 | | | |
| 65 | // Leave up to one empty line if present in the source | | |
| 66 | if (index > start) index -= 1; | | |
| 67 | if (end != tree.source.len and mem.containsAtLeast(u8, tree.source[index..end], 2, "\n")) { | | |
| 68 | try ais.insertNewline(); | | |
| 69 | } | 41 | } |
| 70 | | | |
| 71 | return index != start; | | |
| 72 | } | 42 | } |
| 73 | | 43 | |
| 74 | fn renderMember(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void { | 44 | fn renderMember(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void { |
| ... | @@ -157,6 +127,16 @@ fn renderMember(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) E | ... | @@ -157,6 +127,16 @@ fn renderMember(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) E |
| 157 | } | 127 | } |
| 158 | } | 128 | } |
| 159 | | 129 | |
| | 130 | /// Render all expressions in the slice, keeping empty lines where appropriate |
| | 131 | fn renderExpressions(ais: *Ais, tree: ast.Tree, expressions: []const ast.Node.Index, space: Space) Error!void { |
| | 132 | if (expressions.len == 0) return; |
| | 133 | try renderExpression(ais, tree, expressions[0], space); |
| | 134 | for (expressions[1..]) |expression| { |
| | 135 | try renderExtraNewline(ais, tree, expression); |
| | 136 | try renderExpression(ais, tree, expression, space); |
| | 137 | } |
| | 138 | } |
| | 139 | |
| 160 | fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void { | 140 | fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void { |
| 161 | const token_tags = tree.tokens.items(.tag); | 141 | const token_tags = tree.tokens.items(.tag); |
| 162 | const main_tokens = tree.nodes.items(.main_token); | 142 | const main_tokens = tree.nodes.items(.main_token); |
| ... | @@ -501,7 +481,6 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac | ... | @@ -501,7 +481,6 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac |
| 501 | .tagged_union_enum_tag_comma, | 481 | .tagged_union_enum_tag_comma, |
| 502 | => return renderContainerDecl(ais, tree, tree.taggedUnionEnumTag(node), space), | 482 | => return renderContainerDecl(ais, tree, tree.taggedUnionEnumTag(node), space), |
| 503 | | 483 | |
| 504 | // TODO: handle comments properly | | |
| 505 | .error_set_decl => { | 484 | .error_set_decl => { |
| 506 | const error_token = main_tokens[node]; | 485 | const error_token = main_tokens[node]; |
| 507 | const lbrace = error_token + 1; | 486 | const lbrace = error_token + 1; |
| ... | @@ -521,10 +500,11 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac | ... | @@ -521,10 +500,11 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac |
| 521 | return renderToken(ais, tree, rbrace, space); | 500 | return renderToken(ais, tree, rbrace, space); |
| 522 | } else if (token_tags[rbrace - 1] == .comma) { | 501 | } else if (token_tags[rbrace - 1] == .comma) { |
| 523 | // There is a trailing comma so render each member on a new line. | 502 | // There is a trailing comma so render each member on a new line. |
| | 503 | ais.pushIndentNextLine(); |
| 524 | try renderToken(ais, tree, lbrace, .newline); | 504 | try renderToken(ais, tree, lbrace, .newline); |
| 525 | ais.pushIndent(); | | |
| 526 | var i = lbrace + 1; | 505 | var i = lbrace + 1; |
| 527 | while (i < rbrace) : (i += 1) { | 506 | while (i < rbrace) : (i += 1) { |
| | 507 | if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i); |
| 528 | switch (token_tags[i]) { | 508 | switch (token_tags[i]) { |
| 529 | .doc_comment => try renderToken(ais, tree, i, .newline), | 509 | .doc_comment => try renderToken(ais, tree, i, .newline), |
| 530 | .identifier => try renderToken(ais, tree, i, .comma), | 510 | .identifier => try renderToken(ais, tree, i, .comma), |
| ... | @@ -603,14 +583,12 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac | ... | @@ -603,14 +583,12 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac |
| 603 | try renderExpression(ais, tree, condition, .none); // condtion expression | 583 | try renderExpression(ais, tree, condition, .none); // condtion expression |
| 604 | try renderToken(ais, tree, rparen, .space); // rparen | 584 | try renderToken(ais, tree, rparen, .space); // rparen |
| 605 | | 585 | |
| | 586 | ais.pushIndentNextLine(); |
| 606 | if (cases.len == 0) { | 587 | if (cases.len == 0) { |
| 607 | try renderToken(ais, tree, rparen + 1, .none); // lbrace | 588 | try renderToken(ais, tree, rparen + 1, .none); // lbrace |
| 608 | return renderToken(ais, tree, rparen + 2, space); // rbrace | 589 | } else { |
| 609 | } | 590 | try renderToken(ais, tree, rparen + 1, .newline); // lbrace |
| 610 | ais.pushIndentNextLine(); | 591 | try renderExpressions(ais, tree, cases, .comma); |
| 611 | try renderToken(ais, tree, rparen + 1, .newline); // lbrace | | |
| 612 | for (cases) |case| { | | |
| 613 | try renderExpression(ais, tree, case, .comma); | | |
| 614 | } | 592 | } |
| 615 | ais.popIndent(); | 593 | ais.popIndent(); |
| 616 | return renderToken(ais, tree, tree.lastToken(node), space); // rbrace | 594 | return renderToken(ais, tree, tree.lastToken(node), space); // rbrace |
| ... | @@ -1439,9 +1417,7 @@ fn renderSwitchCase( | ... | @@ -1439,9 +1417,7 @@ fn renderSwitchCase( |
| 1439 | try renderExpression(ais, tree, switch_case.ast.values[0], .space); | 1417 | try renderExpression(ais, tree, switch_case.ast.values[0], .space); |
| 1440 | } else if (trailing_comma) { | 1418 | } else if (trailing_comma) { |
| 1441 | // Render each value on a new line | 1419 | // Render each value on a new line |
| 1442 | for (switch_case.ast.values) |value_expr| { | 1420 | try renderExpressions(ais, tree, switch_case.ast.values, .comma); |
| 1443 | try renderExpression(ais, tree, value_expr, .comma); | | |
| 1444 | } | | |
| 1445 | } else { | 1421 | } else { |
| 1446 | // Render on one line | 1422 | // Render on one line |
| 1447 | for (switch_case.ast.values) |value_expr| { | 1423 | for (switch_case.ast.values) |value_expr| { |
| ... | @@ -1486,22 +1462,20 @@ fn renderBlock( | ... | @@ -1486,22 +1462,20 @@ fn renderBlock( |
| 1486 | try renderToken(ais, tree, lbrace - 1, .space); | 1462 | try renderToken(ais, tree, lbrace - 1, .space); |
| 1487 | } | 1463 | } |
| 1488 | | 1464 | |
| | 1465 | ais.pushIndentNextLine(); |
| 1489 | if (statements.len == 0) { | 1466 | if (statements.len == 0) { |
| 1490 | ais.pushIndentNextLine(); | | |
| 1491 | try renderToken(ais, tree, lbrace, .none); | 1467 | try renderToken(ais, tree, lbrace, .none); |
| 1492 | ais.popIndent(); | 1468 | } else { |
| 1493 | return renderToken(ais, tree, lbrace + 1, space); // rbrace | 1469 | try renderToken(ais, tree, lbrace, .newline); |
| 1494 | } | 1470 | for (statements) |stmt, i| { |
| 1495 | | 1471 | if (i != 0) try renderExtraNewline(ais, tree, stmt); |
| 1496 | ais.pushIndentNextLine(); | 1472 | switch (node_tags[stmt]) { |
| 1497 | try renderToken(ais, tree, lbrace, .newline); | 1473 | .global_var_decl => try renderVarDecl(ais, tree, tree.globalVarDecl(stmt)), |
| 1498 | for (statements) |stmt, i| { | 1474 | .local_var_decl => try renderVarDecl(ais, tree, tree.localVarDecl(stmt)), |
| 1499 | switch (node_tags[stmt]) { | 1475 | .simple_var_decl => try renderVarDecl(ais, tree, tree.simpleVarDecl(stmt)), |
| 1500 | .global_var_decl => try renderVarDecl(ais, tree, tree.globalVarDecl(stmt)), | 1476 | .aligned_var_decl => try renderVarDecl(ais, tree, tree.alignedVarDecl(stmt)), |
| 1501 | .local_var_decl => try renderVarDecl(ais, tree, tree.localVarDecl(stmt)), | 1477 | else => try renderExpression(ais, tree, stmt, .semicolon), |
| 1502 | .simple_var_decl => try renderVarDecl(ais, tree, tree.simpleVarDecl(stmt)), | 1478 | } |
| 1503 | .aligned_var_decl => try renderVarDecl(ais, tree, tree.alignedVarDecl(stmt)), | | |
| 1504 | else => try renderExpression(ais, tree, stmt, .semicolon), | | |
| 1505 | } | 1479 | } |
| 1506 | } | 1480 | } |
| 1507 | ais.popIndent(); | 1481 | ais.popIndent(); |
| ... | @@ -1530,11 +1504,17 @@ fn renderStructInit( | ... | @@ -1530,11 +1504,17 @@ fn renderStructInit( |
| 1530 | const last_field_token = tree.lastToken(last_field); | 1504 | const last_field_token = tree.lastToken(last_field); |
| 1531 | if (token_tags[last_field_token + 1] == .comma) { | 1505 | if (token_tags[last_field_token + 1] == .comma) { |
| 1532 | // Render one field init per line. | 1506 | // Render one field init per line. |
| 1533 | ais.pushIndent(); | 1507 | ais.pushIndentNextLine(); |
| 1534 | try renderToken(ais, tree, struct_init.ast.lbrace, .newline); | 1508 | try renderToken(ais, tree, struct_init.ast.lbrace, .newline); |
| 1535 | | 1509 | |
| 1536 | for (struct_init.ast.fields) |field_init| { | 1510 | try renderToken(ais, tree, struct_init.ast.lbrace + 1, .none); // . |
| | 1511 | try renderToken(ais, tree, struct_init.ast.lbrace + 2, .space); // name |
| | 1512 | try renderToken(ais, tree, struct_init.ast.lbrace + 3, .space); // = |
| | 1513 | try renderExpression(ais, tree, struct_init.ast.fields[0], .comma); |
| | 1514 | |
| | 1515 | for (struct_init.ast.fields[1..]) |field_init| { |
| 1537 | const init_token = tree.firstToken(field_init); | 1516 | const init_token = tree.firstToken(field_init); |
| | 1517 | try renderExtraNewlineToken(ais, tree, init_token - 3); |
| 1538 | try renderToken(ais, tree, init_token - 3, .none); // . | 1518 | try renderToken(ais, tree, init_token - 3, .none); // . |
| 1539 | try renderToken(ais, tree, init_token - 2, .space); // name | 1519 | try renderToken(ais, tree, init_token - 2, .space); // name |
| 1540 | try renderToken(ais, tree, init_token - 1, .space); // = | 1520 | try renderToken(ais, tree, init_token - 1, .space); // = |
| ... | @@ -1573,20 +1553,18 @@ fn renderArrayInit( | ... | @@ -1573,20 +1553,18 @@ fn renderArrayInit( |
| 1573 | try renderExpression(ais, tree, array_init.ast.type_expr, .none); // T | 1553 | try renderExpression(ais, tree, array_init.ast.type_expr, .none); // T |
| 1574 | } | 1554 | } |
| 1575 | if (array_init.ast.elements.len == 0) { | 1555 | if (array_init.ast.elements.len == 0) { |
| | 1556 | ais.pushIndentNextLine(); |
| 1576 | try renderToken(ais, tree, array_init.ast.lbrace, .none); // lbrace | 1557 | try renderToken(ais, tree, array_init.ast.lbrace, .none); // lbrace |
| | 1558 | ais.popIndent(); |
| 1577 | return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace | 1559 | return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace |
| 1578 | } | 1560 | } |
| 1579 | const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1]; | 1561 | const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1]; |
| 1580 | const last_elem_token = tree.lastToken(last_elem); | 1562 | const last_elem_token = tree.lastToken(last_elem); |
| 1581 | if (token_tags[last_elem_token + 1] == .comma) { | 1563 | if (token_tags[last_elem_token + 1] == .comma) { |
| 1582 | // Render one element per line. | 1564 | // Render one element per line. |
| 1583 | ais.pushIndent(); | 1565 | ais.pushIndentNextLine(); |
| 1584 | try renderToken(ais, tree, array_init.ast.lbrace, .newline); | 1566 | try renderToken(ais, tree, array_init.ast.lbrace, .newline); |
| 1585 | | 1567 | try renderExpressions(ais, tree, array_init.ast.elements, .comma); |
| 1586 | for (array_init.ast.elements) |elem| { | | |
| 1587 | try renderExpression(ais, tree, elem, .comma); | | |
| 1588 | } | | |
| 1589 | | | |
| 1590 | ais.popIndent(); | 1568 | ais.popIndent(); |
| 1591 | return renderToken(ais, tree, last_elem_token + 2, space); // rbrace | 1569 | return renderToken(ais, tree, last_elem_token + 2, space); // rbrace |
| 1592 | } else { | 1570 | } else { |
| ... | @@ -1679,11 +1657,9 @@ fn renderContainerDecl( | ... | @@ -1679,11 +1657,9 @@ fn renderContainerDecl( |
| 1679 | } | 1657 | } |
| 1680 | | 1658 | |
| 1681 | // One member per line. | 1659 | // One member per line. |
| 1682 | ais.pushIndent(); | 1660 | ais.pushIndentNextLine(); |
| 1683 | try renderToken(ais, tree, lbrace, .newline); // lbrace | 1661 | try renderToken(ais, tree, lbrace, .newline); // lbrace |
| 1684 | for (container_decl.ast.members) |member| { | 1662 | try renderMembers(ais, tree, container_decl.ast.members); |
| 1685 | try renderMember(ais, tree, member, .newline); | | |
| 1686 | } | | |
| 1687 | ais.popIndent(); | 1663 | ais.popIndent(); |
| 1688 | | 1664 | |
| 1689 | return renderToken(ais, tree, rbrace, space); // rbrace | 1665 | return renderToken(ais, tree, rbrace, space); // rbrace |
| ... | @@ -1745,6 +1721,7 @@ fn renderAsm( | ... | @@ -1745,6 +1721,7 @@ fn renderAsm( |
| 1745 | | 1721 | |
| 1746 | const comma = tree.firstToken(next_asm_output) - 1; | 1722 | const comma = tree.firstToken(next_asm_output) - 1; |
| 1747 | try renderToken(ais, tree, comma, .newline); // , | 1723 | try renderToken(ais, tree, comma, .newline); // , |
| | 1724 | try renderExtraNewlineToken(ais, tree, tree.firstToken(next_asm_output)); |
| 1748 | } else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) { | 1725 | } else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) { |
| 1749 | try renderAsmOutput(ais, tree, asm_output, .newline); | 1726 | try renderAsmOutput(ais, tree, asm_output, .newline); |
| 1750 | ais.popIndent(); | 1727 | ais.popIndent(); |
| ... | @@ -1776,6 +1753,7 @@ fn renderAsm( | ... | @@ -1776,6 +1753,7 @@ fn renderAsm( |
| 1776 | | 1753 | |
| 1777 | const first_token = tree.firstToken(next_asm_input); | 1754 | const first_token = tree.firstToken(next_asm_input); |
| 1778 | try renderToken(ais, tree, first_token - 1, .newline); // , | 1755 | try renderToken(ais, tree, first_token - 1, .newline); // , |
| | 1756 | try renderExtraNewlineToken(ais, tree, first_token); |
| 1779 | } else if (asm_node.first_clobber == null) { | 1757 | } else if (asm_node.first_clobber == null) { |
| 1780 | try renderAsmInput(ais, tree, asm_input, .newline); | 1758 | try renderAsmInput(ais, tree, asm_input, .newline); |
| 1781 | ais.popIndent(); | 1759 | ais.popIndent(); |
| ... | @@ -1834,7 +1812,9 @@ fn renderCall( | ... | @@ -1834,7 +1812,9 @@ fn renderCall( |
| 1834 | const lparen = call.ast.lparen; | 1812 | const lparen = call.ast.lparen; |
| 1835 | const params = call.ast.params; | 1813 | const params = call.ast.params; |
| 1836 | if (params.len == 0) { | 1814 | if (params.len == 0) { |
| | 1815 | ais.pushIndentNextLine(); |
| 1837 | try renderToken(ais, tree, lparen, .none); | 1816 | try renderToken(ais, tree, lparen, .none); |
| | 1817 | ais.popIndent(); |
| 1838 | return renderToken(ais, tree, lparen + 1, space); // ) | 1818 | return renderToken(ais, tree, lparen + 1, space); // ) |
| 1839 | } | 1819 | } |
| 1840 | | 1820 | |
| ... | @@ -1856,6 +1836,8 @@ fn renderCall( | ... | @@ -1856,6 +1836,8 @@ fn renderCall( |
| 1856 | try renderToken(ais, tree, comma, Space.newline); // , | 1836 | try renderToken(ais, tree, comma, Space.newline); // , |
| 1857 | | 1837 | |
| 1858 | if (is_multiline_string) ais.pushIndent(); | 1838 | if (is_multiline_string) ais.pushIndent(); |
| | 1839 | |
| | 1840 | try renderExtraNewline(ais, tree, params[i + 1]); |
| 1859 | } else { | 1841 | } else { |
| 1860 | try renderExpression(ais, tree, param_node, Space.comma); | 1842 | try renderExpression(ais, tree, param_node, Space.comma); |
| 1861 | } | 1843 | } |
| ... | @@ -1928,44 +1910,100 @@ fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Sp | ... | @@ -1928,44 +1910,100 @@ fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Sp |
| 1928 | const lexeme = tree.tokenSlice(token_index); | 1910 | const lexeme = tree.tokenSlice(token_index); |
| 1929 | try ais.writer().writeAll(lexeme); | 1911 | try ais.writer().writeAll(lexeme); |
| 1930 | | 1912 | |
| | 1913 | if (space == .no_comment) return; |
| | 1914 | |
| | 1915 | const comment = try renderComments(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]); |
| 1931 | switch (space) { | 1916 | switch (space) { |
| 1932 | .no_comment => {}, | 1917 | .none => {}, |
| 1933 | .none => _ = try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]), | 1918 | .space => if (!comment) try ais.writer().writeByte(' '), |
| 1934 | .comma => { | 1919 | .newline => if (!comment) try ais.insertNewline(), |
| 1935 | const comment = try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]); | 1920 | |
| 1936 | if (token_tags[token_index + 1] == .comma) { | 1921 | .comma => if (token_tags[token_index + 1] == .comma) { |
| 1937 | return renderToken(ais, tree, token_index + 1, .newline); | 1922 | try renderToken(ais, tree, token_index + 1, .newline); |
| 1938 | } else if (!comment) { | 1923 | } else if (!comment) { |
| 1939 | return ais.insertNewline(); | 1924 | try ais.insertNewline(); |
| 1940 | } | | |
| 1941 | }, | | |
| 1942 | .comma_space => { | | |
| 1943 | const comment = try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]); | | |
| 1944 | if (token_tags[token_index + 1] == .comma) { | | |
| 1945 | return renderToken(ais, tree, token_index + 1, .space); | | |
| 1946 | } else if (!comment) { | | |
| 1947 | return ais.writer().writeByte(' '); | | |
| 1948 | } | | |
| 1949 | }, | 1925 | }, |
| 1950 | .semicolon => { | 1926 | |
| 1951 | const comment = try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]); | 1927 | .comma_space => if (token_tags[token_index + 1] == .comma) { |
| 1952 | if (token_tags[token_index + 1] == .semicolon) { | 1928 | try renderToken(ais, tree, token_index + 1, .space); |
| 1953 | return renderToken(ais, tree, token_index + 1, .newline); | 1929 | } else if (!comment) { |
| 1954 | } else if (!comment) { | 1930 | try ais.writer().writeByte(' '); |
| 1955 | return ais.insertNewline(); | | |
| 1956 | } | | |
| 1957 | }, | 1931 | }, |
| 1958 | .space => { | 1932 | |
| 1959 | const comment = try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]); | 1933 | .semicolon => if (token_tags[token_index + 1] == .semicolon) { |
| 1960 | if (!comment) { | 1934 | try renderToken(ais, tree, token_index + 1, .newline); |
| 1961 | return ais.writer().writeByte(' '); | 1935 | } else if (!comment) { |
| 1962 | } | 1936 | try ais.insertNewline(); |
| 1963 | }, | 1937 | }, |
| 1964 | .newline => { | 1938 | |
| 1965 | if (!try renderCommentsAndNewlines(ais, tree, token_start + lexeme.len, token_starts[token_index + 1])) { | 1939 | .no_comment => unreachable, |
| | 1940 | } |
| | 1941 | } |
| | 1942 | |
| | 1943 | /// Assumes that start is the first byte past the previous token and |
| | 1944 | /// that end is the last byte before the next token. |
| | 1945 | fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!bool { |
| | 1946 | var index: usize = start; |
| | 1947 | while (mem.indexOf(u8, tree.source[index..end], "//")) |offset| { |
| | 1948 | const comment_start = index + offset; |
| | 1949 | const newline = comment_start + |
| | 1950 | mem.indexOfScalar(u8, tree.source[comment_start..end], '\n').?; |
| | 1951 | const untrimmed_comment = tree.source[comment_start..newline]; |
| | 1952 | const trimmed_comment = mem.trimRight(u8, untrimmed_comment, &std.ascii.spaces); |
| | 1953 | |
| | 1954 | // Don't leave any whitespace at the start of the file |
| | 1955 | if (index != 0) { |
| | 1956 | if (index == start and mem.containsAtLeast(u8, tree.source[index..comment_start], 2, "\n")) { |
| | 1957 | // Leave up to one empty line before the first comment |
| | 1958 | try ais.insertNewline(); |
| 1966 | try ais.insertNewline(); | 1959 | try ais.insertNewline(); |
| | 1960 | } else if (mem.indexOfScalar(u8, tree.source[index..comment_start], '\n') != null) { |
| | 1961 | // Respect the newline directly before the comment. |
| | 1962 | // Note: This allows an empty line between comments |
| | 1963 | try ais.insertNewline(); |
| | 1964 | } else if (index == start) { |
| | 1965 | // Otherwise if the first comment is on the same line as |
| | 1966 | // the token before it, prefix it with a single space. |
| | 1967 | try ais.writer().writeByte(' '); |
| 1967 | } | 1968 | } |
| 1968 | }, | 1969 | } |
| | 1970 | |
| | 1971 | try ais.writer().print("{s}\n", .{trimmed_comment}); |
| | 1972 | index = newline + 1; |
| | 1973 | } |
| | 1974 | |
| | 1975 | if (index != start and mem.containsAtLeast(u8, tree.source[index - 1 .. end], 2, "\n")) { |
| | 1976 | try ais.insertNewline(); |
| | 1977 | } |
| | 1978 | |
| | 1979 | return index != start; |
| | 1980 | } |
| | 1981 | |
| | 1982 | fn renderExtraNewline(ais: *Ais, tree: ast.Tree, node: ast.Node.Index) Error!void { |
| | 1983 | return renderExtraNewlineToken(ais, tree, tree.firstToken(node)); |
| | 1984 | } |
| | 1985 | |
| | 1986 | /// Check if there is an empty line immediately before the given token. If so, render it. |
| | 1987 | fn renderExtraNewlineToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex) Error!void { |
| | 1988 | const token_starts = tree.tokens.items(.start); |
| | 1989 | const token_start = token_starts[token_index]; |
| | 1990 | if (token_start == 0) return; |
| | 1991 | const prev_token_end = if (token_index == 0) |
| | 1992 | 0 |
| | 1993 | else |
| | 1994 | token_starts[token_index - 1] + tree.tokenSlice(token_index - 1).len; |
| | 1995 | |
| | 1996 | // If there is a comment present, it will handle the empty line |
| | 1997 | if (mem.indexOf(u8, tree.source[prev_token_end..token_start], "//") != null) return; |
| | 1998 | |
| | 1999 | // Iterate backwards to the end of the previous token, stopping if a |
| | 2000 | // non-whitespace character is encountered or two newlines have been found. |
| | 2001 | var i = token_start - 1; |
| | 2002 | var newlines: u2 = 0; |
| | 2003 | while (std.ascii.isSpace(tree.source[i])) : (i -= 1) { |
| | 2004 | if (tree.source[i] == '\n') newlines += 1; |
| | 2005 | if (newlines == 2) return ais.insertNewline(); |
| | 2006 | if (i == prev_token_end) break; |
| 1969 | } | 2007 | } |
| 1970 | } | 2008 | } |
| 1971 | | 2009 | |
| ... | @@ -1983,19 +2021,15 @@ fn renderDocComments(ais: *Ais, tree: ast.Tree, end_token: ast.TokenIndex) Error | ... | @@ -1983,19 +2021,15 @@ fn renderDocComments(ais: *Ais, tree: ast.Tree, end_token: ast.TokenIndex) Error |
| 1983 | tok += 1; | 2021 | tok += 1; |
| 1984 | } | 2022 | } |
| 1985 | const first_tok = tok; | 2023 | const first_tok = tok; |
| 1986 | if (tok == end_token) return; | 2024 | if (first_tok == end_token) return; |
| | 2025 | try renderExtraNewlineToken(ais, tree, first_tok); |
| 1987 | | 2026 | |
| 1988 | while (true) : (tok += 1) { | 2027 | while (token_tags[tok] == .doc_comment) : (tok += 1) { |
| 1989 | switch (token_tags[tok]) { | 2028 | if (first_tok < end_token) { |
| 1990 | .doc_comment => { | 2029 | try renderToken(ais, tree, tok, .newline); |
| 1991 | if (first_tok < end_token) { | 2030 | } else { |
| 1992 | try renderToken(ais, tree, tok, .newline); | 2031 | try renderToken(ais, tree, tok, .no_comment); |
| 1993 | } else { | 2032 | try ais.insertNewline(); |
| 1994 | try renderToken(ais, tree, tok, .no_comment); | | |
| 1995 | try ais.insertNewline(); | | |
| 1996 | } | | |
| 1997 | }, | | |
| 1998 | else => break, | | |
| 1999 | } | 2033 | } |
| 2000 | } | 2034 | } |
| 2001 | } | 2035 | } |