zls/tests/ErrorBuilder.zig

100 lines
3.0 KiB
Zig
Raw Normal View History

2022-09-18 23:47:06 +01:00
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;
}