| ... | @@ -527,33 +527,12 @@ const args_fmt_spec = []Flag{ | ... | @@ -527,33 +527,12 @@ const args_fmt_spec = []Flag{ |
| 527 | }; | 527 | }; |
| 528 | | 528 | |
| 529 | const Fmt = struct { | 529 | const 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, |
| 533 | | 534 | |
| 534 | // file_path must outlive Fmt | 535 | 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 | }; |
| 558 | | 537 | |
| 559 | fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void { | 538 | 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 { | ... | @@ -664,66 +643,144 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { |
| 664 | os.exit(1); | 643 | os.exit(1); |
| 665 | } | 644 | } |
| 666 | | 645 | |
| | 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 | |
| | 662 | async 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 | |
| | 671 | const 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 | |
| | 692 | async 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 | }; |
| 672 | | 712 | |
| | 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 | } |
| 676 | | 719 | |
| 677 | while (fmt.queue.popFirst()) |node| { | 720 | async 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); |
| 679 | | 723 | |
| 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(); |
| 682 | | 727 | |
| 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); | | |
| 695 | | 730 | |
| 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 | }; |
| 702 | | 756 | 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(); | | |
| 707 | | 757 | |
| 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(); |
| 714 | | 764 | |
| 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); |
| 717 | | 769 | |
| 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 | } |
| 724 | | 776 | |
| 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 | } |
| 729 | | 786 | |