| ... | ... | @@ -6,6 +6,7 @@ const process = std.process; |
| 6 | 6 | const Allocator = std.mem.Allocator; |
| 7 | 7 | const Color = std.zig.Color; |
| 8 | 8 | const fatal = std.process.fatal; |
| 9 | const Serializer = std.zon.Serializer; |
| 9 | 10 | |
| 10 | 11 | const usage_fmt = |
| 11 | 12 | \\Usage: zig fmt [file]... |
| ... | ... | @@ -23,7 +24,7 @@ const usage_fmt = |
| 23 | 24 | \\ --ast-check Run zig ast-check on every file |
| 24 | 25 | \\ --exclude [file] Exclude file or directory from formatting |
| 25 | 26 | \\ --zon Treat all input files as ZON, regardless of file extension |
| 26 | | \\ --complexity Print a complexity report for each file as well as total |
| 27 | \\ --stats Print aggregated statistics in ZON format to stdout |
| 27 | 28 | \\ |
| 28 | 29 | \\ |
| 29 | 30 | ; |
| ... | ... | @@ -39,10 +40,16 @@ const Fmt = struct { |
| 39 | 40 | io: Io, |
| 40 | 41 | out_buffer: std.Io.Writer.Allocating, |
| 41 | 42 | stdout_writer: *Io.File.Writer, |
| 43 | serializer: Serializer, |
| 44 | root_struct: Serializer.Struct, |
| 42 | 45 | |
| 43 | | complexity: bool, |
| 46 | stats: bool, |
| 44 | 47 | total_tokens: u64, |
| 45 | 48 | total_nodes: u64, |
| 49 | /// Count per token tag index |
| 50 | token_stats: []u64, |
| 51 | /// Count per node tag index |
| 52 | node_stats: []u64, |
| 46 | 53 | |
| 47 | 54 | const SeenMap = std.AutoHashMap(Io.File.INode, void); |
| 48 | 55 | }; |
| ... | ... | @@ -53,7 +60,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! |
| 53 | 60 | var check_flag = false; |
| 54 | 61 | var check_ast_flag = false; |
| 55 | 62 | var force_zon = false; |
| 56 | | var complexity = false; |
| 63 | var stats = false; |
| 57 | 64 | |
| 58 | 65 | var input_files = std.array_list.Managed([]const u8).init(gpa); |
| 59 | 66 | defer input_files.deinit(); |
| ... | ... | @@ -84,8 +91,8 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! |
| 84 | 91 | check_flag = true; |
| 85 | 92 | } else if (mem.eql(u8, arg, "--ast-check")) { |
| 86 | 93 | check_ast_flag = true; |
| 87 | | } else if (mem.eql(u8, arg, "--complexity")) { |
| 88 | | complexity = true; |
| 94 | } else if (mem.eql(u8, arg, "--stats")) { |
| 95 | stats = true; |
| 89 | 96 | } else if (mem.eql(u8, arg, "--exclude")) { |
| 90 | 97 | if (i + 1 >= args.len) { |
| 91 | 98 | fatal("expected parameter after --exclude", .{}); |
| ... | ... | @@ -185,13 +192,23 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! |
| 185 | 192 | .color = color, |
| 186 | 193 | .out_buffer = .init(gpa), |
| 187 | 194 | .stdout_writer = &stdout_writer, |
| 188 | | .complexity = complexity, |
| 195 | .stats = stats, |
| 189 | 196 | .total_tokens = 0, |
| 190 | 197 | .total_nodes = 0, |
| 198 | .serializer = .{ .writer = &stdout_writer.interface }, |
| 199 | .root_struct = undefined, |
| 200 | .token_stats = undefined, |
| 201 | .node_stats = undefined, |
| 191 | 202 | }; |
| 192 | 203 | defer fmt.seen.deinit(); |
| 193 | 204 | defer fmt.out_buffer.deinit(); |
| 194 | 205 | |
| 206 | if (stats) { |
| 207 | fmt.root_struct = try fmt.serializer.beginStruct(.{}); |
| 208 | fmt.token_stats = try arena.alloc(u64, @typeInfo(std.zig.Token.Tag).@"enum".field_names.len); |
| 209 | fmt.node_stats = try arena.alloc(u64, @typeInfo(std.zig.Ast.Node.Tag).@"enum".field_names.len); |
| 210 | } |
| 211 | |
| 195 | 212 | // Mark any excluded files/directories as already seen, |
| 196 | 213 | // so that they are skipped later during actual processing |
| 197 | 214 | for (excluded_files.items) |file_path| { |
| ... | ... | @@ -212,8 +229,10 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) ! |
| 212 | 229 | try fmtPath(&fmt, file_path, check_flag, Io.Dir.cwd(), file_path); |
| 213 | 230 | } |
| 214 | 231 | |
| 215 | | if (complexity) { |
| 216 | | std.log.info("total: tokens={d} nodes={d}", .{ fmt.total_tokens, fmt.total_nodes }); |
| 232 | if (stats) { |
| 233 | try fmt.root_struct.field("tokens", fmt.total_tokens, .{}); |
| 234 | try fmt.root_struct.field("nodes", fmt.total_nodes, .{}); |
| 235 | try fmt.root_struct.end(); |
| 217 | 236 | } |
| 218 | 237 | |
| 219 | 238 | try fmt.stdout_writer.flush(); |
| ... | ... | @@ -357,8 +376,13 @@ fn fmtPathFile( |
| 357 | 376 | } |
| 358 | 377 | } |
| 359 | 378 | |
| 360 | | if (fmt.complexity) { |
| 361 | | std.log.info("{s}: tokens={d} nodes={d}", .{ file_path, tree.tokens.len, tree.nodes.len }); |
| 379 | var zon_struct: Serializer.Struct = undefined; |
| 380 | |
| 381 | if (fmt.stats) { |
| 382 | zon_struct = try fmt.root_struct.beginStructField(file_path, .{}); |
| 383 | try zon_struct.field("tokens", tree.tokens.len, .{}); |
| 384 | try zon_struct.field("nodes", tree.nodes.len, .{}); |
| 385 | |
| 362 | 386 | fmt.total_tokens += tree.tokens.len; |
| 363 | 387 | fmt.total_nodes += tree.nodes.len; |
| 364 | 388 | } |
| ... | ... | @@ -370,20 +394,24 @@ fn fmtPathFile( |
| 370 | 394 | tree.render(gpa, &fmt.out_buffer.writer, .{}) catch |err| switch (err) { |
| 371 | 395 | error.WriteFailed, error.OutOfMemory => return error.OutOfMemory, |
| 372 | 396 | }; |
| 373 | | if (mem.eql(u8, fmt.out_buffer.written(), source_code)) |
| 374 | | return; |
| 375 | | |
| 376 | | if (check_mode) { |
| 377 | | try fmt.stdout_writer.interface.print("{s}\n", .{file_path}); |
| 378 | | fmt.any_error = true; |
| 379 | | } else { |
| 380 | | var af = try dir.createFileAtomic(io, sub_path, .{ .permissions = stat.permissions, .replace = true }); |
| 381 | | defer af.deinit(io); |
| 397 | if (!mem.eql(u8, fmt.out_buffer.written(), source_code)) { |
| 398 | if (check_mode) { |
| 399 | fmt.any_error = true; |
| 400 | } else { |
| 401 | var af = try dir.createFileAtomic(io, sub_path, .{ .permissions = stat.permissions, .replace = true }); |
| 402 | defer af.deinit(io); |
| 382 | 403 | |
| 383 | | try af.file.writeStreamingAll(io, fmt.out_buffer.written()); |
| 384 | | try af.replace(io); |
| 385 | | try fmt.stdout_writer.interface.print("{s}\n", .{file_path}); |
| 404 | try af.file.writeStreamingAll(io, fmt.out_buffer.written()); |
| 405 | try af.replace(io); |
| 406 | } |
| 407 | if (fmt.stats) { |
| 408 | try zon_struct.field("dirty", true, .{}); |
| 409 | } else { |
| 410 | try fmt.stdout_writer.interface.print("{s}\n", .{file_path}); |
| 411 | } |
| 386 | 412 | } |
| 413 | |
| 414 | if (fmt.stats) try zon_struct.end(); |
| 387 | 415 | } |
| 388 | 416 | |
| 389 | 417 | /// Provided for debugging/testing purposes; unused by the compiler. |