evented I/O zig fmt

master
Andrew Kelley 2018-08-02 17:24:15 -04:00
parent 821805aa92
commit 951124e177
1 changed files with 128 additions and 71 deletions

View File

@ -527,33 +527,12 @@ 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),
seen: event.Locked(SeenMap),
any_error: bool,
color: errmsg.Color,
loop: *event.Loop,
// file_path must outlive Fmt
fn addToQueue(self: *Fmt, file_path: []const u8) !void {
const new_node = try self.seen.allocator.create(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);
}
}
}
const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
};
fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
@ -664,66 +643,144 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
os.exit(1);
}
var loop: event.Loop = undefined;
try loop.initMultiThreaded(allocator);
defer loop.deinit();
var result: FmtError!void = undefined;
const main_handle = try async<allocator> asyncFmtMainChecked(
&result,
&loop,
flags,
color,
);
defer cancel main_handle;
loop.run();
return result;
}
async fn asyncFmtMainChecked(
result: *(FmtError!void),
loop: *event.Loop,
flags: *const Args,
color: errmsg.Color,
) void {
result.* = await (async asyncFmtMain(loop, flags, color) catch unreachable);
}
const FmtError = error{
SystemResources,
OperationAborted,
IoPending,
BrokenPipe,
Unexpected,
WouldBlock,
FileClosed,
DestinationAddressRequired,
DiskQuota,
FileTooBig,
InputOutput,
NoSpaceLeft,
AccessDenied,
OutOfMemory,
RenameAcrossMountPoints,
ReadOnlyFileSystem,
LinkQuotaExceeded,
FileBusy,
} || os.File.OpenError;
async fn asyncFmtMain(
loop: *event.Loop,
flags: *const Args,
color: errmsg.Color,
) FmtError!void {
suspend |p| {
resume p;
}
// Things we need to make event-based:
// * opening the file in the first place - the open()
// * read()
// * readdir()
// * the actual parsing and rendering
// * rename()
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(),
.seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)),
.any_error = false,
.color = color,
.loop = loop,
};
var group = event.Group(FmtError!void).init(loop);
for (flags.positionals.toSliceConst()) |file_path| {
try fmt.addToQueue(file_path);
try group.call(fmtPath, &fmt, file_path);
}
return await (async group.wait() catch unreachable);
}
async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
const file_path = try std.mem.dupe(fmt.loop.allocator, u8, file_path_ref);
defer fmt.loop.allocator.free(file_path);
{
const held = await (async fmt.seen.acquire() catch unreachable);
defer held.release();
if (try held.value.put(file_path, {})) |_| return;
}
while (fmt.queue.popFirst()) |node| {
const file_path = node.data;
const source_code = (await try async event.fs.readFile(
fmt.loop,
file_path,
2 * 1024 * 1024 * 1024,
)) catch |err| switch (err) {
error.IsDir => {
var dir = try std.os.Dir.open(fmt.loop.allocator, file_path);
defer dir.close();
var file = try os.File.openRead(allocator, file_path);
defer file.close();
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);
var group = event.Group(FmtError!void).init(fmt.loop);
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);
}
}
return await (async group.wait() catch unreachable);
},
else => {
// TODO lock stderr printing
try stderr.print("unable to open '{}': {}\n", file_path, err);
fmt.any_error = true;
continue;
};
defer tree.deinit();
return;
},
};
defer fmt.loop.allocator.free(source_code);
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
defer msg.destroy();
var tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| {
try stderr.print("error parsing file '{}': {}\n", file_path, err);
fmt.any_error = true;
return;
};
defer tree.deinit();
try msg.printToFile(&stderr_file, color);
}
if (tree.errors.len != 0) {
fmt.any_error = true;
continue;
}
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path);
defer fmt.loop.allocator.destroy(msg);
const baf = try io.BufferedAtomicFile.create(allocator, file_path);
defer baf.destroy();
const anything_changed = try std.zig.render(allocator, baf.stream(), &tree);
if (anything_changed) {
try stderr.print("{}\n", file_path);
try baf.finish();
}
try msg.printToFile(&stderr_file, fmt.color);
}
if (tree.errors.len != 0) {
fmt.any_error = true;
return;
}
if (fmt.any_error) {
os.exit(1);
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();
}
}