zig fmt: support directories

zig fmt accepts any number of file paths. For each one, if it
is a file, then it formats the file. If it is a directory, then
zig recursively scans the directory, formatting all files that
end in `.zig`.

it maintains a map of paths that have been seen already, to avoid
softlink loops.

closes #1068
master
Andrew Kelley 2018-06-18 13:55:03 -04:00
parent cd4676a233
commit 1ca90b5856
1 changed files with 55 additions and 8 deletions

View File

@ -700,6 +700,36 @@ const args_fmt_spec = []Flag{
}),
};
const Fmt = struct {
seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8),
queue: std.LinkedList([]const u8),
any_error: bool,
// file_path must outlive Fmt
fn addToQueue(self: *Fmt, file_path: []const u8) !void {
const new_node = try self.seen.allocator.construct(std.LinkedList([]const u8).Node{
.prev = undefined,
.next = undefined,
.data = file_path,
});
if (try self.seen.put(file_path, {})) |_| return;
self.queue.append(new_node);
}
fn addDirToQueue(self: *Fmt, file_path: []const u8) !void {
var dir = try std.os.Dir.open(self.seen.allocator, file_path);
defer dir.close();
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(self.seen.allocator, file_path, entry.name);
try self.addToQueue(full_path);
}
}
}
};
fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
var flags = try Args.parse(allocator, args_fmt_spec, args);
defer flags.deinit();
@ -728,21 +758,38 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
}
};
var fmt_errors = false;
var fmt = Fmt{
.seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator),
.queue = std.LinkedList([]const u8).init(),
.any_error = false,
};
for (flags.positionals.toSliceConst()) |file_path| {
try fmt.addToQueue(file_path);
}
while (fmt.queue.popFirst()) |node| {
const file_path = node.data;
var file = try os.File.openRead(allocator, file_path);
defer file.close();
const source_code = io.readFileAlloc(allocator, file_path) catch |err| {
try stderr.print("unable to open '{}': {}\n", file_path, err);
fmt_errors = true;
continue;
const source_code = io.readFileAlloc(allocator, file_path) catch |err| switch (err) {
error.IsDir => {
try fmt.addDirToQueue(file_path);
continue;
},
else => {
try stderr.print("unable to open '{}': {}\n", file_path, err);
fmt.any_error = true;
continue;
},
};
defer allocator.free(source_code);
var tree = std.zig.parse(allocator, source_code) catch |err| {
try stderr.print("error parsing file '{}': {}\n", file_path, err);
fmt_errors = true;
fmt.any_error = true;
continue;
};
defer tree.deinit();
@ -755,7 +802,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
try errmsg.printToFile(&stderr_file, msg, color);
}
if (tree.errors.len != 0) {
fmt_errors = true;
fmt.any_error = true;
continue;
}
@ -769,7 +816,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
}
}
if (fmt_errors) {
if (fmt.any_error) {
os.exit(1);
}
}