authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-15 22:27:23-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-16 17:20:02-07:00
log2d5d2ba4f51fa5300c9807b477cada2c9b3023cd
tree3f554a622d475367895801b96fd2ca3bb2042d73
parent0389b4c7b9b83434daab05e8b94da315c61166ce

std.zig.Render: update it and references


7 files changed, 388 insertions(+), 336 deletions(-)

lib/std/Io/Writer.zig+6
...@@ -2475,6 +2475,12 @@ pub const Allocating = struct {...@@ -2475,6 +2475,12 @@ pub const Allocating = struct {
2475 return result;2475 return result;
2476 }2476 }
24772477
2478 pub fn ensureUnusedCapacity(a: *Allocating, additional_count: usize) Allocator.Error!void {
2479 var list = a.toArrayList();
2480 defer a.setArrayList(list);
2481 return list.ensureUnusedCapacity(a.allocator, additional_count);
2482 }
2483
2478 pub fn toOwnedSlice(a: *Allocating) error{OutOfMemory}![]u8 {2484 pub fn toOwnedSlice(a: *Allocating) error{OutOfMemory}![]u8 {
2479 var list = a.toArrayList();2485 var list = a.toArrayList();
2480 defer a.setArrayList(list);2486 defer a.setArrayList(list);
lib/std/zig/Ast.zig+2-2
...@@ -207,7 +207,7 @@ pub fn renderAlloc(tree: Ast, gpa: Allocator) error{OutOfMemory}![]u8 {...@@ -207,7 +207,7 @@ pub fn renderAlloc(tree: Ast, gpa: Allocator) error{OutOfMemory}![]u8 {
207 var aw: std.io.Writer.Allocating = .init(gpa);207 var aw: std.io.Writer.Allocating = .init(gpa);
208 defer aw.deinit();208 defer aw.deinit();
209 render(tree, gpa, &aw.writer, .{}) catch |err| switch (err) {209 render(tree, gpa, &aw.writer, .{}) catch |err| switch (err) {
210 error.WriteFailed => return error.OutOfMemory,210 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
211 };211 };
212 return aw.toOwnedSlice();212 return aw.toOwnedSlice();
213}213}
...@@ -215,7 +215,7 @@ pub fn renderAlloc(tree: Ast, gpa: Allocator) error{OutOfMemory}![]u8 {...@@ -215,7 +215,7 @@ pub fn renderAlloc(tree: Ast, gpa: Allocator) error{OutOfMemory}![]u8 {
215pub const Render = @import("Ast/Render.zig");215pub const Render = @import("Ast/Render.zig");
216216
217pub fn render(tree: Ast, gpa: Allocator, w: *Writer, fixups: Render.Fixups) Render.Error!void {217pub fn render(tree: Ast, gpa: Allocator, w: *Writer, fixups: Render.Fixups) Render.Error!void {
218 return Render.tree(gpa, w, tree, fixups);218 return Render.renderTree(gpa, w, tree, fixups);
219}219}
220220
221/// Returns an extra offset for column and byte offset of errors that221/// Returns an extra offset for column and byte offset of errors that
lib/std/zig/Ast/Render.zig+278-254
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1const std = @import("../std.zig");1const std = @import("../../std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const mem = std.mem;3const mem = std.mem;
4const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
...@@ -6,13 +6,24 @@ const meta = std.meta;...@@ -6,13 +6,24 @@ const meta = std.meta;
6const Ast = std.zig.Ast;6const Ast = std.zig.Ast;
7const Token = std.zig.Token;7const Token = std.zig.Token;
8const primitives = std.zig.primitives;8const primitives = std.zig.primitives;
9const Writer = std.io.Writer;
10
11const Render = @This();
12
13gpa: Allocator,
14ais: *AutoIndentingStream,
15tree: Ast,
16fixups: Fixups,
917
10const indent_delta = 4;18const indent_delta = 4;
11const asm_indent_delta = 2;19const asm_indent_delta = 2;
1220
13pub const Error = Ast.RenderError;21pub const Error = error{
1422 /// Ran out of memory allocating call stack frames to complete rendering.
15const Ais = AutoIndentingStream(std.ArrayList(u8).Writer);23 OutOfMemory,
24 /// Transitive failure from
25 WriteFailed,
26};
1627
17pub const Fixups = struct {28pub const Fixups = struct {
18 /// The key is the mut token (`var`/`const`) of the variable declaration29 /// The key is the mut token (`var`/`const`) of the variable declaration
...@@ -72,19 +83,12 @@ pub const Fixups = struct {...@@ -72,19 +83,12 @@ pub const Fixups = struct {
72 }83 }
73};84};
7485
75const Render = struct {86pub fn renderTree(gpa: Allocator, w: *Writer, tree: Ast, fixups: Fixups) Error!void {
76 gpa: Allocator,
77 ais: *Ais,
78 tree: Ast,
79 fixups: Fixups,
80};
81
82pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast, fixups: Fixups) Error!void {
83 assert(tree.errors.len == 0); // Cannot render an invalid tree.87 assert(tree.errors.len == 0); // Cannot render an invalid tree.
84 var auto_indenting_stream = Ais.init(buffer, indent_delta);88 var auto_indenting_stream: AutoIndentingStream = .init(gpa, w, indent_delta);
85 defer auto_indenting_stream.deinit();89 defer auto_indenting_stream.deinit();
86 var r: Render = .{90 var r: Render = .{
87 .gpa = buffer.allocator,91 .gpa = gpa,
88 .ais = &auto_indenting_stream,92 .ais = &auto_indenting_stream,
89 .tree = tree,93 .tree = tree,
90 .fixups = fixups,94 .fixups = fixups,
...@@ -186,7 +190,7 @@ fn renderMember(...@@ -186,7 +190,7 @@ fn renderMember(
186 if (opt_callconv_expr.unwrap()) |callconv_expr| {190 if (opt_callconv_expr.unwrap()) |callconv_expr| {
187 if (tree.nodeTag(callconv_expr) == .enum_literal) {191 if (tree.nodeTag(callconv_expr) == .enum_literal) {
188 if (mem.eql(u8, "@\"inline\"", tree.tokenSlice(tree.nodeMainToken(callconv_expr)))) {192 if (mem.eql(u8, "@\"inline\"", tree.tokenSlice(tree.nodeMainToken(callconv_expr)))) {
189 try ais.writer().writeAll("inline ");193 try ais.underlying_writer.writeAll("inline ");
190 }194 }
191 }195 }
192 }196 }
...@@ -200,7 +204,7 @@ fn renderMember(...@@ -200,7 +204,7 @@ fn renderMember(
200 const lbrace = tree.nodeMainToken(body_node);204 const lbrace = tree.nodeMainToken(body_node);
201 try renderToken(r, lbrace, .newline);205 try renderToken(r, lbrace, .newline);
202 try discardAllParams(r, fn_proto);206 try discardAllParams(r, fn_proto);
203 try ais.writer().writeAll("@trap();");207 try ais.writeAll("@trap();");
204 ais.popIndent();208 ais.popIndent();
205 try ais.insertNewline();209 try ais.insertNewline();
206 try renderToken(r, tree.lastToken(body_node), space); // rbrace210 try renderToken(r, tree.lastToken(body_node), space); // rbrace
...@@ -216,10 +220,9 @@ fn renderMember(...@@ -216,10 +220,9 @@ fn renderMember(
216 const name_ident = param.name_token.?;220 const name_ident = param.name_token.?;
217 assert(tree.tokenTag(name_ident) == .identifier);221 assert(tree.tokenTag(name_ident) == .identifier);
218 if (r.fixups.unused_var_decls.contains(name_ident)) {222 if (r.fixups.unused_var_decls.contains(name_ident)) {
219 const w = ais.writer();223 try ais.writeAll("_ = ");
220 try w.writeAll("_ = ");224 try ais.writeAll(tokenSliceForRender(r.tree, name_ident));
221 try w.writeAll(tokenSliceForRender(r.tree, name_ident));225 try ais.writeAll(";\n");
222 try w.writeAll(";\n");
223 }226 }
224 }227 }
225 var statements_buf: [2]Ast.Node.Index = undefined;228 var statements_buf: [2]Ast.Node.Index = undefined;
...@@ -312,7 +315,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -312,7 +315,7 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
312 const tree = r.tree;315 const tree = r.tree;
313 const ais = r.ais;316 const ais = r.ais;
314 if (r.fixups.replace_nodes_with_string.get(node)) |replacement| {317 if (r.fixups.replace_nodes_with_string.get(node)) |replacement| {
315 try ais.writer().writeAll(replacement);318 try ais.writeAll(replacement);
316 try renderOnlySpace(r, space);319 try renderOnlySpace(r, space);
317 return;320 return;
318 } else if (r.fixups.replace_nodes_with_node.get(node)) |replacement| {321 } else if (r.fixups.replace_nodes_with_node.get(node)) |replacement| {
...@@ -881,7 +884,7 @@ fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!v...@@ -881,7 +884,7 @@ fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!v
881 const ais = r.ais;884 const ais = r.ais;
882 try renderExpression(r, node, space);885 try renderExpression(r, node, space);
883 if (r.fixups.append_string_after_node.get(node)) |bytes| {886 if (r.fixups.append_string_after_node.get(node)) |bytes| {
884 try ais.writer().writeAll(bytes);887 try ais.writeAll(bytes);
885 }888 }
886}889}
887890
...@@ -1086,10 +1089,10 @@ fn renderVarDecl(...@@ -1086,10 +1089,10 @@ fn renderVarDecl(
1086 try renderVarDeclWithoutFixups(r, var_decl, ignore_comptime_token, space);1089 try renderVarDeclWithoutFixups(r, var_decl, ignore_comptime_token, space);
1087 if (r.fixups.unused_var_decls.contains(var_decl.ast.mut_token + 1)) {1090 if (r.fixups.unused_var_decls.contains(var_decl.ast.mut_token + 1)) {
1088 // Discard the variable like this: `_ = foo;`1091 // Discard the variable like this: `_ = foo;`
1089 const w = r.ais.writer();1092 const ais = r.ais;
1090 try w.writeAll("_ = ");1093 try ais.writeAll("_ = ");
1091 try w.writeAll(tokenSliceForRender(r.tree, var_decl.ast.mut_token + 1));1094 try ais.writeAll(tokenSliceForRender(r.tree, var_decl.ast.mut_token + 1));
1092 try w.writeAll(";\n");1095 try ais.writeAll(";\n");
1093 }1096 }
1094}1097}
10951098
...@@ -1567,7 +1570,7 @@ fn renderBuiltinCall(...@@ -1567,7 +1570,7 @@ fn renderBuiltinCall(
1567 defer r.gpa.free(new_string);1570 defer r.gpa.free(new_string);
15681571
1569 try renderToken(r, builtin_token + 1, .none); // (1572 try renderToken(r, builtin_token + 1, .none); // (
1570 try ais.writer().print("\"{f}\"", .{std.zig.fmtString(new_string)});1573 try ais.print("\"{f}\"", .{std.zig.fmtString(new_string)});
1571 return renderToken(r, str_lit_token + 1, space); // )1574 return renderToken(r, str_lit_token + 1, space); // )
1572 }1575 }
1573 }1576 }
...@@ -2125,13 +2128,13 @@ fn renderArrayInit(...@@ -2125,13 +2128,13 @@ fn renderArrayInit(
21252128
2126 const section_exprs = row_exprs[0..section_end];2129 const section_exprs = row_exprs[0..section_end];
21272130
2128 var sub_expr_buffer = std.ArrayList(u8).init(gpa);2131 var sub_expr_buffer: std.io.Writer.Allocating = .init(gpa);
2129 defer sub_expr_buffer.deinit();2132 defer sub_expr_buffer.deinit();
21302133
2131 const sub_expr_buffer_starts = try gpa.alloc(usize, section_exprs.len + 1);2134 const sub_expr_buffer_starts = try gpa.alloc(usize, section_exprs.len + 1);
2132 defer gpa.free(sub_expr_buffer_starts);2135 defer gpa.free(sub_expr_buffer_starts);
21332136
2134 var auto_indenting_stream = Ais.init(&sub_expr_buffer, indent_delta);2137 var auto_indenting_stream: AutoIndentingStream = .init(gpa, &sub_expr_buffer.writer, indent_delta);
2135 defer auto_indenting_stream.deinit();2138 defer auto_indenting_stream.deinit();
2136 var sub_render: Render = .{2139 var sub_render: Render = .{
2137 .gpa = r.gpa,2140 .gpa = r.gpa,
...@@ -2145,13 +2148,14 @@ fn renderArrayInit(...@@ -2145,13 +2148,14 @@ fn renderArrayInit(
2145 var single_line = true;2148 var single_line = true;
2146 var contains_newline = false;2149 var contains_newline = false;
2147 for (section_exprs, 0..) |expr, i| {2150 for (section_exprs, 0..) |expr, i| {
2148 const start = sub_expr_buffer.items.len;2151 const start = sub_expr_buffer.getWritten().len;
2149 sub_expr_buffer_starts[i] = start;2152 sub_expr_buffer_starts[i] = start;
21502153
2151 if (i + 1 < section_exprs.len) {2154 if (i + 1 < section_exprs.len) {
2152 try renderExpression(&sub_render, expr, .none);2155 try renderExpression(&sub_render, expr, .none);
2153 const width = sub_expr_buffer.items.len - start;2156 const written = sub_expr_buffer.getWritten();
2154 const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start..], '\n') != null;2157 const width = written.len - start;
2158 const this_contains_newline = mem.indexOfScalar(u8, written[start..], '\n') != null;
2155 contains_newline = contains_newline or this_contains_newline;2159 contains_newline = contains_newline or this_contains_newline;
2156 expr_widths[i] = width;2160 expr_widths[i] = width;
2157 expr_newlines[i] = this_contains_newline;2161 expr_newlines[i] = this_contains_newline;
...@@ -2173,8 +2177,9 @@ fn renderArrayInit(...@@ -2173,8 +2177,9 @@ fn renderArrayInit(
2173 try renderExpression(&sub_render, expr, .comma);2177 try renderExpression(&sub_render, expr, .comma);
2174 ais.popSpace();2178 ais.popSpace();
21752179
2176 const width = sub_expr_buffer.items.len - start - 2;2180 const written = sub_expr_buffer.getWritten();
2177 const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start .. sub_expr_buffer.items.len - 1], '\n') != null;2181 const width = written.len - start - 2;
2182 const this_contains_newline = mem.indexOfScalar(u8, written[start .. written.len - 1], '\n') != null;
2178 contains_newline = contains_newline or this_contains_newline;2183 contains_newline = contains_newline or this_contains_newline;
2179 expr_widths[i] = width;2184 expr_widths[i] = width;
2180 expr_newlines[i] = contains_newline;2185 expr_newlines[i] = contains_newline;
...@@ -2185,20 +2190,20 @@ fn renderArrayInit(...@@ -2185,20 +2190,20 @@ fn renderArrayInit(
2185 }2190 }
2186 }2191 }
2187 }2192 }
2188 sub_expr_buffer_starts[section_exprs.len] = sub_expr_buffer.items.len;2193 sub_expr_buffer_starts[section_exprs.len] = sub_expr_buffer.getWritten().len;
21892194
2190 // Render exprs in current section.2195 // Render exprs in current section.
2191 column_counter = 0;2196 column_counter = 0;
2192 for (section_exprs, 0..) |expr, i| {2197 for (section_exprs, 0..) |expr, i| {
2193 const start = sub_expr_buffer_starts[i];2198 const start = sub_expr_buffer_starts[i];
2194 const end = sub_expr_buffer_starts[i + 1];2199 const end = sub_expr_buffer_starts[i + 1];
2195 const expr_text = sub_expr_buffer.items[start..end];2200 const expr_text = sub_expr_buffer.getWritten()[start..end];
2196 if (!expr_newlines[i]) {2201 if (!expr_newlines[i]) {
2197 try ais.writer().writeAll(expr_text);2202 try ais.writeAll(expr_text);
2198 } else {2203 } else {
2199 var by_line = std.mem.splitScalar(u8, expr_text, '\n');2204 var by_line = std.mem.splitScalar(u8, expr_text, '\n');
2200 var last_line_was_empty = false;2205 var last_line_was_empty = false;
2201 try ais.writer().writeAll(by_line.first());2206 try ais.writeAll(by_line.first());
2202 while (by_line.next()) |line| {2207 while (by_line.next()) |line| {
2203 if (std.mem.startsWith(u8, line, "//") and last_line_was_empty) {2208 if (std.mem.startsWith(u8, line, "//") and last_line_was_empty) {
2204 try ais.insertNewline();2209 try ais.insertNewline();
...@@ -2206,7 +2211,7 @@ fn renderArrayInit(...@@ -2206,7 +2211,7 @@ fn renderArrayInit(
2206 try ais.maybeInsertNewline();2211 try ais.maybeInsertNewline();
2207 }2212 }
2208 last_line_was_empty = (line.len == 0);2213 last_line_was_empty = (line.len == 0);
2209 try ais.writer().writeAll(line);2214 try ais.writeAll(line);
2210 }2215 }
2211 }2216 }
22122217
...@@ -2220,7 +2225,7 @@ fn renderArrayInit(...@@ -2220,7 +2225,7 @@ fn renderArrayInit(
2220 try renderToken(r, comma, .space); // ,2225 try renderToken(r, comma, .space); // ,
2221 assert(column_widths[column_counter % row_size] >= expr_widths[i]);2226 assert(column_widths[column_counter % row_size] >= expr_widths[i]);
2222 const padding = column_widths[column_counter % row_size] - expr_widths[i];2227 const padding = column_widths[column_counter % row_size] - expr_widths[i];
2223 try ais.writer().writeByteNTimes(' ', padding);2228 try ais.splatByteAll(' ', padding);
22242229
2225 column_counter += 1;2230 column_counter += 1;
2226 continue;2231 continue;
...@@ -2799,7 +2804,7 @@ fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) Error!void...@@ -2799,7 +2804,7 @@ fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) Error!void
2799 const tree = r.tree;2804 const tree = r.tree;
2800 const ais = r.ais;2805 const ais = r.ais;
2801 const lexeme = tokenSliceForRender(tree, token_index);2806 const lexeme = tokenSliceForRender(tree, token_index);
2802 try ais.writer().writeAll(lexeme);2807 try ais.writeAll(lexeme);
2803 try renderSpace(r, token_index, lexeme.len, space);2808 try renderSpace(r, token_index, lexeme.len, space);
2804}2809}
28052810
...@@ -2807,7 +2812,7 @@ fn renderTokenOverrideSpaceMode(r: *Render, token_index: Ast.TokenIndex, space:...@@ -2807,7 +2812,7 @@ fn renderTokenOverrideSpaceMode(r: *Render, token_index: Ast.TokenIndex, space:
2807 const tree = r.tree;2812 const tree = r.tree;
2808 const ais = r.ais;2813 const ais = r.ais;
2809 const lexeme = tokenSliceForRender(tree, token_index);2814 const lexeme = tokenSliceForRender(tree, token_index);
2810 try ais.writer().writeAll(lexeme);2815 try ais.writeAll(lexeme);
2811 ais.enableSpaceMode(override_space);2816 ais.enableSpaceMode(override_space);
2812 defer ais.disableSpaceMode();2817 defer ais.disableSpaceMode();
2813 try renderSpace(r, token_index, lexeme.len, space);2818 try renderSpace(r, token_index, lexeme.len, space);
...@@ -2822,7 +2827,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space...@@ -2822,7 +2827,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
2822 if (space == .skip) return;2827 if (space == .skip) return;
28232828
2824 if (space == .comma and next_token_tag != .comma) {2829 if (space == .comma and next_token_tag != .comma) {
2825 try ais.writer().writeByte(',');2830 try ais.writeByte(',');
2826 }2831 }
2827 if (space == .semicolon or space == .comma) ais.enableSpaceMode(space);2832 if (space == .semicolon or space == .comma) ais.enableSpaceMode(space);
2828 defer ais.disableSpaceMode();2833 defer ais.disableSpaceMode();
...@@ -2833,7 +2838,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space...@@ -2833,7 +2838,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
2833 );2838 );
2834 switch (space) {2839 switch (space) {
2835 .none => {},2840 .none => {},
2836 .space => if (!comment) try ais.writer().writeByte(' '),2841 .space => if (!comment) try ais.writeByte(' '),
2837 .newline => if (!comment) try ais.insertNewline(),2842 .newline => if (!comment) try ais.insertNewline(),
28382843
2839 .comma => if (next_token_tag == .comma) {2844 .comma => if (next_token_tag == .comma) {
...@@ -2845,7 +2850,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space...@@ -2845,7 +2850,7 @@ fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space
2845 .comma_space => if (next_token_tag == .comma) {2850 .comma_space => if (next_token_tag == .comma) {
2846 try renderToken(r, token_index + 1, .space);2851 try renderToken(r, token_index + 1, .space);
2847 } else if (!comment) {2852 } else if (!comment) {
2848 try ais.writer().writeByte(' ');2853 try ais.writeByte(' ');
2849 },2854 },
28502855
2851 .semicolon => if (next_token_tag == .semicolon) {2856 .semicolon => if (next_token_tag == .semicolon) {
...@@ -2862,11 +2867,11 @@ fn renderOnlySpace(r: *Render, space: Space) Error!void {...@@ -2862,11 +2867,11 @@ fn renderOnlySpace(r: *Render, space: Space) Error!void {
2862 const ais = r.ais;2867 const ais = r.ais;
2863 switch (space) {2868 switch (space) {
2864 .none => {},2869 .none => {},
2865 .space => try ais.writer().writeByte(' '),2870 .space => try ais.writeByte(' '),
2866 .newline => try ais.insertNewline(),2871 .newline => try ais.insertNewline(),
2867 .comma => try ais.writer().writeAll(",\n"),2872 .comma => try ais.writeAll(",\n"),
2868 .comma_space => try ais.writer().writeAll(", "),2873 .comma_space => try ais.writeAll(", "),
2869 .semicolon => try ais.writer().writeAll(";\n"),2874 .semicolon => try ais.writeAll(";\n"),
2870 .skip => unreachable,2875 .skip => unreachable,
2871 }2876 }
2872}2877}
...@@ -2883,7 +2888,7 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote...@@ -2883,7 +2888,7 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote
2883 const lexeme = tokenSliceForRender(tree, token_index);2888 const lexeme = tokenSliceForRender(tree, token_index);
28842889
2885 if (r.fixups.rename_identifiers.get(lexeme)) |mangled| {2890 if (r.fixups.rename_identifiers.get(lexeme)) |mangled| {
2886 try r.ais.writer().writeAll(mangled);2891 try r.ais.writeAll(mangled);
2887 try renderSpace(r, token_index, lexeme.len, space);2892 try renderSpace(r, token_index, lexeme.len, space);
2888 return;2893 return;
2889 }2894 }
...@@ -2992,15 +2997,15 @@ fn renderQuotedIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space,...@@ -2992,15 +2997,15 @@ fn renderQuotedIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space,
2992 const lexeme = tokenSliceForRender(tree, token_index);2997 const lexeme = tokenSliceForRender(tree, token_index);
2993 assert(lexeme.len >= 3 and lexeme[0] == '@');2998 assert(lexeme.len >= 3 and lexeme[0] == '@');
29942999
2995 if (!unquote) try ais.writer().writeAll("@\"");3000 if (!unquote) try ais.writeAll("@\"");
2996 const contents = lexeme[2 .. lexeme.len - 1];3001 const contents = lexeme[2 .. lexeme.len - 1];
2997 try renderIdentifierContents(ais.writer(), contents);3002 try renderIdentifierContents(ais, contents);
2998 if (!unquote) try ais.writer().writeByte('\"');3003 if (!unquote) try ais.writeByte('\"');
29993004
3000 try renderSpace(r, token_index, lexeme.len, space);3005 try renderSpace(r, token_index, lexeme.len, space);
3001}3006}
30023007
3003fn renderIdentifierContents(writer: anytype, bytes: []const u8) !void {3008fn renderIdentifierContents(ais: *AutoIndentingStream, bytes: []const u8) !void {
3004 var pos: usize = 0;3009 var pos: usize = 0;
3005 while (pos < bytes.len) {3010 while (pos < bytes.len) {
3006 const byte = bytes[pos];3011 const byte = bytes[pos];
...@@ -3013,23 +3018,23 @@ fn renderIdentifierContents(writer: anytype, bytes: []const u8) !void {...@@ -3013,23 +3018,23 @@ fn renderIdentifierContents(writer: anytype, bytes: []const u8) !void {
3013 .success => |codepoint| {3018 .success => |codepoint| {
3014 if (codepoint <= 0x7f) {3019 if (codepoint <= 0x7f) {
3015 const buf = [1]u8{@as(u8, @intCast(codepoint))};3020 const buf = [1]u8{@as(u8, @intCast(codepoint))};
3016 try std.fmt.format(writer, "{f}", .{std.zig.fmtString(&buf)});3021 try ais.print("{f}", .{std.zig.fmtString(&buf)});
3017 } else {3022 } else {
3018 try writer.writeAll(escape_sequence);3023 try ais.writeAll(escape_sequence);
3019 }3024 }
3020 },3025 },
3021 .failure => {3026 .failure => {
3022 try writer.writeAll(escape_sequence);3027 try ais.writeAll(escape_sequence);
3023 },3028 },
3024 }3029 }
3025 },3030 },
3026 0x00...('\\' - 1), ('\\' + 1)...0x7f => {3031 0x00...('\\' - 1), ('\\' + 1)...0x7f => {
3027 const buf = [1]u8{byte};3032 const buf = [1]u8{byte};
3028 try std.fmt.format(writer, "{f}", .{std.zig.fmtString(&buf)});3033 try ais.print("{f}", .{std.zig.fmtString(&buf)});
3029 pos += 1;3034 pos += 1;
3030 },3035 },
3031 0x80...0xff => {3036 0x80...0xff => {
3032 try writer.writeByte(byte);3037 try ais.writeByte(byte);
3033 pos += 1;3038 pos += 1;
3034 },3039 },
3035 }3040 }
...@@ -3091,7 +3096,7 @@ fn renderComments(r: *Render, start: usize, end: usize) Error!bool {...@@ -3091,7 +3096,7 @@ fn renderComments(r: *Render, start: usize, end: usize) Error!bool {
3091 } else if (index == start) {3096 } else if (index == start) {
3092 // Otherwise if the first comment is on the same line as3097 // Otherwise if the first comment is on the same line as
3093 // the token before it, prefix it with a single space.3098 // the token before it, prefix it with a single space.
3094 try ais.writer().writeByte(' ');3099 try ais.writeByte(' ');
3095 }3100 }
3096 }3101 }
30973102
...@@ -3108,11 +3113,11 @@ fn renderComments(r: *Render, start: usize, end: usize) Error!bool {...@@ -3108,11 +3113,11 @@ fn renderComments(r: *Render, start: usize, end: usize) Error!bool {
3108 ais.disabled_offset = null;3113 ais.disabled_offset = null;
3109 } else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) {3114 } else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) {
3110 // Write with the canonical single space.3115 // Write with the canonical single space.
3111 try ais.writer().writeAll("// zig fmt: off\n");3116 try ais.writeAll("// zig fmt: off\n");
3112 ais.disabled_offset = index;3117 ais.disabled_offset = index;
3113 } else {3118 } else {
3114 // Write the comment minus trailing whitespace.3119 // Write the comment minus trailing whitespace.
3115 try ais.writer().print("{s}\n", .{trimmed_comment});3120 try ais.print("{s}\n", .{trimmed_comment});
3116 }3121 }
3117 }3122 }
31183123
...@@ -3213,10 +3218,9 @@ fn discardAllParams(r: *Render, fn_proto_node: Ast.Node.Index) Error!void {...@@ -3213,10 +3218,9 @@ fn discardAllParams(r: *Render, fn_proto_node: Ast.Node.Index) Error!void {
3213 while (it.next()) |param| {3218 while (it.next()) |param| {
3214 const name_ident = param.name_token.?;3219 const name_ident = param.name_token.?;
3215 assert(tree.tokenTag(name_ident) == .identifier);3220 assert(tree.tokenTag(name_ident) == .identifier);
3216 const w = ais.writer();3221 try ais.writeAll("_ = ");
3217 try w.writeAll("_ = ");3222 try ais.writeAll(tokenSliceForRender(r.tree, name_ident));
3218 try w.writeAll(tokenSliceForRender(r.tree, name_ident));3223 try ais.writeAll(";\n");
3219 try w.writeAll(";\n");
3220 }3224 }
3221}3225}
32223226
...@@ -3269,11 +3273,11 @@ fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenI...@@ -3269,11 +3273,11 @@ fn anythingBetween(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.TokenI
3269 return false;3273 return false;
3270}3274}
32713275
3272fn writeFixingWhitespace(writer: std.ArrayList(u8).Writer, slice: []const u8) Error!void {3276fn writeFixingWhitespace(w: *Writer, slice: []const u8) Error!void {
3273 for (slice) |byte| switch (byte) {3277 for (slice) |byte| switch (byte) {
3274 '\t' => try writer.writeAll(" " ** indent_delta),3278 '\t' => try w.splatByteAll(' ', indent_delta),
3275 '\r' => {},3279 '\r' => {},
3276 else => try writer.writeByte(byte),3280 else => try w.writeByte(byte),
3277 };3281 };
3278}3282}
32793283
...@@ -3398,224 +3402,244 @@ fn rowSize(tree: Ast, exprs: []const Ast.Node.Index, rtoken: Ast.TokenIndex) usi...@@ -3398,224 +3402,244 @@ fn rowSize(tree: Ast, exprs: []const Ast.Node.Index, rtoken: Ast.TokenIndex) usi
3398/// of the appropriate indentation level for them with pushSpace/popSpace.3402/// of the appropriate indentation level for them with pushSpace/popSpace.
3399/// This should be done whenever a scope that ends in a .semicolon or a3403/// This should be done whenever a scope that ends in a .semicolon or a
3400/// .comma is introduced.3404/// .comma is introduced.
3401fn AutoIndentingStream(comptime UnderlyingWriter: type) type {3405const AutoIndentingStream = struct {
3402 return struct {3406 underlying_writer: *Writer,
3403 const Self = @This();3407
3404 pub const WriteError = UnderlyingWriter.Error;3408 /// Offset into the source at which formatting has been disabled with
3405 pub const Writer = std.io.GenericWriter(*Self, WriteError, write);3409 /// a `zig fmt: off` comment.
34063410 ///
3407 pub const IndentType = enum {3411 /// If non-null, the AutoIndentingStream will not write any bytes
3408 normal,3412 /// to the underlying writer. It will however continue to track the
3409 after_equals,3413 /// indentation level.
3410 binop,3414 disabled_offset: ?usize = null,
3411 field_access,3415
3412 };3416 indent_count: usize = 0,
3413 const StackElem = struct {3417 indent_delta: usize,
3414 indent_type: IndentType,3418 indent_stack: std.ArrayList(StackElem),
3415 realized: bool,3419 space_stack: std.ArrayList(SpaceElem),
3416 };3420 space_mode: ?usize = null,
3417 const SpaceElem = struct {3421 disable_indent_committing: usize = 0,
3418 space: Space,3422 current_line_empty: bool = true,
3419 indent_count: usize,3423 /// the most recently applied indent
3424 applied_indent: usize = 0,
3425
3426 pub const IndentType = enum {
3427 normal,
3428 after_equals,
3429 binop,
3430 field_access,
3431 };
3432 const StackElem = struct {
3433 indent_type: IndentType,
3434 realized: bool,
3435 };
3436 const SpaceElem = struct {
3437 space: Space,
3438 indent_count: usize,
3439 };
3440
3441 pub fn init(gpa: Allocator, w: *Writer, starting_indent_delta: usize) AutoIndentingStream {
3442 return .{
3443 .underlying_writer = w,
3444 .indent_delta = starting_indent_delta,
3445 .indent_stack = .init(gpa),
3446 .space_stack = .init(gpa),
3420 };3447 };
3448 }
34213449
3422 underlying_writer: UnderlyingWriter,3450 pub fn deinit(self: *AutoIndentingStream) void {
34233451 self.indent_stack.deinit();
3424 /// Offset into the source at which formatting has been disabled with3452 self.space_stack.deinit();
3425 /// a `zig fmt: off` comment.3453 }
3426 ///
3427 /// If non-null, the AutoIndentingStream will not write any bytes
3428 /// to the underlying writer. It will however continue to track the
3429 /// indentation level.
3430 disabled_offset: ?usize = null,
3431
3432 indent_count: usize = 0,
3433 indent_delta: usize,
3434 indent_stack: std.ArrayList(StackElem),
3435 space_stack: std.ArrayList(SpaceElem),
3436 space_mode: ?usize = null,
3437 disable_indent_committing: usize = 0,
3438 current_line_empty: bool = true,
3439 /// the most recently applied indent
3440 applied_indent: usize = 0,
3441
3442 pub fn init(buffer: *std.ArrayList(u8), indent_delta_: usize) Self {
3443 return .{
3444 .underlying_writer = buffer.writer(),
3445 .indent_delta = indent_delta_,
3446 .indent_stack = std.ArrayList(StackElem).init(buffer.allocator),
3447 .space_stack = std.ArrayList(SpaceElem).init(buffer.allocator),
3448 };
3449 }
34503454
3451 pub fn deinit(self: *Self) void {3455 pub fn writeAll(ais: *AutoIndentingStream, bytes: []const u8) Error!void {
3452 self.indent_stack.deinit();3456 if (bytes.len == 0) return;
3453 self.space_stack.deinit();3457 try ais.applyIndent();
3454 }3458 if (ais.disabled_offset == null) try ais.underlying_writer.writeAll(bytes);
3459 if (bytes[bytes.len - 1] == '\n') ais.resetLine();
3460 }
34553461
3456 pub fn writer(self: *Self) Writer {3462 /// Assumes that if the printed data ends with a newline, it is directly
3457 return .{ .context = self };3463 /// contained in the format string.
3458 }3464 pub fn print(ais: *AutoIndentingStream, comptime format: []const u8, args: anytype) Error!void {
3465 try ais.applyIndent();
3466 if (ais.disabled_offset == null) try ais.underlying_writer.print(format, args);
3467 if (format[format.len - 1] == '\n') ais.resetLine();
3468 }
34593469
3460 pub fn write(self: *Self, bytes: []const u8) WriteError!usize {3470 pub fn writeByte(ais: *AutoIndentingStream, byte: u8) Error!void {
3461 if (bytes.len == 0)3471 try ais.applyIndent();
3462 return @as(usize, 0);3472 if (ais.disabled_offset == null) try ais.underlying_writer.writeByte(byte);
3473 assert(byte != '\n');
3474 }
34633475
3464 try self.applyIndent();3476 pub fn splatByteAll(ais: *AutoIndentingStream, byte: u8, n: usize) Error!void {
3465 return self.writeNoIndent(bytes);3477 assert(byte != '\n');
3466 }3478 try ais.applyIndent();
3479 if (ais.disabled_offset == null) try ais.underlying_writer.splatByteAll(byte, n);
3480 }
34673481
3468 // Change the indent delta without changing the final indentation level3482 // Change the indent delta without changing the final indentation level
3469 pub fn setIndentDelta(self: *Self, new_indent_delta: usize) void {3483 pub fn setIndentDelta(ais: *AutoIndentingStream, new_indent_delta: usize) void {
3470 if (self.indent_delta == new_indent_delta) {3484 if (ais.indent_delta == new_indent_delta) {
3471 return;3485 return;
3472 } else if (self.indent_delta > new_indent_delta) {3486 } else if (ais.indent_delta > new_indent_delta) {
3473 assert(self.indent_delta % new_indent_delta == 0);3487 assert(ais.indent_delta % new_indent_delta == 0);
3474 self.indent_count = self.indent_count * (self.indent_delta / new_indent_delta);3488 ais.indent_count = ais.indent_count * (ais.indent_delta / new_indent_delta);
3475 } else {3489 } else {
3476 // assert that the current indentation (in spaces) in a multiple of the new delta3490 // assert that the current indentation (in spaces) in a multiple of the new delta
3477 assert((self.indent_count * self.indent_delta) % new_indent_delta == 0);3491 assert((ais.indent_count * ais.indent_delta) % new_indent_delta == 0);
3478 self.indent_count = self.indent_count / (new_indent_delta / self.indent_delta);3492 ais.indent_count = ais.indent_count / (new_indent_delta / ais.indent_delta);
3479 }
3480 self.indent_delta = new_indent_delta;
3481 }3493 }
3494 ais.indent_delta = new_indent_delta;
3495 }
34823496
3483 fn writeNoIndent(self: *Self, bytes: []const u8) WriteError!usize {3497 pub fn insertNewline(ais: *AutoIndentingStream) Error!void {
3484 if (bytes.len == 0)3498 if (ais.disabled_offset == null) try ais.underlying_writer.writeByte('\n');
3485 return @as(usize, 0);3499 ais.resetLine();
3500 }
34863501
3487 if (self.disabled_offset == null) try self.underlying_writer.writeAll(bytes);3502 /// Insert a newline unless the current line is blank
3488 if (bytes[bytes.len - 1] == '\n')3503 pub fn maybeInsertNewline(ais: *AutoIndentingStream) Error!void {
3489 self.resetLine();3504 if (!ais.current_line_empty)
3490 return bytes.len;3505 try ais.insertNewline();
3491 }3506 }
34923507
3493 pub fn insertNewline(self: *Self) WriteError!void {3508 /// Push an indent that is automatically popped after being applied
3494 _ = try self.writeNoIndent("\n");3509 pub fn pushIndentOneShot(ais: *AutoIndentingStream) void {
3495 }3510 ais.indent_one_shot_count += 1;
3511 ais.pushIndent();
3512 }
34963513
3497 fn resetLine(self: *Self) void {3514 /// Turns all one-shot indents into regular indents
3498 self.current_line_empty = true;3515 /// Returns number of indents that must now be manually popped
3516 pub fn lockOneShotIndent(ais: *AutoIndentingStream) usize {
3517 const locked_count = ais.indent_one_shot_count;
3518 ais.indent_one_shot_count = 0;
3519 return locked_count;
3520 }
34993521
3500 if (self.disable_indent_committing > 0) return;3522 /// Push an indent that should not take effect until the next line
3523 pub fn pushIndentNextLine(ais: *AutoIndentingStream) void {
3524 ais.indent_next_line += 1;
3525 ais.pushIndent();
3526 }
35013527
3502 if (self.indent_stack.items.len > 0) {3528 /// Checks to see if the most recent indentation exceeds the currently pushed indents
3503 // By default, we realize the most recent indentation scope.3529 pub fn isLineOverIndented(ais: *AutoIndentingStream) bool {
3504 var to_realize = self.indent_stack.items.len - 1;3530 if (ais.current_line_empty) return false;
3531 return ais.applied_indent > ais.currentIndent();
3532 }
35053533
3506 if (self.indent_stack.items.len >= 2 and3534 fn resetLine(ais: *AutoIndentingStream) void {
3507 self.indent_stack.items[to_realize - 1].indent_type == .after_equals and3535 ais.current_line_empty = true;
3508 self.indent_stack.items[to_realize - 1].realized and
3509 self.indent_stack.items[to_realize].indent_type == .binop)
3510 {
3511 // If we are in a .binop scope and our direct parent is .after_equals, don't indent.
3512 // This ensures correct indentation in the below example:
3513 //
3514 // const foo =
3515 // (x >= 'a' and x <= 'z') or //<-- we are here
3516 // (x >= 'A' and x <= 'Z');
3517 //
3518 return;
3519 }
35203536
3521 if (self.indent_stack.items[to_realize].indent_type == .field_access) {3537 if (ais.disable_indent_committing > 0) return;
3522 // Only realize the top-most field_access in a chain.
3523 while (to_realize > 0 and self.indent_stack.items[to_realize - 1].indent_type == .field_access)
3524 to_realize -= 1;
3525 }
35263538
3527 if (self.indent_stack.items[to_realize].realized) return;3539 if (ais.indent_stack.items.len > 0) {
3528 self.indent_stack.items[to_realize].realized = true;3540 // By default, we realize the most recent indentation scope.
3529 self.indent_count += 1;3541 var to_realize = ais.indent_stack.items.len - 1;
3542
3543 if (ais.indent_stack.items.len >= 2 and
3544 ais.indent_stack.items[to_realize - 1].indent_type == .after_equals and
3545 ais.indent_stack.items[to_realize - 1].realized and
3546 ais.indent_stack.items[to_realize].indent_type == .binop)
3547 {
3548 // If we are in a .binop scope and our direct parent is .after_equals, don't indent.
3549 // This ensures correct indentation in the below example:
3550 //
3551 // const foo =
3552 // (x >= 'a' and x <= 'z') or //<-- we are here
3553 // (x >= 'A' and x <= 'Z');
3554 //
3555 return;
3530 }3556 }
3531 }
35323557
3533 /// Disables indentation level changes during the next newlines until re-enabled.3558 if (ais.indent_stack.items[to_realize].indent_type == .field_access) {
3534 pub fn disableIndentCommitting(self: *Self) void {3559 // Only realize the top-most field_access in a chain.
3535 self.disable_indent_committing += 1;3560 while (to_realize > 0 and ais.indent_stack.items[to_realize - 1].indent_type == .field_access)
3536 }3561 to_realize -= 1;
3562 }
35373563
3538 pub fn enableIndentCommitting(self: *Self) void {3564 if (ais.indent_stack.items[to_realize].realized) return;
3539 assert(self.disable_indent_committing > 0);3565 ais.indent_stack.items[to_realize].realized = true;
3540 self.disable_indent_committing -= 1;3566 ais.indent_count += 1;
3541 }3567 }
3568 }
35423569
3543 pub fn pushSpace(self: *Self, space: Space) !void {3570 /// Disables indentation level changes during the next newlines until re-enabled.
3544 try self.space_stack.append(.{ .space = space, .indent_count = self.indent_count });3571 pub fn disableIndentCommitting(ais: *AutoIndentingStream) void {
3545 }3572 ais.disable_indent_committing += 1;
3573 }
35463574
3547 pub fn popSpace(self: *Self) void {3575 pub fn enableIndentCommitting(ais: *AutoIndentingStream) void {
3548 _ = self.space_stack.pop();3576 assert(ais.disable_indent_committing > 0);
3549 }3577 ais.disable_indent_committing -= 1;
3578 }
35503579
3551 /// Sets current indentation level to be the same as that of the last pushSpace.3580 pub fn pushSpace(ais: *AutoIndentingStream, space: Space) !void {
3552 pub fn enableSpaceMode(self: *Self, space: Space) void {3581 try ais.space_stack.append(.{ .space = space, .indent_count = ais.indent_count });
3553 if (self.space_stack.items.len == 0) return;3582 }
3554 const curr = self.space_stack.getLast();
3555 if (curr.space != space) return;
3556 self.space_mode = curr.indent_count;
3557 }
35583583
3559 pub fn disableSpaceMode(self: *Self) void {3584 pub fn popSpace(ais: *AutoIndentingStream) void {
3560 self.space_mode = null;3585 _ = ais.space_stack.pop();
3561 }3586 }
35623587
3563 pub fn lastSpaceModeIndent(self: *Self) usize {3588 /// Sets current indentation level to be the same as that of the last pushSpace.
3564 if (self.space_stack.items.len == 0) return 0;3589 pub fn enableSpaceMode(ais: *AutoIndentingStream, space: Space) void {
3565 return self.space_stack.getLast().indent_count * self.indent_delta;3590 if (ais.space_stack.items.len == 0) return;
3566 }3591 const curr = ais.space_stack.getLast();
3592 if (curr.space != space) return;
3593 ais.space_mode = curr.indent_count;
3594 }
35673595
3568 /// Insert a newline unless the current line is blank3596 pub fn disableSpaceMode(ais: *AutoIndentingStream) void {
3569 pub fn maybeInsertNewline(self: *Self) WriteError!void {3597 ais.space_mode = null;
3570 if (!self.current_line_empty)3598 }
3571 try self.insertNewline();
3572 }
35733599
3574 /// Push default indentation3600 pub fn lastSpaceModeIndent(ais: *AutoIndentingStream) usize {
3575 /// Doesn't actually write any indentation.3601 if (ais.space_stack.items.len == 0) return 0;
3576 /// Just primes the stream to be able to write the correct indentation if it needs to.3602 return ais.space_stack.getLast().indent_count * ais.indent_delta;
3577 pub fn pushIndent(self: *Self, indent_type: IndentType) !void {3603 }
3578 try self.indent_stack.append(.{ .indent_type = indent_type, .realized = false });
3579 }
35803604
3581 /// Forces an indentation level to be realized.3605 /// Push default indentation
3582 pub fn forcePushIndent(self: *Self, indent_type: IndentType) !void {3606 /// Doesn't actually write any indentation.
3583 try self.indent_stack.append(.{ .indent_type = indent_type, .realized = true });3607 /// Just primes the stream to be able to write the correct indentation if it needs to.
3584 self.indent_count += 1;3608 pub fn pushIndent(ais: *AutoIndentingStream, indent_type: IndentType) !void {
3585 }3609 try ais.indent_stack.append(.{ .indent_type = indent_type, .realized = false });
3610 }
35863611
3587 pub fn popIndent(self: *Self) void {3612 /// Forces an indentation level to be realized.
3588 if (self.indent_stack.pop().?.realized) {3613 pub fn forcePushIndent(ais: *AutoIndentingStream, indent_type: IndentType) !void {
3589 assert(self.indent_count > 0);3614 try ais.indent_stack.append(.{ .indent_type = indent_type, .realized = true });
3590 self.indent_count -= 1;3615 ais.indent_count += 1;
3591 }3616 }
3592 }
35933617
3594 pub fn indentStackEmpty(self: *Self) bool {3618 pub fn popIndent(ais: *AutoIndentingStream) void {
3595 return self.indent_stack.items.len == 0;3619 if (ais.indent_stack.pop().?.realized) {
3620 assert(ais.indent_count > 0);
3621 ais.indent_count -= 1;
3596 }3622 }
3623 }
35973624
3598 /// Writes ' ' bytes if the current line is empty3625 pub fn indentStackEmpty(ais: *AutoIndentingStream) bool {
3599 fn applyIndent(self: *Self) WriteError!void {3626 return ais.indent_stack.items.len == 0;
3600 const current_indent = self.currentIndent();3627 }
3601 if (self.current_line_empty and current_indent > 0) {
3602 if (self.disabled_offset == null) {
3603 try self.underlying_writer.writeByteNTimes(' ', current_indent);
3604 }
3605 self.applied_indent = current_indent;
3606 }
3607 self.current_line_empty = false;
3608 }
36093628
3610 /// Checks to see if the most recent indentation exceeds the currently pushed indents3629 /// Writes ' ' bytes if the current line is empty
3611 pub fn isLineOverIndented(self: *Self) bool {3630 fn applyIndent(ais: *AutoIndentingStream) Error!void {
3612 if (self.current_line_empty) return false;3631 const current_indent = ais.currentIndent();
3613 return self.applied_indent > self.currentIndent();3632 if (ais.current_line_empty and current_indent > 0) {
3633 if (ais.disabled_offset == null) {
3634 try ais.underlying_writer.splatByteAll(' ', current_indent);
3635 }
3636 ais.applied_indent = current_indent;
3614 }3637 }
3638 ais.current_line_empty = false;
3639 }
36153640
3616 fn currentIndent(self: *Self) usize {3641 fn currentIndent(ais: *AutoIndentingStream) usize {
3617 const indent_count = self.space_mode orelse self.indent_count;3642 const indent_count = ais.space_mode orelse ais.indent_count;
3618 return indent_count * self.indent_delta;3643 return indent_count * ais.indent_delta;
3619 }3644 }
3620 };3645};
3621}
lib/std/zig/ZonGen.zig+69-59
...@@ -1,5 +1,16 @@...@@ -1,5 +1,16 @@
1//! Ingests an `Ast` and produces a `Zoir`.1//! Ingests an `Ast` and produces a `Zoir`.
22
3const std = @import("std");
4const assert = std.debug.assert;
5const mem = std.mem;
6const Allocator = mem.Allocator;
7const StringIndexAdapter = std.hash_map.StringIndexAdapter;
8const StringIndexContext = std.hash_map.StringIndexContext;
9const ZonGen = @This();
10const Zoir = @import("Zoir.zig");
11const Ast = @import("Ast.zig");
12const Writer = std.io.Writer;
13
3gpa: Allocator,14gpa: Allocator,
4tree: Ast,15tree: Ast,
516
...@@ -446,37 +457,44 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator...@@ -446,37 +457,44 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
446 }457 }
447}458}
448459
449fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 {460fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) error{ OutOfMemory, BadString }!u32 {
461 const gpa = zg.gpa;
450 const tree = zg.tree;462 const tree = zg.tree;
451 assert(tree.tokenTag(ident_token) == .identifier);463 assert(tree.tokenTag(ident_token) == .identifier);
452 const ident_name = tree.tokenSlice(ident_token);464 const ident_name = tree.tokenSlice(ident_token);
453 if (!mem.startsWith(u8, ident_name, "@")) {465 if (!mem.startsWith(u8, ident_name, "@")) {
454 const start = zg.string_bytes.items.len;466 const start = zg.string_bytes.items.len;
455 try zg.string_bytes.appendSlice(zg.gpa, ident_name);467 try zg.string_bytes.appendSlice(gpa, ident_name);
456 return @intCast(start);468 return @intCast(start);
457 } else {469 }
458 const offset = 1;470 const offset = 1;
459 const start: u32 = @intCast(zg.string_bytes.items.len);471 const start: u32 = @intCast(zg.string_bytes.items.len);
460 const raw_string = zg.tree.tokenSlice(ident_token)[offset..];472 const raw_string = zg.tree.tokenSlice(ident_token)[offset..];
461 try zg.string_bytes.ensureUnusedCapacity(zg.gpa, raw_string.len);473 try zg.string_bytes.ensureUnusedCapacity(gpa, raw_string.len);
462 switch (try std.zig.string_literal.parseWrite(zg.string_bytes.writer(zg.gpa), raw_string)) {474 const result = r: {
463 .success => {},475 var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &zg.string_bytes);
464 .failure => |err| {476 defer zg.string_bytes = aw.toArrayList();
465 try zg.lowerStrLitError(err, ident_token, raw_string, offset);477 break :r std.zig.string_literal.parseWrite(&aw.writer, raw_string) catch |err| switch (err) {
466 return error.BadString;478 error.WriteFailed => return error.OutOfMemory,
467 },479 };
468 }480 };
469481 switch (result) {
470 const slice = zg.string_bytes.items[start..];482 .success => {},
471 if (mem.indexOfScalar(u8, slice, 0) != null) {483 .failure => |err| {
472 try zg.addErrorTok(ident_token, "identifier cannot contain null bytes", .{});484 try zg.lowerStrLitError(err, ident_token, raw_string, offset);
473 return error.BadString;
474 } else if (slice.len == 0) {
475 try zg.addErrorTok(ident_token, "identifier cannot be empty", .{});
476 return error.BadString;485 return error.BadString;
477 }486 },
478 return start;487 }
488
489 const slice = zg.string_bytes.items[start..];
490 if (mem.indexOfScalar(u8, slice, 0) != null) {
491 try zg.addErrorTok(ident_token, "identifier cannot contain null bytes", .{});
492 return error.BadString;
493 } else if (slice.len == 0) {
494 try zg.addErrorTok(ident_token, "identifier cannot be empty", .{});
495 return error.BadString;
479 }496 }
497 return start;
480}498}
481499
482/// Estimates the size of a string node without parsing it.500/// Estimates the size of a string node without parsing it.
...@@ -507,8 +525,8 @@ pub fn strLitSizeHint(tree: Ast, node: Ast.Node.Index) usize {...@@ -507,8 +525,8 @@ pub fn strLitSizeHint(tree: Ast, node: Ast.Node.Index) usize {
507pub fn parseStrLit(525pub fn parseStrLit(
508 tree: Ast,526 tree: Ast,
509 node: Ast.Node.Index,527 node: Ast.Node.Index,
510 writer: anytype,528 writer: *Writer,
511) error{OutOfMemory}!std.zig.string_literal.Result {529) Writer.Error!std.zig.string_literal.Result {
512 switch (tree.nodeTag(node)) {530 switch (tree.nodeTag(node)) {
513 .string_literal => {531 .string_literal => {
514 const token = tree.nodeMainToken(node);532 const token = tree.nodeMainToken(node);
...@@ -543,15 +561,22 @@ const StringLiteralResult = union(enum) {...@@ -543,15 +561,22 @@ const StringLiteralResult = union(enum) {
543 slice: struct { start: u32, len: u32 },561 slice: struct { start: u32, len: u32 },
544};562};
545563
546fn strLitAsString(zg: *ZonGen, str_node: Ast.Node.Index) !StringLiteralResult {564fn strLitAsString(zg: *ZonGen, str_node: Ast.Node.Index) error{ OutOfMemory, BadString }!StringLiteralResult {
547 if (!zg.options.parse_str_lits) return .{ .slice = .{ .start = 0, .len = 0 } };565 if (!zg.options.parse_str_lits) return .{ .slice = .{ .start = 0, .len = 0 } };
548566
549 const gpa = zg.gpa;567 const gpa = zg.gpa;
550 const string_bytes = &zg.string_bytes;568 const string_bytes = &zg.string_bytes;
551 const str_index: u32 = @intCast(zg.string_bytes.items.len);569 const str_index: u32 = @intCast(zg.string_bytes.items.len);
552 const size_hint = strLitSizeHint(zg.tree, str_node);570 const size_hint = strLitSizeHint(zg.tree, str_node);
553 try string_bytes.ensureUnusedCapacity(zg.gpa, size_hint);571 try string_bytes.ensureUnusedCapacity(gpa, size_hint);
554 switch (try parseStrLit(zg.tree, str_node, zg.string_bytes.writer(zg.gpa))) {572 const result = r: {
573 var aw: std.io.Writer.Allocating = .fromArrayList(gpa, &zg.string_bytes);
574 defer zg.string_bytes = aw.toArrayList();
575 break :r parseStrLit(zg.tree, str_node, &aw.writer) catch |err| switch (err) {
576 error.WriteFailed => return error.OutOfMemory,
577 };
578 };
579 switch (result) {
555 .success => {},580 .success => {},
556 .failure => |err| {581 .failure => |err| {
557 const token = zg.tree.nodeMainToken(str_node);582 const token = zg.tree.nodeMainToken(str_node);
...@@ -793,10 +818,7 @@ fn lowerNumberError(zg: *ZonGen, err: std.zig.number_literal.Error, token: Ast.T...@@ -793,10 +818,7 @@ fn lowerNumberError(zg: *ZonGen, err: std.zig.number_literal.Error, token: Ast.T
793818
794fn errNoteNode(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, args: anytype) Allocator.Error!Zoir.CompileError.Note {819fn errNoteNode(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, args: anytype) Allocator.Error!Zoir.CompileError.Note {
795 const message_idx: u32 = @intCast(zg.string_bytes.items.len);820 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
796 const writer = zg.string_bytes.writer(zg.gpa);821 try zg.string_bytes.print(zg.gpa, format ++ "\x00", args);
797 try writer.print(format, args);
798 try writer.writeByte(0);
799
800 return .{822 return .{
801 .msg = @enumFromInt(message_idx),823 .msg = @enumFromInt(message_idx),
802 .token = .none,824 .token = .none,
...@@ -806,10 +828,7 @@ fn errNoteNode(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, a...@@ -806,10 +828,7 @@ fn errNoteNode(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, a
806828
807fn errNoteTok(zg: *ZonGen, tok: Ast.TokenIndex, comptime format: []const u8, args: anytype) Allocator.Error!Zoir.CompileError.Note {829fn errNoteTok(zg: *ZonGen, tok: Ast.TokenIndex, comptime format: []const u8, args: anytype) Allocator.Error!Zoir.CompileError.Note {
808 const message_idx: u32 = @intCast(zg.string_bytes.items.len);830 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
809 const writer = zg.string_bytes.writer(zg.gpa);831 try zg.string_bytes.print(zg.gpa, format ++ "\x00", args);
810 try writer.print(format, args);
811 try writer.writeByte(0);
812
813 return .{832 return .{
814 .msg = @enumFromInt(message_idx),833 .msg = @enumFromInt(message_idx),
815 .token = .fromToken(tok),834 .token = .fromToken(tok),
...@@ -850,9 +869,7 @@ fn addErrorInner(...@@ -850,9 +869,7 @@ fn addErrorInner(
850 try zg.error_notes.appendSlice(gpa, notes);869 try zg.error_notes.appendSlice(gpa, notes);
851870
852 const message_idx: u32 = @intCast(zg.string_bytes.items.len);871 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
853 const writer = zg.string_bytes.writer(zg.gpa);872 try zg.string_bytes.print(gpa, format ++ "\x00", args);
854 try writer.print(format, args);
855 try writer.writeByte(0);
856873
857 try zg.compile_errors.append(gpa, .{874 try zg.compile_errors.append(gpa, .{
858 .msg = @enumFromInt(message_idx),875 .msg = @enumFromInt(message_idx),
...@@ -868,8 +885,9 @@ fn lowerAstErrors(zg: *ZonGen) Allocator.Error!void {...@@ -868,8 +885,9 @@ fn lowerAstErrors(zg: *ZonGen) Allocator.Error!void {
868 const tree = zg.tree;885 const tree = zg.tree;
869 assert(tree.errors.len > 0);886 assert(tree.errors.len > 0);
870887
871 var msg: std.ArrayListUnmanaged(u8) = .empty;888 var msg: std.io.Writer.Allocating = .init(gpa);
872 defer msg.deinit(gpa);889 defer msg.deinit();
890 const msg_bw = &msg.writer;
873891
874 var notes: std.ArrayListUnmanaged(Zoir.CompileError.Note) = .empty;892 var notes: std.ArrayListUnmanaged(Zoir.CompileError.Note) = .empty;
875 defer notes.deinit(gpa);893 defer notes.deinit(gpa);
...@@ -877,18 +895,20 @@ fn lowerAstErrors(zg: *ZonGen) Allocator.Error!void {...@@ -877,18 +895,20 @@ fn lowerAstErrors(zg: *ZonGen) Allocator.Error!void {
877 var cur_err = tree.errors[0];895 var cur_err = tree.errors[0];
878 for (tree.errors[1..]) |err| {896 for (tree.errors[1..]) |err| {
879 if (err.is_note) {897 if (err.is_note) {
880 try tree.renderError(err, msg.writer(gpa));898 tree.renderError(err, msg_bw) catch return error.OutOfMemory;
881 try notes.append(gpa, try zg.errNoteTok(err.token, "{s}", .{msg.items}));899 try notes.append(gpa, try zg.errNoteTok(err.token, "{s}", .{msg.getWritten()}));
882 } else {900 } else {
883 // Flush error901 // Flush error
884 try tree.renderError(cur_err, msg.writer(gpa));902 tree.renderError(cur_err, msg_bw) catch return error.OutOfMemory;
885 const extra_offset = tree.errorOffset(cur_err);903 const extra_offset = tree.errorOffset(cur_err);
886 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);904 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.getWritten()}, notes.items);
887 notes.clearRetainingCapacity();905 notes.clearRetainingCapacity();
888 cur_err = err;906 cur_err = err;
889907
890 // TODO: `Parse` currently does not have good error recovery mechanisms, so the remaining errors could be bogus.908 // TODO: `Parse` currently does not have good error recovery
891 // As such, we'll ignore all remaining errors for now. We should improve `Parse` so that we can report all the errors.909 // mechanisms, so the remaining errors could be bogus. As such,
910 // we'll ignore all remaining errors for now. We should improve
911 // `Parse` so that we can report all the errors.
892 return;912 return;
893 }913 }
894 msg.clearRetainingCapacity();914 msg.clearRetainingCapacity();
...@@ -896,16 +916,6 @@ fn lowerAstErrors(zg: *ZonGen) Allocator.Error!void {...@@ -896,16 +916,6 @@ fn lowerAstErrors(zg: *ZonGen) Allocator.Error!void {
896916
897 // Flush error917 // Flush error
898 const extra_offset = tree.errorOffset(cur_err);918 const extra_offset = tree.errorOffset(cur_err);
899 try tree.renderError(cur_err, msg.writer(gpa));919 tree.renderError(cur_err, msg_bw) catch return error.OutOfMemory;
900 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);920 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.getWritten()}, notes.items);
901}921}
902
903const std = @import("std");
904const assert = std.debug.assert;
905const mem = std.mem;
906const Allocator = mem.Allocator;
907const StringIndexAdapter = std.hash_map.StringIndexAdapter;
908const StringIndexContext = std.hash_map.StringIndexContext;
909const ZonGen = @This();
910const Zoir = @import("Zoir.zig");
911const Ast = @import("Ast.zig");
lib/std/zig/parser_test.zig+4-2
...@@ -6367,7 +6367,9 @@ test "ampersand" {...@@ -6367,7 +6367,9 @@ test "ampersand" {
6367var fixed_buffer_mem: [100 * 1024]u8 = undefined;6367var fixed_buffer_mem: [100 * 1024]u8 = undefined;
63686368
6369fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 {6369fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 {
6370 const stderr = std.fs.File.stderr().deprecatedWriter();6370 var buffer: [64]u8 = undefined;
6371 const stderr = std.debug.lockStderrWriter(&buffer);
6372 defer std.debug.unlockStderrWriter();
63716373
6372 var tree = try std.zig.Ast.parse(allocator, source, .zig);6374 var tree = try std.zig.Ast.parse(allocator, source, .zig);
6373 defer tree.deinit(allocator);6375 defer tree.deinit(allocator);
...@@ -6390,7 +6392,7 @@ fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *...@@ -6390,7 +6392,7 @@ fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *
6390 return error.ParseError;6392 return error.ParseError;
6391 }6393 }
63926394
6393 const formatted = try tree.render(allocator);6395 const formatted = try tree.renderAlloc(allocator);
6394 anything_changed.* = !mem.eql(u8, formatted, source);6396 anything_changed.* = !mem.eql(u8, formatted, source);
6395 return formatted;6397 return formatted;
6396}6398}
lib/std/zig/string_literal.zig+15-11
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const utf8Encode = std.unicode.utf8Encode;3const utf8Encode = std.unicode.utf8Encode;
4const Writer = std.io.Writer;
45
5pub const ParseError = error{6pub const ParseError = error{
6 OutOfMemory,7 OutOfMemory,
...@@ -315,9 +316,10 @@ test parseCharLiteral {...@@ -315,9 +316,10 @@ test parseCharLiteral {
315 );316 );
316}317}
317318
318/// Parses `bytes` as a Zig string literal and writes the result to the `std.io.GenericWriter` type.319/// Parses `bytes` as a Zig string literal and writes the result to the `Writer` type.
320///
319/// Asserts `bytes` has '"' at beginning and end.321/// Asserts `bytes` has '"' at beginning and end.
320pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result {322pub fn parseWrite(writer: *Writer, bytes: []const u8) Writer.Error!Result {
321 assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');323 assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');
322324
323 var index: usize = 1;325 var index: usize = 1;
...@@ -333,18 +335,18 @@ pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result...@@ -333,18 +335,18 @@ pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result
333 if (bytes[escape_char_index] == 'u') {335 if (bytes[escape_char_index] == 'u') {
334 var buf: [4]u8 = undefined;336 var buf: [4]u8 = undefined;
335 const len = utf8Encode(codepoint, &buf) catch {337 const len = utf8Encode(codepoint, &buf) catch {
336 return Result{ .failure = .{ .invalid_unicode_codepoint = escape_char_index + 1 } };338 return .{ .failure = .{ .invalid_unicode_codepoint = escape_char_index + 1 } };
337 };339 };
338 try writer.writeAll(buf[0..len]);340 try writer.writeAll(buf[0..len]);
339 } else {341 } else {
340 try writer.writeByte(@as(u8, @intCast(codepoint)));342 try writer.writeByte(@as(u8, @intCast(codepoint)));
341 }343 }
342 },344 },
343 .failure => |err| return Result{ .failure = err },345 .failure => |err| return .{ .failure = err },
344 }346 }
345 },347 },
346 '\n' => return Result{ .failure = .{ .invalid_character = index } },348 '\n' => return .{ .failure = .{ .invalid_character = index } },
347 '"' => return Result.success,349 '"' => return .success,
348 else => {350 else => {
349 try writer.writeByte(b);351 try writer.writeByte(b);
350 index += 1;352 index += 1;
...@@ -356,11 +358,13 @@ pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result...@@ -356,11 +358,13 @@ pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result
356/// Higher level API. Does not return extra info about parse errors.358/// Higher level API. Does not return extra info about parse errors.
357/// Caller owns returned memory.359/// Caller owns returned memory.
358pub fn parseAlloc(allocator: std.mem.Allocator, bytes: []const u8) ParseError![]u8 {360pub fn parseAlloc(allocator: std.mem.Allocator, bytes: []const u8) ParseError![]u8 {
359 var buf = std.ArrayList(u8).init(allocator);361 var aw: std.io.Writer.Allocating = .init(allocator);
360 defer buf.deinit();362 defer aw.deinit();
361363 const result = parseWrite(&aw.writer, bytes) catch |err| switch (err) {
362 switch (try parseWrite(buf.writer(), bytes)) {364 error.WriteFailed => return error.OutOfMemory,
363 .success => return buf.toOwnedSlice(),365 };
366 switch (result) {
367 .success => return aw.toOwnedSlice(),
364 .failure => return error.InvalidLiteral,368 .failure => return error.InvalidLiteral,
365 }369 }
366}370}
lib/std/zon/parse.zig+14-8
...@@ -411,18 +411,22 @@ const Parser = struct {...@@ -411,18 +411,22 @@ const Parser = struct {
411 diag: ?*Diagnostics,411 diag: ?*Diagnostics,
412 options: Options,412 options: Options,
413413
414 fn parseExpr(self: *@This(), T: type, node: Zoir.Node.Index) error{ ParseZon, OutOfMemory }!T {414 const ParseExprError = error{ ParseZon, OutOfMemory };
415
416 fn parseExpr(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprError!T {
415 return self.parseExprInner(T, node) catch |err| switch (err) {417 return self.parseExprInner(T, node) catch |err| switch (err) {
416 error.WrongType => return self.failExpectedType(T, node),418 error.WrongType => return self.failExpectedType(T, node),
417 else => |e| return e,419 else => |e| return e,
418 };420 };
419 }421 }
420422
423 const ParseExprInnerError = error{ ParseZon, OutOfMemory, WrongType };
424
421 fn parseExprInner(425 fn parseExprInner(
422 self: *@This(),426 self: *@This(),
423 T: type,427 T: type,
424 node: Zoir.Node.Index,428 node: Zoir.Node.Index,
425 ) error{ ParseZon, OutOfMemory, WrongType }!T {429 ) ParseExprInnerError!T {
426 if (T == Zoir.Node.Index) {430 if (T == Zoir.Node.Index) {
427 return node;431 return node;
428 }432 }
...@@ -611,15 +615,17 @@ const Parser = struct {...@@ -611,15 +615,17 @@ const Parser = struct {
611 }615 }
612 }616 }
613617
614 fn parseString(self: *@This(), T: type, node: Zoir.Node.Index) !T {618 fn parseString(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprInnerError!T {
615 const ast_node = node.getAstNode(self.zoir);619 const ast_node = node.getAstNode(self.zoir);
616 const pointer = @typeInfo(T).pointer;620 const pointer = @typeInfo(T).pointer;
617 var size_hint = ZonGen.strLitSizeHint(self.ast, ast_node);621 var size_hint = ZonGen.strLitSizeHint(self.ast, ast_node);
618 if (pointer.sentinel() != null) size_hint += 1;622 if (pointer.sentinel() != null) size_hint += 1;
619623
620 var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(self.gpa, size_hint);624 var aw: std.Io.Writer.Allocating = .init(self.gpa);
621 defer buf.deinit(self.gpa);625 try aw.ensureUnusedCapacity(size_hint);
622 switch (try ZonGen.parseStrLit(self.ast, ast_node, buf.writer(self.gpa))) {626 defer aw.deinit();
627 const result = ZonGen.parseStrLit(self.ast, ast_node, &aw.writer) catch return error.OutOfMemory;
628 switch (result) {
623 .success => {},629 .success => {},
624 .failure => |err| {630 .failure => |err| {
625 const token = self.ast.nodeMainToken(ast_node);631 const token = self.ast.nodeMainToken(ast_node);
...@@ -638,9 +644,9 @@ const Parser = struct {...@@ -638,9 +644,9 @@ const Parser = struct {
638 }644 }
639645
640 if (pointer.sentinel() != null) {646 if (pointer.sentinel() != null) {
641 return buf.toOwnedSliceSentinel(self.gpa, 0);647 return aw.toOwnedSliceSentinel(0);
642 } else {648 } else {
643 return buf.toOwnedSlice(self.gpa);649 return aw.toOwnedSlice();
644 }650 }
645 }651 }
646652