From 97501c4ec7c4dbca16c773470fc1b60352c4a71f Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 10 Sep 2024 21:47:13 +0200 Subject: [PATCH] feat: hightlight returned range in LSP hover responses --- src/Project.zig | 48 +++++++++++++++++++++++++++++++++++--------- src/tui/mainview.zig | 25 ++++++++++++++++++++--- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/Project.zig b/src/Project.zig index f967090..aa2a8fb 100644 --- a/src/Project.zig +++ b/src/Project.zig @@ -621,25 +621,40 @@ pub fn hover(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, c fn send_hover(self: *Self, to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, result: []const u8) !void { var iter = result; var len = cbor.decodeMapHeader(&iter) catch return; + var contents: []const u8 = ""; + var range: ?Range = null; while (len > 0) : (len -= 1) { var field_name: []const u8 = undefined; if (!(try cbor.matchString(&iter, &field_name))) return error.InvalidMessage; if (std.mem.eql(u8, field_name, "contents")) { - var value: []const u8 = ""; - if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&value)))) return error.InvalidMessageField; - return self.send_contents(to, "hover", file_path, row, col, value); + if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&contents)))) return error.InvalidMessageField; + } else if (std.mem.eql(u8, field_name, "range")) { + var range_: []const u8 = undefined; + if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&range_)))) return error.InvalidMessageField; + range = try read_range(range_); } else { try cbor.skipValue(&iter); } } + if (contents.len > 0) + return self.send_contents(to, "hover", file_path, row, col, contents, range); } -fn send_contents(self: *Self, to: tp.pid_ref, tag: []const u8, file_path: []const u8, row: usize, col: usize, result: []const u8) !void { +fn send_contents( + self: *Self, + to: tp.pid_ref, + tag: []const u8, + file_path: []const u8, + row: usize, + col: usize, + result: []const u8, + range: ?Range, +) !void { var iter = result; var kind: []const u8 = "plaintext"; var value: []const u8 = ""; if (try cbor.matchValue(&iter, cbor.extract(&value))) - return send_content_msg(to, tag, file_path, row, col, kind, value); + return send_content_msg(to, tag, file_path, row, col, kind, value, range); var is_list = true; var len = cbor.decodeArrayHeader(&iter) catch blk: { @@ -657,7 +672,7 @@ fn send_contents(self: *Self, to: tp.pid_ref, tag: []const u8, file_path: []cons if (len > 1) try content.appendSlice("\n"); } } - return send_content_msg(to, tag, file_path, row, col, kind, content.items); + return send_content_msg(to, tag, file_path, row, col, kind, content.items, range); } while (len > 0) : (len -= 1) { @@ -671,15 +686,28 @@ fn send_contents(self: *Self, to: tp.pid_ref, tag: []const u8, file_path: []cons try cbor.skipValue(&iter); } } - return send_content_msg(to, tag, file_path, row, col, kind, value); + return send_content_msg(to, tag, file_path, row, col, kind, value, range); } -fn send_content_msg(to: tp.pid_ref, tag: []const u8, file_path: []const u8, row: usize, col: usize, kind: []const u8, content: []const u8) !void { - return try to.send(.{ tag, file_path, row, col, kind, content }); +fn send_content_msg( + to: tp.pid_ref, + tag: []const u8, + file_path: []const u8, + row: usize, + col: usize, + kind: []const u8, + content: []const u8, + range: ?Range, +) !void { + const r = range orelse Range{ + .start = .{ .line = row, .character = col }, + .end = .{ .line = row, .character = col }, + }; + return try to.send(.{ tag, file_path, kind, content, r.start.line, r.start.character, r.end.line, r.end.character }); } fn send_content_msg_empty(to: tp.pid_ref, tag: []const u8, file_path: []const u8, row: usize, col: usize) !void { - return try to.send(.{ tag, file_path, row, col, "plaintext", "" }); + return send_content_msg(to, tag, file_path, row, col, "plaintext", "", null); } pub fn publish_diagnostics(self: *Self, to: tp.pid_ref, params_cb: []const u8) !void { diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index a439f24..dea93ec 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -123,8 +123,8 @@ pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { } else if (try m.match(.{ "FIF", "done" })) { self.find_in_files_done = true; return true; - } else if (try m.match(.{ "hover", tp.extract(&path), tp.extract(&begin_line), tp.extract(&begin_pos), tp.string, tp.extract(&lines) })) { - try self.add_info_content(lines); + } else if (try m.match(.{ "hover", tp.extract(&path), tp.string, tp.extract(&lines), tp.extract(&begin_line), tp.extract(&begin_pos), tp.extract(&end_line), tp.extract(&end_pos) })) { + try self.add_info_content(begin_line, begin_pos, end_line, end_pos, lines); return true; } else if (try m.match(.{"write_restore_info"})) { self.write_restore_info(); @@ -743,9 +743,28 @@ fn clear_find_in_files_results(self: *Self, file_list_type: FileListType) void { fl.reset(); } -fn add_info_content(self: *Self, content: []const u8) tp.result { +fn add_info_content( + self: *Self, + begin_line: usize, + begin_pos: usize, + end_line: usize, + end_pos: usize, + content: []const u8, +) tp.result { if (!self.is_panel_view_showing(info_view)) _ = self.toggle_panel_view(info_view, false) catch |e| return tp.exit_error(e, @errorReturnTrace()); const info = self.get_panel_view(info_view) orelse @panic("info_view missing"); info.set_content(content) catch |e| return tp.exit_error(e, @errorReturnTrace()); + + const match: ed.Match = .{ .begin = .{ .row = begin_line, .col = begin_pos }, .end = .{ .row = end_line, .col = end_pos } }; + if (self.editor) |editor| + switch (editor.matches.items.len) { + 0 => { + (editor.matches.addOne() catch return).* = match; + }, + 1 => { + editor.matches.items[0] = match; + }, + else => {}, + }; }