From e41c2d34f714e56607e72b77267322f24eaef4d5 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 17 Apr 2024 23:26:19 +0200 Subject: [PATCH] feat: render diagnostics (part 1) --- src/Project.zig | 2 +- src/project_manager.zig | 10 +++++++ src/tui/editor.zig | 58 ++++++++++++++++++++++++++++++++++++++--- src/tui/mainview.zig | 11 +------- 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/Project.zig b/src/Project.zig index 3527034..80c2351 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -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 { - try to.send(.{ "cmd", "clear_diagnostics", file_path }); + try to.send(.{ "cmd", "clear_diagnostics", .{file_path} }); } const Range = struct { start: Position, end: Position }; diff --git a/src/project_manager.zig b/src/project_manager.zig index 0d2a141..9d34cd5 100644 --- a/src/project_manager.zig +++ b/src/project_manager.zig @@ -564,3 +564,13 @@ fn walk_filtered(dir: std.fs.Dir, allocator: std.mem.Allocator) !FilteredWalker .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; +} + diff --git a/src/tui/editor.zig b/src/tui/editor.zig index b250d27..602e7dc 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -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 { const SelectMode = enum { char, @@ -222,6 +236,8 @@ pub const Editor = struct { style_cache: ?StyleCache = null, style_cache_theme: []const u8 = "", + diagnostics: std.ArrayList(Diagnostic), + const StyleCache = std.AutoHashMap(u32, ?Widget.Theme.Token); pub fn write_state(self: *const Self, writer: Buffer.MetaWriter) !void { @@ -291,10 +307,13 @@ pub const Editor = struct { .matches = Match.List.init(a), .enable_terminal_cursor = tui.current().config.enable_terminal_cursor, .show_whitespace = tui.current().config.show_whitespace, + .diagnostics = std.ArrayList(Diagnostic).init(a), }; } fn deinit(self: *Self) void { + for (self.diagnostics.items) |*d| d.deinit(self.diagnostics.allocator); + self.diagnostics.deinit(); if (self.syntax) |syn| syn.destroy(); self.cursels.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 {}; } self.render_syntax(theme, cache, root) catch {}; + self.render_diagnostics(theme, root) 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 { var cell = self.plane.cell_init(); _ = 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); } - pub fn clear_diagnostics(self: *Self, _: command.Context) tp.result { - self.logger.print("diag: clear", .{}); + pub fn clear_diagnostics(self: *Self, ctx: command.Context) tp.result { + 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 { @@ -3257,7 +3299,17 @@ pub const Editor = struct { tp.extract(&sel.end.row), tp.extract(&sel.end.col), })) 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 { diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 3f66f45..8e6d0f6 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -207,7 +207,7 @@ const cmds = struct { file = file_name; } 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| std.mem.eql(u8, fp, f) 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 { for (self.file_stack.items, 0..) |file_path_, i| if (std.mem.eql(u8, file_path, file_path_))