From 0eadcd46ef95697b55381b08e0942f1936e95034 Mon Sep 17 00:00:00 2001
From: Nik Revenco <154856872+NikitaRevenco@users.noreply.github.com>
Date: Tue, 18 Mar 2025 02:45:28 +0000
Subject: [PATCH] fix: use relative path when finding file

style: cargo fmt

_
---
 helix-term/src/commands/typed.rs | 37 +++++++++++++++++++++++++++
 helix-vcs/src/git.rs             | 44 ++++++++++++++++++++++----------
 helix-vcs/src/lib.rs             | 12 ++++++---
 3 files changed, 76 insertions(+), 17 deletions(-)

diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs
index 9661689c..d41dbbd5 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -70,6 +70,32 @@ impl CommandCompleter {
     }
 }
 
+fn blame(cx: &mut compositor::Context, _args: Args, event: PromptEvent) -> anyhow::Result<()> {
+    if event != PromptEvent::Validate {
+        return Ok(());
+    }
+
+    let (view, doc) = current_ref!(cx.editor);
+    let selection = doc.selection(view.id);
+    let (from, to) = selection
+        .line_ranges(doc.text().slice(..))
+        .next()
+        .map(|(from, to)| (from as u32, to as u32))
+        .context("No selections")?;
+    let result = cx
+        .editor
+        .diff_providers
+        .blame(doc.path().context("Not in a file")?, from..to)
+        .inspect_err(|err| {
+            log::error!("Could not get blame: {err}");
+        })
+        .context("No blame information")?;
+
+    cx.editor.set_status(result.to_string());
+
+    Ok(())
+}
+
 fn quit(cx: &mut compositor::Context, _args: Args, event: PromptEvent) -> anyhow::Result<()> {
     log::debug!("quitting...");
 
@@ -3555,6 +3581,17 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
             ..Signature::DEFAULT
         },
     },
+    TypableCommand {
+        name: "blame-line-range",
+        aliases: &["blame"],
+        doc: "Blames a range of lines. No args: Blame selection. 1 arg: Blame line. 2 args: Represents (from, to) line range to git blame.",
+        fun: blame,
+        completer: CommandCompleter::none(),
+        signature: Signature {
+            positionals: (0, Some(2)),
+            ..Signature::DEFAULT
+        },
+    },
 ];
 
 pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =
diff --git a/helix-vcs/src/git.rs b/helix-vcs/src/git.rs
index c395bd1d..80021576 100644
--- a/helix-vcs/src/git.rs
+++ b/helix-vcs/src/git.rs
@@ -1,8 +1,8 @@
 use anyhow::{bail, Context, Result};
 use arc_swap::ArcSwap;
+use core::fmt;
 use gix::filter::plumbing::driver::apply::Delay;
 use std::io::Read;
-use std::os::unix::ffi::OsStrExt;
 use std::path::Path;
 use std::sync::Arc;
 
@@ -133,6 +133,16 @@ pub struct BlameInformation {
     pub commit_message: String,
 }
 
+impl fmt::Display for BlameInformation {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "{} - {} - {} - {}",
+            self.author_name, self.commit_date, self.commit_message, self.commit_hash
+        )
+    }
+}
+
 /// Emulates the result of running `git blame` from the command line.
 pub fn blame(file: &Path, range: std::ops::Range<u32>) -> Result<BlameInformation> {
     let repo_dir = get_repo_dir(file)?;
@@ -141,18 +151,30 @@ pub fn blame(file: &Path, range: std::ops::Range<u32>) -> Result<BlameInformatio
         .to_thread_local();
 
     let suspect = repo.head()?.peel_to_commit_in_place()?;
-    let traverse = gix::traverse::commit::topo::Builder::from_iters(
+
+    let relative_path = file
+        .strip_prefix(
+            repo.path()
+                .parent()
+                .context("Could not get parent path of repository")?,
+        )
+        .unwrap_or(file)
+        .to_str()
+        .context("Could not convert path to string")?;
+
+    let traverse_all_commits = gix::traverse::commit::topo::Builder::from_iters(
         &repo.objects,
         [suspect.id],
         None::<Vec<gix::ObjectId>>,
     )
     .build()?;
+
     let mut resource_cache = repo.diff_resource_cache_for_tree_diff()?;
     let latest_commit_id = gix::blame::file(
         &repo.objects,
-        traverse,
+        traverse_all_commits,
         &mut resource_cache,
-        BStr::new(file.as_os_str().as_bytes()),
+        BStr::new(relative_path),
         Some(range),
     )?
     .entries
@@ -161,19 +183,13 @@ pub fn blame(file: &Path, range: std::ops::Range<u32>) -> Result<BlameInformatio
     .commit_id;
 
     let commit = repo.find_commit(latest_commit_id)?;
-
     let author = commit.author()?;
 
-    let commit_date = author.time.format(gix::date::time::format::SHORT);
-    let author_name = author.name.to_string();
-    let commit_hash = commit.short_id()?.to_string();
-    let commit_message = commit.message()?.title.to_string();
-
     Ok(BlameInformation {
-        commit_hash,
-        author_name,
-        commit_date,
-        commit_message,
+        commit_hash: commit.short_id()?.to_string(),
+        author_name: author.name.to_string(),
+        commit_date: author.time.format(gix::date::time::format::SHORT),
+        commit_message: commit.message()?.title.to_string(),
     })
 }
 
diff --git a/helix-vcs/src/lib.rs b/helix-vcs/src/lib.rs
index a4782718..9b73a42e 100644
--- a/helix-vcs/src/lib.rs
+++ b/helix-vcs/src/lib.rs
@@ -1,4 +1,4 @@
-use anyhow::{anyhow, bail, Result};
+use anyhow::{anyhow, bail, Context, Result};
 use arc_swap::ArcSwap;
 use git::BlameInformation;
 use std::{
@@ -49,10 +49,16 @@ impl DiffProviderRegistry {
             })
     }
 
-    pub fn blame(&self, file: &Path, range: std::ops::Range<u32>) -> Option<BlameInformation> {
+    pub fn blame(
+        &self,
+        file: &Path,
+        range: std::ops::Range<u32>,
+    ) -> anyhow::Result<BlameInformation> {
         self.providers
             .iter()
-            .find_map(|provider| provider.blame(file, range.clone()).ok())
+            .map(|provider| provider.blame(file, range.clone()))
+            .next()
+            .context("neno")?
     }
 
     /// Fire-and-forget changed file iteration. Runs everything in a background task. Keeps