| ... | ... | @@ -527,33 +527,12 @@ const args_fmt_spec = []Flag{ |
| 527 | 527 | }; |
| 528 | 528 | |
| 529 | 529 | const Fmt = struct { |
| 530 | | seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8), |
| 531 | | queue: std.LinkedList([]const u8), |
| 530 | seen: event.Locked(SeenMap), |
| 532 | 531 | any_error: bool, |
| 532 | color: errmsg.Color, |
| 533 | loop: *event.Loop, |
| 533 | 534 | |
| 534 | | // file_path must outlive Fmt |
| 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 | | } |
| 535 | const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); |
| 557 | 536 | }; |
| 558 | 537 | |
| 559 | 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 | 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 | 706 | var fmt = Fmt{ |
| 668 | | .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator), |
| 669 | | .queue = std.LinkedList([]const u8).init(), |
| 707 | .seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)), |
| 670 | 708 | .any_error = false, |
| 709 | .color = color, |
| 710 | .loop = loop, |
| 671 | 711 | }; |
| 672 | 712 | |
| 713 | var group = event.Group(FmtError!void).init(loop); |
| 673 | 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| { |
| 678 | | const file_path = node.data; |
| 720 | async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void { |
| 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); |
| 681 | | defer file.close(); |
| 724 | { |
| 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) { |
| 684 | | error.IsDir => { |
| 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); |
| 728 | if (try held.value.put(file_path, {})) |_| return; |
| 729 | } |
| 695 | 730 | |
| 696 | | var tree = std.zig.parse(allocator, source_code) catch |err| { |
| 697 | | try stderr.print("error parsing file '{}': {}\n", file_path, err); |
| 731 | const source_code = (await try async event.fs.readFile( |
| 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 | 752 | fmt.any_error = true; |
| 699 | | continue; |
| 700 | | }; |
| 701 | | defer tree.deinit(); |
| 702 | | |
| 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(); |
| 753 | return; |
| 754 | }, |
| 755 | }; |
| 756 | defer fmt.loop.allocator.free(source_code); |
| 707 | 757 | |
| 708 | | try msg.printToFile(&stderr_file, color); |
| 709 | | } |
| 710 | | if (tree.errors.len != 0) { |
| 711 | | fmt.any_error = true; |
| 712 | | continue; |
| 713 | | } |
| 758 | var tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| { |
| 759 | try stderr.print("error parsing file '{}': {}\n", file_path, err); |
| 760 | fmt.any_error = true; |
| 761 | return; |
| 762 | }; |
| 763 | defer tree.deinit(); |
| 714 | 764 | |
| 715 | | const baf = try io.BufferedAtomicFile.create(allocator, file_path); |
| 716 | | defer baf.destroy(); |
| 765 | var error_it = tree.errors.iterator(0); |
| 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); |
| 719 | | if (anything_changed) { |
| 720 | | try stderr.print("{}\n", file_path); |
| 721 | | try baf.finish(); |
| 722 | | } |
| 770 | try msg.printToFile(&stderr_file, fmt.color); |
| 771 | } |
| 772 | if (tree.errors.len != 0) { |
| 773 | fmt.any_error = true; |
| 774 | return; |
| 723 | 775 | } |
| 724 | 776 | |
| 725 | | if (fmt.any_error) { |
| 726 | | os.exit(1); |
| 777 | const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path); |
| 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 | |