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.
This commit is contained in:
Isaac Freund 2021-02-16 20:57:18 +01:00
parent 070e548acf
commit 895fb2bd6d
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
2 changed files with 195 additions and 135 deletions

View File

@ -878,17 +878,17 @@ test "zig fmt: async function" {
);
}
//test "zig fmt: whitespace fixes" {
// try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",
// \\test "" {
// \\ const hi = x;
// \\}
// \\// zig fmt: off
// \\test ""{
// \\ const a = b;}
// \\
// );
//}
test "zig fmt: whitespace fixes" {
try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",
\\test "" {
\\ const hi = x;
\\}
\\// zig fmt: off
\\test ""{
\\ const a = b;}
\\
);
}
test "zig fmt: while else err prong with no block" {
try testCanonical(
@ -1098,128 +1098,154 @@ test "zig fmt: aligned struct field" {
);
}
//test "zig fmt: comment to disable/enable zig fmt first" {
// try testCanonical(
// \\// Test trailing comma syntax
// \\// zig fmt: off
// \\
// \\const struct_trailing_comma = struct { x: i32, y: i32, };
// );
//}
//
//test "zig fmt: comment to disable/enable zig fmt" {
// try testTransform(
// \\const a = b;
// \\// zig fmt: off
// \\const c = d;
// \\// zig fmt: on
// \\const e = f;
// ,
// \\const a = b;
// \\// zig fmt: off
// \\const c = d;
// \\// zig fmt: on
// \\const e = f;
// \\
// );
//}
//
//test "zig fmt: line comment following 'zig fmt: off'" {
// try testCanonical(
// \\// zig fmt: off
// \\// Test
// \\const e = f;
// );
//}
//
//test "zig fmt: doc comment following 'zig fmt: off'" {
// try testCanonical(
// \\// zig fmt: off
// \\/// test
// \\const e = f;
// );
//}
//
//test "zig fmt: line and doc comment following 'zig fmt: off'" {
// try testCanonical(
// \\// zig fmt: off
// \\// test 1
// \\/// test 2
// \\const e = f;
// );
//}
//
//test "zig fmt: doc and line comment following 'zig fmt: off'" {
// try testCanonical(
// \\// zig fmt: off
// \\/// test 1
// \\// test 2
// \\const e = f;
// );
//}
//
//test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" {
// try testCanonical(
// \\// zig fmt: off
// \\// zig fmt: on
// \\// zig fmt: off
// \\const e = f;
// \\// zig fmt: off
// \\// zig fmt: on
// \\// zig fmt: off
// \\const a = b;
// \\// zig fmt: on
// \\const c = d;
// \\// zig fmt: on
// \\
// );
//}
//
//test "zig fmt: line comment following 'zig fmt: on'" {
// try testCanonical(
// \\// zig fmt: off
// \\const e = f;
// \\// zig fmt: on
// \\// test
// \\const e = f;
// \\
// );
//}
//
//test "zig fmt: doc comment following 'zig fmt: on'" {
// try testCanonical(
// \\// zig fmt: off
// \\const e = f;
// \\// zig fmt: on
// \\/// test
// \\const e = f;
// \\
// );
//}
//
//test "zig fmt: line and doc comment following 'zig fmt: on'" {
// try testCanonical(
// \\// zig fmt: off
// \\const e = f;
// \\// zig fmt: on
// \\// test1
// \\/// test2
// \\const e = f;
// \\
// );
//}
//
//test "zig fmt: doc and line comment following 'zig fmt: on'" {
// try testCanonical(
// \\// zig fmt: off
// \\const e = f;
// \\// zig fmt: on
// \\/// test1
// \\// test2
// \\const e = f;
// \\
// );
//}
test "zig fmt: comment to disable/enable zig fmt first" {
try testCanonical(
\\// Test trailing comma syntax
\\// zig fmt: off
\\
\\const struct_trailing_comma = struct { x: i32, y: i32, };
);
}
test "zig fmt: comment to disable/enable zig fmt" {
try testTransform(
\\const a = b;
\\// zig fmt: off
\\const c = d;
\\// zig fmt: on
\\const e = f;
,
\\const a = b;
\\// zig fmt: off
\\const c = d;
\\// zig fmt: on
\\const e = f;
\\
);
}
test "zig fmt: line comment following 'zig fmt: off'" {
try testCanonical(
\\// zig fmt: off
\\// Test
\\const e = f;
);
}
test "zig fmt: doc comment following 'zig fmt: off'" {
try testCanonical(
\\// zig fmt: off
\\/// test
\\const e = f;
);
}
test "zig fmt: line and doc comment following 'zig fmt: off'" {
try testCanonical(
\\// zig fmt: off
\\// test 1
\\/// test 2
\\const e = f;
);
}
test "zig fmt: doc and line comment following 'zig fmt: off'" {
try testCanonical(
\\// zig fmt: off
\\/// test 1
\\// test 2
\\const e = f;
);
}
test "zig fmt: alternating 'zig fmt: off' and 'zig fmt: on'" {
try testCanonical(
\\// zig fmt: off
\\// zig fmt: on
\\// zig fmt: off
\\const e = f;
\\// zig fmt: off
\\// zig fmt: on
\\// zig fmt: off
\\const a = b;
\\// zig fmt: on
\\const c = d;
\\// zig fmt: on
\\
);
}
test "zig fmt: line comment following 'zig fmt: on'" {
try testCanonical(
\\// zig fmt: off
\\const e = f;
\\// zig fmt: on
\\// test
\\const e = f;
\\
);
}
test "zig fmt: doc comment following 'zig fmt: on'" {
try testCanonical(
\\// zig fmt: off
\\const e = f;
\\// zig fmt: on
\\/// test
\\const e = f;
\\
);
}
test "zig fmt: line and doc comment following 'zig fmt: on'" {
try testCanonical(
\\// zig fmt: off
\\const e = f;
\\// zig fmt: on
\\// test1
\\/// test2
\\const e = f;
\\
);
}
test "zig fmt: doc and line comment following 'zig fmt: on'" {
try testCanonical(
\\// zig fmt: off
\\const e = f;
\\// zig fmt: on
\\/// test1
\\// test2
\\const e = f;
\\
);
}
test "zig fmt: 'zig fmt: (off|on)' works in the middle of code" {
try testTransform(
\\test "" {
\\ const x = 42;
\\
\\ if (foobar) |y| {
\\ // zig fmt: off
\\ }// zig fmt: on
\\
\\ const z = 420;
\\}
\\
,
\\test "" {
\\ const x = 42;
\\
\\ if (foobar) |y| {
\\ // zig fmt: off
\\ }// zig fmt: on
\\
\\ const z = 420;
\\}
\\
);
}
test "zig fmt: pointer of unknown length" {
try testCanonical(

View File

@ -30,6 +30,10 @@ pub fn renderTree(buffer: *std.ArrayList(u8), tree: ast.Tree) Error!void {
_ = try renderComments(ais, tree, 0, comment_end_loc);
try renderMembers(ais, tree, tree.rootDecls());
if (ais.disabled_offset) |disabled_offset| {
try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..]);
}
}
/// 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
const comment_start = index + offset;
const newline = comment_start +
mem.indexOfScalar(u8, tree.source[comment_start..end], '\n').?;
const untrimmed_comment = tree.source[comment_start..newline];
const trimmed_comment = mem.trimRight(u8, untrimmed_comment, &std.ascii.spaces);
@ -1993,6 +1998,17 @@ fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!boo
try ais.writer().print("{s}\n", .{trimmed_comment});
index = newline + 1;
if (ais.disabled_offset) |disabled_offset| {
if (mem.eql(u8, trimmed_comment, "// zig fmt: on")) {
// write the source for which formatting was disabled directly
// to the underlying writer, fixing up invaild whitespace
try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..index]);
ais.disabled_offset = null;
}
} else if (mem.eql(u8, trimmed_comment, "// zig fmt: off")) {
ais.disabled_offset = index;
}
}
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 {
return ret;
}
fn writeFixingWhitespace(writer: std.ArrayList(u8).Writer, slice: []const u8) Error!void {
for (slice) |byte| switch (byte) {
'\t' => try writer.writeAll(" " ** 4),
'\r' => {},
else => try writer.writeByte(byte),
};
}
fn nodeIsBlock(tag: ast.Node.Tag) bool {
return switch (tag) {
.block,
@ -2145,6 +2169,14 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
underlying_writer: UnderlyingWriter,
/// Offset into the source at which formatting has been disabled with
/// a `zig fmt: off` comment.
///
/// If non-null, the AutoIndentingStream will not write any bytes
/// to the underlying writer. It will however continue to track the
/// indentation level.
disabled_offset: ?usize = null,
indent_count: usize = 0,
indent_delta: usize,
current_line_empty: bool = true,
@ -2183,7 +2215,7 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
if (bytes.len == 0)
return @as(usize, 0);
try self.underlying_writer.writeAll(bytes);
if (self.disabled_offset == null) try self.underlying_writer.writeAll(bytes);
if (bytes[bytes.len - 1] == '\n')
self.resetLine();
return bytes.len;
@ -2243,7 +2275,9 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
fn applyIndent(self: *Self) Error!void {
const current_indent = self.currentIndent();
if (self.current_line_empty and current_indent > 0) {
try self.underlying_writer.writeByteNTimes(' ', current_indent);
if (self.disabled_offset == null) {
try self.underlying_writer.writeByteNTimes(' ', current_indent);
}
self.applied_indent = current_indent;
}