git_ui: Improve connection between the graph and commit views (#50027)
- Enabled opening the Git Graph, with the corresponding commit detail drawer open, from the commit view - Redesigned the commit view's header and toolbar to allow addition of the Git Graph icon button - Redesigned icons for the Git Graph and commit view https://github.com/user-attachments/assets/8efef60a-0893-4752-9b40-838da21ceb54 --- Before you mark this PR as ready for review, make sure that you have: - [x] Added a solid test coverage and/or screenshots from doing manual testing - [x] Done a self-review taking into account security and performance aspects - [x] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Release Notes: - N/A (_Git Graph is still feature flagged, so no release notes for now_)
This commit is contained in:
1 parent
6b7c97eb8d
commit
f8ef6c8c20
9 files changed
+290
-160
No files matched your search
Generated
+1
@@ -7217,6 +7217,7 @@ dependencies = [
|
||||
"ctor",
|
||||
"db",
|
||||
"editor",
|
||||
"feature_flags",
|
||||
"futures 0.3.31",
|
||||
"fuzzy",
|
||||
"git",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 2V6" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8 10C9.10457 10 10 9.10457 10 8C10 6.89543 9.10457 6 8 6C6.89543 6 6 6.89543 6 8C6 9.10457 6.89543 10 8 10Z" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8 10V14" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 513 B |
@@ -1,4 +1,7 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 3V11.8889C3 12.1836 3.11706 12.4662 3.32544 12.6746C3.53381 12.8829 3.81643 13 4.11111 13H13" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.8889 6.33333L9.11112 9.11111L6.8889 6.88888L5.22223 8.55555" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.19617 6.09808L4.19617 13.7058" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.8159 11.8038L11.8159 13.7058" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.816 11.8038C12.8664 11.8038 13.7179 10.9523 13.7179 9.90192C13.7179 8.85152 12.8664 8 11.816 8C10.7656 8 9.91403 8.85152 9.91403 9.90192C9.91403 10.9523 10.7656 11.8038 11.816 11.8038Z" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.19617 6.09808C5.24657 6.09808 6.09809 5.24656 6.09809 4.19616C6.09809 3.14575 5.24657 2.29424 4.19617 2.29424C3.14577 2.29424 2.29425 3.14575 2.29425 4.19616C2.29425 5.24656 3.14577 6.09808 4.19617 6.09808Z" stroke="#C6CAD0" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.0842 9.90192H7.30429C5.58772 9.90192 4.19617 8.51036 4.19617 6.79379V5.58465" stroke="#C6CAD0" stroke-width="1.2"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 453 B After Width: | Height: | Size: 1.1 KiB |
@@ -57,6 +57,12 @@ impl FeatureFlag for DiffReviewFeatureFlag {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GitGraphFeatureFlag;
|
||||
|
||||
impl FeatureFlag for GitGraphFeatureFlag {
|
||||
const NAME: &'static str = "git-graph";
|
||||
}
|
||||
|
||||
pub struct StreamingEditFileToolFeatureFlag;
|
||||
|
||||
impl FeatureFlag for StreamingEditFileToolFeatureFlag {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use collections::{BTreeMap, HashMap};
|
||||
use feature_flags::{FeatureFlag, FeatureFlagAppExt as _};
|
||||
use feature_flags::{FeatureFlagAppExt as _, GitGraphFeatureFlag};
|
||||
use git::{
|
||||
BuildCommitPermalinkParams, GitHostingProviderRegistry, GitRemote, Oid, ParsedGitRemote,
|
||||
parse_git_remote_url,
|
||||
@@ -39,7 +39,7 @@ use ui::{
|
||||
};
|
||||
use workspace::{
|
||||
Workspace,
|
||||
item::{Item, ItemEvent, SerializableItem},
|
||||
item::{Item, ItemEvent, SerializableItem, TabTooltipContent},
|
||||
};
|
||||
|
||||
const COMMIT_CIRCLE_RADIUS: Pixels = px(3.5);
|
||||
@@ -48,6 +48,7 @@ const LANE_WIDTH: Pixels = px(16.0);
|
||||
const LEFT_PADDING: Pixels = px(12.0);
|
||||
const LINE_WIDTH: Pixels = px(1.5);
|
||||
const RESIZE_HANDLE_WIDTH: f32 = 8.0;
|
||||
const PENDING_SELECT_MAX_RETRIES: usize = 5;
|
||||
const COPIED_STATE_DURATION: Duration = Duration::from_secs(2);
|
||||
|
||||
struct CopiedState {
|
||||
@@ -246,12 +247,6 @@ actions!(
|
||||
]
|
||||
);
|
||||
|
||||
pub struct GitGraphFeatureFlag;
|
||||
|
||||
impl FeatureFlag for GitGraphFeatureFlag {
|
||||
const NAME: &'static str = "git-graph";
|
||||
}
|
||||
|
||||
fn timestamp_format() -> &'static [BorrowedFormatItem<'static>] {
|
||||
static FORMAT: OnceLock<Vec<BorrowedFormatItem<'static>>> = OnceLock::new();
|
||||
FORMAT.get_or_init(|| {
|
||||
@@ -710,29 +705,66 @@ pub fn init(cx: &mut App) {
|
||||
|div| {
|
||||
let workspace = workspace.weak_handle();
|
||||
|
||||
div.on_action(move |_: &git_ui::git_panel::Open, window, cx| {
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
let existing = workspace.items_of_type::<GitGraph>(cx).next();
|
||||
if let Some(existing) = existing {
|
||||
workspace.activate_item(&existing, true, true, window, cx);
|
||||
return;
|
||||
}
|
||||
div.on_action({
|
||||
let workspace = workspace.clone();
|
||||
move |_: &git_ui::git_panel::Open, window, cx| {
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
let existing = workspace.items_of_type::<GitGraph>(cx).next();
|
||||
if let Some(existing) = existing {
|
||||
workspace.activate_item(&existing, true, true, window, cx);
|
||||
return;
|
||||
}
|
||||
|
||||
let project = workspace.project().clone();
|
||||
let workspace_handle = workspace.weak_handle();
|
||||
let git_graph = cx
|
||||
.new(|cx| GitGraph::new(project, workspace_handle, window, cx));
|
||||
workspace.add_item_to_active_pane(
|
||||
Box::new(git_graph),
|
||||
None,
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
let project = workspace.project().clone();
|
||||
let workspace_handle = workspace.weak_handle();
|
||||
let git_graph = cx.new(|cx| {
|
||||
GitGraph::new(project, workspace_handle, window, cx)
|
||||
});
|
||||
workspace.add_item_to_active_pane(
|
||||
Box::new(git_graph),
|
||||
None,
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.on_action(
|
||||
move |action: &git_ui::git_panel::OpenAtCommit, window, cx| {
|
||||
let sha = action.sha.clone();
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
let existing = workspace.items_of_type::<GitGraph>(cx).next();
|
||||
if let Some(existing) = existing {
|
||||
existing.update(cx, |graph, cx| {
|
||||
graph.select_commit_by_sha(&sha, cx);
|
||||
});
|
||||
workspace.activate_item(&existing, true, true, window, cx);
|
||||
return;
|
||||
}
|
||||
|
||||
let project = workspace.project().clone();
|
||||
let workspace_handle = workspace.weak_handle();
|
||||
let git_graph = cx.new(|cx| {
|
||||
let mut graph =
|
||||
GitGraph::new(project, workspace_handle, window, cx);
|
||||
graph.select_commit_by_sha(&sha, cx);
|
||||
graph
|
||||
});
|
||||
workspace.add_item_to_active_pane(
|
||||
Box::new(git_graph),
|
||||
None,
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
});
|
||||
@@ -821,6 +853,7 @@ pub struct GitGraph {
|
||||
commit_details_split_state: Entity<SplitState>,
|
||||
selected_repo_id: Option<RepositoryId>,
|
||||
changed_files_scroll_handle: UniformListScrollHandle,
|
||||
pending_select_sha: Option<(String, usize)>,
|
||||
}
|
||||
|
||||
impl GitGraph {
|
||||
@@ -918,6 +951,7 @@ impl GitGraph {
|
||||
commit_details_split_state: cx.new(|_cx| SplitState::new()),
|
||||
selected_repo_id: active_repository,
|
||||
changed_files_scroll_handle: UniformListScrollHandle::new(),
|
||||
pending_select_sha: None,
|
||||
};
|
||||
|
||||
this.fetch_initial_graph_data(cx);
|
||||
@@ -944,8 +978,10 @@ impl GitGraph {
|
||||
self.graph_data.add_commits(commits);
|
||||
});
|
||||
cx.notify();
|
||||
self.retry_pending_select(cx);
|
||||
}
|
||||
RepositoryEvent::BranchChanged | RepositoryEvent::MergeHeadsChanged => {
|
||||
self.pending_select_sha = None;
|
||||
// Only invalidate if we scanned atleast once,
|
||||
// meaning we are not inside the initial repo loading state
|
||||
// NOTE: this fixes an loading performance regression
|
||||
@@ -1153,6 +1189,37 @@ impl GitGraph {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn select_commit_by_sha(&mut self, sha: &str, cx: &mut Context<Self>) {
|
||||
let Ok(oid) = sha.parse::<Oid>() else {
|
||||
return;
|
||||
};
|
||||
for (idx, commit) in self.graph_data.commits.iter().enumerate() {
|
||||
if commit.data.sha == oid {
|
||||
self.pending_select_sha = None;
|
||||
self.select_entry(idx, cx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.pending_select_sha = Some((sha.to_string(), PENDING_SELECT_MAX_RETRIES));
|
||||
}
|
||||
|
||||
fn retry_pending_select(&mut self, cx: &mut Context<Self>) {
|
||||
let Some((sha, retries_remaining)) = self.pending_select_sha.take() else {
|
||||
return;
|
||||
};
|
||||
if let Ok(oid) = sha.parse::<Oid>() {
|
||||
for (idx, commit) in self.graph_data.commits.iter().enumerate() {
|
||||
if commit.data.sha == oid {
|
||||
self.select_entry(idx, cx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if retries_remaining > 0 {
|
||||
self.pending_select_sha = Some((sha, retries_remaining - 1));
|
||||
}
|
||||
}
|
||||
|
||||
fn open_selected_commit_view(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(selected_entry_index) = self.selected_entry_idx else {
|
||||
return;
|
||||
@@ -2179,6 +2246,30 @@ impl Focusable for GitGraph {
|
||||
impl Item for GitGraph {
|
||||
type Event = ItemEvent;
|
||||
|
||||
fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
|
||||
Some(Icon::new(IconName::GitGraph))
|
||||
}
|
||||
|
||||
fn tab_tooltip_content(&self, cx: &App) -> Option<TabTooltipContent> {
|
||||
let repo_name = self.get_selected_repository(cx).and_then(|repo| {
|
||||
repo.read(cx)
|
||||
.work_directory_abs_path
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().to_string())
|
||||
});
|
||||
|
||||
Some(TabTooltipContent::Custom(Box::new(Tooltip::element({
|
||||
move |_, _| {
|
||||
v_flex()
|
||||
.child(Label::new("Git Graph"))
|
||||
.when_some(repo_name.clone(), |this, name| {
|
||||
this.child(Label::new(name).color(Color::Muted).size(LabelSize::Small))
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
}))))
|
||||
}
|
||||
|
||||
fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString {
|
||||
"Git Graph".into()
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ component.workspace = true
|
||||
db.workspace = true
|
||||
editor.workspace = true
|
||||
futures.workspace = true
|
||||
feature_flags.workspace = true
|
||||
fuzzy.workspace = true
|
||||
git.workspace = true
|
||||
gpui.workspace = true
|
||||
|
||||
+144
-129
@@ -3,6 +3,7 @@ use buffer_diff::BufferDiff;
|
||||
use collections::HashMap;
|
||||
use editor::display_map::{BlockPlacement, BlockProperties, BlockStyle};
|
||||
use editor::{Addon, Editor, EditorEvent, ExcerptRange, MultiBuffer, multibuffer_context_lines};
|
||||
use feature_flags::{FeatureFlagAppExt as _, GitGraphFeatureFlag};
|
||||
use git::repository::{CommitDetails, CommitDiff, RepoPath, is_binary_content};
|
||||
use git::status::{FileStatus, StatusCode, TrackedStatus};
|
||||
use git::{
|
||||
@@ -27,7 +28,7 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{ButtonLike, DiffStat, Tooltip, prelude::*};
|
||||
use ui::{DiffStat, Divider, Tooltip, prelude::*};
|
||||
use util::{ResultExt, paths::PathStyle, rel_path::RelPath, truncate_and_trailoff};
|
||||
use workspace::item::TabTooltipContent;
|
||||
use workspace::{
|
||||
@@ -450,6 +451,7 @@ impl CommitView {
|
||||
fn render_header(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let commit = &self.commit;
|
||||
let author_name = commit.author_name.clone();
|
||||
let author_email = commit.author_email.clone();
|
||||
let commit_sha = commit.sha.clone();
|
||||
let commit_date = time::OffsetDateTime::from_unix_timestamp(commit.commit_timestamp)
|
||||
.unwrap_or_else(|_| time::OffsetDateTime::now_utc());
|
||||
@@ -461,36 +463,6 @@ impl CommitView {
|
||||
time_format::TimestampFormat::MediumAbsolute,
|
||||
);
|
||||
|
||||
let remote_info = self
|
||||
.remote
|
||||
.as_ref()
|
||||
.filter(|_| self.stash.is_none())
|
||||
.map(|remote| {
|
||||
let provider = remote.host.name();
|
||||
let parsed_remote = ParsedGitRemote {
|
||||
owner: remote.owner.as_ref().into(),
|
||||
repo: remote.repo.as_ref().into(),
|
||||
};
|
||||
let params = BuildCommitPermalinkParams { sha: &commit.sha };
|
||||
let url = remote
|
||||
.host
|
||||
.build_commit_permalink(&parsed_remote, params)
|
||||
.to_string();
|
||||
(provider, url)
|
||||
});
|
||||
|
||||
let (additions, deletions) = self.calculate_changed_lines(cx);
|
||||
|
||||
let commit_diff_stat = if additions > 0 || deletions > 0 {
|
||||
Some(DiffStat::new(
|
||||
"commit-diff-stat",
|
||||
additions as usize,
|
||||
deletions as usize,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let gutter_width = self.editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(window, cx);
|
||||
let style = editor.style(cx);
|
||||
@@ -501,116 +473,75 @@ impl CommitView {
|
||||
.full_width()
|
||||
});
|
||||
|
||||
let clipboard_has_link = cx
|
||||
let clipboard_has_sha = cx
|
||||
.read_from_clipboard()
|
||||
.and_then(|entry| entry.text())
|
||||
.map_or(false, |clipboard_text| {
|
||||
clipboard_text.trim() == commit_sha.as_ref()
|
||||
});
|
||||
|
||||
let (copy_icon, copy_icon_color) = if clipboard_has_link {
|
||||
let (copy_icon, copy_icon_color) = if clipboard_has_sha {
|
||||
(IconName::Check, Color::Success)
|
||||
} else {
|
||||
(IconName::Copy, Color::Muted)
|
||||
};
|
||||
|
||||
h_flex()
|
||||
.py_2()
|
||||
.pr_2p5()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.w_full()
|
||||
.child(
|
||||
h_flex()
|
||||
.w(gutter_width)
|
||||
.justify_center()
|
||||
.child(self.render_commit_avatar(&commit.sha, rems_from_px(48.), window, cx)),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.py_4()
|
||||
.pl_1()
|
||||
.pr_4()
|
||||
.w_full()
|
||||
.items_start()
|
||||
.justify_between()
|
||||
.flex_wrap()
|
||||
.child(h_flex().w(gutter_width).justify_center().child(
|
||||
self.render_commit_avatar(&commit.sha, rems_from_px(40.), window, cx),
|
||||
))
|
||||
.child(
|
||||
v_flex()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(Label::new(author_name).color(Color::Default))
|
||||
.child({
|
||||
ButtonLike::new("sha")
|
||||
.child(
|
||||
h_flex()
|
||||
.group("sha_btn")
|
||||
.size_full()
|
||||
.max_w_32()
|
||||
.gap_0p5()
|
||||
.child(
|
||||
Label::new(commit_sha.clone())
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small)
|
||||
.truncate()
|
||||
.buffer_font(cx),
|
||||
)
|
||||
.child(
|
||||
div().visible_on_hover("sha_btn").child(
|
||||
Icon::new(copy_icon)
|
||||
.color(copy_icon_color)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
),
|
||||
)
|
||||
.tooltip({
|
||||
let commit_sha = commit_sha.clone();
|
||||
move |_, cx| {
|
||||
Tooltip::with_meta(
|
||||
"Copy Commit SHA",
|
||||
None,
|
||||
commit_sha.clone(),
|
||||
cx,
|
||||
)
|
||||
}
|
||||
})
|
||||
.on_click(move |_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(
|
||||
commit_sha.to_string(),
|
||||
));
|
||||
})
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1p5()
|
||||
.child(
|
||||
Label::new(date_string)
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small),
|
||||
)
|
||||
.child(
|
||||
Label::new("•")
|
||||
.color(Color::Ignored)
|
||||
.size(LabelSize::Small),
|
||||
)
|
||||
.children(commit_diff_stat),
|
||||
),
|
||||
)
|
||||
.children(remote_info.map(|(provider_name, url)| {
|
||||
let icon = match provider_name.as_str() {
|
||||
"GitHub" => IconName::Github,
|
||||
_ => IconName::Link,
|
||||
};
|
||||
|
||||
Button::new("view_on_provider", format!("View on {}", provider_name))
|
||||
.icon(icon)
|
||||
.icon_color(Color::Muted)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_position(IconPosition::Start)
|
||||
.on_click(move |_, _, cx| cx.open_url(&url))
|
||||
})),
|
||||
v_flex().child(Label::new(author_name)).child(
|
||||
h_flex()
|
||||
.gap_1p5()
|
||||
.child(
|
||||
Label::new(date_string)
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small),
|
||||
)
|
||||
.child(
|
||||
Label::new("•")
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted)
|
||||
.alpha(0.5),
|
||||
)
|
||||
.child(
|
||||
Label::new(author_email)
|
||||
.color(Color::Muted)
|
||||
.size(LabelSize::Small),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.when(self.stash.is_none(), |this| {
|
||||
this.child(
|
||||
Button::new("sha", "Commit SHA")
|
||||
.icon(copy_icon)
|
||||
.icon_color(copy_icon_color)
|
||||
.icon_position(IconPosition::Start)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip({
|
||||
let commit_sha = commit_sha.clone();
|
||||
move |_, cx| {
|
||||
Tooltip::with_meta("Copy Commit SHA", None, commit_sha.clone(), cx)
|
||||
}
|
||||
})
|
||||
.on_click(move |_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(
|
||||
commit_sha.to_string(),
|
||||
));
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn apply_stash(workspace: &mut Workspace, window: &mut Window, cx: &mut App) {
|
||||
@@ -898,7 +829,7 @@ impl Item for CommitView {
|
||||
type Event = EditorEvent;
|
||||
|
||||
fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
|
||||
Some(Icon::new(IconName::GitBranch).color(Color::Muted))
|
||||
Some(Icon::new(IconName::GitCommit).color(Color::Muted))
|
||||
}
|
||||
|
||||
fn tab_content(&self, params: TabContentParams, _window: &Window, cx: &App) -> AnyElement {
|
||||
@@ -1081,8 +1012,93 @@ impl CommitViewToolbar {
|
||||
impl EventEmitter<ToolbarItemEvent> for CommitViewToolbar {}
|
||||
|
||||
impl Render for CommitViewToolbar {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div().hidden()
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let Some(commit_view) = self.commit_view.as_ref().and_then(|w| w.upgrade()) else {
|
||||
return div();
|
||||
};
|
||||
|
||||
let commit_view_ref = commit_view.read(cx);
|
||||
let is_stash = commit_view_ref.stash.is_some();
|
||||
|
||||
let (additions, deletions) = commit_view_ref.calculate_changed_lines(cx);
|
||||
|
||||
let commit_sha = commit_view_ref.commit.sha.clone();
|
||||
|
||||
let remote_info = commit_view_ref.remote.as_ref().map(|remote| {
|
||||
let provider = remote.host.name();
|
||||
let parsed_remote = ParsedGitRemote {
|
||||
owner: remote.owner.as_ref().into(),
|
||||
repo: remote.repo.as_ref().into(),
|
||||
};
|
||||
let params = BuildCommitPermalinkParams { sha: &commit_sha };
|
||||
let url = remote
|
||||
.host
|
||||
.build_commit_permalink(&parsed_remote, params)
|
||||
.to_string();
|
||||
(provider, url)
|
||||
});
|
||||
|
||||
let sha_for_graph = commit_sha.to_string();
|
||||
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.when(additions > 0 || deletions > 0, |this| {
|
||||
this.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.child(DiffStat::new(
|
||||
"toolbar-diff-stat",
|
||||
additions as usize,
|
||||
deletions as usize,
|
||||
))
|
||||
.child(Divider::vertical()),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
IconButton::new("buffer-search", IconName::MagnifyingGlass)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(move |_, cx| {
|
||||
Tooltip::for_action(
|
||||
"Buffer Search",
|
||||
&zed_actions::buffer_search::Deploy::find(),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.on_click(|_, window, cx| {
|
||||
window.dispatch_action(
|
||||
Box::new(zed_actions::buffer_search::Deploy::find()),
|
||||
cx,
|
||||
);
|
||||
}),
|
||||
)
|
||||
.when(!is_stash, |this| {
|
||||
this.when(cx.has_flag::<GitGraphFeatureFlag>(), |this| {
|
||||
this.child(
|
||||
IconButton::new("show-in-git-graph", IconName::GitGraph)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text("Show in Git Graph"))
|
||||
.on_click(move |_, window, cx| {
|
||||
window.dispatch_action(
|
||||
Box::new(crate::git_panel::OpenAtCommit {
|
||||
sha: sha_for_graph.clone(),
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
}),
|
||||
)
|
||||
})
|
||||
.children(remote_info.map(|(provider_name, url)| {
|
||||
let icon = match provider_name.as_str() {
|
||||
"GitHub" => IconName::Github,
|
||||
_ => IconName::Link,
|
||||
};
|
||||
|
||||
IconButton::new("view_on_provider", icon)
|
||||
.icon_size(IconSize::Small)
|
||||
.tooltip(Tooltip::text(format!("View on {}", provider_name)))
|
||||
.on_click(move |_, _, cx| cx.open_url(&url))
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1093,12 +1109,11 @@ impl ToolbarItemView for CommitViewToolbar {
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> ToolbarItemLocation {
|
||||
if let Some(entity) = active_pane_item.and_then(|i| i.act_as::<CommitView>(cx))
|
||||
&& entity.read(cx).stash.is_some()
|
||||
{
|
||||
if let Some(entity) = active_pane_item.and_then(|i| i.act_as::<CommitView>(cx)) {
|
||||
self.commit_view = Some(entity.downgrade());
|
||||
return ToolbarItemLocation::PrimaryRight;
|
||||
}
|
||||
self.commit_view = None;
|
||||
ToolbarItemLocation::Hidden
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,13 @@ actions!(
|
||||
]
|
||||
);
|
||||
|
||||
/// Opens the Git Graph Tab at a specific commit.
|
||||
#[derive(Clone, PartialEq, serde::Deserialize, schemars::JsonSchema, gpui::Action)]
|
||||
#[action(namespace = git_graph)]
|
||||
pub struct OpenAtCommit {
|
||||
pub sha: String,
|
||||
}
|
||||
|
||||
fn prompt<T>(
|
||||
msg: &str,
|
||||
detail: Option<&str>,
|
||||
|
||||
@@ -142,6 +142,7 @@ pub enum IconName {
|
||||
GitBranch,
|
||||
GitBranchAlt,
|
||||
GitBranchPlus,
|
||||
GitCommit,
|
||||
GitGraph,
|
||||
Github,
|
||||
Hash,
|
||||
|
||||
Reference in new issue
Block a user