blob 2f94d1cf (84829B) - Raw
1 const std = @import("../index.zig"); 2 const builtin = @import("builtin"); 3 const assert = std.debug.assert; 4 const mem = std.mem; 5 const ast = std.zig.ast; 6 const Token = std.zig.Token; 7 8 const indent_delta = 4; 9 10 pub const Error = error{ 11 /// Ran out of memory allocating call stack frames to complete rendering. 12 OutOfMemory, 13 }; 14 15 pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf(stream).Child.Error || Error)!void { 16 comptime assert(@typeId(@typeOf(stream)) == builtin.TypeId.Pointer); 17 18 // render all the line comments at the beginning of the file 19 var tok_it = tree.tokens.iterator(0); 20 while (tok_it.next()) |token| { 21 if (token.id != Token.Id.LineComment) break; 22 try stream.print("{}\n", mem.trimRight(u8, tree.tokenSlicePtr(token), " ")); 23 if (tok_it.peek()) |next_token| { 24 const loc = tree.tokenLocationPtr(token.end, next_token); 25 if (loc.line >= 2) { 26 try stream.writeByte('\n'); 27 } 28 } 29 } 30 31 32 var it = tree.root_node.decls.iterator(0); 33 while (it.next()) |decl| { 34 try renderTopLevelDecl(allocator, stream, tree, 0, decl.*); 35 if (it.peek()) |next_decl| { 36 try renderExtraNewline(tree, stream, next_decl.*); 37 } 38 } 39 } 40 41 fn renderExtraNewline(tree: &ast.Tree, stream: var, node: &ast.Node) !void { 42 var first_token = node.firstToken(); 43 while (tree.tokens.at(first_token - 1).id == Token.Id.DocComment) { 44 first_token -= 1; 45 } 46 const prev_token_end = tree.tokens.at(first_token - 1).end; 47 const loc = tree.tokenLocation(prev_token_end, first_token); 48 if (loc.line >= 2) { 49 try stream.writeByte('\n'); 50 } 51 } 52 53 fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, decl: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { 54 switch (decl.id) { 55 ast.Node.Id.FnProto => { 56 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); 57 58 try renderDocComments(tree, stream, fn_proto, indent); 59 60 if (fn_proto.body_node) |body_node| { 61 try renderExpression(allocator, stream, tree, indent, decl, Space.Space); 62 try renderExpression(allocator, stream, tree, indent, body_node, Space.Newline); 63 } else { 64 try renderExpression(allocator, stream, tree, indent, decl, Space.None); 65 try renderToken(tree, stream, tree.nextToken(decl.lastToken()), indent, Space.Newline); 66 } 67 }, 68 69 ast.Node.Id.Use => { 70 const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); 71 72 if (use_decl.visib_token) |visib_token| { 73 try renderToken(tree, stream, visib_token, indent, Space.Space); // pub 74 } 75 try renderToken(tree, stream, use_decl.use_token, indent, Space.Space); // use 76 try renderExpression(allocator, stream, tree, indent, use_decl.expr, Space.None); 77 try renderToken(tree, stream, use_decl.semicolon_token, indent, Space.Newline); // ; 78 }, 79 80 ast.Node.Id.VarDecl => { 81 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); 82 83 try renderDocComments(tree, stream, var_decl, indent); 84 try renderVarDecl(allocator, stream, tree, indent, var_decl); 85 }, 86 87 ast.Node.Id.TestDecl => { 88 const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); 89 90 try renderDocComments(tree, stream, test_decl, indent); 91 try renderToken(tree, stream, test_decl.test_token, indent, Space.Space); 92 try renderExpression(allocator, stream, tree, indent, test_decl.name, Space.Space); 93 try renderExpression(allocator, stream, tree, indent, test_decl.body_node, Space.Newline); 94 }, 95 96 ast.Node.Id.StructField => { 97 const field = @fieldParentPtr(ast.Node.StructField, "base", decl); 98 99 try renderDocComments(tree, stream, field, indent); 100 if (field.visib_token) |visib_token| { 101 try renderToken(tree, stream, visib_token, indent, Space.Space); // pub 102 } 103 try renderToken(tree, stream, field.name_token, indent, Space.None); // name 104 try renderToken(tree, stream, tree.nextToken(field.name_token), indent, Space.Space); // : 105 try renderTrailingComma(allocator, stream, tree, indent, field.type_expr, Space.Newline); // type, 106 }, 107 108 ast.Node.Id.UnionTag => { 109 const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); 110 111 try renderDocComments(tree, stream, tag, indent); 112 113 const name_space = if (tag.type_expr == null and tag.value_expr != null) Space.Space else Space.None; 114 try renderToken(tree, stream, tag.name_token, indent, name_space); // name 115 116 if (tag.type_expr) |type_expr| { 117 try renderToken(tree, stream, tree.nextToken(tag.name_token), indent, Space.Space); // : 118 119 const after_type_space = if (tag.value_expr == null) Space.None else Space.Space; 120 try renderExpression(allocator, stream, tree, indent, type_expr, after_type_space); 121 } 122 123 if (tag.value_expr) |value_expr| { 124 try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, Space.Space); // = 125 try renderExpression(allocator, stream, tree, indent, value_expr, Space.None); 126 } 127 128 try renderToken(tree, stream, tree.nextToken(decl.lastToken()), indent, Space.Newline); // , 129 }, 130 131 ast.Node.Id.EnumTag => { 132 const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); 133 134 try renderDocComments(tree, stream, tag, indent); 135 136 const after_name_space = if (tag.value == null) Space.None else Space.Space; 137 try renderToken(tree, stream, tag.name_token, indent, after_name_space); // name 138 139 if (tag.value) |value| { 140 try renderToken(tree, stream, tree.nextToken(tag.name_token), indent, Space.Space); // = 141 try renderExpression(allocator, stream, tree, indent, value, Space.None); 142 } 143 144 try renderToken(tree, stream, tree.nextToken(decl.lastToken()), indent, Space.Newline); // , 145 }, 146 147 ast.Node.Id.Comptime => { 148 assert(!decl.requireSemiColon()); 149 try renderExpression(allocator, stream, tree, indent, decl, Space.Newline); 150 }, 151 else => unreachable, 152 } 153 } 154 155 fn renderExpression(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node, space: Space) (@typeOf(stream).Child.Error || Error)!void { 156 switch (base.id) { 157 ast.Node.Id.Identifier => { 158 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); 159 try renderToken(tree, stream, identifier.token, indent, space); 160 }, 161 ast.Node.Id.Block => { 162 const block = @fieldParentPtr(ast.Node.Block, "base", base); 163 164 if (block.label) |label| { 165 try renderToken(tree, stream, label, indent, Space.None); 166 try renderToken(tree, stream, tree.nextToken(label), indent, Space.Space); 167 } 168 169 if (block.statements.len == 0) { 170 try renderToken(tree, stream, block.lbrace, indent + indent_delta, Space.None); 171 try renderToken(tree, stream, block.rbrace, indent, space); 172 } else { 173 const block_indent = indent + indent_delta; 174 try renderToken(tree, stream, block.lbrace, block_indent, Space.Newline); 175 176 var it = block.statements.iterator(0); 177 while (it.next()) |statement| { 178 try stream.writeByteNTimes(' ', block_indent); 179 try renderStatement(allocator, stream, tree, block_indent, statement.*); 180 181 if (it.peek()) |next_statement| { 182 try renderExtraNewline(tree, stream, next_statement.*); 183 } 184 } 185 186 try stream.writeByteNTimes(' ', indent); 187 try renderToken(tree, stream, block.rbrace, indent, space); 188 } 189 }, 190 ast.Node.Id.Defer => { 191 const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); 192 193 try renderToken(tree, stream, defer_node.defer_token, indent, Space.Space); 194 try renderExpression(allocator, stream, tree, indent, defer_node.expr, space); 195 }, 196 ast.Node.Id.Comptime => { 197 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); 198 199 try renderToken(tree, stream, comptime_node.comptime_token, indent, Space.Space); 200 try renderExpression(allocator, stream, tree, indent, comptime_node.expr, space); 201 }, 202 203 ast.Node.Id.AsyncAttribute => { 204 const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); 205 206 if (async_attr.allocator_type) |allocator_type| { 207 try renderToken(tree, stream, async_attr.async_token, indent, Space.None); 208 209 try renderToken(tree, stream, tree.nextToken(async_attr.async_token), indent, Space.None); 210 try renderExpression(allocator, stream, tree, indent, allocator_type, Space.None); 211 try renderToken(tree, stream, tree.nextToken(allocator_type.lastToken()), indent, space); 212 } else { 213 try renderToken(tree, stream, async_attr.async_token, indent, space); 214 } 215 }, 216 217 ast.Node.Id.Suspend => { 218 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); 219 220 if (suspend_node.label) |label| { 221 try renderToken(tree, stream, label, indent, Space.None); 222 try renderToken(tree, stream, tree.nextToken(label), indent, Space.Space); 223 } 224 225 if (suspend_node.payload) |payload| { 226 if (suspend_node.body) |body| { 227 try renderToken(tree, stream, suspend_node.suspend_token, indent, Space.Space); 228 try renderExpression(allocator, stream, tree, indent, payload, Space.Space); 229 try renderExpression(allocator, stream, tree, indent, body, space); 230 } else { 231 try renderToken(tree, stream, suspend_node.suspend_token, indent, Space.Space); 232 try renderExpression(allocator, stream, tree, indent, payload, space); 233 } 234 } else if (suspend_node.body) |body| { 235 try renderToken(tree, stream, suspend_node.suspend_token, indent, Space.Space); 236 try renderExpression(allocator, stream, tree, indent, body, space); 237 } else { 238 try renderToken(tree, stream, suspend_node.suspend_token, indent, space); 239 } 240 }, 241 242 ast.Node.Id.InfixOp => { 243 const infix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); 244 245 const op_token = tree.tokens.at(infix_op_node.op_token); 246 const op_space = switch (infix_op_node.op) { 247 ast.Node.InfixOp.Op.Period, ast.Node.InfixOp.Op.ErrorUnion, ast.Node.InfixOp.Op.Range => Space.None, 248 else => Space.Space, 249 }; 250 try renderExpression(allocator, stream, tree, indent, infix_op_node.lhs, op_space); 251 try renderToken(tree, stream, infix_op_node.op_token, indent, op_space); 252 253 switch (infix_op_node.op) { 254 ast.Node.InfixOp.Op.Catch => |maybe_payload| if (maybe_payload) |payload| { 255 try renderExpression(allocator, stream, tree, indent, payload, Space.Space); 256 }, 257 else => {}, 258 } 259 260 try renderExpression(allocator, stream, tree, indent, infix_op_node.rhs, space); 261 }, 262 263 ast.Node.Id.PrefixOp => { 264 const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); 265 266 switch (prefix_op_node.op) { 267 ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { 268 try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); // & 269 if (addr_of_info.align_info) |align_info| { 270 const lparen_token = tree.prevToken(align_info.node.firstToken()); 271 const align_token = tree.prevToken(lparen_token); 272 273 try renderToken(tree, stream, align_token, indent, Space.None); // align 274 try renderToken(tree, stream, lparen_token, indent, Space.None); // ( 275 276 try renderExpression(allocator, stream, tree, indent, align_info.node, Space.None); 277 278 if (align_info.bit_range) |bit_range| { 279 const colon1 = tree.prevToken(bit_range.start.firstToken()); 280 const colon2 = tree.prevToken(bit_range.end.firstToken()); 281 282 try renderToken(tree, stream, colon1, indent, Space.None); // : 283 try renderExpression(allocator, stream, tree, indent, bit_range.start, Space.None); 284 try renderToken(tree, stream, colon2, indent, Space.None); // : 285 try renderExpression(allocator, stream, tree, indent, bit_range.end, Space.None); 286 287 const rparen_token = tree.nextToken(bit_range.end.lastToken()); 288 try renderToken(tree, stream, rparen_token, indent, Space.Space); // ) 289 } else { 290 const rparen_token = tree.nextToken(align_info.node.lastToken()); 291 try renderToken(tree, stream, rparen_token, indent, Space.Space); // ) 292 } 293 } 294 if (addr_of_info.const_token) |const_token| { 295 try renderToken(tree, stream, const_token, indent, Space.Space); // const 296 } 297 if (addr_of_info.volatile_token) |volatile_token| { 298 try renderToken(tree, stream, volatile_token, indent, Space.Space); // volatile 299 } 300 }, 301 302 ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { 303 try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); // [ 304 try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, Space.None); // ] 305 306 if (addr_of_info.align_info) |align_info| { 307 const lparen_token = tree.prevToken(align_info.node.firstToken()); 308 const align_token = tree.prevToken(lparen_token); 309 310 try renderToken(tree, stream, align_token, indent, Space.None); // align 311 try renderToken(tree, stream, lparen_token, indent, Space.None); // ( 312 313 try renderExpression(allocator, stream, tree, indent, align_info.node, Space.None); 314 315 if (align_info.bit_range) |bit_range| { 316 const colon1 = tree.prevToken(bit_range.start.firstToken()); 317 const colon2 = tree.prevToken(bit_range.end.firstToken()); 318 319 try renderToken(tree, stream, colon1, indent, Space.None); // : 320 try renderExpression(allocator, stream, tree, indent, bit_range.start, Space.None); 321 try renderToken(tree, stream, colon2, indent, Space.None); // : 322 try renderExpression(allocator, stream, tree, indent, bit_range.end, Space.None); 323 324 const rparen_token = tree.nextToken(bit_range.end.lastToken()); 325 try renderToken(tree, stream, rparen_token, indent, Space.Space); // ) 326 } else { 327 const rparen_token = tree.nextToken(align_info.node.lastToken()); 328 try renderToken(tree, stream, rparen_token, indent, Space.Space); // ) 329 } 330 } 331 if (addr_of_info.const_token) |const_token| { 332 try renderToken(tree, stream, const_token, indent, Space.Space); 333 } 334 if (addr_of_info.volatile_token) |volatile_token| { 335 try renderToken(tree, stream, volatile_token, indent, Space.Space); 336 } 337 }, 338 339 ast.Node.PrefixOp.Op.ArrayType => |array_index| { 340 try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); // [ 341 try renderExpression(allocator, stream, tree, indent, array_index, Space.None); 342 try renderToken(tree, stream, tree.nextToken(array_index.lastToken()), indent, Space.None); // ] 343 }, 344 ast.Node.PrefixOp.Op.BitNot, 345 ast.Node.PrefixOp.Op.BoolNot, 346 ast.Node.PrefixOp.Op.Negation, 347 ast.Node.PrefixOp.Op.NegationWrap, 348 ast.Node.PrefixOp.Op.UnwrapMaybe, 349 ast.Node.PrefixOp.Op.MaybeType, 350 ast.Node.PrefixOp.Op.PointerType => { 351 try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.None); 352 }, 353 354 ast.Node.PrefixOp.Op.Try, 355 ast.Node.PrefixOp.Op.Await, 356 ast.Node.PrefixOp.Op.Cancel, 357 ast.Node.PrefixOp.Op.Resume => { 358 try renderToken(tree, stream, prefix_op_node.op_token, indent, Space.Space); 359 }, 360 } 361 362 try renderExpression(allocator, stream, tree, indent, prefix_op_node.rhs, space); 363 }, 364 365 ast.Node.Id.SuffixOp => { 366 const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); 367 368 switch (suffix_op.op) { 369 @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| { 370 if (call_info.async_attr) |async_attr| { 371 try renderExpression(allocator, stream, tree, indent, &async_attr.base, Space.Space); 372 } 373 374 try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); 375 376 const lparen = tree.nextToken(suffix_op.lhs.lastToken()); 377 378 if (call_info.params.len == 0) { 379 try renderToken(tree, stream, lparen, indent, Space.None); 380 try renderToken(tree, stream, suffix_op.rtoken, indent, space); 381 return; 382 } 383 384 const src_has_trailing_comma = blk: { 385 const maybe_comma = tree.prevToken(suffix_op.rtoken); 386 break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma; 387 }; 388 389 if (src_has_trailing_comma) { 390 const new_indent = indent + indent_delta; 391 try renderToken(tree, stream, lparen, new_indent, Space.Newline); 392 393 var it = call_info.params.iterator(0); 394 while (true) { 395 const param_node = ??it.next(); 396 397 const param_node_new_indent = if (param_node.*.id == ast.Node.Id.MultilineStringLiteral) blk: { 398 break :blk indent; 399 } else blk: { 400 try stream.writeByteNTimes(' ', new_indent); 401 break :blk new_indent; 402 }; 403 404 if (it.peek()) |next_node| { 405 try renderExpression(allocator, stream, tree, param_node_new_indent, param_node.*, Space.None); 406 const comma = tree.nextToken(param_node.*.lastToken()); 407 try renderToken(tree, stream, comma, new_indent, Space.Newline); // , 408 try renderExtraNewline(tree, stream, next_node.*); 409 } else { 410 try renderTrailingComma(allocator, stream, tree, param_node_new_indent, param_node.*, Space.Newline); 411 try stream.writeByteNTimes(' ', indent); 412 try renderToken(tree, stream, suffix_op.rtoken, indent, space); 413 return; 414 } 415 } 416 } 417 418 try renderToken(tree, stream, lparen, indent, Space.None); // ( 419 420 var it = call_info.params.iterator(0); 421 while (it.next()) |param_node| { 422 try renderExpression(allocator, stream, tree, indent, param_node.*, Space.None); 423 424 if (it.peek() != null) { 425 const comma = tree.nextToken(param_node.*.lastToken()); 426 try renderToken(tree, stream, comma, indent, Space.Space); 427 } 428 } 429 try renderToken(tree, stream, suffix_op.rtoken, indent, space); 430 }, 431 432 ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { 433 const lbracket = tree.prevToken(index_expr.firstToken()); 434 const rbracket = tree.nextToken(index_expr.lastToken()); 435 436 try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); 437 try renderToken(tree, stream, lbracket, indent, Space.None); // [ 438 try renderExpression(allocator, stream, tree, indent, index_expr, Space.None); 439 try renderToken(tree, stream, rbracket, indent, space); // ] 440 }, 441 442 ast.Node.SuffixOp.Op.Deref => { 443 try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); 444 try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, Space.None); // . 445 try renderToken(tree, stream, suffix_op.rtoken, indent, space); // * 446 }, 447 448 @TagType(ast.Node.SuffixOp.Op).Slice => |range| { 449 try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); 450 451 const lbracket = tree.prevToken(range.start.firstToken()); 452 const dotdot = tree.nextToken(range.start.lastToken()); 453 454 try renderToken(tree, stream, lbracket, indent, Space.None); // [ 455 try renderExpression(allocator, stream, tree, indent, range.start, Space.None); 456 try renderToken(tree, stream, dotdot, indent, Space.None); // .. 457 if (range.end) |end| { 458 try renderExpression(allocator, stream, tree, indent, end, Space.None); 459 } 460 try renderToken(tree, stream, suffix_op.rtoken, indent, space); // ] 461 }, 462 463 ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| { 464 const lbrace = tree.nextToken(suffix_op.lhs.lastToken()); 465 466 if (field_inits.len == 0) { 467 try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); 468 try renderToken(tree, stream, lbrace, indent, Space.None); 469 try renderToken(tree, stream, suffix_op.rtoken, indent, space); 470 return; 471 } 472 473 if (field_inits.len == 1) blk: { 474 const field_init = ??field_inits.at(0).*.cast(ast.Node.FieldInitializer); 475 476 if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| { 477 if (nested_suffix_op.op == ast.Node.SuffixOp.Op.StructInitializer) { 478 break :blk; 479 } 480 } 481 482 try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); 483 try renderToken(tree, stream, lbrace, indent, Space.Space); 484 try renderExpression(allocator, stream, tree, indent, &field_init.base, Space.Space); 485 try renderToken(tree, stream, suffix_op.rtoken, indent, space); 486 return; 487 } 488 489 const src_has_trailing_comma = blk: { 490 const maybe_comma = tree.prevToken(suffix_op.rtoken); 491 break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma; 492 }; 493 494 const src_same_line = blk: { 495 const loc = tree.tokenLocation(tree.tokens.at(lbrace).end, suffix_op.rtoken); 496 break :blk loc.line == 0; 497 }; 498 499 if (!src_has_trailing_comma and src_same_line) { 500 // render all on one line, no trailing comma 501 try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); 502 try renderToken(tree, stream, lbrace, indent, Space.Space); 503 504 var it = field_inits.iterator(0); 505 while (it.next()) |field_init| { 506 if (it.peek() != null) { 507 try renderExpression(allocator, stream, tree, indent, field_init.*, Space.None); 508 509 const comma = tree.nextToken(field_init.*.lastToken()); 510 try renderToken(tree, stream, comma, indent, Space.Space); 511 } else { 512 try renderExpression(allocator, stream, tree, indent, field_init.*, Space.Space); 513 } 514 } 515 516 try renderToken(tree, stream, suffix_op.rtoken, indent, space); 517 return; 518 } 519 520 try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); 521 try renderToken(tree, stream, lbrace, indent, Space.Newline); 522 523 const new_indent = indent + indent_delta; 524 525 var it = field_inits.iterator(0); 526 while (it.next()) |field_init| { 527 try stream.writeByteNTimes(' ', new_indent); 528 529 if (it.peek()) |next_field_init| { 530 try renderExpression(allocator, stream, tree, new_indent, field_init.*, Space.None); 531 532 const comma = tree.nextToken(field_init.*.lastToken()); 533 try renderToken(tree, stream, comma, new_indent, Space.Newline); 534 535 try renderExtraNewline(tree, stream, next_field_init.*); 536 } else { 537 try renderTrailingComma(allocator, stream, tree, new_indent, field_init.*, Space.Newline); 538 } 539 } 540 541 try stream.writeByteNTimes(' ', indent); 542 try renderToken(tree, stream, suffix_op.rtoken, indent, space); 543 }, 544 545 ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| { 546 const lbrace = tree.nextToken(suffix_op.lhs.lastToken()); 547 548 if (exprs.len == 0) { 549 try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); 550 try renderToken(tree, stream, lbrace, indent, Space.None); 551 try renderToken(tree, stream, suffix_op.rtoken, indent, space); 552 return; 553 } 554 if (exprs.len == 1) { 555 const expr = exprs.at(0).*; 556 557 try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); 558 try renderToken(tree, stream, lbrace, indent, Space.None); 559 try renderExpression(allocator, stream, tree, indent, expr, Space.None); 560 try renderToken(tree, stream, suffix_op.rtoken, indent, space); 561 return; 562 } 563 564 try renderExpression(allocator, stream, tree, indent, suffix_op.lhs, Space.None); 565 566 // scan to find row size 567 const maybe_row_size: ?usize = blk: { 568 var count: usize = 1; 569 var it = exprs.iterator(0); 570 while (true) { 571 const expr = (??it.next()).*; 572 if (it.peek()) |next_expr| { 573 const expr_last_token = expr.*.lastToken() + 1; 574 const loc = tree.tokenLocation(tree.tokens.at(expr_last_token).end, next_expr.*.firstToken()); 575 if (loc.line != 0) break :blk count; 576 count += 1; 577 } else { 578 const expr_last_token = expr.*.lastToken(); 579 const loc = tree.tokenLocation(tree.tokens.at(expr_last_token).end, suffix_op.rtoken); 580 if (loc.line == 0) { 581 // all on one line 582 const src_has_trailing_comma = trailblk: { 583 const maybe_comma = tree.prevToken(suffix_op.rtoken); 584 break :trailblk tree.tokens.at(maybe_comma).id == Token.Id.Comma; 585 }; 586 if (src_has_trailing_comma) { 587 break :blk 1; // force row size 1 588 } else { 589 break :blk null; // no newlines 590 } 591 } 592 break :blk count; 593 } 594 } 595 }; 596 597 if (maybe_row_size) |row_size| { 598 const new_indent = indent + indent_delta; 599 try renderToken(tree, stream, lbrace, new_indent, Space.Newline); 600 try stream.writeByteNTimes(' ', new_indent); 601 602 var it = exprs.iterator(0); 603 var i: usize = 1; 604 while (it.next()) |expr| { 605 if (it.peek()) |next_expr| { 606 try renderExpression(allocator, stream, tree, new_indent, expr.*, Space.None); 607 608 const comma = tree.nextToken(expr.*.lastToken()); 609 610 if (i != row_size) { 611 try renderToken(tree, stream, comma, new_indent, Space.Space); // , 612 i += 1; 613 continue; 614 } 615 i = 1; 616 617 try renderToken(tree, stream, comma, new_indent, Space.Newline); // , 618 619 try renderExtraNewline(tree, stream, next_expr.*); 620 try stream.writeByteNTimes(' ', new_indent); 621 } else { 622 try renderTrailingComma(allocator, stream, tree, new_indent, expr.*, Space.Newline); // , 623 } 624 } 625 try stream.writeByteNTimes(' ', indent); 626 try renderToken(tree, stream, suffix_op.rtoken, indent, space); 627 return; 628 } else { 629 try renderToken(tree, stream, lbrace, indent, Space.Space); 630 var it = exprs.iterator(0); 631 while (it.next()) |expr| { 632 if (it.peek()) |next_expr| { 633 try renderExpression(allocator, stream, tree, indent, expr.*, Space.None); 634 const comma = tree.nextToken(expr.*.lastToken()); 635 try renderToken(tree, stream, comma, indent, Space.Space); // , 636 } else { 637 try renderExpression(allocator, stream, tree, indent, expr.*, Space.Space); 638 } 639 } 640 641 try renderToken(tree, stream, suffix_op.rtoken, indent, space); 642 return; 643 } 644 }, 645 } 646 }, 647 648 ast.Node.Id.ControlFlowExpression => { 649 const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); 650 651 switch (flow_expr.kind) { 652 ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { 653 const kw_space = if (maybe_label != null or flow_expr.rhs != null) Space.Space else space; 654 try renderToken(tree, stream, flow_expr.ltoken, indent, kw_space); 655 if (maybe_label) |label| { 656 const colon = tree.nextToken(flow_expr.ltoken); 657 try renderToken(tree, stream, colon, indent, Space.None); 658 659 const expr_space = if (flow_expr.rhs != null) Space.Space else space; 660 try renderExpression(allocator, stream, tree, indent, label, expr_space); 661 } 662 }, 663 ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { 664 const kw_space = if (maybe_label != null or flow_expr.rhs != null) Space.Space else space; 665 try renderToken(tree, stream, flow_expr.ltoken, indent, kw_space); 666 if (maybe_label) |label| { 667 const colon = tree.nextToken(flow_expr.ltoken); 668 try renderToken(tree, stream, colon, indent, Space.None); 669 670 const expr_space = if (flow_expr.rhs != null) Space.Space else space; 671 try renderExpression(allocator, stream, tree, indent, label, space); 672 } 673 }, 674 ast.Node.ControlFlowExpression.Kind.Return => { 675 const kw_space = if (flow_expr.rhs != null) Space.Space else space; 676 try renderToken(tree, stream, flow_expr.ltoken, indent, kw_space); 677 }, 678 } 679 680 if (flow_expr.rhs) |rhs| { 681 try renderExpression(allocator, stream, tree, indent, rhs, space); 682 } 683 }, 684 685 ast.Node.Id.Payload => { 686 const payload = @fieldParentPtr(ast.Node.Payload, "base", base); 687 688 try renderToken(tree, stream, payload.lpipe, indent, Space.None); 689 try renderExpression(allocator, stream, tree, indent, payload.error_symbol, Space.None); 690 try renderToken(tree, stream, payload.rpipe, indent, space); 691 }, 692 693 ast.Node.Id.PointerPayload => { 694 const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); 695 696 try renderToken(tree, stream, payload.lpipe, indent, Space.None); 697 if (payload.ptr_token) |ptr_token| { 698 try renderToken(tree, stream, ptr_token, indent, Space.None); 699 } 700 try renderExpression(allocator, stream, tree, indent, payload.value_symbol, Space.None); 701 try renderToken(tree, stream, payload.rpipe, indent, space); 702 }, 703 704 ast.Node.Id.PointerIndexPayload => { 705 const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); 706 707 try renderToken(tree, stream, payload.lpipe, indent, Space.None); 708 if (payload.ptr_token) |ptr_token| { 709 try renderToken(tree, stream, ptr_token, indent, Space.None); 710 } 711 try renderExpression(allocator, stream, tree, indent, payload.value_symbol, Space.None); 712 713 if (payload.index_symbol) |index_symbol| { 714 const comma = tree.nextToken(payload.value_symbol.lastToken()); 715 716 try renderToken(tree, stream, comma, indent, Space.Space); 717 try renderExpression(allocator, stream, tree, indent, index_symbol, Space.None); 718 } 719 720 try renderToken(tree, stream, payload.rpipe, indent, space); 721 }, 722 723 ast.Node.Id.GroupedExpression => { 724 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); 725 726 try renderToken(tree, stream, grouped_expr.lparen, indent, Space.None); 727 try renderExpression(allocator, stream, tree, indent, grouped_expr.expr, Space.None); 728 try renderToken(tree, stream, grouped_expr.rparen, indent, space); 729 }, 730 731 ast.Node.Id.FieldInitializer => { 732 const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); 733 734 try renderToken(tree, stream, field_init.period_token, indent, Space.None); // . 735 try renderToken(tree, stream, field_init.name_token, indent, Space.Space); // name 736 try renderToken(tree, stream, tree.nextToken(field_init.name_token), indent, Space.Space); // = 737 try renderExpression(allocator, stream, tree, indent, field_init.expr, space); 738 }, 739 740 ast.Node.Id.IntegerLiteral => { 741 const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); 742 try renderToken(tree, stream, integer_literal.token, indent, space); 743 }, 744 ast.Node.Id.FloatLiteral => { 745 const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); 746 try renderToken(tree, stream, float_literal.token, indent, space); 747 }, 748 ast.Node.Id.StringLiteral => { 749 const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); 750 try renderToken(tree, stream, string_literal.token, indent, space); 751 }, 752 ast.Node.Id.CharLiteral => { 753 const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); 754 try renderToken(tree, stream, char_literal.token, indent, space); 755 }, 756 ast.Node.Id.BoolLiteral => { 757 const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); 758 try renderToken(tree, stream, bool_literal.token, indent, space); 759 }, 760 ast.Node.Id.NullLiteral => { 761 const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); 762 try renderToken(tree, stream, null_literal.token, indent, space); 763 }, 764 ast.Node.Id.ThisLiteral => { 765 const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); 766 try renderToken(tree, stream, this_literal.token, indent, space); 767 }, 768 ast.Node.Id.Unreachable => { 769 const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); 770 try renderToken(tree, stream, unreachable_node.token, indent, space); 771 }, 772 ast.Node.Id.ErrorType => { 773 const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); 774 try renderToken(tree, stream, error_type.token, indent, space); 775 }, 776 ast.Node.Id.VarType => { 777 const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); 778 try renderToken(tree, stream, var_type.token, indent, space); 779 }, 780 ast.Node.Id.ContainerDecl => { 781 const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); 782 783 if (container_decl.layout_token) |layout_token| { 784 try renderToken(tree, stream, layout_token, indent, Space.Space); 785 } 786 787 switch (container_decl.init_arg_expr) { 788 ast.Node.ContainerDecl.InitArg.None => { 789 try renderToken(tree, stream, container_decl.kind_token, indent, Space.Space); // union 790 }, 791 ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { 792 try renderToken(tree, stream, container_decl.kind_token, indent, Space.None); // union 793 794 const lparen = tree.nextToken(container_decl.kind_token); 795 const enum_token = tree.nextToken(lparen); 796 797 try renderToken(tree, stream, lparen, indent, Space.None); // ( 798 try renderToken(tree, stream, enum_token, indent, Space.None); // enum 799 800 if (enum_tag_type) |expr| { 801 try renderToken(tree, stream, tree.nextToken(enum_token), indent, Space.None); // ( 802 try renderExpression(allocator, stream, tree, indent, expr, Space.None); 803 804 const rparen = tree.nextToken(expr.lastToken()); 805 try renderToken(tree, stream, rparen, indent, Space.None); // ) 806 try renderToken(tree, stream, tree.nextToken(rparen), indent, Space.Space); // ) 807 } else { 808 try renderToken(tree, stream, tree.nextToken(enum_token), indent, Space.Space); // ) 809 } 810 }, 811 ast.Node.ContainerDecl.InitArg.Type => |type_expr| { 812 try renderToken(tree, stream, container_decl.kind_token, indent, Space.None); // union 813 814 const lparen = tree.nextToken(container_decl.kind_token); 815 const rparen = tree.nextToken(type_expr.lastToken()); 816 817 try renderToken(tree, stream, lparen, indent, Space.None); // ( 818 try renderExpression(allocator, stream, tree, indent, type_expr, Space.None); 819 try renderToken(tree, stream, rparen, indent, Space.Space); // ) 820 }, 821 } 822 823 if (container_decl.fields_and_decls.len == 0) { 824 try renderToken(tree, stream, container_decl.lbrace_token, indent + indent_delta, Space.None); // { 825 try renderToken(tree, stream, container_decl.rbrace_token, indent, space); // } 826 } else { 827 const new_indent = indent + indent_delta; 828 try renderToken(tree, stream, container_decl.lbrace_token, new_indent, Space.Newline); // { 829 830 var it = container_decl.fields_and_decls.iterator(0); 831 while (it.next()) |decl| { 832 try stream.writeByteNTimes(' ', new_indent); 833 try renderTopLevelDecl(allocator, stream, tree, new_indent, decl.*); 834 835 if (it.peek()) |next_decl| { 836 try renderExtraNewline(tree, stream, next_decl.*); 837 } 838 } 839 840 try stream.writeByteNTimes(' ', indent); 841 try renderToken(tree, stream, container_decl.rbrace_token, indent, space); // } 842 } 843 }, 844 845 ast.Node.Id.ErrorSetDecl => { 846 const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); 847 848 const lbrace = tree.nextToken(err_set_decl.error_token); 849 850 if (err_set_decl.decls.len == 0) { 851 try renderToken(tree, stream, err_set_decl.error_token, indent, Space.None); 852 try renderToken(tree, stream, lbrace, indent, Space.None); 853 try renderToken(tree, stream, err_set_decl.rbrace_token, indent, space); 854 return; 855 } 856 857 if (err_set_decl.decls.len == 1) blk: { 858 const node = err_set_decl.decls.at(0).*; 859 860 // if there are any doc comments or same line comments 861 // don't try to put it all on one line 862 if (node.cast(ast.Node.ErrorTag)) |tag| { 863 if (tag.doc_comments != null) break :blk; 864 } else { 865 break :blk; 866 } 867 868 try renderToken(tree, stream, err_set_decl.error_token, indent, Space.None); // error 869 try renderToken(tree, stream, lbrace, indent, Space.None); // { 870 try renderExpression(allocator, stream, tree, indent, node, Space.None); 871 try renderToken(tree, stream, err_set_decl.rbrace_token, indent, space); // } 872 return; 873 } 874 875 try renderToken(tree, stream, err_set_decl.error_token, indent, Space.None); // error 876 try renderToken(tree, stream, lbrace, indent, Space.Newline); // { 877 const new_indent = indent + indent_delta; 878 879 var it = err_set_decl.decls.iterator(0); 880 while (it.next()) |node| { 881 try stream.writeByteNTimes(' ', new_indent); 882 883 if (it.peek()) |next_node| { 884 try renderExpression(allocator, stream, tree, new_indent, node.*, Space.None); 885 try renderToken(tree, stream, tree.nextToken(node.*.lastToken()), new_indent, Space.Newline); // , 886 887 try renderExtraNewline(tree, stream, next_node.*); 888 } else { 889 try renderTrailingComma(allocator, stream, tree, new_indent, node.*, Space.Newline); 890 } 891 } 892 893 try stream.writeByteNTimes(' ', indent); 894 try renderToken(tree, stream, err_set_decl.rbrace_token, indent, space); // } 895 }, 896 897 ast.Node.Id.ErrorTag => { 898 const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base); 899 900 try renderDocComments(tree, stream, tag, indent); 901 try renderToken(tree, stream, tag.name_token, indent, space); // name 902 }, 903 904 ast.Node.Id.MultilineStringLiteral => { 905 const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); 906 907 var skip_first_indent = true; 908 if (tree.tokens.at(multiline_str_literal.firstToken() - 1).id != Token.Id.LineComment) { 909 try stream.print("\n"); 910 skip_first_indent = false; 911 } 912 913 var i: usize = 0; 914 while (i < multiline_str_literal.lines.len) : (i += 1) { 915 const t = multiline_str_literal.lines.at(i).*; 916 if (!skip_first_indent) { 917 try stream.writeByteNTimes(' ', indent + indent_delta); 918 } 919 try renderToken(tree, stream, t, indent, Space.None); 920 skip_first_indent = false; 921 } 922 try stream.writeByteNTimes(' ', indent); 923 }, 924 ast.Node.Id.UndefinedLiteral => { 925 const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); 926 try renderToken(tree, stream, undefined_literal.token, indent, space); 927 }, 928 929 ast.Node.Id.BuiltinCall => { 930 const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); 931 932 try renderToken(tree, stream, builtin_call.builtin_token, indent, Space.None); // @name 933 try renderToken(tree, stream, tree.nextToken(builtin_call.builtin_token), indent, Space.None); // ( 934 935 var it = builtin_call.params.iterator(0); 936 while (it.next()) |param_node| { 937 try renderExpression(allocator, stream, tree, indent, param_node.*, Space.None); 938 939 if (it.peek() != null) { 940 const comma_token = tree.nextToken(param_node.*.lastToken()); 941 try renderToken(tree, stream, comma_token, indent, Space.Space); // , 942 } 943 } 944 try renderToken(tree, stream, builtin_call.rparen_token, indent, space); // ) 945 }, 946 947 ast.Node.Id.FnProto => { 948 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); 949 950 if (fn_proto.visib_token) |visib_token_index| { 951 const visib_token = tree.tokens.at(visib_token_index); 952 assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); 953 954 try renderToken(tree, stream, visib_token_index, indent, Space.Space); // pub 955 } 956 957 if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { 958 try renderToken(tree, stream, extern_export_inline_token, indent, Space.Space); // extern/export 959 } 960 961 if (fn_proto.lib_name) |lib_name| { 962 try renderExpression(allocator, stream, tree, indent, lib_name, Space.Space); 963 } 964 965 if (fn_proto.cc_token) |cc_token| { 966 try renderToken(tree, stream, cc_token, indent, Space.Space); // stdcallcc 967 } 968 969 if (fn_proto.async_attr) |async_attr| { 970 try renderExpression(allocator, stream, tree, indent, &async_attr.base, Space.Space); 971 } 972 973 if (fn_proto.name_token) |name_token| blk: { 974 try renderToken(tree, stream, fn_proto.fn_token, indent, Space.Space); // fn 975 try renderToken(tree, stream, name_token, indent, Space.None); // name 976 try renderToken(tree, stream, tree.nextToken(name_token), indent, Space.None); // ( 977 } else blk: { 978 try renderToken(tree, stream, fn_proto.fn_token, indent, Space.None); // fn 979 try renderToken(tree, stream, tree.nextToken(fn_proto.fn_token), indent, Space.None); // ( 980 } 981 982 var it = fn_proto.params.iterator(0); 983 while (it.next()) |param_decl_node| { 984 try renderParamDecl(allocator, stream, tree, indent, param_decl_node.*); 985 986 if (it.peek() != null) { 987 const comma = tree.nextToken(param_decl_node.*.lastToken()); 988 try renderToken(tree, stream, comma, indent, Space.Space); // , 989 } 990 } 991 992 const rparen = tree.prevToken(switch (fn_proto.return_type) { 993 ast.Node.FnProto.ReturnType.Explicit => |node| node.firstToken(), 994 ast.Node.FnProto.ReturnType.InferErrorSet => |node| tree.prevToken(node.firstToken()), 995 }); 996 try renderToken(tree, stream, rparen, indent, Space.Space); // ) 997 998 if (fn_proto.align_expr) |align_expr| { 999 const align_rparen = tree.nextToken(align_expr.lastToken()); 1000 const align_lparen = tree.prevToken(align_expr.firstToken()); 1001 const align_kw = tree.prevToken(align_lparen); 1002 1003 try renderToken(tree, stream, align_kw, indent, Space.None); // align 1004 try renderToken(tree, stream, align_lparen, indent, Space.None); // ( 1005 try renderExpression(allocator, stream, tree, indent, align_expr, Space.None); 1006 try renderToken(tree, stream, align_rparen, indent, Space.Space); // ) 1007 } 1008 1009 switch (fn_proto.return_type) { 1010 ast.Node.FnProto.ReturnType.Explicit => |node| { 1011 try renderExpression(allocator, stream, tree, indent, node, space); 1012 }, 1013 ast.Node.FnProto.ReturnType.InferErrorSet => |node| { 1014 try renderToken(tree, stream, tree.prevToken(node.firstToken()), indent, Space.None); // ! 1015 try renderExpression(allocator, stream, tree, indent, node, space); 1016 }, 1017 } 1018 }, 1019 1020 ast.Node.Id.PromiseType => { 1021 const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); 1022 1023 if (promise_type.result) |result| { 1024 try renderToken(tree, stream, promise_type.promise_token, indent, Space.None); // promise 1025 try renderToken(tree, stream, result.arrow_token, indent, Space.None); // -> 1026 try renderExpression(allocator, stream, tree, indent, result.return_type, space); 1027 } else { 1028 try renderToken(tree, stream, promise_type.promise_token, indent, space); // promise 1029 } 1030 }, 1031 1032 ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes 1033 1034 ast.Node.Id.Switch => { 1035 const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); 1036 1037 try renderToken(tree, stream, switch_node.switch_token, indent, Space.Space); // switch 1038 try renderToken(tree, stream, tree.nextToken(switch_node.switch_token), indent, Space.None); // ( 1039 1040 const rparen = tree.nextToken(switch_node.expr.lastToken()); 1041 const lbrace = tree.nextToken(rparen); 1042 1043 if (switch_node.cases.len == 0) { 1044 try renderExpression(allocator, stream, tree, indent, switch_node.expr, Space.None); 1045 try renderToken(tree, stream, rparen, indent, Space.Space); // ) 1046 try renderToken(tree, stream, lbrace, indent, Space.None); // { 1047 try renderToken(tree, stream, switch_node.rbrace, indent, space); // } 1048 return; 1049 } 1050 1051 try renderExpression(allocator, stream, tree, indent, switch_node.expr, Space.None); 1052 1053 const new_indent = indent + indent_delta; 1054 1055 try renderToken(tree, stream, rparen, indent, Space.Space); // ) 1056 try renderToken(tree, stream, lbrace, new_indent, Space.Newline); // { 1057 1058 var it = switch_node.cases.iterator(0); 1059 while (it.next()) |node| { 1060 try stream.writeByteNTimes(' ', new_indent); 1061 try renderExpression(allocator, stream, tree, new_indent, node.*, Space.Newline); 1062 1063 if (it.peek()) |next_node| { 1064 try renderExtraNewline(tree, stream, next_node.*); 1065 } 1066 } 1067 1068 try stream.writeByteNTimes(' ', indent); 1069 try renderToken(tree, stream, switch_node.rbrace, indent, space); // } 1070 }, 1071 1072 ast.Node.Id.SwitchCase => { 1073 const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); 1074 1075 assert(switch_case.items.len != 0); 1076 const src_has_trailing_comma = blk: { 1077 const last_node = switch_case.items.at(switch_case.items.len - 1).*; 1078 const maybe_comma = tree.nextToken(last_node.lastToken()); 1079 break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma; 1080 }; 1081 1082 if (switch_case.items.len == 1 or !src_has_trailing_comma) { 1083 var it = switch_case.items.iterator(0); 1084 while (it.next()) |node| { 1085 if (it.peek()) |next_node| { 1086 try renderExpression(allocator, stream, tree, indent, node.*, Space.None); 1087 1088 const comma_token = tree.nextToken(node.*.lastToken()); 1089 try renderToken(tree, stream, comma_token, indent, Space.Space); // , 1090 try renderExtraNewline(tree, stream, next_node.*); 1091 } else { 1092 try renderExpression(allocator, stream, tree, indent, node.*, Space.Space); 1093 } 1094 } 1095 } else { 1096 var it = switch_case.items.iterator(0); 1097 while (true) { 1098 const node = ??it.next(); 1099 if (it.peek()) |next_node| { 1100 try renderExpression(allocator, stream, tree, indent, node.*, Space.None); 1101 1102 const comma_token = tree.nextToken(node.*.lastToken()); 1103 try renderToken(tree, stream, comma_token, indent, Space.Newline); // , 1104 try renderExtraNewline(tree, stream, next_node.*); 1105 try stream.writeByteNTimes(' ', indent); 1106 } else { 1107 try renderTrailingComma(allocator, stream, tree, indent, node.*, Space.Space); 1108 break; 1109 } 1110 } 1111 } 1112 1113 try renderToken(tree, stream, switch_case.arrow_token, indent, Space.Space); // => 1114 1115 if (switch_case.payload) |payload| { 1116 try renderExpression(allocator, stream, tree, indent, payload, Space.Space); 1117 } 1118 1119 try renderTrailingComma(allocator, stream, tree, indent, switch_case.expr, space); 1120 }, 1121 ast.Node.Id.SwitchElse => { 1122 const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); 1123 try renderToken(tree, stream, switch_else.token, indent, space); 1124 }, 1125 ast.Node.Id.Else => { 1126 const else_node = @fieldParentPtr(ast.Node.Else, "base", base); 1127 1128 const block_body = switch (else_node.body.id) { 1129 ast.Node.Id.Block, 1130 ast.Node.Id.If, 1131 ast.Node.Id.For, 1132 ast.Node.Id.While, 1133 ast.Node.Id.Switch => true, 1134 else => false, 1135 }; 1136 1137 const after_else_space = if (block_body or else_node.payload != null) Space.Space else Space.Newline; 1138 try renderToken(tree, stream, else_node.else_token, indent, after_else_space); 1139 1140 if (else_node.payload) |payload| { 1141 const payload_space = if (block_body) Space.Space else Space.Newline; 1142 try renderExpression(allocator, stream, tree, indent, payload, Space.Space); 1143 } 1144 1145 if (block_body) { 1146 try renderExpression(allocator, stream, tree, indent, else_node.body, space); 1147 } else { 1148 try stream.writeByteNTimes(' ', indent + indent_delta); 1149 try renderExpression(allocator, stream, tree, indent, else_node.body, space); 1150 } 1151 }, 1152 1153 ast.Node.Id.While => { 1154 const while_node = @fieldParentPtr(ast.Node.While, "base", base); 1155 1156 if (while_node.label) |label| { 1157 try renderToken(tree, stream, label, indent, Space.None); // label 1158 try renderToken(tree, stream, tree.nextToken(label), indent, Space.Space); // : 1159 } 1160 1161 if (while_node.inline_token) |inline_token| { 1162 try renderToken(tree, stream, inline_token, indent, Space.Space); // inline 1163 } 1164 1165 try renderToken(tree, stream, while_node.while_token, indent, Space.Space); // while 1166 try renderToken(tree, stream, tree.nextToken(while_node.while_token), indent, Space.None); // ( 1167 try renderExpression(allocator, stream, tree, indent, while_node.condition, Space.None); 1168 1169 { 1170 const rparen = tree.nextToken(while_node.condition.lastToken()); 1171 const rparen_space = if (while_node.payload != null or while_node.continue_expr != null or 1172 while_node.body.id == ast.Node.Id.Block) Space.Space else Space.Newline; 1173 try renderToken(tree, stream, rparen, indent, rparen_space); // ) 1174 } 1175 1176 if (while_node.payload) |payload| { 1177 try renderExpression(allocator, stream, tree, indent, payload, Space.Space); 1178 } 1179 1180 if (while_node.continue_expr) |continue_expr| { 1181 const rparen = tree.nextToken(continue_expr.lastToken()); 1182 const lparen = tree.prevToken(continue_expr.firstToken()); 1183 const colon = tree.prevToken(lparen); 1184 1185 try renderToken(tree, stream, colon, indent, Space.Space); // : 1186 try renderToken(tree, stream, lparen, indent, Space.None); // ( 1187 1188 try renderExpression(allocator, stream, tree, indent, continue_expr, Space.None); 1189 1190 const rparen_space = if (while_node.body.id == ast.Node.Id.Block) Space.Space else Space.Newline; 1191 try renderToken(tree, stream, rparen, indent, rparen_space); // ) 1192 } 1193 1194 const body_space = blk: { 1195 if (while_node.@"else" != null) { 1196 break :blk if (while_node.body.id == ast.Node.Id.Block) Space.Space else Space.Newline; 1197 } else { 1198 break :blk space; 1199 } 1200 }; 1201 1202 if (while_node.body.id == ast.Node.Id.Block) { 1203 try renderExpression(allocator, stream, tree, indent, while_node.body, body_space); 1204 } else { 1205 try stream.writeByteNTimes(' ', indent + indent_delta); 1206 try renderExpression(allocator, stream, tree, indent, while_node.body, body_space); 1207 } 1208 1209 if (while_node.@"else") |@"else"| { 1210 if (while_node.body.id == ast.Node.Id.Block) { 1211 } else { 1212 try stream.writeByteNTimes(' ', indent); 1213 } 1214 1215 try renderExpression(allocator, stream, tree, indent, &@"else".base, space); 1216 } 1217 }, 1218 1219 ast.Node.Id.For => { 1220 const for_node = @fieldParentPtr(ast.Node.For, "base", base); 1221 1222 if (for_node.label) |label| { 1223 try renderToken(tree, stream, label, indent, Space.None); // label 1224 try renderToken(tree, stream, tree.nextToken(label), indent, Space.Space); // : 1225 } 1226 1227 if (for_node.inline_token) |inline_token| { 1228 try renderToken(tree, stream, inline_token, indent, Space.Space); // inline 1229 } 1230 1231 try renderToken(tree, stream, for_node.for_token, indent, Space.Space); // for 1232 try renderToken(tree, stream, tree.nextToken(for_node.for_token), indent, Space.None); // ( 1233 try renderExpression(allocator, stream, tree, indent, for_node.array_expr, Space.None); 1234 1235 const rparen = tree.nextToken(for_node.array_expr.lastToken()); 1236 const rparen_space = if (for_node.payload != null or 1237 for_node.body.id == ast.Node.Id.Block) Space.Space else Space.Newline; 1238 try renderToken(tree, stream, rparen, indent, rparen_space); // ) 1239 1240 if (for_node.payload) |payload| { 1241 const payload_space = if (for_node.body.id == ast.Node.Id.Block) Space.Space else Space.Newline; 1242 try renderExpression(allocator, stream, tree, indent, payload, payload_space); 1243 } 1244 1245 const body_space = blk: { 1246 if (for_node.@"else" != null) { 1247 if (for_node.body.id == ast.Node.Id.Block) { 1248 break :blk Space.Space; 1249 } else { 1250 break :blk Space.Newline; 1251 } 1252 } else { 1253 break :blk space; 1254 } 1255 }; 1256 if (for_node.body.id == ast.Node.Id.Block) { 1257 try renderExpression(allocator, stream, tree, indent, for_node.body, body_space); 1258 } else { 1259 try stream.writeByteNTimes(' ', indent + indent_delta); 1260 try renderExpression(allocator, stream, tree, indent, for_node.body, body_space); 1261 } 1262 1263 if (for_node.@"else") |@"else"| { 1264 if (for_node.body.id != ast.Node.Id.Block) { 1265 try stream.writeByteNTimes(' ', indent); 1266 } 1267 1268 try renderExpression(allocator, stream, tree, indent, &@"else".base, space); 1269 } 1270 }, 1271 1272 ast.Node.Id.If => { 1273 const if_node = @fieldParentPtr(ast.Node.If, "base", base); 1274 1275 try renderToken(tree, stream, if_node.if_token, indent, Space.Space); 1276 try renderToken(tree, stream, tree.prevToken(if_node.condition.firstToken()), indent, Space.None); 1277 1278 try renderExpression(allocator, stream, tree, indent, if_node.condition, Space.None); 1279 try renderToken(tree, stream, tree.nextToken(if_node.condition.lastToken()), indent, Space.Space); 1280 1281 if (if_node.payload) |payload| { 1282 try renderExpression(allocator, stream, tree, indent, payload, Space.Space); 1283 } 1284 1285 switch (if_node.body.id) { 1286 ast.Node.Id.Block, 1287 ast.Node.Id.If, 1288 ast.Node.Id.For, 1289 ast.Node.Id.While, 1290 ast.Node.Id.Switch => { 1291 if (if_node.@"else") |@"else"| { 1292 if (if_node.body.id == ast.Node.Id.Block) { 1293 try renderExpression(allocator, stream, tree, indent, if_node.body, Space.Space); 1294 } else { 1295 try renderExpression(allocator, stream, tree, indent, if_node.body, Space.Newline); 1296 try stream.writeByteNTimes(' ', indent); 1297 } 1298 1299 try renderExpression(allocator, stream, tree, indent, &@"else".base, space); 1300 } else { 1301 try renderExpression(allocator, stream, tree, indent, if_node.body, space); 1302 } 1303 }, 1304 else => { 1305 if (if_node.@"else") |@"else"| { 1306 try renderExpression(allocator, stream, tree, indent, if_node.body, Space.Space); 1307 try renderToken(tree, stream, @"else".else_token, indent, Space.Space); 1308 1309 if (@"else".payload) |payload| { 1310 try renderExpression(allocator, stream, tree, indent, payload, Space.Space); 1311 } 1312 1313 try renderExpression(allocator, stream, tree, indent, @"else".body, space); 1314 } else { 1315 try renderExpression(allocator, stream, tree, indent, if_node.body, space); 1316 } 1317 }, 1318 } 1319 }, 1320 1321 ast.Node.Id.Asm => { 1322 const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); 1323 1324 try renderToken(tree, stream, asm_node.asm_token, indent, Space.Space); // asm 1325 1326 if (asm_node.volatile_token) |volatile_token| { 1327 try renderToken(tree, stream, volatile_token, indent, Space.Space); // volatile 1328 try renderToken(tree, stream, tree.nextToken(volatile_token), indent, Space.None); // ( 1329 } else { 1330 try renderToken(tree, stream, tree.nextToken(asm_node.asm_token), indent, Space.None); // ( 1331 } 1332 1333 if (asm_node.outputs.len == 0 and asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) { 1334 try renderExpression(allocator, stream, tree, indent, asm_node.template, Space.None); 1335 try renderToken(tree, stream, asm_node.rparen, indent, space); 1336 return; 1337 } 1338 1339 try renderExpression(allocator, stream, tree, indent, asm_node.template, Space.Newline); 1340 1341 const indent_once = indent + indent_delta; 1342 try stream.writeByteNTimes(' ', indent_once); 1343 1344 const colon1 = tree.nextToken(asm_node.template.lastToken()); 1345 const indent_extra = indent_once + 2; 1346 1347 const colon2 = if (asm_node.outputs.len == 0) blk: { 1348 try renderToken(tree, stream, colon1, indent, Space.Newline); // : 1349 try stream.writeByteNTimes(' ', indent_once); 1350 1351 break :blk tree.nextToken(colon1); 1352 } else blk: { 1353 try renderToken(tree, stream, colon1, indent, Space.Space); // : 1354 1355 var it = asm_node.outputs.iterator(0); 1356 while (true) { 1357 const asm_output = ??it.next(); 1358 const node = &(asm_output.*).base; 1359 1360 if (it.peek()) |next_asm_output| { 1361 try renderExpression(allocator, stream, tree, indent_extra, node, Space.None); 1362 const next_node = &(next_asm_output.*).base; 1363 1364 const comma = tree.prevToken(next_asm_output.*.firstToken()); 1365 try renderToken(tree, stream, comma, indent_extra, Space.Newline); // , 1366 try renderExtraNewline(tree, stream, next_node); 1367 1368 try stream.writeByteNTimes(' ', indent_extra); 1369 } else if (asm_node.inputs.len == 0 and asm_node.clobbers.len == 0) { 1370 try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline); 1371 try stream.writeByteNTimes(' ', indent); 1372 try renderToken(tree, stream, asm_node.rparen, indent, space); 1373 return; 1374 } else { 1375 try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline); 1376 try stream.writeByteNTimes(' ', indent_once); 1377 const comma_or_colon = tree.nextToken(node.lastToken()); 1378 break :blk switch (tree.tokens.at(comma_or_colon).id) { 1379 Token.Id.Comma => tree.nextToken(comma_or_colon), 1380 else => comma_or_colon, 1381 }; 1382 } 1383 } 1384 }; 1385 1386 const colon3 = if (asm_node.inputs.len == 0) blk: { 1387 try renderToken(tree, stream, colon2, indent, Space.Newline); // : 1388 try stream.writeByteNTimes(' ', indent_once); 1389 1390 break :blk tree.nextToken(colon2); 1391 } else blk: { 1392 try renderToken(tree, stream, colon2, indent, Space.Space); // : 1393 1394 var it = asm_node.inputs.iterator(0); 1395 while (true) { 1396 const asm_input = ??it.next(); 1397 const node = &(asm_input.*).base; 1398 1399 if (it.peek()) |next_asm_input| { 1400 try renderExpression(allocator, stream, tree, indent_extra, node, Space.None); 1401 const next_node = &(next_asm_input.*).base; 1402 1403 const comma = tree.prevToken(next_asm_input.*.firstToken()); 1404 try renderToken(tree, stream, comma, indent_extra, Space.Newline); // , 1405 try renderExtraNewline(tree, stream, next_node); 1406 1407 try stream.writeByteNTimes(' ', indent_extra); 1408 } else if (asm_node.clobbers.len == 0) { 1409 try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline); 1410 try stream.writeByteNTimes(' ', indent); 1411 try renderToken(tree, stream, asm_node.rparen, indent, space); // ) 1412 return; 1413 } else { 1414 try renderExpression(allocator, stream, tree, indent_extra, node, Space.Newline); 1415 try stream.writeByteNTimes(' ', indent_once); 1416 const comma_or_colon = tree.nextToken(node.lastToken()); 1417 break :blk switch (tree.tokens.at(comma_or_colon).id) { 1418 Token.Id.Comma => tree.nextToken(comma_or_colon), 1419 else => comma_or_colon, 1420 }; 1421 } 1422 } 1423 }; 1424 1425 try renderToken(tree, stream, colon3, indent, Space.Space); // : 1426 1427 var it = asm_node.clobbers.iterator(0); 1428 while (true) { 1429 const clobber_token = ??it.next(); 1430 1431 if (it.peek() == null) { 1432 try renderToken(tree, stream, clobber_token.*, indent_once, Space.Newline); 1433 try stream.writeByteNTimes(' ', indent); 1434 try renderToken(tree, stream, asm_node.rparen, indent, space); 1435 return; 1436 } else { 1437 try renderToken(tree, stream, clobber_token.*, indent_once, Space.None); 1438 const comma = tree.nextToken(clobber_token.*); 1439 try renderToken(tree, stream, comma, indent_once, Space.Space); // , 1440 } 1441 } 1442 }, 1443 1444 ast.Node.Id.AsmInput => { 1445 const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); 1446 1447 try stream.write("["); 1448 try renderExpression(allocator, stream, tree, indent, asm_input.symbolic_name, Space.None); 1449 try stream.write("] "); 1450 try renderExpression(allocator, stream, tree, indent, asm_input.constraint, Space.None); 1451 try stream.write(" ("); 1452 try renderExpression(allocator, stream, tree, indent, asm_input.expr, Space.None); 1453 try renderToken(tree, stream, asm_input.lastToken(), indent, space); // ) 1454 }, 1455 1456 ast.Node.Id.AsmOutput => { 1457 const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); 1458 1459 try stream.write("["); 1460 try renderExpression(allocator, stream, tree, indent, asm_output.symbolic_name, Space.None); 1461 try stream.write("] "); 1462 try renderExpression(allocator, stream, tree, indent, asm_output.constraint, Space.None); 1463 try stream.write(" ("); 1464 1465 switch (asm_output.kind) { 1466 ast.Node.AsmOutput.Kind.Variable => |variable_name| { 1467 try renderExpression(allocator, stream, tree, indent, &variable_name.base, Space.None); 1468 }, 1469 ast.Node.AsmOutput.Kind.Return => |return_type| { 1470 try stream.write("-> "); 1471 try renderExpression(allocator, stream, tree, indent, return_type, Space.None); 1472 }, 1473 } 1474 1475 try renderToken(tree, stream, asm_output.lastToken(), indent, space); // ) 1476 }, 1477 1478 ast.Node.Id.StructField, 1479 ast.Node.Id.UnionTag, 1480 ast.Node.Id.EnumTag, 1481 ast.Node.Id.Root, 1482 ast.Node.Id.VarDecl, 1483 ast.Node.Id.Use, 1484 ast.Node.Id.TestDecl, 1485 ast.Node.Id.ParamDecl => unreachable, 1486 } 1487 } 1488 1489 fn renderVarDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, 1490 var_decl: &ast.Node.VarDecl) (@typeOf(stream).Child.Error || Error)!void 1491 { 1492 if (var_decl.visib_token) |visib_token| { 1493 try renderToken(tree, stream, visib_token, indent, Space.Space); // pub 1494 } 1495 1496 if (var_decl.extern_export_token) |extern_export_token| { 1497 try renderToken(tree, stream, extern_export_token, indent, Space.Space); // extern 1498 1499 if (var_decl.lib_name) |lib_name| { 1500 try renderExpression(allocator, stream, tree, indent, lib_name, Space.Space); // "lib" 1501 } 1502 } 1503 1504 if (var_decl.comptime_token) |comptime_token| { 1505 try renderToken(tree, stream, comptime_token, indent, Space.Space); // comptime 1506 } 1507 1508 try renderToken(tree, stream, var_decl.mut_token, indent, Space.Space); // var 1509 1510 const name_space = if (var_decl.type_node == null and (var_decl.align_node != null or 1511 var_decl.init_node != null)) Space.Space else Space.None; 1512 try renderToken(tree, stream, var_decl.name_token, indent, name_space); 1513 1514 if (var_decl.type_node) |type_node| { 1515 try renderToken(tree, stream, tree.nextToken(var_decl.name_token), indent, Space.Space); 1516 const s = if (var_decl.align_node != null or var_decl.init_node != null) Space.Space else Space.None; 1517 try renderExpression(allocator, stream, tree, indent, type_node, s); 1518 } 1519 1520 if (var_decl.align_node) |align_node| { 1521 const lparen = tree.prevToken(align_node.firstToken()); 1522 const align_kw = tree.prevToken(lparen); 1523 const rparen = tree.nextToken(align_node.lastToken()); 1524 try renderToken(tree, stream, align_kw, indent, Space.None); // align 1525 try renderToken(tree, stream, lparen, indent, Space.None); // ( 1526 try renderExpression(allocator, stream, tree, indent, align_node, Space.None); 1527 const s = if (var_decl.init_node != null) Space.Space else Space.None; 1528 try renderToken(tree, stream, rparen, indent, s); // ) 1529 } 1530 1531 if (var_decl.init_node) |init_node| { 1532 const s = if (init_node.id == ast.Node.Id.MultilineStringLiteral) Space.None else Space.Space; 1533 try renderToken(tree, stream, var_decl.eq_token, indent, s); // = 1534 try renderExpression(allocator, stream, tree, indent, init_node, Space.None); 1535 } 1536 1537 try renderToken(tree, stream, var_decl.semicolon_token, indent, Space.Newline); 1538 } 1539 1540 fn renderParamDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { 1541 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); 1542 1543 if (param_decl.comptime_token) |comptime_token| { 1544 try renderToken(tree, stream, comptime_token, indent, Space.Space); 1545 } 1546 if (param_decl.noalias_token) |noalias_token| { 1547 try renderToken(tree, stream, noalias_token, indent, Space.Space); 1548 } 1549 if (param_decl.name_token) |name_token| { 1550 try renderToken(tree, stream, name_token, indent, Space.None); 1551 try renderToken(tree, stream, tree.nextToken(name_token), indent, Space.Space); // : 1552 } 1553 if (param_decl.var_args_token) |var_args_token| { 1554 try renderToken(tree, stream, var_args_token, indent, Space.None); 1555 } else { 1556 try renderExpression(allocator, stream, tree, indent, param_decl.type_node, Space.None); 1557 } 1558 } 1559 1560 fn renderStatement(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { 1561 switch (base.id) { 1562 ast.Node.Id.VarDecl => { 1563 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); 1564 try renderVarDecl(allocator, stream, tree, indent, var_decl); 1565 }, 1566 else => { 1567 if (base.requireSemiColon()) { 1568 try renderExpression(allocator, stream, tree, indent, base, Space.None); 1569 1570 const semicolon_index = tree.nextToken(base.lastToken()); 1571 assert(tree.tokens.at(semicolon_index).id == Token.Id.Semicolon); 1572 try renderToken(tree, stream, semicolon_index, indent, Space.Newline); 1573 } else { 1574 try renderExpression(allocator, stream, tree, indent, base, Space.Newline); 1575 } 1576 }, 1577 } 1578 } 1579 1580 const Space = enum { 1581 None, 1582 Newline, 1583 Space, 1584 NoNewline, 1585 NoIndent, 1586 NoComment, 1587 IgnoreEmptyComment, 1588 }; 1589 1590 fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { 1591 var token = tree.tokens.at(token_index); 1592 try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(token), " ")); 1593 1594 if (space == Space.NoComment) return; 1595 1596 var next_token = tree.tokens.at(token_index + 1); 1597 if (next_token.id != Token.Id.LineComment) { 1598 switch (space) { 1599 Space.None, Space.NoNewline, Space.NoIndent => return, 1600 Space.Newline => { 1601 if (next_token.id == Token.Id.MultilineStringLiteralLine) { 1602 return; 1603 } else { 1604 return stream.write("\n"); 1605 } 1606 }, 1607 Space.Space, Space.IgnoreEmptyComment => return stream.writeByte(' '), 1608 Space.NoComment => unreachable, 1609 } 1610 } 1611 1612 const comment_is_empty = mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ").len == 2; 1613 if (comment_is_empty) { 1614 switch (space) { 1615 Space.IgnoreEmptyComment => return stream.writeByte(' '), 1616 Space.Newline => return stream.writeByte('\n'), 1617 else => {}, 1618 } 1619 } 1620 1621 var loc = tree.tokenLocationPtr(token.end, next_token); 1622 var offset: usize = 1; 1623 if (loc.line == 0) { 1624 try stream.print(" {}", mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ")); 1625 offset = 2; 1626 token = next_token; 1627 next_token = tree.tokens.at(token_index + offset); 1628 if (next_token.id != Token.Id.LineComment) { 1629 switch (space) { 1630 Space.None, Space.Space => { 1631 try stream.writeByte('\n'); 1632 const after_comment_token = tree.tokens.at(token_index + offset); 1633 const next_line_indent = switch (after_comment_token.id) { 1634 Token.Id.RParen, Token.Id.RBrace, Token.Id.RBracket => indent, 1635 else => indent + indent_delta, 1636 }; 1637 try stream.writeByteNTimes(' ', next_line_indent); 1638 }, 1639 Space.Newline, Space.NoIndent => { 1640 if (next_token.id == Token.Id.MultilineStringLiteralLine) { 1641 return; 1642 } else { 1643 return stream.write("\n"); 1644 } 1645 }, 1646 Space.NoNewline => {}, 1647 Space.NoComment, Space.IgnoreEmptyComment => unreachable, 1648 } 1649 return; 1650 } 1651 loc = tree.tokenLocationPtr(token.end, next_token); 1652 } 1653 1654 while (true) { 1655 assert(loc.line != 0); 1656 const newline_count = if (loc.line == 1) u8(1) else u8(2); 1657 try stream.writeByteNTimes('\n', newline_count); 1658 try stream.writeByteNTimes(' ', indent); 1659 try stream.write(mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ")); 1660 1661 offset += 1; 1662 token = next_token; 1663 next_token = tree.tokens.at(token_index + offset); 1664 if (next_token.id != Token.Id.LineComment) { 1665 switch (space) { 1666 Space.Newline, Space.NoIndent => { 1667 if (next_token.id == Token.Id.MultilineStringLiteralLine) { 1668 return; 1669 } else { 1670 return stream.write("\n"); 1671 } 1672 }, 1673 Space.None, Space.Space => { 1674 try stream.writeByte('\n'); 1675 1676 const after_comment_token = tree.tokens.at(token_index + offset); 1677 const next_line_indent = switch (after_comment_token.id) { 1678 Token.Id.RParen, Token.Id.RBrace, Token.Id.RBracket => indent - indent_delta, 1679 else => indent, 1680 }; 1681 try stream.writeByteNTimes(' ', next_line_indent); 1682 }, 1683 Space.NoNewline => {}, 1684 Space.NoComment, Space.IgnoreEmptyComment => unreachable, 1685 } 1686 return; 1687 } 1688 loc = tree.tokenLocationPtr(token.end, next_token); 1689 } 1690 } 1691 1692 fn renderDocComments(tree: &ast.Tree, stream: var, node: var, indent: usize) (@typeOf(stream).Child.Error || Error)!void { 1693 const comment = node.doc_comments ?? return; 1694 var it = comment.lines.iterator(0); 1695 while (it.next()) |line_token_index| { 1696 try renderToken(tree, stream, line_token_index.*, indent, Space.Newline); 1697 try stream.writeByteNTimes(' ', indent); 1698 } 1699 } 1700 1701 fn renderTrailingComma(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node, 1702 space: Space) (@typeOf(stream).Child.Error || Error)!void 1703 { 1704 const end_token = base.lastToken() + 1; 1705 switch (tree.tokens.at(end_token).id) { 1706 Token.Id.Comma => { 1707 try renderExpression(allocator, stream, tree, indent, base, Space.None); 1708 try renderToken(tree, stream, end_token, indent, space); // , 1709 }, 1710 Token.Id.LineComment => { 1711 try renderExpression(allocator, stream, tree, indent, base, Space.NoComment); 1712 try stream.write(", "); 1713 try renderToken(tree, stream, end_token, indent, space); 1714 }, 1715 else => { 1716 try renderExpression(allocator, stream, tree, indent, base, Space.None); 1717 try stream.write(",\n"); 1718 assert(space == Space.Newline); 1719 }, 1720 } 1721 } 1722 1723 fn renderTrailingCommaAndEmptyComment(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, base: &ast.Node) (@typeOf(stream).Child.Error || Error)!void 1724 { 1725 const end_token = base.lastToken() + 1; 1726 switch (tree.tokens.at(end_token).id) { 1727 Token.Id.Comma => { 1728 try renderExpression(allocator, stream, tree, indent, base, Space.None); 1729 try renderToken(tree, stream, end_token, indent, Space.Space); // , 1730 1731 const next_token = tree.tokens.at(end_token + 1); 1732 if (next_token.id != Token.Id.LineComment) { 1733 try stream.print("//\n"); 1734 } 1735 }, 1736 Token.Id.LineComment => { 1737 try renderExpression(allocator, stream, tree, indent, base, Space.NoComment); 1738 try stream.write(", "); 1739 try renderToken(tree, stream, end_token, indent, Space.Newline); 1740 }, 1741 else => { 1742 try renderExpression(allocator, stream, tree, indent, base, Space.None); 1743 try stream.write(", //\n"); 1744 }, 1745 } 1746 }