authorgravatar for i@yvt.jpyvt <i@yvt.jp> 2019-08-28 15:35:49+09:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-28 11:57:01-04:00
logc98f792ff8ed7af0d5e427836600c2ed7b30965e
tree5de21dd53c87442e41e5734d065101ec64dc3f2f
parent7139eef4cfc7c75655f9951849a994b116e96abe

Improve the handling of `zig fmt: off/on`

This commit reworks the handling of `zig fmt: off/on`. A motivating example is shown below: const c = d; // zig fmt: off // comment const a = b; // zig fmt: on Before processing the decl `const a = b;`, `renderRoot` looks for `zig fmt: off` that appears between this decl and the previous one. If it finds one, it searches for the next `zig fmt: on` that re-enables reformatting (or EOF if none was found), and copies the input code between `zig fmt: off` and `zig fmt: on` to the output stream. After that, it proceeds to the next decl. The important thing to notice here is that `renderTopLevelDecl` emits line comment tokens that follow the decl. Therefore, when copying code, we must be careful not to copy the line comment tokens that already have been written to the output stream. The original code failed to take this fact into consideration. It did skip `// zig fmt: off`, but not the remaining ones. As a result, when the above example is fed as input, it duplicated the line `// comment`.

2 files changed, 180 insertions(+), 26 deletions(-)

