authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-02-16 20:57:18+01:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-02-16 23:20:53+01:00
log895fb2bd6da5e1b69668dd6572df9d559ebf2407
treec44f7896934bc266d16a6586e5c3ea2180e20d82
parent070e548acf8b5cb22459b779ce771b42157f49f7
signaturelock-open Commit is signed but in an unrecognized format.

zig fmt: implement 'zig fmt: (on|off)' directives

With the new implementation, these now work anywhere in the source code as opposed to only at the top level.

2 files changed, 195 insertions(+), 135 deletions(-)

lib/std/zig/parser_test.zig+159-133
...@@ -878,17 +878,17 @@ test "zig fmt: async function" {...@@ -878,17 +878,17 @@ test "zig fmt: async function" {
878 );878 );
879}879}
880880
881//test "zig fmt: whitespace fixes" {881test "zig fmt: whitespace fixes" {
882// try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",882 try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",
883// \\test "" {883 \\test "" {
884// \\ const hi = x;884 \\ const hi = x;
885// \\}885 \\}
886// \\// zig fmt: off886 \\// zig fmt: off
887// \\test ""{887 \\test ""{
888// \\ const a = b;}888 \\ const a = b;}
889// \\889 \\
890// );890 );
891//}891}
892892
893test "zig fmt: while else err prong with no block" {893test "zig fmt: while else err prong with no block" {
894 try testCanonical(894 try testCanonical(
...@@ -1098,128 +1098,154 @@ test "zig fmt: aligned struct field" {...@@ -1098,128 +1098,154 @@ test "zig fmt: aligned struct field" {
1098 );1098 );
1099}1099}
11001100
1101//test "zig fmt: comment to disable/enable zig fmt first" {1101test "zig fmt: comment to disable/enable zig fmt first" {
1102// try testCanonical(1102 try testCanonical(
1103// \\// Test trailing comma syntax1103 \\// Test trailing comma syntax
1104// \\// zig fmt: off1104 \\// zig fmt: off
1105// \\1105 \\
1106// \\const struct_trailing_comma = struct { x: i32, y: i32, };1106 \\const struct_trailing_comma = struct { x: i32, y: i32, };
1107// );1107 );
1108//}1108}
1109//1109
1110//test "zig fmt: comment to disable/enable zig fmt" {1110test "zig fmt: comment to disable/enable zig fmt" {
1111// try testTransform(1111 try testTransform(
1112// \\const a = b;1112 \\const a = b;
1113// \\// zig fmt: off1113 \\// zig fmt: off
1114// \\const c = d;1114 \\const c = d;
1115// \\// zig fmt: on1115 \\// zig fmt: on
1116// \\const e = f;1116 \\const e = f;
1117// ,1117 ,
1118// \\const a = b;1118 \\const a = b;
1119// \\// zig fmt: off1119 \\// zig fmt: off
1120// \\const c = d;1120 \\const c = d;
1121// \\// zig fmt: on1121 \\// zig fmt: on
1122// \\const e = f;1122 \\const e = f;
1123// \\1123 \\
1124// );1124 );
1125//}1125}
1126//1126
1127//test "zig fmt: line comment following 'zig fmt: off'" {1127test "zig fmt: line comment following 'zig fmt: off'" {
1128// try testCanonical(1128 try testCanonical(
1129// \\// zig fmt: off1129 \\// zig fmt: off
1130// \\// Test1130 \\// Test
1131// \\const e = f;1131 \\const e = f;
1132// );1132 );
1133//}1133}
1134//1134
1135//test "zig fmt: doc comment following 'zig fmt: off'" {1135test "zig fmt: doc comment following 'zig fmt: off'" {
1136// try testCanonical(1136 try testCanonical(
1137// \\// zig fmt: off1137 \\// zig fmt: off
1138// \\/// test1138 \\/// test
1139// \\const e = f;1139 \\const e = f;
1140// );1140 );
1141//}1141}
1142//1142
1143//test "zig fmt: line and doc comment following 'zig fmt: off'" {1143test "zig fmt: line and doc comment following 'zig fmt: off'" {
1144// try testCanonical(1144 try testCanonical(
1145// \\// zig fmt: off1145 \\// zig fmt: off
1146// \\// test 11146 \\// test 1
1147// \\/// test 21147 \\/// test 2
1148// \\const e = f;1148 \\const e = f;
1149// );1149 );
1150//}1150}
1151//1151
1152//test "zig fmt: doc and line comment following 'zig fmt: off'" {1152test "zig fmt: doc and line comment following 'zig fmt: off'" {
1153// try testCanonical(1153 try testCanonical(
1154// \\// zig fmt: off1154 \\// zig fmt: off
1155// \\/// test 11155 \\/// test 1
1156// \\// test 21156 \\// test 2
1157// \\const e = f;1157 \\const e = f;
1158// );1158 );
1159//}1159}
1160//1160
1161//test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" {1161test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" {
1162// try testCanonical(1162 try testCanonical(
1163// \\// zig fmt: off1163 \\// zig fmt: off
1164// \\// zig fmt: on1164 \\// zig fmt: on
1165// \\// zig fmt: off1165 \\// zig fmt: off
1166// \\const e = f;1166 \\const e = f;
1167// \\// zig fmt: off1167 \\// zig fmt: off
1168// \\// zig fmt: on1168 \\// zig fmt: on
1169// \\// zig fmt: off1169 \\// zig fmt: off
1170// \\const a = b;1170 \\const a = b;
1171// \\// zig fmt: on1171 \\// zig fmt: on
1172// \\const c = d;1172 \\const c = d;
1173// \\// zig fmt: on1173 \\// zig fmt: on
1174// \\1174 \\
1175// );1175 );
1176//}1176}
1177//1177
1178//test "zig fmt: line comment following 'zig fmt: on'" {1178test "zig fmt: line comment following 'zig fmt: on'" {
1179// try testCanonical(1179 try testCanonical(
1180// \\// zig fmt: off1180 \\// zig fmt: off
1181// \\const e = f;1181 \\const e = f;
1182// \\// zig fmt: on1182 \\// zig fmt: on
1183// \\// test1183 \\// test
1184// \\const e = f;1184 \\const e = f;
1185// \\1185 \\
1186// );1186 );
1187//}1187}
1188//1188
1189//test "zig fmt: doc comment following 'zig fmt: on'" {1189test "zig fmt: doc comment following 'zig fmt: on'" {
1190// try testCanonical(1190 try testCanonical(
1191// \\// zig fmt: off1191 \\// zig fmt: off
1192// \\const e = f;1192 \\const e = f;
1193// \\// zig fmt: on1193 \\// zig fmt: on
1194// \\/// test1194 \\/// test
1195// \\const e = f;1195 \\const e = f;
1196// \\1196 \\
1197// );1197 );
1198//}1198}
1199//1199
1200//test "zig fmt: line and doc comment following 'zig fmt: on'" {1200test "zig fmt: line and doc comment following 'zig fmt: on'" {
1201// try testCanonical(1201 try testCanonical(
1202// \\// zig fmt: off1202 \\// zig fmt: off
1203// \\const e = f;1203 \\const e = f;
1204// \\// zig fmt: on1204 \\// zig fmt: on
1205// \\// test11205 \\// test1
1206// \\/// test21206 \\/// test2
1207// \\const e = f;1207 \\const e = f;
1208// \\1208 \\
1209// );1209 );
1210//}1210}
1211//1211
1212//test "zig fmt: doc and line comment following 'zig fmt: on'" {1212test "zig fmt: doc and line comment following 'zig fmt: on'" {
1213// try testCanonical(1213 try testCanonical(
1214// \\// zig fmt: off1214 \\// zig fmt: off
1215// \\const e = f;1215 \\const e = f;
1216// \\// zig fmt: on1216 \\// zig fmt: on
1217// \\/// test11217 \\/// test1
1218// \\// test21218 \\// test2
1219// \\const e = f;1219 \\const e = f;
1220// \\1220 \\
1221// );1221 );
1222//}1222}
1223
1224test "zig fmt: 'zig fmt: (off|on)' works in the middle of code" {
1225 try testTransform(
1226 \\test "" {
1227 \\ const x = 42;
1228 \\
1229 \\ if (foobar) |y| {
1230 \\ // zig fmt: off
1231 \\ }// zig fmt: on
1232 \\
1233 \\ const z = 420;
1234 \\}
1235 \\
1236 ,
1237 \\test "" {
1238 \\ const x = 42;
1239 \\
1240 \\ if (foobar) |y| {
1241 \\ // zig fmt: off
1242 \\ }// zig fmt: on
1243 \\
1244 \\ const z = 420;
1245 \\}
1246 \\
1247 );
1248}
12231249
1224test "zig fmt: pointer of unknown length" {1250test "zig fmt: pointer of unknown length" {
1225 try testCanonical(1251 try testCanonical(
lib/std/zig/render.zig+36-2
...@@ -30,6 +30,10 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void {...@@ -30,6 +30,10 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void {
30 _ = try renderComments(ais, tree, 0, comment_end_loc);30 _ = try renderComments(ais, tree, 0, comment_end_loc);
3131
32 try renderMembers(ais, tree, tree.rootDecls());32 try renderMembers(ais, tree, tree.rootDecls());
33
34 if (ais.disabled_offset) |disabled_offset| {
35 try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..]);
36 }
33}37}
3438
35/// Render all members in the given slice, keeping empty lines where appropriate39/// Render all members in the given slice, keeping empty lines where appropriate
...@@ -1971,6 +1975,7 @@ fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!boo...@@ -1971,6 +1975,7 @@ fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!boo
1971 const comment_start = index + offset;1975 const comment_start = index + offset;
1972 const newline = comment_start +1976 const newline = comment_start +
1973 mem.indexOfScalar(u8, tree.source[comment_start..end], '\n').?;1977 mem.indexOfScalar(u8, tree.source[comment_start..end], '\n').?;
1978
1974 const untrimmed_comment = tree.source[comment_start..newline];1979 const untrimmed_comment = tree.source[comment_start..newline];
1975 const trimmed_comment = mem.trimRight(u8, untrimmed_comment, &std.ascii.spaces);1980 const trimmed_comment = mem.trimRight(u8, untrimmed_comment, &std.ascii.spaces);
19761981
...@@ -1993,6 +1998,17 @@ fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!boo...@@ -1993,6 +1998,17 @@ fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!boo
19931998
1994 try ais.writer().print("{s}\n", .{trimmed_comment});1999 try ais.writer().print("{s}\n", .{trimmed_comment});
1995 index = newline + 1;2000 index = newline + 1;
2001
2002 if (ais.disabled_offset) |disabled_offset| {
2003 if (mem.eql(u8, trimmed_comment, "// zig fmt: on")) {
2004 // write the source for which formatting was disabled directly
2005 // to the underlying writer, fixing up invaild whitespace
2006 try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..index]);
2007 ais.disabled_offset = null;
2008 }
2009 } else if (mem.eql(u8, trimmed_comment, "// zig fmt: off")) {
2010 ais.disabled_offset = index;
2011 }
1996 }2012 }
19972013
1998 if (index != start and mem.containsAtLeast(u8, tree.source[index - 1 .. end], 2, "\n")) {2014 if (index != start and mem.containsAtLeast(u8, tree.source[index - 1 .. end], 2, "\n")) {
...@@ -2066,6 +2082,14 @@ fn tokenSliceForRender(tree: ast.Tree, token_index: ast.TokenIndex) []const u8 {...@@ -2066,6 +2082,14 @@ fn tokenSliceForRender(tree: ast.Tree, token_index: ast.TokenIndex) []const u8 {
2066 return ret;2082 return ret;
2067}2083}
20682084
2085fn writeFixingWhitespace(writer: std.ArrayList(u8).Writer, slice: []const u8) Error!void {
2086 for (slice) |byte| switch (byte) {
2087 '\t' => try writer.writeAll(" " ** 4),
2088 '\r' => {},
2089 else => try writer.writeByte(byte),
2090 };
2091}
2092
2069fn nodeIsBlock(tag: ast.Node.Tag) bool {2093fn nodeIsBlock(tag: ast.Node.Tag) bool {
2070 return switch (tag) {2094 return switch (tag) {
2071 .block,2095 .block,
...@@ -2145,6 +2169,14 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {...@@ -2145,6 +2169,14 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
21452169
2146 underlying_writer: UnderlyingWriter,2170 underlying_writer: UnderlyingWriter,
21472171
2172 /// Offset into the source at which formatting has been disabled with
2173 /// a `zig fmt: off` comment.
2174 ///
2175 /// If non-null, the AutoIndentingStream will not write any bytes
2176 /// to the underlying writer. It will however continue to track the
2177 /// indentation level.
2178 disabled_offset: ?usize = null,
2179
2148 indent_count: usize = 0,2180 indent_count: usize = 0,
2149 indent_delta: usize,2181 indent_delta: usize,
2150 current_line_empty: bool = true,2182 current_line_empty: bool = true,
...@@ -2183,7 +2215,7 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {...@@ -2183,7 +2215,7 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
2183 if (bytes.len == 0)2215 if (bytes.len == 0)
2184 return @as(usize, 0);2216 return @as(usize, 0);
21852217
2186 try self.underlying_writer.writeAll(bytes);2218 if (self.disabled_offset == null) try self.underlying_writer.writeAll(bytes);
2187 if (bytes[bytes.len - 1] == '\n')2219 if (bytes[bytes.len - 1] == '\n')
2188 self.resetLine();2220 self.resetLine();
2189 return bytes.len;2221 return bytes.len;
...@@ -2243,7 +2275,9 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {...@@ -2243,7 +2275,9 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
2243 fn applyIndent(self: *Self) Error!void {2275 fn applyIndent(self: *Self) Error!void {
2244 const current_indent = self.currentIndent();2276 const current_indent = self.currentIndent();
2245 if (self.current_line_empty and current_indent > 0) {2277 if (self.current_line_empty and current_indent > 0) {
2246 try self.underlying_writer.writeByteNTimes(' ', current_indent);2278 if (self.disabled_offset == null) {
2279 try self.underlying_writer.writeByteNTimes(' ', current_indent);
2280 }
2247 self.applied_indent = current_indent;2281 self.applied_indent = current_indent;
2248 }2282 }
22492283