feat: add git blame with inline display (wip)

This commit is contained in:
Miguel Granero 2026-01-23 21:36:14 +01:00 committed by CJ van den Berg
parent 3402a54a2e
commit 90a817066a
7 changed files with 223 additions and 2 deletions

View file

@ -1258,6 +1258,7 @@ pub const Editor = struct {
self.render_whitespace_map(theme, ctx_.cell_map) catch {};
if (tui.config().inline_diagnostics)
self.render_diagnostics(theme, hl_row, ctx_.cell_map) catch {};
self.render_blame(theme, ctx_.cell_map) catch {};
self.render_column_highlights() catch {};
self.render_cursors(theme, ctx_.cell_map, focused) catch {};
}
@ -1455,6 +1456,44 @@ pub const Editor = struct {
_ = self.plane.putc(&cell) catch {};
}
fn render_blame(self: *Self, theme: *const Widget.Theme, cell_map: CellMap) !void {
const cursor = self.get_primary().cursor;
const row_min = self.view.row;
const row_max = row_min + self.view.rows;
if (cursor.row < row_min or row_max < cursor.row)
return;
const row = cursor.row - self.view.row;
const col = cursor.col;
const screen_width = self.view.cols;
var style = theme.editor_hint;
style = .{ .fg = style.fg, .bg = theme.editor_hint.bg };
// @TODO: Get diff rows and edit this line number
const blame_name = self.buffer.?.get_line_blame(row) orelse return;
self.plane.cursor_move_yx(@intCast(row), @intCast(col));
self.render_diagnostic_cell(style);
var space_begin = screen_width;
while (space_begin > 0) : (space_begin -= 1)
if (cell_map.get_yx(row, space_begin).cell_type != .empty) break;
if (screen_width > min_diagnostic_view_len and space_begin < screen_width - min_diagnostic_view_len) {
self.plane.set_style(style);
// Opposite as diagnostics
switch (tui.config().inline_diagnostics_alignment) {
.right => {
const width = self.plane.window.width;
self.plane.cursor_move_yx(@intCast(row), @intCast(width -| (screen_width - space_begin) + 2));
_ = self.plane.print("  {s}", .{blame_name}) catch 0;
},
.left => _ = self.plane.print_aligned_right(@intCast(row), "  {s}", .{blame_name[0..@min(screen_width - space_begin - 3, blame_name.len)]}) catch {},
}
}
}
inline fn render_selection_cell(_: *const Self, theme: *const Widget.Theme, cell: *Cell) void {
cell.set_style_bg_opaque(theme.editor);
cell.set_style_bg(theme.editor_selection);

View file

@ -2129,9 +2129,11 @@ pub fn vcs_id_update(self: *Self, m: tp.message) void {
if (m.match(.{ "PRJ", "vcs_id", tp.extract(&file_path), tp.extract(&vcs_id) }) catch return) {
const buffer = self.buffer_manager.get_buffer_for_file(file_path) orelse return;
const need_vcs_content = buffer.set_vcs_id(vcs_id) catch false;
if (need_vcs_content)
const vcs_id_updated = buffer.set_vcs_id(vcs_id) catch false;
if (vcs_id_updated) {
project_manager.request_vcs_content(file_path, vcs_id) catch {};
project_manager.request_vcs_blame(file_path) catch {};
}
}
}
@ -2150,6 +2152,20 @@ pub fn vcs_content_update(self: *Self, m: tp.message) void {
}
}
pub fn vcs_blame_update(self: *Self, m: tp.message) void {
var file_path: []const u8 = undefined;
var blame_info: []const u8 = undefined;
if (m.match(.{ "PRJ", "git_blame", tp.extract(&file_path), tp.extract(&blame_info) }) catch return) {
const buffer = self.buffer_manager.get_buffer_for_file(file_path) orelse return;
buffer.set_vcs_blame(blame_info) catch {};
} else if (m.match(.{ "PRJ", "git_blame", tp.extract(&file_path), tp.null_ }) catch return) {
const buffer = self.buffer_manager.get_buffer_for_file(file_path) orelse return;
buffer.parse_git_blame() catch return;
if (self.get_editor_for_buffer(buffer)) |editor|
editor.vcs_content_update() catch {};
}
}
pub fn trigger_characters_update(self: *Self, m: tp.message) void {
self.lsp_info.add_from_event(m.buf) catch return;
self.foreach_editor(ed.Editor.update_completion_triggers);

View file

@ -538,6 +538,10 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void {
if (try m.match(.{ "PRJ", "vcs_content", tp.more }))
return if (mainview()) |mv| mv.vcs_content_update(m);
if (try m.match(.{ "PRJ", "git_blame", tp.more })) {
return if (mainview()) |mv| mv.vcs_blame_update(m);
}
if (try m.match(.{ "PRJ", "triggerCharacters", tp.more }))
return if (mainview()) |mv| mv.trigger_characters_update(m);