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 #1068master
parent
cd4676a233
commit
1ca90b5856
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue