Commit 0c3bd0c3d1

Andrew Kelley <superjoe30@gmail.com>
2018-11-15 01:50:55
zig fmt: add --check flag
closes #1558 closes #1555
1 parent 65cddc5
Changed files (4)
src-self-hosted
std
src-self-hosted/arg.zig
@@ -149,7 +149,7 @@ pub const Args = struct {
     }
 
     // e.g. --help
-    pub fn present(self: *Args, name: []const u8) bool {
+    pub fn present(self: *const Args, name: []const u8) bool {
         return self.flags.contains(name);
     }
 
src-self-hosted/main.zig
@@ -514,17 +514,22 @@ const usage_fmt =
     \\usage: zig fmt [file]...
     \\
     \\   Formats the input files and modifies them in-place.
+    \\   Arguments can be files or directories, which are searched
+    \\   recursively.
     \\
     \\Options:
     \\   --help                 Print this help and exit
     \\   --color [auto|off|on]  Enable or disable colored error messages
-    \\   --stdin                Format code from stdin
+    \\   --stdin                Format code from stdin; output to stdout
+    \\   --check                List non-conforming files and exit with an error
+    \\                          if the list is non-empty
     \\
     \\
 ;
 
 const args_fmt_spec = []Flag{
     Flag.Bool("--help"),
+    Flag.Bool("--check"),
     Flag.Option("--color", []const []const u8{
         "auto",
         "off",
@@ -640,6 +645,11 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
         if (tree.errors.len != 0) {
             os.exit(1);
         }
+        if (flags.present("check")) {
+            const anything_changed = try std.zig.render(allocator, io.noop_out_stream, &tree);
+            const code = if (anything_changed) u8(1) else u8(0);
+            os.exit(code);
+        }
 
         _ = try std.zig.render(allocator, stdout, &tree);
         return;
@@ -711,9 +721,11 @@ async fn asyncFmtMain(
         .loop = loop,
     };
 
+    const check_mode = flags.present("check");
+
     var group = event.Group(FmtError!void).init(loop);
     for (flags.positionals.toSliceConst()) |file_path| {
-        try group.call(fmtPath, &fmt, file_path);
+        try group.call(fmtPath, &fmt, file_path, check_mode);
     }
     try await (async group.wait() catch unreachable);
     if (fmt.any_error) {
@@ -721,7 +733,7 @@ async fn asyncFmtMain(
     }
 }
 
-async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
+async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void {
     const file_path = try std.mem.dupe(fmt.loop.allocator, u8, file_path_ref);
     defer fmt.loop.allocator.free(file_path);
 
@@ -746,7 +758,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
             while (try dir.next()) |entry| {
                 if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
                     const full_path = try os.path.join(fmt.loop.allocator, file_path, entry.name);
-                    try group.call(fmtPath, fmt, full_path);
+                    try group.call(fmtPath, fmt, full_path, check_mode);
                 }
             }
             return await (async group.wait() catch unreachable);
@@ -779,14 +791,22 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
         return;
     }
 
-    // TODO make this evented
-    const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path);
-    defer baf.destroy();
-
-    const anything_changed = try std.zig.render(fmt.loop.allocator, baf.stream(), &tree);
-    if (anything_changed) {
-        try stderr.print("{}\n", file_path);
-        try baf.finish();
+    if (check_mode) {
+        const anything_changed = try std.zig.render(fmt.loop.allocator, io.noop_out_stream, &tree);
+        if (anything_changed) {
+            try stderr.print("{}\n", file_path);
+            fmt.any_error = true;
+        }
+    } else {
+        // TODO make this evented
+        const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path);
+        defer baf.destroy();
+
+        const anything_changed = try std.zig.render(fmt.loop.allocator, baf.stream(), &tree);
+        if (anything_changed) {
+            try stderr.print("{}\n", file_path);
+            try baf.finish();
+        }
     }
 }
 
std/zig/render.zig
@@ -133,7 +133,7 @@ fn renderRoot(
     }
 }
 
-fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) !void {
+fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @typeOf(stream).Child.Error!void {
     const first_token = node.firstToken();
     var prev_token = first_token;
     while (tree.tokens.at(prev_token - 1).id == Token.Id.DocComment) {
std/io.zig
@@ -203,20 +203,20 @@ pub fn OutStream(comptime WriteError: type) type {
 
         writeFn: fn (self: *Self, bytes: []const u8) Error!void,
 
-        pub fn print(self: *Self, comptime format: []const u8, args: ...) !void {
+        pub fn print(self: *Self, comptime format: []const u8, args: ...) Error!void {
             return std.fmt.format(self, Error, self.writeFn, format, args);
         }
 
-        pub fn write(self: *Self, bytes: []const u8) !void {
+        pub fn write(self: *Self, bytes: []const u8) Error!void {
             return self.writeFn(self, bytes);
         }
 
-        pub fn writeByte(self: *Self, byte: u8) !void {
+        pub fn writeByte(self: *Self, byte: u8) Error!void {
             const slice = (*[1]u8)(&byte)[0..];
             return self.writeFn(self, slice);
         }
 
-        pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) !void {
+        pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void {
             const slice = (*[1]u8)(&byte)[0..];
             var i: usize = 0;
             while (i < n) : (i += 1) {
@@ -225,23 +225,23 @@ pub fn OutStream(comptime WriteError: type) type {
         }
 
         /// Write a native-endian integer.
-        pub fn writeIntNe(self: *Self, comptime T: type, value: T) !void {
+        pub fn writeIntNe(self: *Self, comptime T: type, value: T) Error!void {
             return self.writeInt(builtin.endian, T, value);
         }
 
-        pub fn writeIntLe(self: *Self, comptime T: type, value: T) !void {
+        pub fn writeIntLe(self: *Self, comptime T: type, value: T) Error!void {
             var bytes: [@sizeOf(T)]u8 = undefined;
             mem.writeIntLE(T, &bytes, value);
             return self.writeFn(self, bytes);
         }
 
-        pub fn writeIntBe(self: *Self, comptime T: type, value: T) !void {
+        pub fn writeIntBe(self: *Self, comptime T: type, value: T) Error!void {
             var bytes: [@sizeOf(T)]u8 = undefined;
             mem.writeIntBE(T, &bytes, value);
             return self.writeFn(self, bytes);
         }
 
-        pub fn writeInt(self: *Self, endian: builtin.Endian, comptime T: type, value: T) !void {
+        pub fn writeInt(self: *Self, endian: builtin.Endian, comptime T: type, value: T) Error!void {
             var bytes: [@sizeOf(T)]u8 = undefined;
             mem.writeInt(bytes[0..], value, endian);
             return self.writeFn(self, bytes);
@@ -249,6 +249,13 @@ pub fn OutStream(comptime WriteError: type) type {
     };
 }
 
+pub const noop_out_stream = &noop_out_stream_state;
+const NoopOutStreamError = error{};
+var noop_out_stream_state = OutStream(NoopOutStreamError){
+    .writeFn = noop_out_stream_write,
+};
+fn noop_out_stream_write(self: *OutStream(NoopOutStreamError), bytes: []const u8) error{}!void {}
+
 pub fn writeFile(path: []const u8, data: []const u8) !void {
     var file = try File.openWrite(path);
     defer file.close();