std/zig/parser_test.zig+97
...@@ -210,6 +210,103 @@ test "zig fmt: comment to disable/enable zig fmt" {...@@ -210,6 +210,103 @@ test "zig fmt: comment to disable/enable zig fmt" {
210 );210 );
211}211}
212212
213test "zig fmt: line comment following 'zig fmt: off'" {
214 try testCanonical(
215 \\// zig fmt: off
216 \\// Test
217 \\const e = f;
218 );
219}
220
221test "zig fmt: doc comment following 'zig fmt: off'" {
222 try testCanonical(
223 \\// zig fmt: off
224 \\/// test
225 \\const e = f;
226 );
227}
228
229test "zig fmt: line and doc comment following 'zig fmt: off'" {
230 try testCanonical(
231 \\// zig fmt: off
232 \\// test 1
233 \\/// test 2
234 \\const e = f;
235 );
236}
237
238test "zig fmt: doc and line comment following 'zig fmt: off'" {
239 try testCanonical(
240 \\// zig fmt: off
241 \\/// test 1
242 \\// test 2
243 \\const e = f;
244 );
245}
246
247test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" {
248 try testCanonical(
249 \\// zig fmt: off
250 \\// zig fmt: on
251 \\// zig fmt: off
252 \\const e = f;
253 \\// zig fmt: off
254 \\// zig fmt: on
255 \\// zig fmt: off
256 \\const a = b;
257 \\// zig fmt: on
258 \\const c = d;
259 \\// zig fmt: on
260 \\
261 );
262}
263
264test "zig fmt: line comment following 'zig fmt: on'" {
265 try testCanonical(
266 \\// zig fmt: off
267 \\const e = f;
268 \\// zig fmt: on
269 \\// test
270 \\const e = f;
271 \\
272 );
273}
274
275test "zig fmt: doc comment following 'zig fmt: on'" {
276 try testCanonical(
277 \\// zig fmt: off
278 \\const e = f;
279 \\// zig fmt: on
280 \\/// test
281 \\const e = f;
282 \\
283 );
284}
285
286test "zig fmt: line and doc comment following 'zig fmt: on'" {
287 try testCanonical(
288 \\// zig fmt: off
289 \\const e = f;
290 \\// zig fmt: on
291 \\// test1
292 \\/// test2
293 \\const e = f;
294 \\
295 );
296}
297
298test "zig fmt: doc and line comment following 'zig fmt: on'" {
299 try testCanonical(
300 \\// zig fmt: off
301 \\const e = f;
302 \\// zig fmt: on
303 \\/// test1
304 \\// test2
305 \\const e = f;
306 \\
307 );
308}
309
213test "zig fmt: pointer of unknown length" {310test "zig fmt: pointer of unknown length" {
214 try testCanonical(311 try testCanonical(
215 \\fn foo(ptr: [*]u8) void {}312 \\fn foo(ptr: [*]u8) void {}
std/zig/render.zig+83-26
...@@ -89,41 +89,98 @@ fn renderRoot(...@@ -89,41 +89,98 @@ fn renderRoot(
89 var it = tree.root_node.decls.iterator(0);89 var it = tree.root_node.decls.iterator(0);
90 while (true) {90 while (true) {
91 var decl = (it.next() orelse return).*;91 var decl = (it.next() orelse return).*;
92 // look for zig fmt: off comment92
93 var start_token_index = decl.firstToken();93 // This loop does the following:
94 zig_fmt_loop: while (start_token_index != 0) {94 //
95 start_token_index -= 1;95 // - Iterates through line/doc comment tokens that precedes the current
96 const start_token = tree.tokens.at(start_token_index);96 // decl.
97 switch (start_token.id) {97 // - Figures out the first token index (`copy_start_token_index`) which
98 // hasn't been copied to the output stream yet.
99 // - Detects `zig fmt: (off|on)` in the line comment tokens, and
100 // determines whether the current decl should be reformatted or not.
101 //
102 var token_index = decl.firstToken();
103 var fmt_active = true;
104 var found_fmt_directive = false;
105
106 var copy_start_token_index = token_index;
107
108 while (token_index != 0) {
109 token_index -= 1;
110 const token = tree.tokens.at(token_index);
111 switch (token.id) {
98 Token.Id.LineComment => {},112 Token.Id.LineComment => {},
99 Token.Id.DocComment => continue,113 Token.Id.DocComment => {
114 copy_start_token_index = token_index;
115 continue;
116 },
100 else => break,117 else => break,
101 }118 }
102 if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(start_token)[2..], " "), "zig fmt: off")) {119
103 var end_token_index = start_token_index;120 if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: off")) {
104 while (true) {121 if (!found_fmt_directive) {
105 end_token_index += 1;122 fmt_active = false;
106 const end_token = tree.tokens.at(end_token_index);123 found_fmt_directive = true;
107 switch (end_token.id) {124 }
125 } else if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: on")) {
126 if (!found_fmt_directive) {
127 fmt_active = true;
128 found_fmt_directive = true;
129 }
130 }
131 }
132
133 if (!fmt_active) {
134 // Reformatting is disabled for the current decl and possibly some
135 // more decls that follow.
136 // Find the next `decl` for which reformatting is re-enabled.
137 token_index = decl.firstToken();
138
139 while (!fmt_active) {
140 decl = (it.next() orelse {
141 // If there's no next reformatted `decl`, just copy the
142 // remaining input tokens and bail out.
143 const start = tree.tokens.at(copy_start_token_index).start;
144 try copyFixingWhitespace(stream, tree.source[start..]);
145 return;
146 }).*;
147 var decl_first_token_index = decl.firstToken();
148
149 while (token_index < decl_first_token_index) : (token_index += 1) {
150 const token = tree.tokens.at(token_index);
151 switch (token.id) {
108 Token.Id.LineComment => {},152 Token.Id.LineComment => {},
109 Token.Id.Eof => {153 Token.Id.Eof => unreachable,
110 const start = tree.tokens.at(start_token_index + 1).start;
111 try copyFixingWhitespace(stream, tree.source[start..]);
112 return;
113 },
114 else => continue,154 else => continue,
115 }155 }
116 if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(end_token)[2..], " "), "zig fmt: on")) {156 if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: on")) {
117 const start = tree.tokens.at(start_token_index + 1).start;157 fmt_active = true;
118 try copyFixingWhitespace(stream, tree.source[start..end_token.end]);158 } else if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: off")) {
119 try stream.writeByte('\n');159 fmt_active = false;
120 while (tree.tokens.at(decl.firstToken()).start < end_token.end) {
121 decl = (it.next() orelse return).*;
122 }
123 break :zig_fmt_loop;
124 }160 }
125 }161 }
126 }162 }
163
164 // Found the next `decl` for which reformatting is enabled. Copy
165 // the input tokens before the `decl` that haven't been copied yet.
166 var copy_end_token_index = decl.firstToken();
167 token_index = copy_end_token_index;
168 while (token_index != 0) {
169 token_index -= 1;
170 const token = tree.tokens.at(token_index);
171 switch (token.id) {
172 Token.Id.LineComment => {},
173 Token.Id.DocComment => {
174 copy_end_token_index = token_index;
175 continue;
176 },
177 else => break,
178 }
179 }
180
181 const start = tree.tokens.at(copy_start_token_index).start;
182 const end = tree.tokens.at(copy_end_token_index).start;
183 try copyFixingWhitespace(stream, tree.source[start..end]);
127 }184 }
128185
129 try renderTopLevelDecl(allocator, stream, tree, 0, &start_col, decl);186 try renderTopLevelDecl(allocator, stream, tree, 0, &start_col, decl);