Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions crates/editor/src/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,16 @@ impl EditorElement {
register_action(editor, window, Editor::toggle_git_blame_inline);
if editor.read(cx).blame().is_some() {
register_action(editor, window, Editor::open_git_blame_commit);
register_action(editor, window, Editor::blame_revision);
register_action(editor, window, Editor::blame_previous_revision);
if editor.update(cx, |editor, cx| {
editor.blame_revision_target(window, cx).is_some()
}) {
register_action(editor, window, Editor::blame_revision);
}
if editor.update(cx, |editor, cx| {
editor.blame_previous_revision_target(window, cx).is_some()
}) {
register_action(editor, window, Editor::blame_previous_revision);
}
}
register_action(editor, window, Editor::toggle_selected_diff_hunks);
register_action(editor, window, Editor::toggle_staged_selected_diff_hunks);
Expand Down
70 changes: 28 additions & 42 deletions crates/editor/src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2310,32 +2310,40 @@ impl Editor {
Some((blame_entry, repository))
}

pub(crate) fn blame_revision_target(
&mut self,
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<(RepoPath, Oid, Entity<Repository>)> {
let (blame_entry, repository) = self.blame_entry_at_cursor(window, cx)?;
let highlighted_sha = self
.blame
.as_ref()
.and_then(|blame| blame.read(cx).highlighted_sha());
let (revision, path) = blame_entry.revision_target(highlighted_sha)?;
Some((path, revision, repository))
}

pub(crate) fn blame_previous_revision_target(
&mut self,
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<(RepoPath, Oid, Entity<Repository>)> {
let (blame_entry, repository) = self.blame_entry_at_cursor(window, cx)?;
let (revision, path) = blame_entry.previous_revision_target()?;
Some((path, revision, repository))
}

pub(super) fn blame_revision(
&mut self,
_: &BlameRevision,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some((blame_entry, repository)) = self.blame_entry_at_cursor(window, cx) else {
self.show_blame_revision_toast("No blame entry for this line", cx);
let Some((path, revision, repository)) = self.blame_revision_target(window, cx) else {
return;
};
if blame_entry.sha.is_zero() {
self.show_blame_revision_toast("Cannot blame revision: the line is not committed", cx);
return;
}
if self
.blame
.as_ref()
.is_some_and(|blame| blame.read(cx).highlighted_sha() == Some(blame_entry.sha))
{
self.show_blame_revision_toast("Already blaming at this revision", cx);
return;
}
let Some(path) = RepoPath::new(&blame_entry.filename).log_err() else {
return;
};
self.open_blame_revision(path, blame_entry.sha, repository, window, cx);
self.open_blame_revision(path, revision, repository, window, cx);
}

pub(super) fn blame_previous_revision(
Expand All @@ -2344,15 +2352,8 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some((blame_entry, repository)) = self.blame_entry_at_cursor(window, cx) else {
self.show_blame_revision_toast("No blame entry for this line", cx);
return;
};
let Some((revision, filename)) = blame_entry.previous_sha_and_filename() else {
self.show_blame_revision_toast("No previous revision for this line", cx);
return;
};
let Some(path) = RepoPath::new(filename).log_err() else {
let Some((path, revision, repository)) = self.blame_previous_revision_target(window, cx)
else {
return;
};
self.open_blame_revision(path, revision, repository, window, cx);
Expand Down Expand Up @@ -2380,21 +2381,6 @@ impl Editor {
);
}

fn show_blame_revision_toast(&self, message: &str, cx: &mut Context<Self>) {
struct BlameRevisionToast;
if let Some(workspace) = self.workspace() {
workspace.update(cx, |workspace, cx| {
workspace.show_toast(
Toast::new(
NotificationId::unique::<BlameRevisionToast>(),
message.to_owned(),
),
cx,
);
});
}
}

fn has_blame_entries(&self, cx: &App) -> bool {
self.blame()
.is_some_and(|blame| blame.read(cx).has_generated_entries())
Expand Down
12 changes: 12 additions & 0 deletions crates/git/src/blame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,18 @@ impl BlameEntry {
Some((sha.parse().ok()?, filename))
}

pub fn revision_target(&self, highlighted_sha: Option<Oid>) -> Option<(Oid, RepoPath)> {
if self.sha.is_zero() || Some(self.sha) == highlighted_sha {
return None;
}
Some((self.sha, RepoPath::new(&self.filename).ok()?))
}

pub fn previous_revision_target(&self) -> Option<(Oid, RepoPath)> {
let (sha, filename) = self.previous_sha_and_filename()?;
Some((sha, RepoPath::new(filename).ok()?))
}

pub fn author_offset_date_time(&self) -> Result<time::OffsetDateTime> {
if let (Some(author_time), Some(author_tz)) = (self.author_time, &self.author_tz) {
let format = format_description!("[offset_hour][offset_minute]");
Expand Down
9 changes: 2 additions & 7 deletions crates/git_ui/src/blame_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,13 +555,8 @@ fn deploy_blame_entry_context_menu(
.and_then(|blame| blame.read(cx).highlighted_sha());
let context_menu = ContextMenu::build(window, cx, move |menu, _, _| {
let sha = format!("{}", blame_entry.sha);
let blame_revision = (!blame_entry.sha.is_zero()
&& Some(blame_entry.sha) != highlighted_sha)
.then_some(blame_entry.sha)
.zip(RepoPath::new(&blame_entry.filename).ok());
let blame_previous_revision = blame_entry
.previous_sha_and_filename()
.and_then(|(sha, filename)| Some((sha, RepoPath::new(filename).ok()?)));
let blame_revision = blame_entry.revision_target(highlighted_sha);
let blame_previous_revision = blame_entry.previous_revision_target();
let has_blame_targets = blame_revision.is_some() || blame_previous_revision.is_some();
menu.on_blur_subscription(Subscription::new(|| {}))
.entry("Copy Commit SHA", None, move |_, cx| {
Expand Down
Loading