diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-06-20 23:06:04 -0400 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-06-20 23:06:04 -0400 |
| commit | edea7a46e5484e30c338699207c8f4b4d8370c97 (patch) | |
| tree | af5d6b5650fe48c56d22a38e54db74e3871c98a8 /src-self-hosted | |
| parent | 5229f6ec68708c3e66393a50ad9f9032ee7f4257 (diff) | |
| parent | faf783e5959a09fb2a1680f8bb7558db96dcbed9 (diff) | |
| download | zig-edea7a46e5484e30c338699207c8f4b4d8370c97.tar.gz zig-edea7a46e5484e30c338699207c8f4b4d8370c97.zip | |
Merge branch 'DrDeano-master'
closes #5648
Diffstat (limited to 'src-self-hosted')
| -rw-r--r-- | src-self-hosted/main.zig | 138 |
1 files changed, 86 insertions, 52 deletions
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 6c13f8ab00..29930270eb 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -546,8 +546,9 @@ const Fmt = struct { any_error: bool, color: Color, gpa: *Allocator, + out_buffer: std.ArrayList(u8), - const SeenMap = std.BufSet; + const SeenMap = std.AutoHashMap(fs.File.INode, void); }; pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { @@ -641,10 +642,20 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { .seen = Fmt.SeenMap.init(gpa), .any_error = false, .color = color, + .out_buffer = std.ArrayList(u8).init(gpa), }; + defer fmt.seen.deinit(); + defer fmt.out_buffer.deinit(); for (input_files.span()) |file_path| { - try fmtPath(&fmt, file_path, check_flag); + // Get the real path here to avoid Windows failing on relative file paths with . or .. in them. + const real_path = fs.realpathAlloc(gpa, file_path) catch |err| { + std.debug.warn("unable to open '{}': {}\n", .{ file_path, err }); + process.exit(1); + }; + defer gpa.free(real_path); + + try fmtPath(&fmt, file_path, check_flag, fs.cwd(), real_path); } if (fmt.any_error) { process.exit(1); @@ -670,61 +681,79 @@ const FmtError = error{ ReadOnlyFileSystem, LinkQuotaExceeded, FileBusy, + EndOfStream, } || fs.File.OpenError; -fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void { - // get the real path here to avoid Windows failing on relative file paths with . or .. in them - var real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| { - std.debug.warn("unable to open '{}': {}\n", .{ file_path, err }); - fmt.any_error = true; - return; - }; - defer fmt.gpa.free(real_path); - - if (fmt.seen.exists(real_path)) return; - try fmt.seen.put(real_path); - - const source_file = fs.cwd().openFile(real_path, .{}) catch |err| switch (err) { - error.IsDir, error.AccessDenied => { - var dir = try fs.cwd().openDir(file_path, .{ .iterate = true }); - defer dir.close(); - - var dir_it = dir.iterate(); - - while (try dir_it.next()) |entry| { - if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) { - const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name }); - try fmtPath(fmt, full_path, check_mode); - } - } - return; - }, +fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void { + fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) { + error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path), else => { - std.debug.warn("unable to open '{}': {}\n", .{ file_path, err }); + std.debug.warn("unable to format '{}': {}\n", .{ file_path, err }); fmt.any_error = true; return; }, }; +} + +fn fmtPathDir( + fmt: *Fmt, + file_path: []const u8, + check_mode: bool, + parent_dir: fs.Dir, + parent_sub_path: []const u8, +) FmtError!void { + var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true }); + defer dir.close(); + + const stat = try dir.stat(); + if (try fmt.seen.put(stat.inode, {})) |_| return; + + var dir_it = dir.iterate(); + while (try dir_it.next()) |entry| { + const is_dir = entry.kind == .Directory; + if (is_dir or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name }); + defer fmt.gpa.free(full_path); + + if (is_dir) { + try fmtPathDir(fmt, full_path, check_mode, dir, entry.name); + } else { + fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| { + std.debug.warn("unable to format '{}': {}\n", .{ full_path, err }); + fmt.any_error = true; + return; + }; + } + } + } +} + +fn fmtPathFile( + fmt: *Fmt, + file_path: []const u8, + check_mode: bool, + dir: fs.Dir, + sub_path: []const u8, +) FmtError!void { + const source_file = try dir.openFile(sub_path, .{}); defer source_file.close(); - const stat = source_file.stat() catch |err| { - std.debug.warn("unable to stat '{}': {}\n", .{ file_path, err }); - fmt.any_error = true; - return; - }; + const stat = try source_file.stat(); - const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| { - std.debug.warn("unable to read '{}': {}\n", .{ file_path, err }); - fmt.any_error = true; - return; + if (stat.kind == .Directory) + return error.IsDir; + + const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) { + error.ConnectionResetByPeer => unreachable, + error.ConnectionTimedOut => unreachable, + else => |e| return e, }; defer fmt.gpa.free(source_code); - const tree = std.zig.parse(fmt.gpa, source_code) catch |err| { - std.debug.warn("error parsing file '{}': {}\n", .{ file_path, err }); - fmt.any_error = true; - return; - }; + // Add to set after no longer possible to get error.IsDir. + if (try fmt.seen.put(stat.inode, {})) |_| return; + + const tree = try std.zig.parse(fmt.gpa, source_code); defer tree.deinit(); for (tree.errors) |parse_error| { @@ -742,14 +771,19 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void { fmt.any_error = true; } } else { - const baf = try io.BufferedAtomicFile.create(fmt.gpa, fs.cwd(), real_path, .{ .mode = stat.mode }); - defer baf.destroy(); - - const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree); - if (anything_changed) { - std.debug.warn("{}\n", .{file_path}); - try baf.finish(); - } + // As a heuristic, we make enough capacity for the same as the input source. + try fmt.out_buffer.ensureCapacity(source_code.len); + fmt.out_buffer.items.len = 0; + const anything_changed = try std.zig.render(fmt.gpa, fmt.out_buffer.writer(), tree); + if (!anything_changed) + return; // Good thing we didn't waste any file system access on this. + + var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode }); + defer af.deinit(); + + try af.file.writeAll(fmt.out_buffer.items); + try af.finish(); + std.debug.warn("{}\n", .{file_path}); } } |
