fix: use relative path when finding file

style: cargo fmt

_
This commit is contained in:
Nik Revenco 2025-03-18 02:45:28 +00:00
parent 4b6690fc0a
commit 0eadcd46ef
3 changed files with 76 additions and 17 deletions
helix-term/src/commands
helix-vcs/src

View file

@ -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>> =

View file

@ -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(),
})
}

View file

@ -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