authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-02-24 16:44:55+01:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-02-24 17:28:29+01:00
log52c45bf44d797f28594db5a75c306ed209cfb933
treef89c6a5a425cec8d8be8a1c284ce3a270a41598c
parent371b21bdfbfa8917b44e809fb8a041411ffc6b8a
signaturelock-open Commit is signed but in an unrecognized format.

zig fmt: rework single statement if/while/for indentation

This approach properly handles nesting unlike the approach in the previous commit.

2 files changed, 101 insertions(+), 64 deletions(-)

lib/std/zig/parser_test.zig+17
......@@ -1496,12 +1496,14 @@ test "zig fmt: if nested" {
14961496 \\ GE_EQUAL
14971497 \\ else
14981498 \\ GE_GREATER
1499 \\ // comment
14991500 \\ else if (aInt > bInt)
15001501 \\ GE_LESS
15011502 \\ else if (aInt == bInt)
15021503 \\ GE_EQUAL
15031504 \\ else
15041505 \\ GE_GREATER;
1506 \\ // comment
15051507 \\}
15061508 \\
15071509 );
......@@ -4189,6 +4191,21 @@ test "zig fmt: line comment after multiline single expr if statement with multil
41894191 \\
41904192 \\ // bar
41914193 \\ baz();
4194 \\
4195 \\ if (foo)
4196 \\ x =
4197 \\ \\hello
4198 \\ \\hello
4199 \\ \\
4200 \\ else
4201 \\ y =
4202 \\ \\hello
4203 \\ \\hello
4204 \\ \\
4205 \\ ;
4206 \\
4207 \\ // bar
4208 \\ baz();
41924209 \\}
41934210 \\
41944211 );
lib/std/zig/render.zig+84-64
......@@ -44,7 +44,6 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void {
4444/// Render all members in the given slice, keeping empty lines where appropriate
4545fn renderMembers(gpa: *Allocator, ais: *Ais, tree: ast.Tree, members: []const ast.Node.Index) Error!void {
4646 if (members.len == 0) return;
47 //try renderExtraNewline(ais, tree, members[0]);
4847 try renderMember(gpa, ais, tree, members[0], .newline);
4948 for (members[1..]) |member| {
5049 try renderExtraNewline(ais, tree, member);
......@@ -202,10 +201,8 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.I
202201 while (locked_indents > 0) : (locked_indents -= 1) ais.popIndent();
203202
204203 switch (space) {
205 .none, .space, .newline => {},
206 .newline_pop => ais.popIndent(),
204 .none, .space, .newline, .skip => {},
207205 .semicolon => if (token_tags[i] == .semicolon) try renderToken(ais, tree, i, .newline),
208 .semicolon_pop => if (token_tags[i] == .semicolon) try renderToken(ais, tree, i, .newline_pop),
209206 .comma => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .newline),
210207 .comma_space => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .space),
211208 }
......@@ -1147,15 +1144,11 @@ fn renderWhile(gpa: *Allocator, ais: *Ais, tree: ast.Tree, while_node: ast.full.
11471144 } else {
11481145 try renderToken(ais, tree, while_node.else_token, .newline); // else
11491146 }
1150 ais.pushIndent();
1151 try renderExpression(gpa, ais, tree, while_node.ast.else_expr, space);
1152 ais.popIndent();
1147 try renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space);
11531148 return;
11541149 }
11551150 } else {
1156 ais.pushIndent();
1157 assert(space == .semicolon);
1158 try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .semicolon_pop);
1151 try renderExpressionIndented(gpa, ais, tree, while_node.ast.then_expr, space);
11591152 return;
11601153 }
11611154 }
......@@ -2168,6 +2161,64 @@ fn renderCall(
21682161 return renderToken(ais, tree, after_last_param_tok, space); // )
21692162}
21702163
2164/// Renders the given expression indented, popping the indent before rendering
2165/// any following line comments
2166fn renderExpressionIndented(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
2167 const token_starts = tree.tokens.items(.start);
2168 const token_tags = tree.tokens.items(.tag);
2169
2170 ais.pushIndent();
2171
2172 var last_token = tree.lastToken(node);
2173 const punctuation = switch (space) {
2174 .none, .space, .newline, .skip => false,
2175 .comma => true,
2176 .comma_space => token_tags[last_token + 1] == .comma,
2177 .semicolon => token_tags[last_token + 1] == .semicolon,
2178 };
2179
2180 try renderExpression(gpa, ais, tree, node, if (punctuation) .none else .skip);
2181
2182 switch (space) {
2183 .none, .space, .newline, .skip => {},
2184 .comma => {
2185 if (token_tags[last_token + 1] == .comma) {
2186 try renderToken(ais, tree, last_token + 1, .skip);
2187 last_token += 1;
2188 } else {
2189 try ais.writer().writeByte(',');
2190 }
2191 },
2192 .comma_space => if (token_tags[last_token + 1] == .comma) {
2193 try renderToken(ais, tree, last_token + 1, .skip);
2194 last_token += 1;
2195 },
2196 .semicolon => if (token_tags[last_token + 1] == .semicolon) {
2197 try renderToken(ais, tree, last_token + 1, .skip);
2198 last_token += 1;
2199 },
2200 }
2201
2202 ais.popIndent();
2203
2204 if (space == .skip) return;
2205
2206 const comment_start = token_starts[last_token] + tokenSliceForRender(tree, last_token).len;
2207 const comment = try renderComments(ais, tree, comment_start, token_starts[last_token + 1]);
2208
2209 if (!comment) switch (space) {
2210 .none => {},
2211 .space,
2212 .comma_space,
2213 => try ais.writer().writeByte(' '),
2214 .newline,
2215 .comma,
2216 .semicolon,
2217 => try ais.insertNewline(),
2218 .skip => unreachable,
2219 };
2220}
2221
21712222/// Render an expression, and the comma that follows it, if it is present in the source.
21722223fn renderExpressionComma(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Space) Error!void {
21732224 const token_tags = tree.tokens.items(.tag);
......@@ -2198,9 +2249,6 @@ const Space = enum {
21982249 space,
21992250 /// Output the token lexeme followed by a newline.
22002251 newline,
2201 /// Same as newline, but pop an indent level before rendering the
2202 /// following comments if any.
2203 newline_pop,
22042252 /// If the next token is a comma, render it as well. If not, insert one.
22052253 /// In either case, a newline will be inserted afterwards.
22062254 comma,
......@@ -2210,9 +2258,9 @@ const Space = enum {
22102258 /// Additionally consume the next token if it is a semicolon.
22112259 /// In either case, a newline will be inserted afterwards.
22122260 semicolon,
2213 /// Same as semicolon, but pop an indent level before rendering the
2214 /// following comments if any.
2215 semicolon_pop,
2261 /// Skip rendering whitespace and comments. If this is used, the caller
2262 /// *must* handle handle whitespace and comments manually.
2263 skip,
22162264};
22172265
22182266fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Space) Error!void {
......@@ -2224,65 +2272,37 @@ fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Sp
22242272
22252273 try ais.writer().writeAll(lexeme);
22262274
2227 const token_end = token_start + lexeme.len;
2228 const next_token_start = token_starts[token_index + 1];
2229 switch (space) {
2230 .none => _ = try renderComments(ais, tree, token_end, next_token_start),
2231
2232 .space => if (!try renderComments(ais, tree, token_end, next_token_start)) {
2233 try ais.writer().writeByte(' ');
2234 },
2275 if (space == .skip) return;
22352276
2236 .newline => if (!try renderComments(ais, tree, token_end, next_token_start)) {
2237 try ais.insertNewline();
2238 },
2277 if (space == .comma and token_tags[token_index + 1] != .comma) {
2278 try ais.writer().writeByte(',');
2279 }
22392280
2240 .newline_pop => {
2241 ais.popIndent();
2242 if (!try renderComments(ais, tree, token_end, next_token_start)) {
2243 try ais.insertNewline();
2244 }
2245 },
2281 const comment = try renderComments(ais, tree, token_start + lexeme.len, token_starts[token_index + 1]);
2282 switch (space) {
2283 .none => {},
2284 .space => if (!comment) try ais.writer().writeByte(' '),
2285 .newline => if (!comment) try ais.insertNewline(),
22462286
22472287 .comma => if (token_tags[token_index + 1] == .comma) {
2248 _ = try renderComments(ais, tree, token_end, next_token_start);
22492288 try renderToken(ais, tree, token_index + 1, .newline);
2250 } else {
2251 try ais.writer().writeByte(',');
2252 if (!try renderComments(ais, tree, token_end, next_token_start)) {
2253 try ais.insertNewline();
2254 }
2289 } else if (!comment) {
2290 try ais.insertNewline();
22552291 },
22562292
2257 .comma_space => {
2258 const comment = try renderComments(ais, tree, token_end, next_token_start);
2259 if (token_tags[token_index + 1] == .comma) {
2260 try renderToken(ais, tree, token_index + 1, .space);
2261 } else if (!comment) {
2262 try ais.writer().writeByte(' ');
2263 }
2293 .comma_space => if (token_tags[token_index + 1] == .comma) {
2294 try renderToken(ais, tree, token_index + 1, .space);
2295 } else if (!comment) {
2296 try ais.writer().writeByte(' ');
22642297 },
22652298
2266 .semicolon => {
2267 const comment = try renderComments(ais, tree, token_end, next_token_start);
2268 if (token_tags[token_index + 1] == .semicolon) {
2269 try renderToken(ais, tree, token_index + 1, .newline);
2270 } else if (!comment) {
2271 try ais.insertNewline();
2272 }
2299 .semicolon => if (token_tags[token_index + 1] == .semicolon) {
2300 try renderToken(ais, tree, token_index + 1, .newline);
2301 } else if (!comment) {
2302 try ais.insertNewline();
22732303 },
22742304
2275 .semicolon_pop => {
2276 if (token_tags[token_index + 1] == .semicolon) {
2277 _ = try renderComments(ais, tree, token_end, next_token_start);
2278 try renderToken(ais, tree, token_index + 1, .newline_pop);
2279 } else {
2280 ais.popIndent();
2281 if (!try renderComments(ais, tree, token_end, next_token_start)) {
2282 try ais.insertNewline();
2283 }
2284 }
2285 },
2305 .skip => unreachable,
22862306 }
22872307}
22882308