From 9278a681864bef1b9f32309f0a49c454de36b49a Mon Sep 17 00:00:00 2001 From: Nik Revenco <154856872+NikitaRevenco@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:08:58 +0000 Subject: [PATCH] fix: account for inserted and deleted lines _ --- helix-term/src/handlers/blame.rs | 48 ++++++++++++++++++++++++++++++-- helix-view/src/handlers.rs | 4 +++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/helix-term/src/handlers/blame.rs b/helix-term/src/handlers/blame.rs index c4f80d0b..640e3a72 100644 --- a/helix-term/src/handlers/blame.rs +++ b/helix-term/src/handlers/blame.rs @@ -9,6 +9,7 @@ use crate::{events::PostCommand, job}; #[derive(Default)] pub struct BlameHandler { worker: Option>>, + cursor_line: u32, } impl helix_event::AsyncHook for BlameHandler { @@ -31,11 +32,32 @@ impl helix_event::AsyncHook for BlameHandler { file, cursor_line, diff_providers, + removed_lines_count, + added_lines_count, } = event; + self.cursor_line = cursor_line; + + // convert 0-based line numbers into 1-based line numbers + let cursor_line = cursor_line + 1; + + // the line for which we compute the blame + // Because gix_blame doesn't care about stuff that is not commited, we have to "normalize" the + // line number to account for uncommited code. + // + // You'll notice that blame_line can be 0 when, for instance we have: + // - removed 0 lines + // - added 10 lines + // - cursor_line is 8 + // + // So when our cursor is on the 10th added line or earlier, blame_line will be 0. This means + // the blame will be incorrect. But that's fine, because when the cursor_line is on some hunk, + // we can show to the user nothing at all + let blame_line = cursor_line.saturating_sub(added_lines_count) + removed_lines_count; + let worker = tokio::spawn(async move { diff_providers - .blame_line(&file, cursor_line) + .blame_line(&file, blame_line) .map(|s| s.to_string()) }); self.worker = Some(worker); @@ -43,6 +65,7 @@ impl helix_event::AsyncHook for BlameHandler { } fn finish_debounce(&mut self) { + let cursor_line = self.cursor_line; if let Some(worker) = &self.worker { if worker.is_finished() { let worker = self.worker.take().unwrap(); @@ -52,7 +75,14 @@ impl helix_event::AsyncHook for BlameHandler { }; job::dispatch(move |editor, _| { let doc = doc_mut!(editor); - doc.blame = Some(outcome); + // if we're on a line that hasn't been commited yet, just show nothing at all + // in order to reduce visual noise. + // Because the git hunks already imply this information + let blame_text = doc + .diff_handle() + .is_some_and(|diff| diff.load().hunk_at(cursor_line, false).is_none()) + .then_some(outcome); + doc.blame = blame_text; }) .await; }); @@ -82,11 +112,25 @@ pub(super) fn register_hooks(handlers: &Handlers) { return Ok(()); }; + let hunks = doc.diff_handle().unwrap().load(); + + let mut removed_lines_count: u32 = 0; + let mut added_lines_count: u32 = 0; + for hunk in hunks.hunks_intersecting_line_ranges(std::iter::once((0, cursor_line as usize))) + { + let lines_inserted = hunk.after.end - hunk.after.start; + let lines_removed = hunk.before.end - hunk.before.start; + added_lines_count += lines_inserted; + removed_lines_count += lines_removed; + } + send_blocking( &tx, BlameEvent::PostCommand { file, cursor_line, + removed_lines_count, + added_lines_count, // ok to clone because diff_providers is very small diff_providers: event.cx.editor.diff_providers.clone(), }, diff --git a/helix-view/src/handlers.rs b/helix-view/src/handlers.rs index 6090bf84..d596d94a 100644 --- a/helix-view/src/handlers.rs +++ b/helix-view/src/handlers.rs @@ -24,6 +24,10 @@ pub enum BlameEvent { PostCommand { file: PathBuf, cursor_line: u32, + /// How many lines were removed before cursor_line + removed_lines_count: u32, + /// How many lines were added before cursor_line + added_lines_count: u32, diff_providers: DiffProviderRegistry, }, }