100 lines
3.0 KiB
Zig
100 lines
3.0 KiB
Zig
|
const std = @import("std");
|
||
|
|
||
|
const zls = @import("zls");
|
||
|
|
||
|
const offsets = zls.offsets;
|
||
|
|
||
|
const ErrorBuilder = @This();
|
||
|
|
||
|
allocator: std.mem.Allocator,
|
||
|
items: std.ArrayListUnmanaged(MsgItem) = .{},
|
||
|
source: []const u8,
|
||
|
|
||
|
pub fn init(allocator: std.mem.Allocator, source: []const u8) ErrorBuilder {
|
||
|
return ErrorBuilder{
|
||
|
.allocator = allocator,
|
||
|
.source = source,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
pub fn deinit(builder: *ErrorBuilder) void {
|
||
|
for (builder.items.items) |item| {
|
||
|
builder.allocator.free(item.message);
|
||
|
}
|
||
|
builder.items.deinit(builder.allocator);
|
||
|
}
|
||
|
|
||
|
pub fn msgAtLoc(builder: *ErrorBuilder, comptime fmt: []const u8, loc: offsets.Loc, level: std.log.Level, args: anytype) !void {
|
||
|
try builder.items.append(builder.allocator, .{
|
||
|
.loc = loc,
|
||
|
.level = level,
|
||
|
.message = try std.fmt.allocPrint(builder.allocator, fmt, args),
|
||
|
});
|
||
|
}
|
||
|
|
||
|
pub fn msgAtIndex(builder: *ErrorBuilder, comptime fmt: []const u8, index: usize, level: std.log.Level, args: anytype) !void {
|
||
|
return msgAtLoc(builder, fmt, .{ .start = index, .end = index }, level, args);
|
||
|
}
|
||
|
|
||
|
pub fn hasMessages(builder: *ErrorBuilder) bool {
|
||
|
return builder.items.items.len != 0;
|
||
|
}
|
||
|
|
||
|
pub fn write(builder: *ErrorBuilder, writer: anytype) !void {
|
||
|
if (!builder.hasMessages()) return;
|
||
|
|
||
|
std.sort.sort(MsgItem, builder.items.items, builder, ErrorBuilder.lessThan);
|
||
|
|
||
|
try writer.writeByte('\n');
|
||
|
|
||
|
var start: usize = 0;
|
||
|
for (builder.items.items) |item| {
|
||
|
const line = offsets.lineLocAtIndex(builder.source, item.loc.start);
|
||
|
defer start = line.end;
|
||
|
|
||
|
try writer.writeAll(builder.source[start..line.end]);
|
||
|
try writer.writeByte('\n');
|
||
|
{
|
||
|
var i: usize = line.start;
|
||
|
while (i < item.loc.start) : (i += 1) try writer.writeByte(' ');
|
||
|
while (i < item.loc.end) : (i += 1) try writer.writeByte('^');
|
||
|
if (item.loc.start == item.loc.end) try writer.writeByte('^');
|
||
|
}
|
||
|
const level_txt: []const u8 = switch (item.level) {
|
||
|
.err => "error",
|
||
|
.warn => "warning",
|
||
|
.info => "info",
|
||
|
.debug => "debug",
|
||
|
};
|
||
|
try writer.print(" {s}: {s}", .{ level_txt, item.message });
|
||
|
}
|
||
|
|
||
|
try writer.writeAll(builder.source[start..builder.source.len]);
|
||
|
try writer.writeByte('\n');
|
||
|
}
|
||
|
|
||
|
pub fn writeDebug(builder: *ErrorBuilder) void {
|
||
|
if (!builder.hasMessages()) return;
|
||
|
std.debug.getStderrMutex().lock();
|
||
|
defer std.debug.getStderrMutex().unlock();
|
||
|
nosuspend builder.write(std.io.getStdErr().writer()) catch return;
|
||
|
}
|
||
|
|
||
|
const MsgItem = struct {
|
||
|
loc: offsets.Loc,
|
||
|
level: std.log.Level,
|
||
|
message: []const u8,
|
||
|
};
|
||
|
|
||
|
fn lessThan(builder: *ErrorBuilder, lhs: MsgItem, rhs: MsgItem) bool {
|
||
|
const is_less = lhs.loc.start < rhs.loc.start;
|
||
|
const text = if (is_less) builder.source[lhs.loc.start..rhs.loc.start] else builder.source[rhs.loc.start..lhs.loc.start];
|
||
|
|
||
|
// report messages on the same line in reverse order
|
||
|
if (std.mem.indexOfScalar(u8, text, '\n') == null) {
|
||
|
return !is_less;
|
||
|
}
|
||
|
|
||
|
return is_less;
|
||
|
}
|