Commit 2c96f19fd3

Andrew Kelley <superjoe30@gmail.com>
2018-05-30 20:55:59
std.zig.render returns bool of whether anything changed
zig fmt only renames files and prints to stdout for files which changed
1 parent 15302e8
Changed files (4)
src-self-hosted/main.zig
@@ -719,6 +719,9 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {
         };
         defer tree.deinit();
 
+        var old_digest: [256]u8 = undefined;
+        std.crypto.Sha256.hash(source_code, old_digest[0..]);
+
         var error_it = tree.errors.iterator(0);
         while (error_it.next()) |parse_error| {
             const token = tree.tokens.at(parse_error.loc());
@@ -745,13 +748,14 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {
             continue;
         }
 
-        try stderr.print("{}\n", file_path);
-
         const baf = try io.BufferedAtomicFile.create(allocator, file_path);
         defer baf.destroy();
 
-        try std.zig.render(allocator, baf.stream(), &tree);
-        try baf.finish();
+        const anything_changed = try std.zig.render(allocator, baf.stream(), &tree);
+        if (anything_changed) {
+            try stderr.print("{}\n", file_path);
+            try baf.finish();
+        }
     }
 }
 
src-self-hosted/module.zig
@@ -255,7 +255,7 @@ pub const Module = struct {
         const out_stream = &stderr_file_out_stream.stream;
 
         warn("====fmt:====\n");
-        try std.zig.render(self.allocator, out_stream, &tree);
+        _ = try std.zig.render(self.allocator, out_stream, &tree);
 
         warn("====ir:====\n");
         warn("TODO\n\n");
std/zig/parser_test.zig
@@ -1792,7 +1792,7 @@ const io = std.io;
 
 var fixed_buffer_mem: [100 * 1024]u8 = undefined;
 
-fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
+fn testParse(source: []const u8, allocator: &mem.Allocator, changes_expected: bool) ![]u8 {
     var stderr_file = try io.getStdErr();
     var stderr = &io.FileOutStream.init(&stderr_file).stream;
 
@@ -1829,16 +1829,18 @@ fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
     errdefer buffer.deinit();
 
     var buffer_out_stream = io.BufferOutStream.init(&buffer);
-    try std.zig.render(allocator, &buffer_out_stream.stream, &tree);
+    const anything_changed = try std.zig.render(allocator, &buffer_out_stream.stream, &tree);
+    std.debug.assert(anything_changed == changes_expected);
     return buffer.toOwnedSlice();
 }
 
 fn testTransform(source: []const u8, expected_source: []const u8) !void {
+    const changes_expected = source.ptr != expected_source.ptr;
     const needed_alloc_count = x: {
         // Try it once with unlimited memory, make sure it works
         var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
         var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
-        const result_source = try testParse(source, &failing_allocator.allocator);
+        const result_source = try testParse(source, &failing_allocator.allocator, changes_expected);
         if (!mem.eql(u8, result_source, expected_source)) {
             warn("\n====== expected this output: =========\n");
             warn("{}", expected_source);
@@ -1855,7 +1857,7 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void {
     while (fail_index < needed_alloc_count) : (fail_index += 1) {
         var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
         var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
-        if (testParse(source, &failing_allocator.allocator)) |_| {
+        if (testParse(source, &failing_allocator.allocator, changes_expected)) |_| {
             return error.NondeterministicMemoryUsage;
         } else |err| switch (err) {
             error.OutOfMemory => {
std/zig/render.zig
@@ -12,9 +12,61 @@ pub const Error = error{
     OutOfMemory,
 };
 
-pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf(stream).Child.Error || Error)!void {
+/// Returns whether anything changed
+pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf(stream).Child.Error || Error)!bool {
     comptime assert(@typeId(@typeOf(stream)) == builtin.TypeId.Pointer);
 
+    var anything_changed: bool = false;
+
+    // make a passthrough stream that checks whether something changed
+    const MyStream = struct {
+        const MyStream = this;
+        const StreamError = @typeOf(stream).Child.Error;
+        const Stream = std.io.OutStream(StreamError);
+
+        anything_changed_ptr: &bool,
+        child_stream: @typeOf(stream),
+        stream: Stream,
+        source_index: usize,
+        source: []const u8,
+
+        fn write(iface_stream: &Stream, bytes: []const u8) StreamError!void {
+            const self = @fieldParentPtr(MyStream, "stream", iface_stream);
+
+            if (!self.anything_changed_ptr.*) {
+                const end = self.source_index + bytes.len;
+                if (end > self.source.len) {
+                    self.anything_changed_ptr.* = true;
+                } else {
+                    const src_slice = self.source[self.source_index..end];
+                    self.source_index += bytes.len;
+                    if (!mem.eql(u8, bytes, src_slice)) {
+                        self.anything_changed_ptr.* = true;
+                    }
+                }
+            }
+
+            try self.child_stream.write(bytes);
+        }
+    };
+    var my_stream = MyStream{
+        .stream = MyStream.Stream{ .writeFn = MyStream.write },
+        .child_stream = stream,
+        .anything_changed_ptr = &anything_changed,
+        .source_index = 0,
+        .source = tree.source,
+    };
+
+    try renderRoot(allocator, &my_stream.stream, tree);
+
+    return anything_changed;
+}
+
+fn renderRoot(
+    allocator: &mem.Allocator,
+    stream: var,
+    tree: &ast.Tree,
+) (@typeOf(stream).Child.Error || Error)!void {
     // render all the line comments at the beginning of the file
     var tok_it = tree.tokens.iterator(0);
     while (tok_it.next()) |token| {