feat: render diagnostics (part 1)
This commit is contained in:
parent
33dd4486eb
commit
e41c2d34f7
4 changed files with 67 additions and 14 deletions
|
@ -538,7 +538,7 @@ fn send_diagnostic(_: *Self, to: tp.pid_ref, file_path: []const u8, diagnostic:
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_clear_diagnostics(_: *Self, to: tp.pid_ref, file_path: []const u8) !void {
|
fn send_clear_diagnostics(_: *Self, to: tp.pid_ref, file_path: []const u8) !void {
|
||||||
try to.send(.{ "cmd", "clear_diagnostics", file_path });
|
try to.send(.{ "cmd", "clear_diagnostics", .{file_path} });
|
||||||
}
|
}
|
||||||
|
|
||||||
const Range = struct { start: Position, end: Position };
|
const Range = struct { start: Position, end: Position };
|
||||||
|
|
|
@ -564,3 +564,13 @@ fn walk_filtered(dir: std.fs.Dir, allocator: std.mem.Allocator) !FilteredWalker
|
||||||
.name_buffer = name_buffer,
|
.name_buffer = name_buffer,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn normalize_file_path(file_path: []const u8) []const u8 {
|
||||||
|
const project = tp.env.get().str("project");
|
||||||
|
if (project.len == 0) return file_path;
|
||||||
|
if (project.len >= file_path.len) return file_path;
|
||||||
|
if (std.mem.eql(u8, project, file_path[0..project.len]) and file_path[project.len] == std.fs.path.sep)
|
||||||
|
return file_path[project.len + 1 ..];
|
||||||
|
return file_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,20 @@ pub const CurSel = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Diagnostic = struct {
|
||||||
|
source: []const u8,
|
||||||
|
code: []const u8,
|
||||||
|
message: []const u8,
|
||||||
|
severity: i32,
|
||||||
|
sel: Selection,
|
||||||
|
|
||||||
|
fn deinit(self: *Diagnostic, a: std.mem.Allocator) void {
|
||||||
|
a.free(self.source);
|
||||||
|
a.free(self.code);
|
||||||
|
a.free(self.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const Editor = struct {
|
pub const Editor = struct {
|
||||||
const SelectMode = enum {
|
const SelectMode = enum {
|
||||||
char,
|
char,
|
||||||
|
@ -222,6 +236,8 @@ pub const Editor = struct {
|
||||||
style_cache: ?StyleCache = null,
|
style_cache: ?StyleCache = null,
|
||||||
style_cache_theme: []const u8 = "",
|
style_cache_theme: []const u8 = "",
|
||||||
|
|
||||||
|
diagnostics: std.ArrayList(Diagnostic),
|
||||||
|
|
||||||
const StyleCache = std.AutoHashMap(u32, ?Widget.Theme.Token);
|
const StyleCache = std.AutoHashMap(u32, ?Widget.Theme.Token);
|
||||||
|
|
||||||
pub fn write_state(self: *const Self, writer: Buffer.MetaWriter) !void {
|
pub fn write_state(self: *const Self, writer: Buffer.MetaWriter) !void {
|
||||||
|
@ -291,10 +307,13 @@ pub const Editor = struct {
|
||||||
.matches = Match.List.init(a),
|
.matches = Match.List.init(a),
|
||||||
.enable_terminal_cursor = tui.current().config.enable_terminal_cursor,
|
.enable_terminal_cursor = tui.current().config.enable_terminal_cursor,
|
||||||
.show_whitespace = tui.current().config.show_whitespace,
|
.show_whitespace = tui.current().config.show_whitespace,
|
||||||
|
.diagnostics = std.ArrayList(Diagnostic).init(a),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *Self) void {
|
fn deinit(self: *Self) void {
|
||||||
|
for (self.diagnostics.items) |*d| d.deinit(self.diagnostics.allocator);
|
||||||
|
self.diagnostics.deinit();
|
||||||
if (self.syntax) |syn| syn.destroy();
|
if (self.syntax) |syn| syn.destroy();
|
||||||
self.cursels.deinit();
|
self.cursels.deinit();
|
||||||
self.matches.deinit();
|
self.matches.deinit();
|
||||||
|
@ -688,6 +707,7 @@ pub const Editor = struct {
|
||||||
_ = root.walk_from_line_begin_const(self.view.row, ctx.walker, &ctx_) catch {};
|
_ = root.walk_from_line_begin_const(self.view.row, ctx.walker, &ctx_) catch {};
|
||||||
}
|
}
|
||||||
self.render_syntax(theme, cache, root) catch {};
|
self.render_syntax(theme, cache, root) catch {};
|
||||||
|
self.render_diagnostics(theme, root) catch {};
|
||||||
self.render_cursors(theme) catch {};
|
self.render_cursors(theme) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,6 +786,24 @@ pub const Editor = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_diagnostics(self: *const Self, theme: *const Widget.Theme, root: Buffer.Root) !void {
|
||||||
|
for (self.diagnostics.items) |*diag| self.render_diagnostic(diag, theme, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_diagnostic(self: *const Self, diag: *const Diagnostic, theme: *const Widget.Theme, _: Buffer.Root) void {
|
||||||
|
if (self.screen_cursor(&diag.sel.begin)) |pos| {
|
||||||
|
self.plane.cursor_move_yx(@intCast(pos.row), @intCast(pos.col)) catch return;
|
||||||
|
self.render_diagnostic_cell(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn render_diagnostic_cell(self: *const Self, theme: *const Widget.Theme) void {
|
||||||
|
var cell = self.plane.cell_init();
|
||||||
|
_ = self.plane.at_cursor_cell(&cell) catch return;
|
||||||
|
tui.set_cell_style(&cell, theme.editor_error);
|
||||||
|
_ = self.plane.putc(&cell) catch {};
|
||||||
|
}
|
||||||
|
|
||||||
inline fn render_cursor_cell(self: *const Self, theme: *const Widget.Theme) void {
|
inline fn render_cursor_cell(self: *const Self, theme: *const Widget.Theme) void {
|
||||||
var cell = self.plane.cell_init();
|
var cell = self.plane.cell_init();
|
||||||
_ = self.plane.at_cursor_cell(&cell) catch return;
|
_ = self.plane.at_cursor_cell(&cell) catch return;
|
||||||
|
@ -3235,8 +3273,12 @@ pub const Editor = struct {
|
||||||
return project_manager.goto_definition(file_path, primary.cursor.row, primary.cursor.col);
|
return project_manager.goto_definition(file_path, primary.cursor.row, primary.cursor.col);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_diagnostics(self: *Self, _: command.Context) tp.result {
|
pub fn clear_diagnostics(self: *Self, ctx: command.Context) tp.result {
|
||||||
self.logger.print("diag: clear", .{});
|
var file_path: []const u8 = undefined;
|
||||||
|
if (!try ctx.args.match(.{tp.extract(&file_path)})) return tp.exit_error(error.InvalidArgument);
|
||||||
|
if (!std.mem.eql(u8, file_path, self.file_path orelse return)) return;
|
||||||
|
for (self.diagnostics.items) |*d| d.deinit(self.diagnostics.allocator);
|
||||||
|
self.diagnostics.clearRetainingCapacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_diagnostic(self: *Self, ctx: command.Context) tp.result {
|
pub fn add_diagnostic(self: *Self, ctx: command.Context) tp.result {
|
||||||
|
@ -3257,7 +3299,17 @@ pub const Editor = struct {
|
||||||
tp.extract(&sel.end.row),
|
tp.extract(&sel.end.row),
|
||||||
tp.extract(&sel.end.col),
|
tp.extract(&sel.end.col),
|
||||||
})) return tp.exit_error(error.InvalidArgument);
|
})) return tp.exit_error(error.InvalidArgument);
|
||||||
self.logger.print("diag: {d} {s} {s} {s} {any}", .{ severity, source, code, message, sel });
|
file_path = project_manager.normalize_file_path(file_path);
|
||||||
|
if (!std.mem.eql(u8, file_path, self.file_path orelse return)) return;
|
||||||
|
|
||||||
|
self.logger.print("diag: {d}:{d} {s}", .{ sel.begin.row, sel.begin.col, message });
|
||||||
|
(self.diagnostics.addOne() catch |e| return tp.exit_error(e)).* = .{
|
||||||
|
.source = self.diagnostics.allocator.dupe(u8, source) catch |e| return tp.exit_error(e),
|
||||||
|
.code = self.diagnostics.allocator.dupe(u8, code) catch |e| return tp.exit_error(e),
|
||||||
|
.message = self.diagnostics.allocator.dupe(u8, message) catch |e| return tp.exit_error(e),
|
||||||
|
.severity = severity,
|
||||||
|
.sel = sel,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(self: *Self, ctx: command.Context) tp.result {
|
pub fn select(self: *Self, ctx: command.Context) tp.result {
|
||||||
|
|
|
@ -207,7 +207,7 @@ const cmds = struct {
|
||||||
file = file_name;
|
file = file_name;
|
||||||
} else return tp.exit_error(error.InvalidArgument);
|
} else return tp.exit_error(error.InvalidArgument);
|
||||||
|
|
||||||
const f = normalize_file_path(file orelse return);
|
const f = project_manager.normalize_file_path(file orelse return);
|
||||||
const same_file = if (self.editor) |editor| if (editor.file_path) |fp|
|
const same_file = if (self.editor) |editor| if (editor.file_path) |fp|
|
||||||
std.mem.eql(u8, fp, f)
|
std.mem.eql(u8, fp, f)
|
||||||
else
|
else
|
||||||
|
@ -462,15 +462,6 @@ fn read_restore_info(self: *Self) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalize_file_path(file_path: []const u8) []const u8 {
|
|
||||||
const project = tp.env.get().str("project");
|
|
||||||
if (project.len == 0) return file_path;
|
|
||||||
if (project.len >= file_path.len) return file_path;
|
|
||||||
if (std.mem.eql(u8, project, file_path[0..project.len]) and file_path[project.len] == std.fs.path.sep)
|
|
||||||
return file_path[project.len + 1 ..];
|
|
||||||
return file_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_file_stack(self: *Self, file_path: []const u8) !void {
|
fn push_file_stack(self: *Self, file_path: []const u8) !void {
|
||||||
for (self.file_stack.items, 0..) |file_path_, i|
|
for (self.file_stack.items, 0..) |file_path_, i|
|
||||||
if (std.mem.eql(u8, file_path, file_path_))
|
if (std.mem.eql(u8, file_path, file_path_))
|
||||||
|
|
Loading…
Add table
Reference in a new issue