authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-02 17:24:15-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-02 17:24:15-04:00
log951124e1772c7013c2b1a674cf98a0b638c36262
tree2aa73698141a1bc3003016726e55349e4c08b8d0
parent821805aa92898cfdb770b87ac916e45e428621b8

evented I/O zig fmt


1 files changed, 126 insertions(+), 69 deletions(-)

src-self-hosted/main.zig+126-69
...@@ -527,33 +527,12 @@ const args_fmt_spec = []Flag{...@@ -527,33 +527,12 @@ const args_fmt_spec = []Flag{
527};527};
528528
529const Fmt = struct {529const Fmt = struct {
530 seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8),530 seen: event.Locked(SeenMap),
531 queue: std.LinkedList([]const u8),
532 any_error: bool,531 any_error: bool,
532 color: errmsg.Color,
533 loop: *event.Loop,
533534
534 // file_path must outlive Fmt535 const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
535 fn addToQueue(self: *Fmt, file_path: []const u8) !void {
536 const new_node = try self.seen.allocator.create(std.LinkedList([]const u8).Node{
537 .prev = undefined,
538 .next = undefined,
539 .data = file_path,
540 });
541
542 if (try self.seen.put(file_path, {})) |_| return;
543
544 self.queue.append(new_node);
545 }
546
547 fn addDirToQueue(self: *Fmt, file_path: []const u8) !void {
548 var dir = try std.os.Dir.open(self.seen.allocator, file_path);
549 defer dir.close();
550 while (try dir.next()) |entry| {
551 if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
552 const full_path = try os.path.join(self.seen.allocator, file_path, entry.name);
553 try self.addToQueue(full_path);
554 }
555 }
556 }
557};536};
558537
559fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {538fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
...@@ -664,66 +643,144 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -664,66 +643,144 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
664 os.exit(1);643 os.exit(1);
665 }644 }
666645
646 var loop: event.Loop = undefined;
647 try loop.initMultiThreaded(allocator);
648 defer loop.deinit();
649
650 var result: FmtError!void = undefined;
651 const main_handle = try async<allocator> asyncFmtMainChecked(
652 &result,
653 &loop,
654 flags,
655 color,
656 );
657 defer cancel main_handle;
658 loop.run();
659 return result;
660}
661
662async fn asyncFmtMainChecked(
663 result: *(FmtError!void),
664 loop: *event.Loop,
665 flags: *const Args,
666 color: errmsg.Color,
667) void {
668 result.* = await (async asyncFmtMain(loop, flags, color) catch unreachable);
669}
670
671const FmtError = error{
672 SystemResources,
673 OperationAborted,
674 IoPending,
675 BrokenPipe,
676 Unexpected,
677 WouldBlock,
678 FileClosed,
679 DestinationAddressRequired,
680 DiskQuota,
681 FileTooBig,
682 InputOutput,
683 NoSpaceLeft,
684 AccessDenied,
685 OutOfMemory,
686 RenameAcrossMountPoints,
687 ReadOnlyFileSystem,
688 LinkQuotaExceeded,
689 FileBusy,
690} || os.File.OpenError;
691
692async fn asyncFmtMain(
693 loop: *event.Loop,
694 flags: *const Args,
695 color: errmsg.Color,
696) FmtError!void {
697 suspend |p| {
698 resume p;
699 }
700 // Things we need to make event-based:
701 // * opening the file in the first place - the open()
702 // * read()
703 // * readdir()
704 // * the actual parsing and rendering
705 // * rename()
667 var fmt = Fmt{706 var fmt = Fmt{
668 .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator),707 .seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)),
669 .queue = std.LinkedList([]const u8).init(),
670 .any_error = false,708 .any_error = false,
709 .color = color,
710 .loop = loop,
671 };711 };
672712
713 var group = event.Group(FmtError!void).init(loop);
673 for (flags.positionals.toSliceConst()) |file_path| {714 for (flags.positionals.toSliceConst()) |file_path| {
674 try fmt.addToQueue(file_path);715 try group.call(fmtPath, &fmt, file_path);
675 }716 }
717 return await (async group.wait() catch unreachable);
718}
676719
677 while (fmt.queue.popFirst()) |node| {720async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
678 const file_path = node.data;721 const file_path = try std.mem.dupe(fmt.loop.allocator, u8, file_path_ref);
722 defer fmt.loop.allocator.free(file_path);
679723
680 var file = try os.File.openRead(allocator, file_path);724 {
681 defer file.close();725 const held = await (async fmt.seen.acquire() catch unreachable);
726 defer held.release();
682727
683 const source_code = io.readFileAlloc(allocator, file_path) catch |err| switch (err) {728 if (try held.value.put(file_path, {})) |_| return;
684 error.IsDir => {729 }
685 try fmt.addDirToQueue(file_path);
686 continue;
687 },
688 else => {
689 try stderr.print("unable to open '{}': {}\n", file_path, err);
690 fmt.any_error = true;
691 continue;
692 },
693 };
694 defer allocator.free(source_code);
695730
696 var tree = std.zig.parse(allocator, source_code) catch |err| {731 const source_code = (await try async event.fs.readFile(
697 try stderr.print("error parsing file '{}': {}\n", file_path, err);732 fmt.loop,
733 file_path,
734 2 * 1024 * 1024 * 1024,
735 )) catch |err| switch (err) {
736 error.IsDir => {
737 var dir = try std.os.Dir.open(fmt.loop.allocator, file_path);
738 defer dir.close();
739
740 var group = event.Group(FmtError!void).init(fmt.loop);
741 while (try dir.next()) |entry| {
742 if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
743 const full_path = try os.path.join(fmt.loop.allocator, file_path, entry.name);
744 try group.call(fmtPath, fmt, full_path);
745 }
746 }
747 return await (async group.wait() catch unreachable);
748 },
749 else => {
750 // TODO lock stderr printing
751 try stderr.print("unable to open '{}': {}\n", file_path, err);
698 fmt.any_error = true;752 fmt.any_error = true;
699 continue;753 return;
700 };754 },
701 defer tree.deinit();755 };
702756 defer fmt.loop.allocator.free(source_code);
703 var error_it = tree.errors.iterator(0);
704 while (error_it.next()) |parse_error| {
705 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
706 defer msg.destroy();
707757
708 try msg.printToFile(&stderr_file, color);758 var tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| {
709 }759 try stderr.print("error parsing file '{}': {}\n", file_path, err);
710 if (tree.errors.len != 0) {760 fmt.any_error = true;
711 fmt.any_error = true;761 return;
712 continue;762 };
713 }763 defer tree.deinit();
714764
715 const baf = try io.BufferedAtomicFile.create(allocator, file_path);765 var error_it = tree.errors.iterator(0);
716 defer baf.destroy();766 while (error_it.next()) |parse_error| {
767 const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path);
768 defer fmt.loop.allocator.destroy(msg);
717769
718 const anything_changed = try std.zig.render(allocator, baf.stream(), &tree);770 try msg.printToFile(&stderr_file, fmt.color);
719 if (anything_changed) {771 }
720 try stderr.print("{}\n", file_path);772 if (tree.errors.len != 0) {
721 try baf.finish();773 fmt.any_error = true;
722 }774 return;
723 }775 }
724776
725 if (fmt.any_error) {777 const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path);
726 os.exit(1);778 defer baf.destroy();
779
780 const anything_changed = try std.zig.render(fmt.loop.allocator, baf.stream(), &tree);
781 if (anything_changed) {
782 try stderr.print("{}\n", file_path);
783 try baf.finish();
727 }784 }
728}785}
729786