Skip to content

Commit

Permalink
Remove utils.Colors, use std.io.tty.Config instead
Browse files Browse the repository at this point in the history
Now that the standard library's tty color stuff includes the color yellow, we can use it.
  • Loading branch information
squeek502 committed Jun 11, 2023
1 parent ade74ca commit 59e858a
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 167 deletions.
56 changes: 24 additions & 32 deletions src/cli.zig
Original file line number Diff line number Diff line change
Expand Up @@ -86,24 +86,16 @@ pub const Diagnostics = struct {
try self.errors.append(self.allocator, error_details);
}

pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8) void {
// Set the codepage to UTF-8 unconditionally to ensure that everything renders okay
// TODO: Reset codepage afterwards?
if (@import("builtin").os.tag == .windows) {
_ = std.os.windows.kernel32.SetConsoleOutputCP(65001);
}

// TODO: take this as a param probably
const colors = utils.Colors.detect();
pub fn renderToStdErr(self: *Diagnostics, args: []const []const u8, config: std.io.tty.Config) void {
std.debug.getStderrMutex().lock();
defer std.debug.getStderrMutex().unlock();
const stderr = std.io.getStdErr().writer();
self.renderToWriter(args, stderr, colors) catch return;
self.renderToWriter(args, stderr, config) catch return;
}

pub fn renderToWriter(self: *Diagnostics, args: []const []const u8, writer: anytype, colors: utils.Colors) !void {
pub fn renderToWriter(self: *Diagnostics, args: []const []const u8, writer: anytype, config: std.io.tty.Config) !void {
for (self.errors.items) |err_details| {
try renderErrorMessage(writer, colors, err_details, args);
try renderErrorMessage(writer, config, err_details, args);
}
}

Expand Down Expand Up @@ -941,41 +933,41 @@ test parsePercent {
try std.testing.expectError(error.InvalidFormat, parsePercent("~1"));
}

pub fn renderErrorMessage(writer: anytype, colors: utils.Colors, err_details: Diagnostics.ErrorDetails, args: []const []const u8) !void {
colors.set(writer, .dim);
pub fn renderErrorMessage(writer: anytype, config: std.io.tty.Config, err_details: Diagnostics.ErrorDetails, args: []const []const u8) !void {
try config.setColor(writer, .dim);
try writer.writeAll("<cli>");
colors.set(writer, .reset);
colors.set(writer, .bold);
try config.setColor(writer, .reset);
try config.setColor(writer, .bold);
try writer.writeAll(": ");
switch (err_details.type) {
.err => {
colors.set(writer, .red);
try config.setColor(writer, .red);
try writer.writeAll("error: ");
},
.warning => {
colors.set(writer, .yellow);
try config.setColor(writer, .yellow);
try writer.writeAll("warning: ");
},
.note => {
colors.set(writer, .cyan);
try config.setColor(writer, .cyan);
try writer.writeAll("note: ");
},
}
colors.set(writer, .reset);
colors.set(writer, .bold);
try config.setColor(writer, .reset);
try config.setColor(writer, .bold);
try writer.writeAll(err_details.msg.items);
try writer.writeByte('\n');
colors.set(writer, .reset);
try config.setColor(writer, .reset);

if (!err_details.print_args) {
try writer.writeByte('\n');
return;
}

colors.set(writer, .dim);
try config.setColor(writer, .dim);
const prefix = " ... ";
try writer.writeAll(prefix);
colors.set(writer, .reset);
try config.setColor(writer, .reset);

const arg_with_name = args[err_details.arg_index];
const prefix_slice = arg_with_name[0..err_details.arg_span.prefix_len];
Expand All @@ -986,15 +978,15 @@ pub fn renderErrorMessage(writer: anytype, colors: utils.Colors, err_details: Di

try writer.writeAll(prefix_slice);
if (before_name_slice.len > 0) {
colors.set(writer, .dim);
try config.setColor(writer, .dim);
try writer.writeAll(before_name_slice);
colors.set(writer, .reset);
try config.setColor(writer, .reset);
}
try writer.writeAll(name_slice);
if (after_name_slice.len > 0) {
colors.set(writer, .dim);
try config.setColor(writer, .dim);
try writer.writeAll(after_name_slice);
colors.set(writer, .reset);
try config.setColor(writer, .reset);
}

var next_arg_len: usize = 0;
Expand All @@ -1012,13 +1004,13 @@ pub fn renderErrorMessage(writer: anytype, colors: utils.Colors, err_details: Di
if (err_details.arg_span.value_offset >= arg_with_name.len) {
try writer.writeByte(' ');
}
colors.set(writer, .dim);
try config.setColor(writer, .dim);
try writer.writeAll(" ...");
colors.set(writer, .reset);
try config.setColor(writer, .reset);
}
try writer.writeByte('\n');

colors.set(writer, .green);
try config.setColor(writer, .green);
try writer.writeByteNTimes(' ', prefix.len);
// Special case for when the option is *only* a prefix (e.g. invalid option: -)
if (err_details.arg_span.prefix_len == arg_with_name.len) {
Expand All @@ -1044,7 +1036,7 @@ pub fn renderErrorMessage(writer: anytype, colors: utils.Colors, err_details: Di
}
}
try writer.writeByte('\n');
colors.set(writer, .reset);
try config.setColor(writer, .reset);
}

fn testParse(args: []const []const u8) !Options {
Expand Down
8 changes: 4 additions & 4 deletions src/compile.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3070,7 +3070,7 @@ fn testCompileWithOutputAndOptions(source: []const u8, expected_output: []const
.extra_include_paths = options.extra_include_paths,
}) catch |err| switch (err) {
error.ParseError, error.CompileError => {
diagnostics.renderToStdErr(options.cwd, source, null);
diagnostics.renderToStdErrDetectTTY(options.cwd, source, null);
return err;
},
else => return err,
Expand Down Expand Up @@ -3124,7 +3124,7 @@ fn testCompileErrorDetailsWithOptions(expected_details: []const ExpectedErrorDet
}) catch |err| switch (err) {
error.ParseError, error.CompileError => {
if (!expect_fail) {
diagnostics.renderToStdErr(options.cwd, source, null);
diagnostics.renderToStdErrDetectTTY(options.cwd, source, null);
return err;
}
break :did_fail true;
Expand All @@ -3141,12 +3141,12 @@ fn testCompileErrorDetailsWithOptions(expected_details: []const ExpectedErrorDet

if (expected_details.len != diagnostics.errors.items.len) {
std.debug.print("expected {} error details, got {}:\n", .{ expected_details.len, diagnostics.errors.items.len });
diagnostics.renderToStdErr(options.cwd, source, null);
diagnostics.renderToStdErrDetectTTY(options.cwd, source, null);
return error.ErrorDetailMismatch;
}
for (diagnostics.errors.items, expected_details) |actual, expected| {
std.testing.expectEqual(expected.type, actual.type) catch |e| {
diagnostics.renderToStdErr(options.cwd, source, null);
diagnostics.renderToStdErrDetectTTY(options.cwd, source, null);
return e;
};
var buf: [256]u8 = undefined;
Expand Down
71 changes: 34 additions & 37 deletions src/errors.zig
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,20 @@ pub const Diagnostics = struct {
return @intCast(SmallestStringIndexType, index);
}

pub fn renderToStdErr(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, source_mappings: ?SourceMappings) void {
// Set the codepage to UTF-8 unconditionally to ensure that everything renders okay
// TODO: Reset codepage afterwards?
if (@import("builtin").os.tag == .windows) {
_ = std.os.windows.kernel32.SetConsoleOutputCP(65001);
}

// TODO: take this as a param probably
const colors = utils.Colors.detect();
pub fn renderToStdErr(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, tty_config: std.io.tty.Config, source_mappings: ?SourceMappings) void {
std.debug.getStderrMutex().lock();
defer std.debug.getStderrMutex().unlock();
const stderr = std.io.getStdErr().writer();
for (self.errors.items) |err_details| {
renderErrorMessage(self.allocator, stderr, colors, cwd, err_details, source, self.strings.items, source_mappings) catch return;
renderErrorMessage(self.allocator, stderr, tty_config, cwd, err_details, source, self.strings.items, source_mappings) catch return;
}
}

pub fn renderToStdErrDetectTTY(self: *Diagnostics, cwd: std.fs.Dir, source: []const u8, source_mappings: ?SourceMappings) void {
const tty_config = std.io.tty.detectConfig(std.io.getStdErr());
return self.renderToStdErr(cwd, source, tty_config, source_mappings);
}

pub fn contains(self: *const Diagnostics, err: ErrorDetails.Error) bool {
for (self.errors.items) |details| {
if (details.err == err) return true;
Expand Down Expand Up @@ -746,7 +743,7 @@ pub const ErrorDetails = struct {
}
};

pub fn renderErrorMessage(allocator: std.mem.Allocator, writer: anytype, colors: utils.Colors, cwd: std.fs.Dir, err_details: ErrorDetails, source: []const u8, strings: []const []const u8, source_mappings: ?SourceMappings) !void {
pub fn renderErrorMessage(allocator: std.mem.Allocator, writer: anytype, tty_config: std.io.tty.Config, cwd: std.fs.Dir, err_details: ErrorDetails, source: []const u8, strings: []const []const u8, source_mappings: ?SourceMappings) !void {
if (err_details.type == .hint) return;

const source_line_start = err_details.token.getLineStart(source);
Expand All @@ -760,36 +757,36 @@ pub fn renderErrorMessage(allocator: std.mem.Allocator, writer: anytype, colors:

const err_line = if (corresponding_span) |span| span.start_line else err_details.token.line_number;

colors.set(writer, .bold);
try tty_config.setColor(writer, .bold);
if (corresponding_file) |file| {
try writer.writeAll(file);
} else {
colors.set(writer, .dim);
try tty_config.setColor(writer, .dim);
try writer.writeAll("<after preprocessor>");
colors.set(writer, .reset);
colors.set(writer, .bold);
try tty_config.setColor(writer, .reset);
try tty_config.setColor(writer, .bold);
}
try writer.print(":{d}:{d}: ", .{ err_line, column });
switch (err_details.type) {
.err => {
colors.set(writer, .red);
try tty_config.setColor(writer, .red);
try writer.writeAll("error: ");
},
.warning => {
colors.set(writer, .yellow);
try tty_config.setColor(writer, .yellow);
try writer.writeAll("warning: ");
},
.note => {
colors.set(writer, .cyan);
try tty_config.setColor(writer, .cyan);
try writer.writeAll("note: ");
},
.hint => unreachable,
}
colors.set(writer, .reset);
colors.set(writer, .bold);
try tty_config.setColor(writer, .reset);
try tty_config.setColor(writer, .bold);
try err_details.render(writer, source, strings);
try writer.writeByte('\n');
colors.set(writer, .reset);
try tty_config.setColor(writer, .reset);

if (!err_details.print_source_line) {
try writer.writeByte('\n');
Expand All @@ -808,15 +805,15 @@ pub fn renderErrorMessage(allocator: std.mem.Allocator, writer: anytype, colors:
if (err_details.err == .string_literal_too_long) {
const before_slice = source_line[0..@min(source_line.len, visual_info.point_offset + 16)];
try writeSourceSlice(writer, before_slice);
colors.set(writer, .dim);
try tty_config.setColor(writer, .dim);
try writer.writeAll("<...truncated...>");
colors.set(writer, .reset);
try tty_config.setColor(writer, .reset);
} else {
try writer.writeAll(source_line_for_display_buf.items);
}
try writer.writeByte('\n');

colors.set(writer, .green);
try tty_config.setColor(writer, .green);
const num_spaces = visual_info.point_offset - visual_info.before_len;
try writer.writeByteNTimes(' ', num_spaces);
try writer.writeByteNTimes('~', visual_info.before_len);
Expand All @@ -829,46 +826,46 @@ pub fn renderErrorMessage(allocator: std.mem.Allocator, writer: anytype, colors:
try writer.writeByteNTimes('~', num_squiggles);
}
try writer.writeByte('\n');
colors.set(writer, .reset);
try tty_config.setColor(writer, .reset);

if (source_mappings) |_| {
var corresponding_lines = try CorrespondingLines.init(allocator, cwd, err_details, source_line_for_display_buf.items, corresponding_span.?, corresponding_file.?);
defer corresponding_lines.deinit(allocator);

if (!corresponding_lines.worth_printing_note) return;

colors.set(writer, .bold);
try tty_config.setColor(writer, .bold);
if (corresponding_file) |file| {
try writer.writeAll(file);
} else {
colors.set(writer, .dim);
try tty_config.setColor(writer, .dim);
try writer.writeAll("<after preprocessor>");
colors.set(writer, .reset);
colors.set(writer, .bold);
try tty_config.setColor(writer, .reset);
try tty_config.setColor(writer, .bold);
}
try writer.print(":{d}:{d}: ", .{ err_line, column });
colors.set(writer, .cyan);
try tty_config.setColor(writer, .cyan);
try writer.writeAll("note: ");
colors.set(writer, .reset);
colors.set(writer, .bold);
try tty_config.setColor(writer, .reset);
try tty_config.setColor(writer, .bold);
try writer.writeAll("this line originated from line");
if (corresponding_span.?.start_line != corresponding_span.?.end_line) {
try writer.print("s {}-{}", .{ corresponding_span.?.start_line, corresponding_span.?.end_line });
} else {
try writer.print(" {}", .{corresponding_span.?.start_line});
}
try writer.print(" of file '{s}'\n", .{corresponding_file.?});
colors.set(writer, .reset);
try tty_config.setColor(writer, .reset);

if (!corresponding_lines.worth_printing_lines) return;

if (corresponding_lines.lines_is_error_message) {
colors.set(writer, .red);
try tty_config.setColor(writer, .red);
try writer.writeAll(" | ");
colors.set(writer, .reset);
colors.set(writer, .dim);
try tty_config.setColor(writer, .reset);
try tty_config.setColor(writer, .dim);
try writer.writeAll(corresponding_lines.lines.items);
colors.set(writer, .reset);
try tty_config.setColor(writer, .reset);
try writer.writeAll("\n\n");
return;
}
Expand Down
15 changes: 11 additions & 4 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ pub fn main() !void {
defer std.debug.assert(gpa.deinit() == .ok);
const allocator = gpa.allocator();

// Set the codepage to UTF-8 unconditionally to ensure that everything renders okay
// TODO: Reset codepage afterwards?
if (@import("builtin").os.tag == .windows) {
_ = std.os.windows.kernel32.SetConsoleOutputCP(65001);
}
const stderr_config = std.io.tty.detectConfig(std.io.getStdErr());

var options = options: {
var args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);
Expand All @@ -20,15 +27,15 @@ pub fn main() !void {
defer cli_diagnostics.deinit();
var options = cli.parse(allocator, args, &cli_diagnostics) catch |err| switch (err) {
error.ParseError => {
cli_diagnostics.renderToStdErr(args);
cli_diagnostics.renderToStdErr(args, stderr_config);
std.os.exit(1);
},
else => |e| return e,
};
try options.maybeAppendRC(std.fs.cwd());

// print any warnings/notes
cli_diagnostics.renderToStdErr(args);
cli_diagnostics.renderToStdErr(args, stderr_config);
// If there was something printed, then add an extra newline separator
// so that there is a clear separation between the cli diagnostics and whatever
// gets printed after
Expand Down Expand Up @@ -203,7 +210,7 @@ pub fn main() !void {
.warn_instead_of_error_on_invalid_code_page = options.warn_instead_of_error_on_invalid_code_page,
}) catch |err| switch (err) {
error.ParseError, error.CompileError => {
diagnostics.renderToStdErr(std.fs.cwd(), final_input, mapping_results.mappings);
diagnostics.renderToStdErr(std.fs.cwd(), final_input, stderr_config, mapping_results.mappings);
// Delete the output file on error
output_file.close();
output_file_closed = true;
Expand All @@ -217,5 +224,5 @@ pub fn main() !void {
try output_buffered_stream.flush();

// print any warnings/notes
diagnostics.renderToStdErr(std.fs.cwd(), final_input, mapping_results.mappings);
diagnostics.renderToStdErr(std.fs.cwd(), final_input, stderr_config, mapping_results.mappings);
}
Loading

0 comments on commit 59e858a

Please sign in to comment.