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