╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ fmt.zig ──────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
49 50 pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void { 51 var color: Color = .auto; 52 var stdin_flag = false; 53 var check_flag = false; 54 var check_ast_flag = false; 55 var force_zon = false; 56 var complexity = false; 57 58 var input_files = std.array_list.Managed([]const u8).init(gpa); 59 defer input_files.deinit(); 60 61 var excluded_files = std.array_list.Managed([]const u8).init(gpa); 62 defer excluded_files.deinit(); 63 64 { 65 var i: usize = 0; 66 while (i < args.len) : (i += 1) { 67 const arg = args[i]; 68 if (mem.startsWith(u8, arg, "-")) { 69 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { 70 try Io.File.stdout().writeStreamingAll(io, usage_fmt); 71 return process.cleanExit(io); 72 } else if (mem.eql(u8, arg, "--color")) { 73 if (i + 1 >= args.len) { 74 fatal("expected [auto|on|off] after --color", .{}); 75 } 76 i += 1; 77 const next_arg = args[i]; 78 color = std.meta.stringToEnum(Color, next_arg) orelse { 79 fatal("expected [auto|on|off] after --color, found {q}", .{next_arg}); 80 }; 81 } else if (mem.eql(u8, arg, "--stdin")) { 82 stdin_flag = true; 83 } else if (mem.eql(u8, arg, "--check")) { 84 check_flag = true; 85 } else if (mem.eql(u8, arg, "--ast-check")) { 86 check_ast_flag = true; 87 } else if (mem.eql(u8, arg, "--complexity")) { 88 complexity = true; 89 } else if (mem.eql(u8, arg, "--exclude")) { 90 if (i + 1 >= args.len) { 91 fatal("expected parameter after --exclude", .{}); 92 } 93 i += 1; 94 const next_arg = args[i]; 95 try excluded_files.append(next_arg); 96 } else if (mem.eql(u8, arg, "--zon")) { 97 force_zon = true; 98 } else { 99 fatal("unrecognized parameter: {q}", .{arg}); 100 } 101 } else { 102 try input_files.append(arg); 103 } 104 } 105 } 106 107 if (stdin_flag) { 108 if (input_files.items.len != 0) { 109 fatal("cannot use --stdin with positional arguments", .{}); 110 } 111 112 const stdin: Io.File = .stdin(); 113 var stdio_buffer: [1024]u8 = undefined; 114 var file_reader: Io.File.Reader = stdin.reader(io, &stdio_buffer); 115 const source_code = std.zig.readSourceFileToEndAlloc(gpa, &file_reader) catch |err| { 116 fatal("unable to read stdin: {}", .{err}); 117 }; 118 defer gpa.free(source_code); 119 120 var tree = std.zig.Ast.parse(gpa, source_code, .{ 121 .mode = if (force_zon) .zon else .zig, 122 }) catch |err| { 123 fatal("error parsing stdin: {}", .{err}); 124 }; 125 defer tree.deinit(gpa); 126 127 if (check_ast_flag) { 128 if (!force_zon) { 129 var zir = try std.zig.AstGen.generate(gpa, tree); 130 defer zir.deinit(gpa); 131 132 if (zir.hasCompileErrors()) { 133 var wip_errors: std.zig.ErrorBundle.Wip = undefined; 134 try wip_errors.init(gpa); 135 defer wip_errors.deinit(); 136 try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>"); 137 var error_bundle = try wip_errors.toOwnedBundle(""); 138 defer error_bundle.deinit(gpa); 139 error_bundle.renderToStderr(io, .{}, color) catch {}; 140 process.exit(2); 141 } 142 } else { 143 const zoir = try std.zig.ZonGen.generate(gpa, tree, .{}); 144 defer zoir.deinit(gpa); 145 146 if (zoir.hasCompileErrors()) { 147 var wip_errors: std.zig.ErrorBundle.Wip = undefined; 148 try wip_errors.init(gpa); 149 defer wip_errors.deinit(); 150 try wip_errors.addZoirErrorMessages(zoir, tree, source_code, "<stdin>"); 151 var error_bundle = try wip_errors.toOwnedBundle(""); 152 defer error_bundle.deinit(gpa); 153 error_bundle.renderToStderr(io, .{}, color) catch {}; 154 process.exit(2); 155 } 156 } 157 } else if (tree.errors.len != 0) { 158 std.zig.printAstErrorsToStderr(gpa, io, tree, "<stdin>", color) catch {}; 159 process.exit(2); 160 } 161 const formatted = try tree.renderAlloc(gpa); 162 defer gpa.free(formatted); 163 164 if (check_flag) { 165 const code: u8 = @intFromBool(!mem.eql(u8, formatted, source_code)); 166 process.exit(code); 167 } 168 169 return Io.File.stdout().writeStreamingAll(io, formatted); 170 } 171 172 if (input_files.items.len == 0) { 173 fatal("expected at least one file or directory argument", .{}); 174 } 175 176 var stdout_buffer: [4096]u8 = undefined; 177 var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer); 178 179 var fmt: Fmt = .{ 180 .gpa = gpa, 181 .arena = arena, 182 .io = io, 183 .seen = .init(gpa), 184 .any_error = false, 185 .check_ast = check_ast_flag, 186 .force_zon = force_zon, 187 .color = color, 188 .out_buffer = .init(gpa), 189 .stdout_writer = &stdout_writer, 190 .complexity = complexity, 191 .total_tokens = 0, 192 .total_nodes = 0, 193 }; 194 defer fmt.seen.deinit(); 195 defer fmt.out_buffer.deinit(); 196 197 // Mark any excluded files/directories as already seen, 198 // so that they are skipped later during actual processing 199 for (excluded_files.items) |file_path| { 200 const stat = Io.Dir.cwd().statFile(io, file_path, .{}) catch |err| switch (err) { 201 error.FileNotFound => continue, 202 // On Windows, statFile does not work for directories 203 error.IsDir => dir: { 204 var dir = try Io.Dir.cwd().openDir(io, file_path, .{}); 205 defer dir.close(io); 206 break :dir try dir.stat(io); 207 }, 208 else => |e| return e, 209 }; 210 try fmt.seen.put(stat.inode, {}); 211 } 212 213 for (input_files.items) |file_path| { 214 try fmtPath(&fmt, file_path, check_flag, Io.Dir.cwd(), file_path); 215 } 216 217 if (complexity) { 218 std.log.info("total: tokens={d} nodes={d}", .{ fmt.total_tokens, fmt.total_nodes }); 219 } 220 221 try fmt.stdout_writer.flush(); 222 if (fmt.any_error) { 223 process.exit(1); 224 } 225 } 226 227 fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: Io.Dir, sub_path: []const u8) !void { 228 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) { 229 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path), 230 else => { 231 std.log.err("formatting {q}: {t}", .{ file_path, err }); 232 fmt.any_error = true; 233 return; 234 }, 235 }; 236 } 237 238 fn fmtPathDir( 239 fmt: *Fmt, 240 file_path: []const u8, 241 check_mode: bool, 242 parent_dir: Io.Dir, 243 parent_sub_path: []const u8, 244 ) !void { 245 const io = fmt.io; 246 247 var dir = try parent_dir.openDir(io, parent_sub_path, .{ .iterate = true }); 248 defer dir.close(io); 249 250 const stat = try dir.stat(io); 251 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; 252 253 var dir_it = dir.iterate(); 254 while (try dir_it.next(io)) |entry| { 255 const is_dir = entry.kind == .directory; 256 257 if (mem.startsWith(u8, entry.name, ".")) continue; 258 259 if (is_dir or entry.kind == .file and (mem.endsWith(u8, entry.name, ".zig") or mem.endsWith(u8, entry.name, ".zon"))) { 260 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name }); 261 defer fmt.gpa.free(full_path); 262 263 if (is_dir) { 264 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name); 265 } else { 266 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| { 267 std.log.err("unable to format {q}: {t}", .{ full_path, err }); 268 fmt.any_error = true; 269 return; 270 }; 271 } 272 } 273 } 274 } 275 276 fn fmtPathFile( 277 fmt: *Fmt, 278 file_path: []const u8, 279 check_mode: bool, 280 dir: Io.Dir, 281 sub_path: []const u8, 282 ) !void { 283 const io = fmt.io; 284 285 const source_file = try dir.openFile(io, sub_path, .{}); 286 var file_closed = false; 287 errdefer if (!file_closed) source_file.close(io); 288 289 const stat = try source_file.stat(io); 290 291 if (stat.kind == .directory) 292 return error.IsDir; 293 294 var read_buffer: [1024]u8 = undefined; 295 var file_reader: Io.File.Reader = source_file.reader(io, &read_buffer); 296 file_reader.size = stat.size; 297 298 const gpa = fmt.gpa; 299 const source_code = std.zig.readSourceFileToEndAlloc(gpa, &file_reader) catch |err| switch (err) { 300 error.ReadFailed => return file_reader.err.?, 301 else => |e| return e, 302 }; 303 defer gpa.free(source_code); 304 305 source_file.close(io); 306 file_closed = true; 307 308 // Add to set after no longer possible to get error.IsDir. 309 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; 310 311 const mode: std.zig.Ast.Mode = mode: { 312 if (fmt.force_zon) break :mode .zon; 313 if (mem.endsWith(u8, sub_path, ".zon")) break :mode .zon; 314 break :mode .zig; 315 }; 316 317 var tree = try std.zig.Ast.parse(gpa, source_code, .{ .mode = mode }); 318 defer tree.deinit(gpa); 319 320 if (tree.errors.len != 0) { 321 try std.zig.printAstErrorsToStderr(gpa, io, tree, file_path, fmt.color); 322 fmt.any_error = true; 323 return; 324 } 325 326 if (fmt.check_ast) { 327 if (stat.size > std.zig.max_src_size) 328 return error.FileTooBig; 329 330 switch (mode) { 331 .zig => { 332 var zir = try std.zig.AstGen.generate(gpa, tree); 333 defer zir.deinit(gpa); 334 335 if (zir.hasCompileErrors()) { 336 var wip_errors: std.zig.ErrorBundle.Wip = undefined; 337 try wip_errors.init(gpa); 338 defer wip_errors.deinit(); 339 try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path); 340 var error_bundle = try wip_errors.toOwnedBundle(""); 341 defer error_bundle.deinit(gpa); 342 try error_bundle.renderToStderr(io, .{}, fmt.color); 343 fmt.any_error = true; 344 } 345 }, 346 .zon => { 347 var zoir = try std.zig.ZonGen.generate(gpa, tree, .{}); 348 defer zoir.deinit(gpa); 349 350 if (zoir.hasCompileErrors()) { 351 var wip_errors: std.zig.ErrorBundle.Wip = undefined; 352 try wip_errors.init(gpa); 353 defer wip_errors.deinit(); 354 try wip_errors.addZoirErrorMessages(zoir, tree, source_code, file_path); 355 var error_bundle = try wip_errors.toOwnedBundle(""); 356 defer error_bundle.deinit(gpa); 357 try error_bundle.renderToStderr(io, .{}, fmt.color); 358 fmt.any_error = true; 359 } 360 }, 361 } 362 } 363 364 if (fmt.complexity) { 365 std.log.info("{s}: tokens={d} nodes={d}", .{ file_path, tree.tokens.len, tree.nodes.len }); 366 fmt.total_tokens += tree.tokens.len; 367 fmt.total_nodes += tree.nodes.len; 368 } 369 370 // As a heuristic, we make enough capacity for the same as the input source. 371 fmt.out_buffer.clearRetainingCapacity(); 372 try fmt.out_buffer.ensureTotalCapacity(source_code.len); 373 374 tree.render(gpa, &fmt.out_buffer.writer, .{}) catch |err| switch (err) { 375 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, 376 }; 377 if (mem.eql(u8, fmt.out_buffer.written(), source_code)) 378 return; 379 380 if (check_mode) { 381 try fmt.stdout_writer.interface.print("{s}\n", .{file_path}); 382 fmt.any_error = true; 383 } else { 384 var af = try dir.createFileAtomic(io, sub_path, .{ .permissions = stat.permissions, .replace = true }); 385 defer af.deinit(io); 386 387 try af.file.writeStreamingAll(io, fmt.out_buffer.written()); 388 try af.replace(io); 389 try fmt.stdout_writer.interface.print("{s}\n", .{file_path}); 390 } 391 } 392 393 /// Provided for debugging/testing purposes; unused by the compiler. 394 pub fn main(init: process.Init) !void { 395 const args = try init.minimal.args.toSlice(init.arena.allocator()); 396 return run(init.gpa, init.arena.allocator(), init.io, args[1..]); 397 } 398