This was a regression caused by the new error trace support. Stop errors indicate an attempt to navigate outside the bounds of the current document and are not iteresting to log.
136 lines
4.5 KiB
Zig
136 lines
4.5 KiB
Zig
const std = @import("std");
|
|
const tp = @import("thespian");
|
|
const log = @import("log");
|
|
|
|
const Plane = @import("renderer").Plane;
|
|
|
|
const Widget = @import("../Widget.zig");
|
|
const MessageFilter = @import("../MessageFilter.zig");
|
|
const tui = @import("../tui.zig");
|
|
const mainview = @import("../mainview.zig");
|
|
const logview = @import("../logview.zig");
|
|
|
|
parent: Plane,
|
|
plane: Plane,
|
|
msg: std.ArrayList(u8),
|
|
msg_counter: usize = 0,
|
|
clear_timer: ?tp.Cancellable = null,
|
|
level: Level = .info,
|
|
|
|
const message_display_time_seconds = 2;
|
|
const error_display_time_seconds = 4;
|
|
const Self = @This();
|
|
|
|
const Level = enum {
|
|
info,
|
|
err,
|
|
};
|
|
|
|
pub fn create(a: std.mem.Allocator, parent: Plane) !Widget {
|
|
const self: *Self = try a.create(Self);
|
|
self.* = .{
|
|
.parent = parent,
|
|
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),
|
|
.msg = std.ArrayList(u8).init(a),
|
|
};
|
|
logview.init(a);
|
|
try tui.current().message_filters.add(MessageFilter.bind(self, receive_log));
|
|
try log.subscribe();
|
|
return Widget.to(self);
|
|
}
|
|
|
|
pub fn deinit(self: *Self, a: std.mem.Allocator) void {
|
|
if (self.clear_timer) |*t| {
|
|
t.cancel() catch {};
|
|
t.deinit();
|
|
self.clear_timer = null;
|
|
}
|
|
self.msg.deinit();
|
|
log.unsubscribe() catch {};
|
|
tui.current().message_filters.remove_ptr(self);
|
|
self.plane.deinit();
|
|
a.destroy(self);
|
|
}
|
|
|
|
pub fn layout(self: *Self) Widget.Layout {
|
|
return .{ .static = if (self.msg.items.len > 0) self.msg.items.len + 2 else 1 };
|
|
}
|
|
|
|
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
|
const style_normal = theme.statusbar;
|
|
const style_info: Widget.Theme.Style = .{ .fg = theme.statusbar.fg, .fs = theme.editor_information.fs };
|
|
const style_error: Widget.Theme.Style = .{ .fg = theme.editor_error.fg, .fs = theme.editor_error.fs };
|
|
self.plane.set_base_style(" ", style_normal);
|
|
self.plane.erase();
|
|
self.plane.home();
|
|
self.plane.set_style(if (self.level == .err) style_error else style_info);
|
|
_ = self.plane.print(" {s} ", .{self.msg.items}) catch {};
|
|
return false;
|
|
}
|
|
|
|
fn receive_log(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|
var clear_msg_num: usize = 0;
|
|
if (try m.match(.{ "log", tp.more })) {
|
|
logview.process_log(m) catch |e| return tp.exit_error(e, @errorReturnTrace());
|
|
self.process_log(m) catch |e| return tp.exit_error(e, @errorReturnTrace());
|
|
return true;
|
|
} else if (try m.match(.{ "MINILOG", tp.extract(&clear_msg_num) })) {
|
|
if (clear_msg_num == self.msg_counter)
|
|
self.clear();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
fn process_log(self: *Self, m: tp.message) !void {
|
|
var src: []const u8 = undefined;
|
|
var context: []const u8 = undefined;
|
|
var msg: []const u8 = undefined;
|
|
if (try m.match(.{ "log", tp.extract(&src), tp.extract(&msg) })) {
|
|
try self.set(msg, .info);
|
|
} else if (try m.match(.{ "log", "error", tp.extract(&src), tp.extract(&context), "->", tp.extract(&msg) })) {
|
|
const err_stop = "error.Stop";
|
|
if (std.mem.eql(u8, msg, err_stop))
|
|
return;
|
|
if (msg.len >= err_stop.len + 1 and std.mem.eql(u8, msg[0..err_stop.len + 1], err_stop ++ "\n"))
|
|
return;
|
|
try self.set(msg, .err);
|
|
} else if (try m.match(.{ "log", tp.extract(&src), tp.more })) {
|
|
self.level = .err;
|
|
var s = std.json.writeStream(self.msg.writer(), .{});
|
|
var iter: []const u8 = m.buf;
|
|
try @import("cbor").JsonStream(@TypeOf(self.msg)).jsonWriteValue(&s, &iter);
|
|
Widget.need_render();
|
|
try self.update_clear_timer();
|
|
}
|
|
}
|
|
|
|
fn update_clear_timer(self: *Self) !void {
|
|
self.msg_counter += 1;
|
|
const delay = std.time.us_per_s * @as(u64, if (self.level == .err) error_display_time_seconds else message_display_time_seconds);
|
|
if (self.clear_timer) |*t| {
|
|
t.cancel() catch {};
|
|
t.deinit();
|
|
self.clear_timer = null;
|
|
}
|
|
self.clear_timer = try tp.self_pid().delay_send_cancellable(self.msg.allocator, delay, .{ "MINILOG", self.msg_counter });
|
|
}
|
|
|
|
fn set(self: *Self, msg: []const u8, level: Level) !void {
|
|
if (@intFromEnum(level) < @intFromEnum(self.level)) return;
|
|
self.msg.clearRetainingCapacity();
|
|
try self.msg.appendSlice(msg);
|
|
self.level = level;
|
|
Widget.need_render();
|
|
try self.update_clear_timer();
|
|
}
|
|
|
|
fn clear(self: *Self) void {
|
|
if (self.clear_timer) |*t| {
|
|
t.deinit();
|
|
self.clear_timer = null;
|
|
}
|
|
self.level = .info;
|
|
self.msg.clearRetainingCapacity();
|
|
Widget.need_render();
|
|
}
|