authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-05-30 18:26:09-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-05-30 18:26:09-04:00
loga05acaf9fd8ea1b42ec300ce4ba948ac00b89d76
tree5d10ed6f6c238c6d8ffd56f9f0df2042c01aab99
parentd8699ae57ed1e69aac209d674b26685c6c569525

Add --color CLI option to zig fmt

It doesn't actually do terminal color yet because we need to add cross platform terminal color abstractions. But it toggles between the single line error reporting and the multiline error reporting. See #1026

4 files changed, 138 insertions(+), 47 deletions(-)

src-self-hosted/errmsg.zig created+87
...@@ -0,0 +1,87 @@
1const std = @import("std");
2const mem = std.mem;
3const os = std.os;
4const Token = std.zig.Token;
5const ast = std.zig.ast;
6const TokenIndex = std.zig.ast.TokenIndex;
7
8pub const Color = enum {
9 Auto,
10 Off,
11 On,
12};
13
14pub const Msg = struct {
15 path: []const u8,
16 text: []u8,
17 first_token: TokenIndex,
18 last_token: TokenIndex,
19 tree: &ast.Tree,
20};
21
22/// `path` must outlive the returned Msg
23/// `tree` must outlive the returned Msg
24/// Caller owns returned Msg and must free with `allocator`
25pub fn createFromParseError(
26 allocator: &mem.Allocator,
27 parse_error: &const ast.Error,
28 tree: &ast.Tree,
29 path: []const u8,
30) !&Msg {
31 const loc_token = parse_error.loc();
32 var text_buf = try std.Buffer.initSize(allocator, 0);
33 defer text_buf.deinit();
34
35 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
36 try parse_error.render(&tree.tokens, out_stream);
37
38 const msg = try allocator.construct(Msg{
39 .tree = tree,
40 .path = path,
41 .text = text_buf.toOwnedSlice(),
42 .first_token = loc_token,
43 .last_token = loc_token,
44 });
45 errdefer allocator.destroy(msg);
46
47 return msg;
48}
49
50pub fn printToStream(stream: var, msg: &const Msg, color_on: bool) !void {
51 const first_token = msg.tree.tokens.at(msg.first_token);
52 const last_token = msg.tree.tokens.at(msg.last_token);
53 const start_loc = msg.tree.tokenLocationPtr(0, first_token);
54 const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token);
55 if (!color_on) {
56 try stream.print(
57 "{}:{}:{}: error: {}\n",
58 msg.path,
59 start_loc.line + 1,
60 start_loc.column + 1,
61 msg.text,
62 );
63 return;
64 }
65
66 try stream.print(
67 "{}:{}:{}: error: {}\n{}\n",
68 msg.path,
69 start_loc.line + 1,
70 start_loc.column + 1,
71 msg.text,
72 msg.tree.source[start_loc.line_start..start_loc.line_end],
73 );
74 try stream.writeByteNTimes(' ', start_loc.column);
75 try stream.writeByteNTimes('~', last_token.end - first_token.start);
76 try stream.write("\n");
77}
78
79pub fn printToFile(file: &os.File, msg: &const Msg, color: Color) !void {
80 const color_on = switch (color) {
81 Color.Auto => file.isTty(),
82 Color.On => true,
83 Color.Off => false,
84 };
85 var stream = &std.io.FileOutStream.init(file).stream;
86 return printToStream(stream, msg, color_on);
87}
src-self-hosted/main.zig+40-31
...@@ -15,7 +15,9 @@ const Args = arg.Args;...@@ -15,7 +15,9 @@ const Args = arg.Args;
15const Flag = arg.Flag;15const Flag = arg.Flag;
16const Module = @import("module.zig").Module;16const Module = @import("module.zig").Module;
17const Target = @import("target.zig").Target;17const Target = @import("target.zig").Target;
18const errmsg = @import("errmsg.zig");
1819
20var stderr_file: os.File = undefined;
19var stderr: &io.OutStream(io.FileOutStream.Error) = undefined;21var stderr: &io.OutStream(io.FileOutStream.Error) = undefined;
20var stdout: &io.OutStream(io.FileOutStream.Error) = undefined;22var stdout: &io.OutStream(io.FileOutStream.Error) = undefined;
2123
...@@ -51,7 +53,7 @@ pub fn main() !void {...@@ -51,7 +53,7 @@ pub fn main() !void {
51 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);53 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
52 stdout = &stdout_out_stream.stream;54 stdout = &stdout_out_stream.stream;
5355
54 var stderr_file = try std.io.getStdErr();56 stderr_file = try std.io.getStdErr();
55 var stderr_out_stream = std.io.FileOutStream.init(&stderr_file);57 var stderr_out_stream = std.io.FileOutStream.init(&stderr_file);
56 stderr = &stderr_out_stream.stream;58 stderr = &stderr_out_stream.stream;
5759
...@@ -440,18 +442,19 @@ fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Mo...@@ -440,18 +442,19 @@ fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Mo
440 build_mode = builtin.Mode.ReleaseSafe;442 build_mode = builtin.Mode.ReleaseSafe;
441 }443 }
442444
443 var color = Module.ErrColor.Auto;445 const color = blk: {
444 if (flags.single("color")) |color_flag| {446 if (flags.single("color")) |color_flag| {
445 if (mem.eql(u8, color_flag, "auto")) {447 if (mem.eql(u8, color_flag, "auto")) {
446 color = Module.ErrColor.Auto;448 break :blk errmsg.Color.Auto;
447 } else if (mem.eql(u8, color_flag, "on")) {449 } else if (mem.eql(u8, color_flag, "on")) {
448 color = Module.ErrColor.On;450 break :blk errmsg.Color.On;
449 } else if (mem.eql(u8, color_flag, "off")) {451 } else if (mem.eql(u8, color_flag, "off")) {
450 color = Module.ErrColor.Off;452 break :blk errmsg.Color.Off;
453 } else unreachable;
451 } else {454 } else {
452 unreachable;455 break :blk errmsg.Color.Auto;
453 }456 }
454 }457 };
455458
456 var emit_type = Module.Emit.Binary;459 var emit_type = Module.Emit.Binary;
457 if (flags.single("emit")) |emit_flag| {460 if (flags.single("emit")) |emit_flag| {
...@@ -687,7 +690,14 @@ const usage_fmt =...@@ -687,7 +690,14 @@ const usage_fmt =
687 \\690 \\
688;691;
689692
690const args_fmt_spec = []Flag{Flag.Bool("--help")};693const args_fmt_spec = []Flag{
694 Flag.Bool("--help"),
695 Flag.Option("--color", []const []const u8{
696 "auto",
697 "off",
698 "on",
699 }),
700};
691701
692fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {702fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {
693 var flags = try Args.parse(allocator, args_fmt_spec, args);703 var flags = try Args.parse(allocator, args_fmt_spec, args);
...@@ -703,6 +713,20 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {...@@ -703,6 +713,20 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {
703 os.exit(1);713 os.exit(1);
704 }714 }
705715
716 const color = blk: {
717 if (flags.single("color")) |color_flag| {
718 if (mem.eql(u8, color_flag, "auto")) {
719 break :blk errmsg.Color.Auto;
720 } else if (mem.eql(u8, color_flag, "on")) {
721 break :blk errmsg.Color.On;
722 } else if (mem.eql(u8, color_flag, "off")) {
723 break :blk errmsg.Color.Off;
724 } else unreachable;
725 } else {
726 break :blk errmsg.Color.Auto;
727 }
728 };
729
706 for (flags.positionals.toSliceConst()) |file_path| {730 for (flags.positionals.toSliceConst()) |file_path| {
707 var file = try os.File.openRead(allocator, file_path);731 var file = try os.File.openRead(allocator, file_path);
708 defer file.close();732 defer file.close();
...@@ -721,25 +745,10 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {...@@ -721,25 +745,10 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {
721745
722 var error_it = tree.errors.iterator(0);746 var error_it = tree.errors.iterator(0);
723 while (error_it.next()) |parse_error| {747 while (error_it.next()) |parse_error| {
724 const token = tree.tokens.at(parse_error.loc());748 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path);
725 const loc = tree.tokenLocation(0, parse_error.loc());749 defer allocator.destroy(msg);
726 try stderr.print("{}:{}:{}: error: ", file_path, loc.line + 1, loc.column + 1);750
727 try tree.renderError(parse_error, stderr);751 try errmsg.printToFile(&stderr_file, msg, color);
728 try stderr.print("\n{}\n", source_code[loc.line_start..loc.line_end]);
729 {
730 var i: usize = 0;
731 while (i < loc.column) : (i += 1) {
732 try stderr.write(" ");
733 }
734 }
735 {
736 const caret_count = token.end - token.start;
737 var i: usize = 0;
738 while (i < caret_count) : (i += 1) {
739 try stderr.write("~");
740 }
741 }
742 try stderr.write("\n");
743 }752 }
744 if (tree.errors.len != 0) {753 if (tree.errors.len != 0) {
745 continue;754 continue;
src-self-hosted/module.zig+3-8
...@@ -10,6 +10,7 @@ const Target = @import("target.zig").Target;...@@ -10,6 +10,7 @@ const Target = @import("target.zig").Target;
10const warn = std.debug.warn;10const warn = std.debug.warn;
11const Token = std.zig.Token;11const Token = std.zig.Token;
12const ArrayList = std.ArrayList;12const ArrayList = std.ArrayList;
13const errmsg = @import("errmsg.zig");
1314
14pub const Module = struct {15pub const Module = struct {
15 allocator: &mem.Allocator,16 allocator: &mem.Allocator,
...@@ -55,7 +56,7 @@ pub const Module = struct {...@@ -55,7 +56,7 @@ pub const Module = struct {
55 link_libs_list: ArrayList(&LinkLib),56 link_libs_list: ArrayList(&LinkLib),
56 libc_link_lib: ?&LinkLib,57 libc_link_lib: ?&LinkLib,
5758
58 err_color: ErrColor,59 err_color: errmsg.Color,
5960
60 verbose_tokenize: bool,61 verbose_tokenize: bool,
61 verbose_ast_tree: bool,62 verbose_ast_tree: bool,
...@@ -87,12 +88,6 @@ pub const Module = struct {...@@ -87,12 +88,6 @@ pub const Module = struct {
87 Obj,88 Obj,
88 };89 };
8990
90 pub const ErrColor = enum {
91 Auto,
92 Off,
93 On,
94 };
95
96 pub const LinkLib = struct {91 pub const LinkLib = struct {
97 name: []const u8,92 name: []const u8,
98 path: ?[]const u8,93 path: ?[]const u8,
...@@ -195,7 +190,7 @@ pub const Module = struct {...@@ -195,7 +190,7 @@ pub const Module = struct {
195 .windows_subsystem_console = false,190 .windows_subsystem_console = false,
196 .link_libs_list = ArrayList(&LinkLib).init(allocator),191 .link_libs_list = ArrayList(&LinkLib).init(allocator),
197 .libc_link_lib = null,192 .libc_link_lib = null,
198 .err_color = ErrColor.Auto,193 .err_color = errmsg.Color.Auto,
199 .darwin_frameworks = [][]const u8{},194 .darwin_frameworks = [][]const u8{},
200 .darwin_version_min = DarwinVersionMin.None,195 .darwin_version_min = DarwinVersionMin.None,
201 .test_filters = [][]const u8{},196 .test_filters = [][]const u8{},
std/zig/ast.zig+8-8
...@@ -120,7 +120,7 @@ pub const Error = union(enum) {...@@ -120,7 +120,7 @@ pub const Error = union(enum) {
120 ExpectedToken: ExpectedToken,120 ExpectedToken: ExpectedToken,
121 ExpectedCommaOrEnd: ExpectedCommaOrEnd,121 ExpectedCommaOrEnd: ExpectedCommaOrEnd,
122122
123 pub fn render(self: &Error, tokens: &Tree.TokenList, stream: var) !void {123 pub fn render(self: &const Error, tokens: &Tree.TokenList, stream: var) !void {
124 switch (self.*) {124 switch (self.*) {
125 // TODO https://github.com/ziglang/zig/issues/683125 // TODO https://github.com/ziglang/zig/issues/683
126 @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream),126 @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream),
...@@ -145,7 +145,7 @@ pub const Error = union(enum) {...@@ -145,7 +145,7 @@ pub const Error = union(enum) {
145 }145 }
146 }146 }
147147
148 pub fn loc(self: &Error) TokenIndex {148 pub fn loc(self: &const Error) TokenIndex {
149 switch (self.*) {149 switch (self.*) {
150 // TODO https://github.com/ziglang/zig/issues/683150 // TODO https://github.com/ziglang/zig/issues/683
151 @TagType(Error).InvalidToken => |x| return x.token,151 @TagType(Error).InvalidToken => |x| return x.token,
...@@ -190,7 +190,7 @@ pub const Error = union(enum) {...@@ -190,7 +190,7 @@ pub const Error = union(enum) {
190 pub const ExpectedCall = struct {190 pub const ExpectedCall = struct {
191 node: &Node,191 node: &Node,
192192
193 pub fn render(self: &ExpectedCall, tokens: &Tree.TokenList, stream: var) !void {193 pub fn render(self: &const ExpectedCall, tokens: &Tree.TokenList, stream: var) !void {
194 return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", @tagName(self.node.id));194 return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", @tagName(self.node.id));
195 }195 }
196 };196 };
...@@ -198,7 +198,7 @@ pub const Error = union(enum) {...@@ -198,7 +198,7 @@ pub const Error = union(enum) {
198 pub const ExpectedCallOrFnProto = struct {198 pub const ExpectedCallOrFnProto = struct {
199 node: &Node,199 node: &Node,
200200
201 pub fn render(self: &ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void {201 pub fn render(self: &const ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void {
202 return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id));202 return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id));
203 }203 }
204 };204 };
...@@ -207,7 +207,7 @@ pub const Error = union(enum) {...@@ -207,7 +207,7 @@ pub const Error = union(enum) {
207 token: TokenIndex,207 token: TokenIndex,
208 expected_id: @TagType(Token.Id),208 expected_id: @TagType(Token.Id),
209209
210 pub fn render(self: &ExpectedToken, tokens: &Tree.TokenList, stream: var) !void {210 pub fn render(self: &const ExpectedToken, tokens: &Tree.TokenList, stream: var) !void {
211 const token_name = @tagName(tokens.at(self.token).id);211 const token_name = @tagName(tokens.at(self.token).id);
212 return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name);212 return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name);
213 }213 }
...@@ -217,7 +217,7 @@ pub const Error = union(enum) {...@@ -217,7 +217,7 @@ pub const Error = union(enum) {
217 token: TokenIndex,217 token: TokenIndex,
218 end_id: @TagType(Token.Id),218 end_id: @TagType(Token.Id),
219219
220 pub fn render(self: &ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void {220 pub fn render(self: &const ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void {
221 const token_name = @tagName(tokens.at(self.token).id);221 const token_name = @tagName(tokens.at(self.token).id);
222 return stream.print("expected ',' or {}, found {}", @tagName(self.end_id), token_name);222 return stream.print("expected ',' or {}, found {}", @tagName(self.end_id), token_name);
223 }223 }
...@@ -229,7 +229,7 @@ pub const Error = union(enum) {...@@ -229,7 +229,7 @@ pub const Error = union(enum) {
229229
230 token: TokenIndex,230 token: TokenIndex,
231231
232 pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void {232 pub fn render(self: &const ThisError, tokens: &Tree.TokenList, stream: var) !void {
233 const token_name = @tagName(tokens.at(self.token).id);233 const token_name = @tagName(tokens.at(self.token).id);
234 return stream.print(msg, token_name);234 return stream.print(msg, token_name);
235 }235 }
...@@ -242,7 +242,7 @@ pub const Error = union(enum) {...@@ -242,7 +242,7 @@ pub const Error = union(enum) {
242242
243 token: TokenIndex,243 token: TokenIndex,
244244
245 pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void {245 pub fn render(self: &const ThisError, tokens: &Tree.TokenList, stream: var) !void {
246 return stream.write(msg);246 return stream.write(msg);
247 }247 }
248 };248 };