feature. See also
. The project being documented here (as the example) is the Zig library itself.
errors.renderErrorMessage
pub fn renderErrorMessage(
io: Io,
t: Io.Terminal,
cwd: Io.Dir,
err_details: ErrorDetails,
source: []const u8,
strings: []const []const u8,
source_mappings: ?SourceMappings,
) !void
File
Code
pub fn renderErrorMessage(
io: Io,
t: Io.Terminal,
cwd: Io.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.getLineStartForErrorDisplay(source);
// and add one to get a 1-based column
const column = err_details.token.calculateColumn(source, 1, source_line_start) + 1;
const corresponding_span: ?SourceMappings.CorrespondingSpan = if (source_mappings) |mappings|
mappings.getCorrespondingSpan(err_details.token.line_number)
else
null;
const corresponding_file: ?[]const u8 = if (source_mappings != null and corresponding_span != null)
source_mappings.?.files.get(corresponding_span.?.filename_offset)
else
null;
const err_line = if (corresponding_span) |span| span.start_line else err_details.token.line_number;
const writer = t.writer;
try t.setColor(.bold);
if (corresponding_file) |file| {
try writer.writeAll(file);
} else {
try t.setColor(.dim);
try writer.writeAll("<after preprocessor>");
try t.setColor(.reset);
try t.setColor(.bold);
}
try writer.print(":{d}:{d}: ", .{ err_line, column });
switch (err_details.type) {
.err => {
try t.setColor(.red);
try writer.writeAll("error: ");
},
.warning => {
try t.setColor(.yellow);
try writer.writeAll("warning: ");
},
.note => {
try t.setColor(.cyan);
try writer.writeAll("note: ");
},
.hint => unreachable,
}
try t.setColor(.reset);
try t.setColor(.bold);
try err_details.render(writer, source, strings);
try writer.writeByte('\n');
try t.setColor(.reset);
if (!err_details.print_source_line) {
try writer.writeByte('\n');
return;
}
const source_line = err_details.token.getLineForErrorDisplay(source, source_line_start);
const visual_info = err_details.visualTokenInfo(source_line_start, source_line_start + source_line.len, source);
const truncated_visual_info = ErrorDetails.VisualTokenInfo{
.before_len = if (visual_info.point_offset > max_source_line_codepoints and visual_info.before_len > 0)
(visual_info.before_len + 1) -| (visual_info.point_offset - max_source_line_codepoints)
else
visual_info.before_len,
.point_offset = @min(max_source_line_codepoints + 1, visual_info.point_offset),
.after_len = if (visual_info.point_offset > max_source_line_codepoints)
@min(truncated_str.len - 3, visual_info.after_len)
else
@min(max_source_line_codepoints - visual_info.point_offset + (truncated_str.len - 2), visual_info.after_len),
};
var source_line_for_display_buf: [max_source_line_bytes]u8 = undefined;
const source_line_for_display = writeSourceSlice(&source_line_for_display_buf, source_line, err_details.code_page);
try writer.writeAll(source_line_for_display.line);
if (source_line_for_display.truncated) {
try t.setColor(.dim);
try writer.writeAll(truncated_str);
try t.setColor(.reset);
}
try writer.writeByte('\n');
try t.setColor(.green);
const num_spaces = truncated_visual_info.point_offset - truncated_visual_info.before_len;
try writer.splatByteAll(' ', num_spaces);
try writer.splatByteAll('~', truncated_visual_info.before_len);
try writer.writeByte('^');
try writer.splatByteAll('~', truncated_visual_info.after_len);
try writer.writeByte('\n');
try t.setColor(.reset);
if (corresponding_span != null and corresponding_file != null) {
var worth_printing_lines: bool = true;
var initial_lines_err: ?anyerror = null;
var file_reader_buf: [max_source_line_bytes * 2]u8 = undefined;
var corresponding_lines: ?CorrespondingLines = CorrespondingLines.init(
io,
cwd,
err_details,
source_line_for_display.line,
corresponding_span.?,
corresponding_file.?,
&file_reader_buf,
) catch |err| switch (err) {
error.NotWorthPrintingLines => blk: {
worth_printing_lines = false;
break :blk null;
},
error.NotWorthPrintingNote => return,
else => |e| blk: {
initial_lines_err = e;
break :blk null;
},
};
defer if (corresponding_lines) |*cl| cl.deinit(io);
try t.setColor(.bold);
if (corresponding_file) |file| {
try writer.writeAll(file);
} else {
try t.setColor(.dim);
try writer.writeAll("<after preprocessor>");
try t.setColor(.reset);
try t.setColor(.bold);
}
try writer.print(":{d}:{d}: ", .{ err_line, column });
try t.setColor(.cyan);
try writer.writeAll("note: ");
try t.setColor(.reset);
try t.setColor(.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.?});
try t.setColor(.reset);
if (!worth_printing_lines) return;
const write_lines_err: ?anyerror = write_lines: {
if (initial_lines_err) |err| break :write_lines err;
while (corresponding_lines.?.next() catch |err| {
break :write_lines err;
}) |display_line| {
try writer.writeAll(display_line.line);
if (display_line.truncated) {
try t.setColor(.dim);
try writer.writeAll(truncated_str);
try t.setColor(.reset);
}
try writer.writeByte('\n');
}
break :write_lines null;
};
if (write_lines_err) |err| {
try t.setColor(.red);
try writer.writeAll(" | ");
try t.setColor(.reset);
try t.setColor(.dim);
try writer.print("unable to print line(s) from file: {s}\n", .{@errorName(err)});
try t.setColor(.reset);
}
try writer.writeByte('\n');
}
}