authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-02-12 17:41:12+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-13 16:57:34-08:00
log75ba8d8db6d3c067fd8f9c8d32e6fea7c3b9343b
tree7ba200ee782ebfb7103ddd3a09cce1a705e96f8c
parent7630a5c566b106b6325a55f29eb1ed9e584d0949

zig fmt: remove empty lines at start/end of block


2 files changed, 217 insertions(+), 121 deletions(-)

lib/std/zig/parser_test.zig+62
...@@ -239,6 +239,68 @@ test "zig fmt: container declaration, transform trailing comma" {...@@ -239,6 +239,68 @@ test "zig fmt: container declaration, transform trailing comma" {
239 );239 );
240}240}
241241
242test "zig fmt: remove empty lines at start/end of container decl" {
243 try testTransform(
244 \\const X = struct {
245 \\
246 \\ foo: i32,
247 \\
248 \\ bar: i8,
249 \\
250 \\};
251 \\
252 ,
253 \\const X = struct {
254 \\ foo: i32,
255 \\
256 \\ bar: i8,
257 \\};
258 \\
259 );
260}
261
262test "zig fmt: remove empty lines at start/end of block" {
263 try testTransform(
264 \\test {
265 \\
266 \\ if (foo) {
267 \\ foo();
268 \\ }
269 \\
270 \\}
271 \\
272 ,
273 \\test {
274 \\ if (foo) {
275 \\ foo();
276 \\ }
277 \\}
278 \\
279 );
280}
281
282test "zig fmt: allow empty line before commment at start of block" {
283 try testCanonical(
284 \\test {
285 \\
286 \\ // foo
287 \\ const x = 42;
288 \\}
289 \\
290 );
291}
292
293test "zig fmt: allow empty line before commment at start of block" {
294 try testCanonical(
295 \\test {
296 \\
297 \\ // foo
298 \\ const x = 42;
299 \\}
300 \\
301 );
302}
303
242test "zig fmt: trailing comma in fn parameter list" {304test "zig fmt: trailing comma in fn parameter list" {
243 try testCanonical(305 try testCanonical(
244 \\pub fn f(306 \\pub fn f(
lib/std/zig/render.zig+155-121
...@@ -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;
2525
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);
3029
31 for (tree.rootDecls()) |decl| {30 try renderMembers(ais, tree, tree.rootDecls());
32 try renderMember(ais, tree, decl, .newline);
33 }
34}31}
3532
36/// Assumes that start is the first byte past the previous token and33/// Render all members in the given slice, keeping empty lines where appropriate
37/// that end is the last byte before the next token.34fn renderMembers(ais: *Ais, tree: ast.Tree, members: []const ast.Node.Index) Error!void {
38fn 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}
7343
74fn renderMember(ais: *Ais, tree: ast.Tree, decl: ast.Node.Index, space: Space) Error!void {44fn 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}
159129
130/// Render all expressions in the slice, keeping empty lines where appropriate
131fn 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
160fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {140fn 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),
503483
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 expression583 try renderExpression(ais, tree, condition, .none); // condtion expression
604 try renderToken(ais, tree, rparen, .space); // rparen584 try renderToken(ais, tree, rparen, .space); // rparen
605585
586 ais.pushIndentNextLine();
606 if (cases.len == 0) {587 if (cases.len == 0) {
607 try renderToken(ais, tree, rparen + 1, .none); // lbrace588 try renderToken(ais, tree, rparen + 1, .none); // lbrace
608 return renderToken(ais, tree, rparen + 2, space); // rbrace589 } 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); // rbrace594 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 line1419 // 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 line1422 // 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 }
14881464
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); // rbrace1469 try renderToken(ais, tree, lbrace, .newline);
1494 }1470 for (statements) |stmt, i| {
14951471 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);
15351509
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); // name1519 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); // T1553 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); // lbrace1557 try renderToken(ais, tree, array_init.ast.lbrace, .none); // lbrace
1558 ais.popIndent();
1577 return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace1559 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);
15851567 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); // rbrace1569 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 }
16801658
1681 // One member per line.1659 // One member per line.
1682 ais.pushIndent();1660 ais.pushIndentNextLine();
1683 try renderToken(ais, tree, lbrace, .newline); // lbrace1661 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();
16881664
1689 return renderToken(ais, tree, rbrace, space); // rbrace1665 return renderToken(ais, tree, rbrace, space); // rbrace
...@@ -1745,6 +1721,7 @@ fn renderAsm(...@@ -1745,6 +1721,7 @@ fn renderAsm(
17451721
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(
17761753
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 }
18401820
...@@ -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); // ,
18571837
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);
19301912
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.
1945fn 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
1982fn 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.
1987fn 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}
19712009
...@@ -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);
19872026
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}