From 4127cf8bcf4bf8ba8b1c3df804a6c99f14c975a1 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 18 Aug 2024 16:21:38 +0200 Subject: [PATCH] feat: show diagnostics in the file list view if they refer to another file --- src/Project.zig | 12 ++--- src/tui/editor.zig | 31 +++++------ src/tui/filelist_view.zig | 14 ++++- src/tui/mainview.zig | 111 ++++++++++++++++++++++++++++---------- 4 files changed, 115 insertions(+), 53 deletions(-) diff --git a/src/Project.zig b/src/Project.zig index 9f6cc69..681619f 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -485,25 +485,25 @@ pub fn references(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usi if (try response.match(.{ "child", tp.string, "result", tp.null_ })) { return; } else if (try response.match(.{ "child", tp.string, "result", tp.extract_cbor(&locations) })) { - try self.send_location_list(from, locations); + try self.send_reference_list(from, locations); } } -fn send_location_list(self: *Self, to: tp.pid_ref, locations: []const u8) !void { - defer to.send(.{ "FIF", "done" }) catch {}; +fn send_reference_list(self: *Self, to: tp.pid_ref, locations: []const u8) !void { + defer to.send(.{ "REF", "done" }) catch {}; var iter = locations; var len = try cbor.decodeArrayHeader(&iter); const count = len; while (len > 0) : (len -= 1) { var location: []const u8 = undefined; if (try cbor.matchValue(&iter, cbor.extract_cbor(&location))) { - try self.send_location(to, location); + try self.send_reference(to, location); } else return error.InvalidMessageField; } log.logger("lsp").print("found {d} references", .{count}); } -fn send_location(self: *Self, to: tp.pid_ref, location: []const u8) !void { +fn send_reference(self: *Self, to: tp.pid_ref, location: []const u8) !void { var iter = location; var targetUri: ?[]const u8 = null; var targetRange: ?Range = null; @@ -545,7 +545,7 @@ fn send_location(self: *Self, to: tp.pid_ref, location: []const u8) !void { else file_path; try to.send(.{ - "FIF", + "REF", file_path_, targetRange.?.start.line + 1, targetRange.?.start.character, diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 4ee46da..ef52736 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -164,7 +164,7 @@ pub const Diagnostic = struct { a.free(self.message); } - const Severity = enum { Error, Warning, Information, Hint }; + pub const Severity = enum { Error, Warning, Information, Hint }; pub fn get_severity(self: Diagnostic) Severity { return to_severity(self.severity); } @@ -3314,7 +3314,7 @@ pub const Editor = struct { } pub fn goto_next_diagnostic(self: *Self, _: Context) Result { - if (self.diagnostics.items.len == 0) return; + if (self.diagnostics.items.len == 0) return command.executeName("goto_next_file", .{}); self.sort_diagnostics(); const primary = self.get_primary(); for (self.diagnostics.items) |*diag| { @@ -3325,7 +3325,7 @@ pub const Editor = struct { } pub fn goto_prev_diagnostic(self: *Self, _: Context) Result { - if (self.diagnostics.items.len == 0) return; + if (self.diagnostics.items.len == 0) return command.executeName("goto_prev_file", .{}); self.sort_diagnostics(); const primary = self.get_primary(); var i = self.diagnostics.items.len - 1; @@ -3440,21 +3440,6 @@ pub const Editor = struct { return project_manager.completion(file_path, primary.cursor.row, primary.cursor.col); } - pub fn clear_diagnostics(self: *Self, ctx: Context) Result { - var file_path: []const u8 = undefined; - if (!try ctx.args.match(.{tp.extract(&file_path)})) return error.InvalidArgument; - file_path = project_manager.normalize_file_path(file_path); - 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(); - self.diag_errors = 0; - self.diag_warnings = 0; - self.diag_info = 0; - self.diag_hints = 0; - self.send_editor_diagnostics() catch {}; - self.need_render(); - } - pub fn add_diagnostic( self: *Self, file_path: []const u8, @@ -3484,6 +3469,16 @@ pub const Editor = struct { self.need_render(); } + pub fn clear_diagnostics(self: *Self) void { + self.diagnostics.clearRetainingCapacity(); + self.diag_errors = 0; + self.diag_warnings = 0; + self.diag_info = 0; + self.diag_hints = 0; + self.send_editor_diagnostics() catch {}; + self.need_render(); + } + pub fn select(self: *Self, ctx: Context) Result { var sel: Selection = .{}; if (!try ctx.args.match(.{ tp.extract(&sel.begin.row), tp.extract(&sel.begin.col), tp.extract(&sel.end.row), tp.extract(&sel.end.col) })) diff --git a/src/tui/filelist_view.zig b/src/tui/filelist_view.zig index 2ebb025..1b8287f 100644 --- a/src/tui/filelist_view.zig +++ b/src/tui/filelist_view.zig @@ -22,6 +22,7 @@ const Menu = @import("Menu.zig"); const EventHandler = @import("EventHandler.zig"); const Button = @import("Button.zig"); const scrollbar_v = @import("scrollbar_v.zig"); +const editor = @import("editor.zig"); const escape = fmt.fmtSliceEscapeLower; @@ -52,6 +53,7 @@ const Entry = struct { end_line: usize, end_pos: usize, lines: []const u8, + severity: editor.Diagnostic.Severity = .Information, }; pub fn create(allocator: Allocator, parent: Plane) !Widget { @@ -137,7 +139,10 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { fn handle_render_menu(self: *Self, button: *Button.State(*Menu.State(*Self)), theme: *const Widget.Theme, selected: bool) bool { const style_base = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else theme.panel; - const style_info: Widget.Theme.Style = .{ .fg = theme.editor_information.fg, .fs = theme.editor_information.fs, .bg = style_base.bg }; + const style_hint: Widget.Theme.Style = .{ .fg = theme.editor_hint.fg, .fs = theme.editor_hint.fs, .bg = style_base.bg }; + const style_information: Widget.Theme.Style = .{ .fg = theme.editor_information.fg, .fs = theme.editor_information.fs, .bg = style_base.bg }; + const style_warning: Widget.Theme.Style = .{ .fg = theme.editor_warning.fg, .fs = theme.editor_warning.fs, .bg = style_base.bg }; + const style_error: Widget.Theme.Style = .{ .fg = theme.editor_error.fg, .fs = theme.editor_error.fs, .bg = style_base.bg }; const style_separator: Widget.Theme.Style = .{ .fg = theme.editor_selection.bg, .bg = style_base.bg }; // const style_error: Widget.Theme.Style = .{ .fg = theme.editor_error.fg, .fs = theme.editor_error.fs, .bg = style_base.bg }; var idx: usize = undefined; @@ -166,7 +171,12 @@ fn handle_render_menu(self: *Self, button: *Button.State(*Menu.State(*Self)), th button.plane.cursor_move_yx(0, @intCast(max_len)) catch return false; button.plane.set_style(style_separator); _ = button.plane.print(" ▏", .{}) catch {}; - button.plane.set_style(style_info); + switch (entry.severity) { + .Hint => button.plane.set_style(style_hint), + .Information => button.plane.set_style(style_information), + .Warning => button.plane.set_style(style_warning), + .Error => button.plane.set_style(style_error), + } _ = button.plane.print("{s}", .{entry.lines}) catch {}; return false; } diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 3de63a3..9f97564 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -22,6 +22,9 @@ const WidgetStack = @import("WidgetStack.zig"); const ed = @import("editor.zig"); const home = @import("home.zig"); +const logview = @import("logview.zig"); +const filelist_view = @import("filelist_view.zig"); + const Self = @This(); const Commands = command.Collection(cmds); @@ -38,6 +41,7 @@ last_match_text: ?[]const u8 = null, location_history: location_history, file_stack: std.ArrayList([]const u8), find_in_files_done: bool = false, +file_list_type: FileListType = .find_in_files, panel_height: ?usize = null, const NavState = struct { @@ -49,6 +53,12 @@ const NavState = struct { matches: usize = 0, }; +const FileListType = enum { + diagnostics, + references, + find_in_files, +}; + pub fn create(a: std.mem.Allocator) !Widget { const self = try a.create(Self); self.* = .{ @@ -92,8 +102,14 @@ pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { var end_line: usize = undefined; var end_pos: usize = undefined; var lines: []const u8 = undefined; - if (try m.match(.{ "FIF", tp.extract(&path), tp.extract(&begin_line), tp.extract(&begin_pos), tp.extract(&end_line), tp.extract(&end_pos), tp.extract(&lines) })) { - try self.add_find_in_files_result(path, begin_line, begin_pos, end_line, end_pos, lines); + if (try m.match(.{ "REF", tp.extract(&path), tp.extract(&begin_line), tp.extract(&begin_pos), tp.extract(&end_line), tp.extract(&end_pos), tp.extract(&lines) })) { + try self.add_find_in_files_result(.references, path, begin_line, begin_pos, end_line, end_pos, lines, .Information); + return true; + } else if (try m.match(.{ "FIF", tp.extract(&path), tp.extract(&begin_line), tp.extract(&begin_pos), tp.extract(&end_line), tp.extract(&end_pos), tp.extract(&lines) })) { + try self.add_find_in_files_result(.find_in_files, path, begin_line, begin_pos, end_line, end_pos, lines, .Information); + return true; + } else if (try m.match(.{ "REF", "done" })) { + self.find_in_files_done = true; return true; } else if (try m.match(.{ "FIF", "done" })) { self.find_in_files_done = true; @@ -145,8 +161,7 @@ fn statusbar_primary_drag(self: *Self, y: usize) tp.result { panels.layout = .{ .static = self.panel_height.? }; } -fn toggle_panel_view(self: *Self, view: anytype, enable_only: bool) !bool { - var enabled = true; +fn toggle_panel_view(self: *Self, view: anytype, enable_only: bool) !void { if (self.panels) |panels| { if (panels.get(@typeName(view))) |w| { if (!enable_only) { @@ -155,7 +170,6 @@ fn toggle_panel_view(self: *Self, view: anytype, enable_only: bool) !bool { self.widgets.remove(panels.widget()); self.panels = null; } - enabled = false; } } else { try panels.add(try view.create(self.a, self.widgets.plane)); @@ -167,7 +181,6 @@ fn toggle_panel_view(self: *Self, view: anytype, enable_only: bool) !bool { self.panels = panels; } tui.current().resize(); - return enabled; } fn get_panel_view(self: *Self, comptime view: type) ?*view { @@ -312,32 +325,32 @@ const cmds = struct { } pub fn toggle_panel(self: *Self, _: Ctx) Result { - if (self.is_panel_view_showing(@import("logview.zig"))) - _ = try self.toggle_panel_view(@import("logview.zig"), false) - else if (self.is_panel_view_showing(@import("filelist_view.zig"))) - _ = try self.toggle_panel_view(@import("filelist_view.zig"), false) + if (self.is_panel_view_showing(logview)) + try self.toggle_panel_view(logview, false) + else if (self.is_panel_view_showing(filelist_view)) + try self.toggle_panel_view(filelist_view, false) else - _ = try self.toggle_panel_view(@import("logview.zig"), false); + try self.toggle_panel_view(logview, false); } pub fn toggle_logview(self: *Self, _: Ctx) Result { - _ = try self.toggle_panel_view(@import("logview.zig"), false); + try self.toggle_panel_view(logview, false); } pub fn show_logview(self: *Self, _: Ctx) Result { - _ = try self.toggle_panel_view(@import("logview.zig"), true); + try self.toggle_panel_view(logview, true); } pub fn toggle_inputview(self: *Self, _: Ctx) Result { - _ = try self.toggle_panel_view(@import("inputview.zig"), false); + try self.toggle_panel_view(@import("inputview.zig"), false); } pub fn toggle_inspector_view(self: *Self, _: Ctx) Result { - _ = try self.toggle_panel_view(@import("inspector_view.zig"), false); + try self.toggle_panel_view(@import("inspector_view.zig"), false); } pub fn show_inspector_view(self: *Self, _: Ctx) Result { - _ = try self.toggle_panel_view(@import("inspector_view.zig"), true); + try self.toggle_panel_view(@import("inspector_view.zig"), true); } pub fn jump_back(self: *Self, _: Ctx) Result { @@ -377,18 +390,22 @@ const cmds = struct { } pub fn goto_next_file_or_diagnostic(self: *Self, ctx: Ctx) Result { - const filelist_view = @import("filelist_view.zig"); if (self.is_panel_view_showing(filelist_view)) { - try command.executeName("goto_next_file", ctx); + switch (self.file_list_type) { + .diagnostics => try command.executeName("goto_next_diagnostic", ctx), + else => try command.executeName("goto_next_file", ctx), + } } else { try command.executeName("goto_next_diagnostic", ctx); } } pub fn goto_prev_file_or_diagnostic(self: *Self, ctx: Ctx) Result { - const filelist_view = @import("filelist_view.zig"); if (self.is_panel_view_showing(filelist_view)) { - try command.executeName("goto_prev_file", ctx); + switch (self.file_list_type) { + .diagnostics => try command.executeName("goto_prev_diagnostic", ctx), + else => try command.executeName("goto_prev_file", ctx), + } } else { try command.executeName("goto_prev_diagnostic", ctx); } @@ -414,7 +431,21 @@ const cmds = struct { })) return error.InvalidArgument; file_path = project_manager.normalize_file_path(file_path); if (self.editor) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) - try editor.add_diagnostic(file_path, source, code, message, severity, sel); + try editor.add_diagnostic(file_path, source, code, message, severity, sel) + else + try self.add_find_in_files_result(.diagnostics, file_path, sel.begin.row, sel.begin.col, sel.end.row, sel.end.col, message, ed.Diagnostic.to_severity(severity)); + } + + pub fn clear_diagnostics(self: *Self, ctx: Ctx) Result { + var file_path: []const u8 = undefined; + if (!try ctx.args.match(.{tp.extract(&file_path)})) return error.InvalidArgument; + file_path = project_manager.normalize_file_path(file_path); + if (self.editor) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) + editor.clear_diagnostics(); + + self.clear_find_in_files_results(.diagnostics); + if (self.file_list_type == .diagnostics and self.is_panel_view_showing(filelist_view)) + try self.toggle_panel_view(filelist_view, false); } }; @@ -597,14 +628,40 @@ fn pop_file_stack(self: *Self, closed: ?[]const u8) ?[]const u8 { return self.file_stack.popOrNull(); } -fn add_find_in_files_result(self: *Self, path: []const u8, begin_line: usize, begin_pos: usize, end_line: usize, end_pos: usize, lines: []const u8) tp.result { - const filelist_view = @import("filelist_view.zig"); +fn add_find_in_files_result( + self: *Self, + file_list_type: FileListType, + path: []const u8, + begin_line: usize, + begin_pos: usize, + end_line: usize, + end_pos: usize, + lines: []const u8, + severity: ed.Diagnostic.Severity, +) tp.result { if (!self.is_panel_view_showing(filelist_view)) _ = self.toggle_panel_view(filelist_view, false) catch |e| return tp.exit_error(e, @errorReturnTrace()); const fl = self.get_panel_view(filelist_view) orelse @panic("filelist_view missing"); - if (self.find_in_files_done) { - self.find_in_files_done = false; - fl.reset(); + if (self.find_in_files_done or self.file_list_type != file_list_type) { + self.clear_find_in_files_results(self.file_list_type); + self.file_list_type = file_list_type; } - fl.add_item(.{ .path = path, .begin_line = @max(1, begin_line) - 1, .begin_pos = @max(1, begin_pos) - 1, .end_line = @max(1, end_line) - 1, .end_pos = @max(1, end_pos) - 1, .lines = lines }) catch |e| return tp.exit_error(e, @errorReturnTrace()); + fl.add_item(.{ + .path = path, + .begin_line = @max(1, begin_line) - 1, + .begin_pos = @max(1, begin_pos) - 1, + .end_line = @max(1, end_line) - 1, + .end_pos = @max(1, end_pos) - 1, + .lines = lines, + .severity = severity, + }) catch |e| return tp.exit_error(e, @errorReturnTrace()); +} + +fn clear_find_in_files_results(self: *Self, file_list_type: FileListType) void { + if (self.file_list_type != file_list_type) return; + if (!self.is_panel_view_showing(filelist_view)) return; + const fl = self.get_panel_view(filelist_view) orelse @panic("filelist_view missing"); + self.find_in_files_done = false; + self.file_list_type = file_list_type; + fl.reset(); }