| ... | @@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator; | ... | @@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator; |
| 5 | const meta = std.meta; | 5 | const meta = std.meta; |
| 6 | const Ast = std.zig.Ast; | 6 | const Ast = std.zig.Ast; |
| 7 | const Token = std.zig.Token; | 7 | const Token = std.zig.Token; |
| | 8 | const primitives = std.zig.primitives; |
| 8 | | 9 | |
| 9 | const indent_delta = 4; | 10 | const indent_delta = 4; |
| 10 | const asm_indent_delta = 2; | 11 | const asm_indent_delta = 2; |
| ... | @@ -152,8 +153,10 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac | ... | @@ -152,8 +153,10 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac |
| 152 | const test_token = main_tokens[decl]; | 153 | const test_token = main_tokens[decl]; |
| 153 | try renderToken(ais, tree, test_token, .space); | 154 | try renderToken(ais, tree, test_token, .space); |
| 154 | const test_name_tag = token_tags[test_token + 1]; | 155 | const test_name_tag = token_tags[test_token + 1]; |
| 155 | if (test_name_tag == .string_literal or test_name_tag == .identifier) { | 156 | switch (test_name_tag) { |
| 156 | try renderToken(ais, tree, test_token + 1, .space); | 157 | .string_literal => try renderToken(ais, tree, test_token + 1, .space), |
| | 158 | .identifier => try renderIdentifier(ais, tree, test_token + 1, .space, .preserve_when_shadowing), |
| | 159 | else => {}, |
| 157 | } | 160 | } |
| 158 | try renderExpression(gpa, ais, tree, datas[decl].rhs, space); | 161 | try renderExpression(gpa, ais, tree, datas[decl].rhs, space); |
| 159 | }, | 162 | }, |
| ... | @@ -192,11 +195,10 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -192,11 +195,10 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 192 | const lexeme = tokenSliceForRender(tree, token_index); | 195 | const lexeme = tokenSliceForRender(tree, token_index); |
| 193 | if (mem.eql(u8, lexeme, "c_void")) { | 196 | if (mem.eql(u8, lexeme, "c_void")) { |
| 194 | try ais.writer().writeAll("anyopaque"); | 197 | try ais.writer().writeAll("anyopaque"); |
| | 198 | return renderSpace(ais, tree, token_index, lexeme.len, space); |
| 195 | } else { | 199 | } else { |
| 196 | try ais.writer().writeAll(lexeme); | 200 | return renderIdentifier(ais, tree, token_index, space, .preserve_when_shadowing); |
| 197 | } | 201 | } |
| 198 | | | |
| 199 | return renderSpace(ais, tree, token_index, lexeme.len, space); | | |
| 200 | }, | 202 | }, |
| 201 | | 203 | |
| 202 | .number_literal, | 204 | .number_literal, |
| ... | @@ -226,7 +228,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -226,7 +228,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 226 | .error_value => { | 228 | .error_value => { |
| 227 | try renderToken(ais, tree, main_tokens[node], .none); | 229 | try renderToken(ais, tree, main_tokens[node], .none); |
| 228 | try renderToken(ais, tree, main_tokens[node] + 1, .none); | 230 | try renderToken(ais, tree, main_tokens[node] + 1, .none); |
| 229 | return renderToken(ais, tree, main_tokens[node] + 2, space); | 231 | return renderIdentifier(ais, tree, main_tokens[node] + 2, space, .eagerly_unquote); |
| 230 | }, | 232 | }, |
| 231 | | 233 | |
| 232 | .block_two, | 234 | .block_two, |
| ... | @@ -256,7 +258,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -256,7 +258,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 256 | try renderToken(ais, tree, defer_token, .space); | 258 | try renderToken(ais, tree, defer_token, .space); |
| 257 | if (payload_token != 0) { | 259 | if (payload_token != 0) { |
| 258 | try renderToken(ais, tree, payload_token - 1, .none); // | | 260 | try renderToken(ais, tree, payload_token - 1, .none); // | |
| 259 | try renderToken(ais, tree, payload_token, .none); // identifier | 261 | try renderIdentifier(ais, tree, payload_token, .none, .preserve_when_shadowing); // identifier |
| 260 | try renderToken(ais, tree, payload_token + 1, .space); // | | 262 | try renderToken(ais, tree, payload_token + 1, .space); // | |
| 261 | } | 263 | } |
| 262 | return renderExpression(gpa, ais, tree, expr, space); | 264 | return renderExpression(gpa, ais, tree, expr, space); |
| ... | @@ -294,7 +296,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -294,7 +296,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 294 | if (token_tags[fallback_first - 1] == .pipe) { | 296 | if (token_tags[fallback_first - 1] == .pipe) { |
| 295 | try renderToken(ais, tree, main_token, .space); // catch keyword | 297 | try renderToken(ais, tree, main_token, .space); // catch keyword |
| 296 | try renderToken(ais, tree, main_token + 1, .none); // pipe | 298 | try renderToken(ais, tree, main_token + 1, .none); // pipe |
| 297 | try renderToken(ais, tree, main_token + 2, .none); // payload identifier | 299 | try renderIdentifier(ais, tree, main_token + 2, .none, .preserve_when_shadowing); // payload identifier |
| 298 | try renderToken(ais, tree, main_token + 3, after_op_space); // pipe | 300 | try renderToken(ais, tree, main_token + 3, after_op_space); // pipe |
| 299 | } else { | 301 | } else { |
| 300 | assert(token_tags[fallback_first - 1] == .keyword_catch); | 302 | assert(token_tags[fallback_first - 1] == .keyword_catch); |
| ... | @@ -320,7 +322,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -320,7 +322,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 320 | ais.pushIndentOneShot(); | 322 | ais.pushIndentOneShot(); |
| 321 | } | 323 | } |
| 322 | | 324 | |
| 323 | try renderToken(ais, tree, main_token, .none); | 325 | try renderToken(ais, tree, main_token, .none); // . |
| 324 | | 326 | |
| 325 | // This check ensures that zag() is indented in the following example: | 327 | // This check ensures that zag() is indented in the following example: |
| 326 | // const x = foo | 328 | // const x = foo |
| ... | @@ -331,7 +333,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -331,7 +333,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 331 | ais.pushIndentOneShot(); | 333 | ais.pushIndentOneShot(); |
| 332 | } | 334 | } |
| 333 | | 335 | |
| 334 | return renderToken(ais, tree, field_access.rhs, space); | 336 | return renderIdentifier(ais, tree, field_access.rhs, space, .eagerly_unquote); // field |
| 335 | }, | 337 | }, |
| 336 | | 338 | |
| 337 | .error_union, | 339 | .error_union, |
| ... | @@ -514,11 +516,11 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -514,11 +516,11 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 514 | } else if (label_token != 0 and target == 0) { | 516 | } else if (label_token != 0 and target == 0) { |
| 515 | try renderToken(ais, tree, main_token, .space); // break keyword | 517 | try renderToken(ais, tree, main_token, .space); // break keyword |
| 516 | try renderToken(ais, tree, label_token - 1, .none); // colon | 518 | try renderToken(ais, tree, label_token - 1, .none); // colon |
| 517 | try renderToken(ais, tree, label_token, space); // identifier | 519 | try renderIdentifier(ais, tree, label_token, space, .eagerly_unquote); // identifier |
| 518 | } else if (label_token != 0 and target != 0) { | 520 | } else if (label_token != 0 and target != 0) { |
| 519 | try renderToken(ais, tree, main_token, .space); // break keyword | 521 | try renderToken(ais, tree, main_token, .space); // break keyword |
| 520 | try renderToken(ais, tree, label_token - 1, .none); // colon | 522 | try renderToken(ais, tree, label_token - 1, .none); // colon |
| 521 | try renderToken(ais, tree, label_token, .space); // identifier | 523 | try renderIdentifier(ais, tree, label_token, .space, .eagerly_unquote); // identifier |
| 522 | try renderExpression(gpa, ais, tree, target, space); | 524 | try renderExpression(gpa, ais, tree, target, space); |
| 523 | } | 525 | } |
| 524 | }, | 526 | }, |
| ... | @@ -529,7 +531,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -529,7 +531,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 529 | if (label != 0) { | 531 | if (label != 0) { |
| 530 | try renderToken(ais, tree, main_token, .space); // continue | 532 | try renderToken(ais, tree, main_token, .space); // continue |
| 531 | try renderToken(ais, tree, label - 1, .none); // : | 533 | try renderToken(ais, tree, label - 1, .none); // : |
| 532 | return renderToken(ais, tree, label, space); // label | 534 | return renderIdentifier(ais, tree, label, space, .eagerly_unquote); // label |
| 533 | } else { | 535 | } else { |
| 534 | return renderToken(ais, tree, main_token, space); // continue | 536 | return renderToken(ais, tree, main_token, space); // continue |
| 535 | } | 537 | } |
| ... | @@ -590,7 +592,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -590,7 +592,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 590 | // There is exactly one member and no trailing comma or | 592 | // There is exactly one member and no trailing comma or |
| 591 | // comments, so render without surrounding spaces: `error{Foo}` | 593 | // comments, so render without surrounding spaces: `error{Foo}` |
| 592 | try renderToken(ais, tree, lbrace, .none); | 594 | try renderToken(ais, tree, lbrace, .none); |
| 593 | try renderToken(ais, tree, lbrace + 1, .none); // identifier | 595 | try renderIdentifier(ais, tree, lbrace + 1, .none, .eagerly_unquote); // identifier |
| 594 | return renderToken(ais, tree, rbrace, space); | 596 | return renderToken(ais, tree, rbrace, space); |
| 595 | } else if (token_tags[rbrace - 1] == .comma) { | 597 | } else if (token_tags[rbrace - 1] == .comma) { |
| 596 | // There is a trailing comma so render each member on a new line. | 598 | // There is a trailing comma so render each member on a new line. |
| ... | @@ -601,7 +603,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -601,7 +603,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 601 | if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i); | 603 | if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i); |
| 602 | switch (token_tags[i]) { | 604 | switch (token_tags[i]) { |
| 603 | .doc_comment => try renderToken(ais, tree, i, .newline), | 605 | .doc_comment => try renderToken(ais, tree, i, .newline), |
| 604 | .identifier => try renderToken(ais, tree, i, .comma), | 606 | .identifier => try renderIdentifier(ais, tree, i, .comma, .eagerly_unquote), |
| 605 | .comma => {}, | 607 | .comma => {}, |
| 606 | else => unreachable, | 608 | else => unreachable, |
| 607 | } | 609 | } |
| ... | @@ -615,7 +617,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -615,7 +617,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 615 | while (i < rbrace) : (i += 1) { | 617 | while (i < rbrace) : (i += 1) { |
| 616 | switch (token_tags[i]) { | 618 | switch (token_tags[i]) { |
| 617 | .doc_comment => unreachable, // TODO | 619 | .doc_comment => unreachable, // TODO |
| 618 | .identifier => try renderToken(ais, tree, i, .comma_space), | 620 | .identifier => try renderIdentifier(ais, tree, i, .comma_space, .eagerly_unquote), |
| 619 | .comma => {}, | 621 | .comma => {}, |
| 620 | else => unreachable, | 622 | else => unreachable, |
| 621 | } | 623 | } |
| ... | @@ -702,7 +704,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, | ... | @@ -702,7 +704,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, |
| 702 | | 704 | |
| 703 | .enum_literal => { | 705 | .enum_literal => { |
| 704 | try renderToken(ais, tree, main_tokens[node] - 1, .none); // . | 706 | try renderToken(ais, tree, main_tokens[node] - 1, .none); // . |
| 705 | return renderToken(ais, tree, main_tokens[node], space); // name | 707 | return renderIdentifier(ais, tree, main_tokens[node], space, .eagerly_unquote); // name |
| 706 | }, | 708 | }, |
| 707 | | 709 | |
| 708 | .fn_decl => unreachable, | 710 | .fn_decl => unreachable, |
| ... | @@ -887,7 +889,7 @@ fn renderAsmOutput( | ... | @@ -887,7 +889,7 @@ fn renderAsmOutput( |
| 887 | const symbolic_name = main_tokens[asm_output]; | 889 | const symbolic_name = main_tokens[asm_output]; |
| 888 | | 890 | |
| 889 | try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket | 891 | try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket |
| 890 | try renderToken(ais, tree, symbolic_name, .none); // ident | 892 | try renderIdentifier(ais, tree, symbolic_name, .none, .eagerly_unquote); // ident |
| 891 | try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket | 893 | try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket |
| 892 | try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint" | 894 | try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint" |
| 893 | try renderToken(ais, tree, symbolic_name + 3, .none); // lparen | 895 | try renderToken(ais, tree, symbolic_name + 3, .none); // lparen |
| ... | @@ -897,7 +899,7 @@ fn renderAsmOutput( | ... | @@ -897,7 +899,7 @@ fn renderAsmOutput( |
| 897 | try renderExpression(gpa, ais, tree, datas[asm_output].lhs, Space.none); | 899 | try renderExpression(gpa, ais, tree, datas[asm_output].lhs, Space.none); |
| 898 | return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen | 900 | return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen |
| 899 | } else { | 901 | } else { |
| 900 | try renderToken(ais, tree, symbolic_name + 4, .none); // ident | 902 | try renderIdentifier(ais, tree, symbolic_name + 4, .none, .eagerly_unquote); // ident |
| 901 | return renderToken(ais, tree, symbolic_name + 5, space); // rparen | 903 | return renderToken(ais, tree, symbolic_name + 5, space); // rparen |
| 902 | } | 904 | } |
| 903 | } | 905 | } |
| ... | @@ -916,7 +918,7 @@ fn renderAsmInput( | ... | @@ -916,7 +918,7 @@ fn renderAsmInput( |
| 916 | const symbolic_name = main_tokens[asm_input]; | 918 | const symbolic_name = main_tokens[asm_input]; |
| 917 | | 919 | |
| 918 | try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket | 920 | try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket |
| 919 | try renderToken(ais, tree, symbolic_name, .none); // ident | 921 | try renderIdentifier(ais, tree, symbolic_name, .none, .eagerly_unquote); // ident |
| 920 | try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket | 922 | try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket |
| 921 | try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint" | 923 | try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint" |
| 922 | try renderToken(ais, tree, symbolic_name + 3, .none); // lparen | 924 | try renderToken(ais, tree, symbolic_name + 3, .none); // lparen |
| ... | @@ -955,7 +957,7 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec | ... | @@ -955,7 +957,7 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec |
| 955 | Space.space | 957 | Space.space |
| 956 | else | 958 | else |
| 957 | Space.none; | 959 | Space.none; |
| 958 | try renderToken(ais, tree, var_decl.ast.mut_token + 1, name_space); // name | 960 | try renderIdentifier(ais, tree, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name |
| 959 | | 961 | |
| 960 | if (var_decl.ast.type_node != 0) { | 962 | if (var_decl.ast.type_node != 0) { |
| 961 | try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // : | 963 | try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // : |
| ... | @@ -1055,7 +1057,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, | ... | @@ -1055,7 +1057,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, |
| 1055 | const token_tags = tree.tokens.items(.tag); | 1057 | const token_tags = tree.tokens.items(.tag); |
| 1056 | | 1058 | |
| 1057 | if (while_node.label_token) |label| { | 1059 | if (while_node.label_token) |label| { |
| 1058 | try renderToken(ais, tree, label, .none); // label | 1060 | try renderIdentifier(ais, tree, label, .none, .eagerly_unquote); // label |
| 1059 | try renderToken(ais, tree, label + 1, .space); // : | 1061 | try renderToken(ais, tree, label + 1, .space); // : |
| 1060 | } | 1062 | } |
| 1061 | | 1063 | |
| ... | @@ -1080,11 +1082,11 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, | ... | @@ -1080,11 +1082,11 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, |
| 1080 | break :blk payload_token; | 1082 | break :blk payload_token; |
| 1081 | } | 1083 | } |
| 1082 | }; | 1084 | }; |
| 1083 | try renderToken(ais, tree, ident, .none); // identifier | 1085 | try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier |
| 1084 | const pipe = blk: { | 1086 | const pipe = blk: { |
| 1085 | if (token_tags[ident + 1] == .comma) { | 1087 | if (token_tags[ident + 1] == .comma) { |
| 1086 | try renderToken(ais, tree, ident + 1, .space); // , | 1088 | try renderToken(ais, tree, ident + 1, .space); // , |
| 1087 | try renderToken(ais, tree, ident + 2, .none); // index | 1089 | try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // index |
| 1088 | break :blk ident + 3; | 1090 | break :blk ident + 3; |
| 1089 | } else { | 1091 | } else { |
| 1090 | break :blk ident + 1; | 1092 | break :blk ident + 1; |
| ... | @@ -1127,7 +1129,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, | ... | @@ -1127,7 +1129,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, |
| 1127 | if (while_node.error_token) |error_token| { | 1129 | if (while_node.error_token) |error_token| { |
| 1128 | try renderToken(ais, tree, while_node.else_token, .space); // else | 1130 | try renderToken(ais, tree, while_node.else_token, .space); // else |
| 1129 | try renderToken(ais, tree, error_token - 1, .none); // | | 1131 | try renderToken(ais, tree, error_token - 1, .none); // | |
| 1130 | try renderToken(ais, tree, error_token, .none); // identifier | 1132 | try renderIdentifier(ais, tree, error_token, .none, .preserve_when_shadowing); // identifier |
| 1131 | last_else_token = error_token + 1; // | | 1133 | last_else_token = error_token + 1; // | |
| 1132 | } | 1134 | } |
| 1133 | | 1135 | |
| ... | @@ -1163,10 +1165,10 @@ fn renderContainerField( | ... | @@ -1163,10 +1165,10 @@ fn renderContainerField( |
| 1163 | try renderToken(ais, tree, t, .space); // comptime | 1165 | try renderToken(ais, tree, t, .space); // comptime |
| 1164 | } | 1166 | } |
| 1165 | if (field.ast.type_expr == 0 and field.ast.value_expr == 0) { | 1167 | if (field.ast.type_expr == 0 and field.ast.value_expr == 0) { |
| 1166 | return renderTokenComma(ais, tree, field.ast.name_token, space); // name | 1168 | return renderIdentifierComma(ais, tree, field.ast.name_token, space, .eagerly_unquote); // name |
| 1167 | } | 1169 | } |
| 1168 | if (field.ast.type_expr != 0 and field.ast.value_expr == 0) { | 1170 | if (field.ast.type_expr != 0 and field.ast.value_expr == 0) { |
| 1169 | try renderToken(ais, tree, field.ast.name_token, .none); // name | 1171 | try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name |
| 1170 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // : | 1172 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // : |
| 1171 | | 1173 | |
| 1172 | if (field.ast.align_expr != 0) { | 1174 | if (field.ast.align_expr != 0) { |
| ... | @@ -1182,12 +1184,12 @@ fn renderContainerField( | ... | @@ -1182,12 +1184,12 @@ fn renderContainerField( |
| 1182 | } | 1184 | } |
| 1183 | } | 1185 | } |
| 1184 | if (field.ast.type_expr == 0 and field.ast.value_expr != 0) { | 1186 | if (field.ast.type_expr == 0 and field.ast.value_expr != 0) { |
| 1185 | try renderToken(ais, tree, field.ast.name_token, .space); // name | 1187 | try renderIdentifier(ais, tree, field.ast.name_token, .space, .eagerly_unquote); // name |
| 1186 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // = | 1188 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // = |
| 1187 | return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value | 1189 | return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value |
| 1188 | } | 1190 | } |
| 1189 | | 1191 | |
| 1190 | try renderToken(ais, tree, field.ast.name_token, .none); // name | 1192 | try renderIdentifier(ais, tree, field.ast.name_token, .none, .eagerly_unquote); // name |
| 1191 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // : | 1193 | try renderToken(ais, tree, field.ast.name_token + 1, .space); // : |
| 1192 | try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type | 1194 | try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type |
| 1193 | | 1195 | |
| ... | @@ -1294,7 +1296,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt | ... | @@ -1294,7 +1296,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt |
| 1294 | const after_fn_token = fn_proto.ast.fn_token + 1; | 1296 | const after_fn_token = fn_proto.ast.fn_token + 1; |
| 1295 | const lparen = if (token_tags[after_fn_token] == .identifier) blk: { | 1297 | const lparen = if (token_tags[after_fn_token] == .identifier) blk: { |
| 1296 | try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn | 1298 | try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn |
| 1297 | try renderToken(ais, tree, after_fn_token, .none); // name | 1299 | try renderIdentifier(ais, tree, after_fn_token, .none, .preserve_when_shadowing); // name |
| 1298 | break :blk after_fn_token + 1; | 1300 | break :blk after_fn_token + 1; |
| 1299 | } else blk: { | 1301 | } else blk: { |
| 1300 | try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn | 1302 | try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn |
| ... | @@ -1383,7 +1385,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt | ... | @@ -1383,7 +1385,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt |
| 1383 | if (token_tags[last_param_token] == .identifier and | 1385 | if (token_tags[last_param_token] == .identifier and |
| 1384 | token_tags[last_param_token + 1] == .colon) | 1386 | token_tags[last_param_token + 1] == .colon) |
| 1385 | { | 1387 | { |
| 1386 | try renderToken(ais, tree, last_param_token, .none); // name | 1388 | try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name |
| 1387 | last_param_token += 1; | 1389 | last_param_token += 1; |
| 1388 | try renderToken(ais, tree, last_param_token, .space); // : | 1390 | try renderToken(ais, tree, last_param_token, .space); // : |
| 1389 | last_param_token += 1; | 1391 | last_param_token += 1; |
| ... | @@ -1432,7 +1434,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt | ... | @@ -1432,7 +1434,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt |
| 1432 | if (token_tags[last_param_token] == .identifier and | 1434 | if (token_tags[last_param_token] == .identifier and |
| 1433 | token_tags[last_param_token + 1] == .colon) | 1435 | token_tags[last_param_token + 1] == .colon) |
| 1434 | { | 1436 | { |
| 1435 | try renderToken(ais, tree, last_param_token, .none); // name | 1437 | try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name |
| 1436 | last_param_token += 1; | 1438 | last_param_token += 1; |
| 1437 | try renderToken(ais, tree, last_param_token, .space); // : | 1439 | try renderToken(ais, tree, last_param_token, .space); // : |
| 1438 | last_param_token += 1; | 1440 | last_param_token += 1; |
| ... | @@ -1545,7 +1547,7 @@ fn renderSwitchCase( | ... | @@ -1545,7 +1547,7 @@ fn renderSwitchCase( |
| 1545 | else | 1547 | else |
| 1546 | Space.space; | 1548 | Space.space; |
| 1547 | const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space; | 1549 | const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space; |
| 1548 | try renderToken(ais, tree, switch_case.ast.arrow_token, after_arrow_space); | 1550 | try renderToken(ais, tree, switch_case.ast.arrow_token, after_arrow_space); // => |
| 1549 | | 1551 | |
| 1550 | if (switch_case.payload_token) |payload_token| { | 1552 | if (switch_case.payload_token) |payload_token| { |
| 1551 | try renderToken(ais, tree, payload_token - 1, .none); // pipe | 1553 | try renderToken(ais, tree, payload_token - 1, .none); // pipe |
| ... | @@ -1553,10 +1555,10 @@ fn renderSwitchCase( | ... | @@ -1553,10 +1555,10 @@ fn renderSwitchCase( |
| 1553 | if (token_tags[payload_token] == .asterisk) { | 1555 | if (token_tags[payload_token] == .asterisk) { |
| 1554 | try renderToken(ais, tree, payload_token, .none); // asterisk | 1556 | try renderToken(ais, tree, payload_token, .none); // asterisk |
| 1555 | } | 1557 | } |
| 1556 | try renderToken(ais, tree, ident, .none); // identifier | 1558 | try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier |
| 1557 | if (token_tags[ident + 1] == .comma) { | 1559 | if (token_tags[ident + 1] == .comma) { |
| 1558 | try renderToken(ais, tree, ident + 1, .space); // , | 1560 | try renderToken(ais, tree, ident + 1, .space); // , |
| 1559 | try renderToken(ais, tree, ident + 2, .none); // identifier | 1561 | try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // identifier |
| 1560 | try renderToken(ais, tree, ident + 3, pre_target_space); // pipe | 1562 | try renderToken(ais, tree, ident + 3, pre_target_space); // pipe |
| 1561 | } else { | 1563 | } else { |
| 1562 | try renderToken(ais, tree, ident + 1, pre_target_space); // pipe | 1564 | try renderToken(ais, tree, ident + 1, pre_target_space); // pipe |
| ... | @@ -1581,8 +1583,8 @@ fn renderBlock( | ... | @@ -1581,8 +1583,8 @@ fn renderBlock( |
| 1581 | if (token_tags[lbrace - 1] == .colon and | 1583 | if (token_tags[lbrace - 1] == .colon and |
| 1582 | token_tags[lbrace - 2] == .identifier) | 1584 | token_tags[lbrace - 2] == .identifier) |
| 1583 | { | 1585 | { |
| 1584 | try renderToken(ais, tree, lbrace - 2, .none); | 1586 | try renderIdentifier(ais, tree, lbrace - 2, .none, .eagerly_unquote); // identifier |
| 1585 | try renderToken(ais, tree, lbrace - 1, .space); | 1587 | try renderToken(ais, tree, lbrace - 1, .space); // : |
| 1586 | } | 1588 | } |
| 1587 | | 1589 | |
| 1588 | ais.pushIndentNextLine(); | 1590 | ais.pushIndentNextLine(); |
| ... | @@ -1635,7 +1637,7 @@ fn renderStructInit( | ... | @@ -1635,7 +1637,7 @@ fn renderStructInit( |
| 1635 | try renderToken(ais, tree, struct_init.ast.lbrace, .newline); | 1637 | try renderToken(ais, tree, struct_init.ast.lbrace, .newline); |
| 1636 | | 1638 | |
| 1637 | try renderToken(ais, tree, struct_init.ast.lbrace + 1, .none); // . | 1639 | try renderToken(ais, tree, struct_init.ast.lbrace + 1, .none); // . |
| 1638 | try renderToken(ais, tree, struct_init.ast.lbrace + 2, .space); // name | 1640 | try renderIdentifier(ais, tree, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name |
| 1639 | try renderToken(ais, tree, struct_init.ast.lbrace + 3, .space); // = | 1641 | try renderToken(ais, tree, struct_init.ast.lbrace + 3, .space); // = |
| 1640 | try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma); | 1642 | try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma); |
| 1641 | | 1643 | |
| ... | @@ -1643,7 +1645,7 @@ fn renderStructInit( | ... | @@ -1643,7 +1645,7 @@ fn renderStructInit( |
| 1643 | const init_token = tree.firstToken(field_init); | 1645 | const init_token = tree.firstToken(field_init); |
| 1644 | try renderExtraNewlineToken(ais, tree, init_token - 3); | 1646 | try renderExtraNewlineToken(ais, tree, init_token - 3); |
| 1645 | try renderToken(ais, tree, init_token - 3, .none); // . | 1647 | try renderToken(ais, tree, init_token - 3, .none); // . |
| 1646 | try renderToken(ais, tree, init_token - 2, .space); // name | 1648 | try renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name |
| 1647 | try renderToken(ais, tree, init_token - 1, .space); // = | 1649 | try renderToken(ais, tree, init_token - 1, .space); // = |
| 1648 | try renderExpression(gpa, ais, tree, field_init, .comma); | 1650 | try renderExpression(gpa, ais, tree, field_init, .comma); |
| 1649 | } | 1651 | } |
| ... | @@ -1656,7 +1658,7 @@ fn renderStructInit( | ... | @@ -1656,7 +1658,7 @@ fn renderStructInit( |
| 1656 | for (struct_init.ast.fields) |field_init| { | 1658 | for (struct_init.ast.fields) |field_init| { |
| 1657 | const init_token = tree.firstToken(field_init); | 1659 | const init_token = tree.firstToken(field_init); |
| 1658 | try renderToken(ais, tree, init_token - 3, .none); // . | 1660 | try renderToken(ais, tree, init_token - 3, .none); // . |
| 1659 | try renderToken(ais, tree, init_token - 2, .space); // name | 1661 | try renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name |
| 1660 | try renderToken(ais, tree, init_token - 1, .space); // = | 1662 | try renderToken(ais, tree, init_token - 1, .space); // = |
| 1661 | try renderExpression(gpa, ais, tree, field_init, .comma_space); | 1663 | try renderExpression(gpa, ais, tree, field_init, .comma_space); |
| 1662 | } | 1664 | } |
| ... | @@ -2310,6 +2312,19 @@ fn renderTokenComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space) E | ... | @@ -2310,6 +2312,19 @@ fn renderTokenComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space) E |
| 2310 | } | 2312 | } |
| 2311 | } | 2313 | } |
| 2312 | | 2314 | |
| | 2315 | /// Render an identifier, and the comma that follows it, if it is present in the source. |
| | 2316 | /// If a comma is present, and `space` is `Space.comma`, render only a single comma. |
| | 2317 | fn renderIdentifierComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void { |
| | 2318 | const token_tags = tree.tokens.items(.tag); |
| | 2319 | const maybe_comma = token + 1; |
| | 2320 | if (token_tags[maybe_comma] == .comma and space != .comma) { |
| | 2321 | try renderIdentifier(ais, tree, token, .none, quote); |
| | 2322 | return renderToken(ais, tree, maybe_comma, space); |
| | 2323 | } else { |
| | 2324 | return renderIdentifier(ais, tree, token, space, quote); |
| | 2325 | } |
| | 2326 | } |
| | 2327 | |
| 2313 | const Space = enum { | 2328 | const Space = enum { |
| 2314 | /// Output the token lexeme only. | 2329 | /// Output the token lexeme only. |
| 2315 | none, | 2330 | none, |
| ... | @@ -2377,6 +2392,158 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us | ... | @@ -2377,6 +2392,158 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us |
| 2377 | } | 2392 | } |
| 2378 | } | 2393 | } |
| 2379 | | 2394 | |
| | 2395 | const QuoteBehavior = enum { |
| | 2396 | preserve_when_shadowing, |
| | 2397 | eagerly_unquote, |
| | 2398 | }; |
| | 2399 | |
| | 2400 | fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void { |
| | 2401 | const token_tags = tree.tokens.items(.tag); |
| | 2402 | assert(token_tags[token_index] == .identifier); |
| | 2403 | const lexeme = tokenSliceForRender(tree, token_index); |
| | 2404 | if (lexeme[0] != '@') { |
| | 2405 | return renderToken(ais, tree, token_index, space); |
| | 2406 | } |
| | 2407 | |
| | 2408 | assert(lexeme.len >= 3); |
| | 2409 | assert(lexeme[0] == '@'); |
| | 2410 | assert(lexeme[1] == '\"'); |
| | 2411 | assert(lexeme[lexeme.len - 1] == '\"'); |
| | 2412 | const contents = lexeme[2 .. lexeme.len - 1]; // inside the @"" quotation |
| | 2413 | |
| | 2414 | // Empty name can't be unquoted. |
| | 2415 | if (contents.len == 0) { |
| | 2416 | return renderQuotedIdentifier(ais, tree, token_index, space, false); |
| | 2417 | } |
| | 2418 | |
| | 2419 | // Special case for _ which would incorrectly be rejected by isValidId below. |
| | 2420 | if (contents.len == 1 and contents[0] == '_') switch (quote) { |
| | 2421 | .eagerly_unquote => return renderQuotedIdentifier(ais, tree, token_index, space, true), |
| | 2422 | .preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false), |
| | 2423 | }; |
| | 2424 | |
| | 2425 | // Scan the entire name for characters that would (after un-escaping) be illegal in a symbol, |
| | 2426 | // i.e. contents don't match: [A-Za-z_][A-Za-z0-9_]* |
| | 2427 | var contents_i: usize = 0; |
| | 2428 | while (contents_i < contents.len) { |
| | 2429 | switch (contents[contents_i]) { |
| | 2430 | '0'...'9' => if (contents_i == 0) return renderQuotedIdentifier(ais, tree, token_index, space, false), |
| | 2431 | 'A'...'Z', 'a'...'z', '_' => {}, |
| | 2432 | '\\' => { |
| | 2433 | var esc_offset = contents_i; |
| | 2434 | const res = std.zig.string_literal.parseEscapeSequence(contents, &esc_offset); |
| | 2435 | switch (res) { |
| | 2436 | .success => |char| switch (char) { |
| | 2437 | '0'...'9' => if (contents_i == 0) return renderQuotedIdentifier(ais, tree, token_index, space, false), |
| | 2438 | 'A'...'Z', 'a'...'z', '_' => {}, |
| | 2439 | else => return renderQuotedIdentifier(ais, tree, token_index, space, false), |
| | 2440 | }, |
| | 2441 | .failure => return renderQuotedIdentifier(ais, tree, token_index, space, false), |
| | 2442 | } |
| | 2443 | contents_i += esc_offset; |
| | 2444 | continue; |
| | 2445 | }, |
| | 2446 | else => return renderQuotedIdentifier(ais, tree, token_index, space, false), |
| | 2447 | } |
| | 2448 | contents_i += 1; |
| | 2449 | } |
| | 2450 | |
| | 2451 | // Read enough of the name (while un-escaping) to determine if it's a keyword or primitive. |
| | 2452 | // If it's too long to fit in this buffer, we know it's neither and quoting is unnecessary. |
| | 2453 | // If we read the whole thing, we have to do further checks. |
| | 2454 | const longest_keyword_or_primitive_len = comptime blk: { |
| | 2455 | var longest = 0; |
| | 2456 | for (primitives.names.kvs) |kv| { |
| | 2457 | if (kv.key.len > longest) longest = kv.key.len; |
| | 2458 | } |
| | 2459 | for (std.zig.Token.keywords.kvs) |kv| { |
| | 2460 | if (kv.key.len > longest) longest = kv.key.len; |
| | 2461 | } |
| | 2462 | break :blk longest; |
| | 2463 | }; |
| | 2464 | var buf: [longest_keyword_or_primitive_len]u8 = undefined; |
| | 2465 | |
| | 2466 | contents_i = 0; |
| | 2467 | var buf_i: usize = 0; |
| | 2468 | while (contents_i < contents.len and buf_i < longest_keyword_or_primitive_len) { |
| | 2469 | if (contents[contents_i] == '\\') { |
| | 2470 | const res = std.zig.string_literal.parseEscapeSequence(contents, &contents_i).success; |
| | 2471 | buf[buf_i] = @intCast(u8, res); |
| | 2472 | buf_i += 1; |
| | 2473 | } else { |
| | 2474 | buf[buf_i] = contents[contents_i]; |
| | 2475 | contents_i += 1; |
| | 2476 | buf_i += 1; |
| | 2477 | } |
| | 2478 | } |
| | 2479 | |
| | 2480 | // We read the whole thing, so it could be a keyword or primitive. |
| | 2481 | if (contents_i == contents.len) { |
| | 2482 | if (!std.zig.isValidId(buf[0..buf_i])) { |
| | 2483 | return renderQuotedIdentifier(ais, tree, token_index, space, false); |
| | 2484 | } |
| | 2485 | if (primitives.isPrimitive(buf[0..buf_i])) switch (quote) { |
| | 2486 | .eagerly_unquote => return renderQuotedIdentifier(ais, tree, token_index, space, true), |
| | 2487 | .preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false), |
| | 2488 | }; |
| | 2489 | } |
| | 2490 | |
| | 2491 | try renderQuotedIdentifier(ais, tree, token_index, space, true); |
| | 2492 | } |
| | 2493 | |
| | 2494 | // Renders a @"" quoted identifier, normalizing escapes. |
| | 2495 | // Unnecessary escapes are un-escaped, and \u escapes are normalized to \x when they fit. |
| | 2496 | // If unquote is true, the @"" is removed and the result is a bare symbol whose validity is asserted. |
| | 2497 | fn renderQuotedIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space, comptime unquote: bool) !void { |
| | 2498 | const token_tags = tree.tokens.items(.tag); |
| | 2499 | assert(token_tags[token_index] == .identifier); |
| | 2500 | const lexeme = tokenSliceForRender(tree, token_index); |
| | 2501 | assert(lexeme.len >= 3 and lexeme[0] == '@'); |
| | 2502 | |
| | 2503 | if (!unquote) try ais.writer().writeAll("@\""); |
| | 2504 | const contents = lexeme[2 .. lexeme.len - 1]; |
| | 2505 | try renderIdentifierContents(ais.writer(), contents); |
| | 2506 | if (!unquote) try ais.writer().writeByte('\"'); |
| | 2507 | |
| | 2508 | try renderSpace(ais, tree, token_index, lexeme.len, space); |
| | 2509 | } |
| | 2510 | |
| | 2511 | fn renderIdentifierContents(writer: anytype, bytes: []const u8) !void { |
| | 2512 | var pos: usize = 0; |
| | 2513 | while (pos < bytes.len) { |
| | 2514 | const byte = bytes[pos]; |
| | 2515 | switch (byte) { |
| | 2516 | '\\' => { |
| | 2517 | const old_pos = pos; |
| | 2518 | const res = std.zig.string_literal.parseEscapeSequence(bytes, &pos); |
| | 2519 | const escape_sequence = bytes[old_pos..pos]; |
| | 2520 | switch (res) { |
| | 2521 | .success => |codepoint| { |
| | 2522 | if (codepoint <= 0x7f) { |
| | 2523 | const buf = [1]u8{@intCast(u8, codepoint)}; |
| | 2524 | try std.fmt.format(writer, "{}", .{std.zig.fmtEscapes(&buf)}); |
| | 2525 | } else { |
| | 2526 | try writer.writeAll(escape_sequence); |
| | 2527 | } |
| | 2528 | }, |
| | 2529 | .failure => { |
| | 2530 | try writer.writeAll(escape_sequence); |
| | 2531 | }, |
| | 2532 | } |
| | 2533 | }, |
| | 2534 | 0x00...('\\' - 1), ('\\' + 1)...0x7f => { |
| | 2535 | const buf = [1]u8{@intCast(u8, byte)}; |
| | 2536 | try std.fmt.format(writer, "{}", .{std.zig.fmtEscapes(&buf)}); |
| | 2537 | pos += 1; |
| | 2538 | }, |
| | 2539 | 0x80...0xff => { |
| | 2540 | try writer.writeByte(byte); |
| | 2541 | pos += 1; |
| | 2542 | }, |
| | 2543 | } |
| | 2544 | } |
| | 2545 | } |
| | 2546 | |
| 2380 | /// Returns true if there exists a line comment between any of the tokens from | 2547 | /// Returns true if there exists a line comment between any of the tokens from |
| 2381 | /// `start_token` to `end_token`. This is used to determine if e.g. a | 2548 | /// `start_token` to `end_token`. This is used to determine if e.g. a |
| 2382 | /// fn_proto should be wrapped and have a trailing comma inserted even if | 2549 | /// fn_proto should be wrapped and have a trailing comma inserted even if |