Surface the git graph context menu in the git panel's history tab (#60713)
This PR refactors the Git Graph’s commit context menu into its own file so it can be reused by both the Git Graph and the Git panel’s History tab. <img width="511" height="566" alt="SCR-20260709-qlxt" src="https://github.com/user-attachments/assets/8f18f22b-21d7-46a0-8ce2-183cf4af86c9" /> Also, includes a "Show in Git Graph" option only shown in the history tab's context menu: https://github.com/user-attachments/assets/2e30648b-2802-4caa-a99e-2d135e2dc539 Release Notes: - Surfaced the git graph context menu in the git panel's history tab.
This commit is contained in:
1 parent
27095fe544
commit
0188a3a952
4 files changed
+353
-237
No files matched your search
@@ -0,0 +1,243 @@
|
||||
use crate::commit_view::CommitView;
|
||||
use git::Oid;
|
||||
use gpui::{Action, ClipboardItem, Entity, FocusHandle, SharedString, WeakEntity, Window, actions};
|
||||
use project::{GIT_COMMAND_TASK_TAG, git_store::Repository};
|
||||
|
||||
use task::{TaskContext, TaskVariables, VariableName};
|
||||
use ui::{Color, ContextMenu, ContextMenuEntry, IconName, IconPosition, prelude::*};
|
||||
use workspace::Workspace;
|
||||
|
||||
actions!(
|
||||
git_graph,
|
||||
[
|
||||
/// Copies the SHA of the selected commit to the clipboard.
|
||||
CopyCommitSha,
|
||||
/// Copies a tag from the selected commit to the clipboard.
|
||||
CopyCommitTag,
|
||||
/// Opens the commit view for the selected commit.
|
||||
OpenCommitView,
|
||||
]
|
||||
);
|
||||
|
||||
const COMMIT_TAG_LIST_WIDTH_IN_REMS: Rems = rems(10.);
|
||||
const CUSTOM_GIT_COMMANDS_DOCS_SLUG: &str = "tasks#custom-git-commands";
|
||||
|
||||
pub(crate) struct CommitContextMenuData {
|
||||
pub(crate) sha: Oid,
|
||||
pub(crate) tag_names: Vec<SharedString>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum CommitContextMenuSource {
|
||||
GitGraph,
|
||||
GitPanel,
|
||||
}
|
||||
|
||||
pub(crate) fn commit_context_menu(
|
||||
commit: CommitContextMenuData,
|
||||
source: CommitContextMenuSource,
|
||||
ref_name: Option<SharedString>,
|
||||
focus_handle: FocusHandle,
|
||||
repository: Option<WeakEntity<Repository>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Entity<ContextMenu> {
|
||||
let sha = commit.sha;
|
||||
let sha_short = sha.display_short();
|
||||
let git_tasks = git_context_menu_tasks(
|
||||
git_task_context(&repository, sha, ref_name.as_deref(), cx),
|
||||
&workspace,
|
||||
cx,
|
||||
);
|
||||
let header = match &ref_name {
|
||||
Some(ref_name) => format!("Ref {ref_name}"),
|
||||
None => format!("Commit {sha_short}"),
|
||||
};
|
||||
|
||||
ContextMenu::build(window, cx, move |context_menu, _, _| {
|
||||
context_menu
|
||||
.context(focus_handle)
|
||||
.header(header)
|
||||
.entry("View Commit", Some(OpenCommitView.boxed_clone()), {
|
||||
let repository = repository.clone();
|
||||
let workspace = workspace.clone();
|
||||
move |window, cx| {
|
||||
let Some(repository) = repository.clone() else {
|
||||
return;
|
||||
};
|
||||
CommitView::open(
|
||||
sha.to_string(),
|
||||
repository,
|
||||
workspace.clone(),
|
||||
None,
|
||||
None,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
})
|
||||
.entry(
|
||||
"Copy SHA",
|
||||
Some(CopyCommitSha.boxed_clone()),
|
||||
move |_window, cx| {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(sha.to_string()));
|
||||
},
|
||||
)
|
||||
.when_some(ref_name.clone(), |menu, ref_name| {
|
||||
menu.entry("Copy Ref Name", None, move |_window, cx| {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(ref_name.to_string()));
|
||||
})
|
||||
})
|
||||
.when(ref_name.is_none(), |menu| {
|
||||
menu.map(|menu| {
|
||||
let tag_names = commit.tag_names.clone();
|
||||
let copy_tag_label = "Copy Tag";
|
||||
|
||||
match tag_names.as_slice() {
|
||||
[] => menu.item(
|
||||
ContextMenuEntry::new(copy_tag_label)
|
||||
.action(CopyCommitTag.boxed_clone())
|
||||
.disabled(true),
|
||||
),
|
||||
[tag_name] => {
|
||||
let tag_name = tag_name.clone();
|
||||
let label = format!("{copy_tag_label}: {tag_name}");
|
||||
menu.entry(
|
||||
label,
|
||||
Some(CopyCommitTag.boxed_clone()),
|
||||
move |_window, cx| {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(
|
||||
tag_name.to_string(),
|
||||
));
|
||||
},
|
||||
)
|
||||
}
|
||||
_ => menu.submenu(copy_tag_label, move |menu, _window, _cx| {
|
||||
let mut menu = menu.fixed_width(COMMIT_TAG_LIST_WIDTH_IN_REMS.into());
|
||||
|
||||
for tag_name in tag_names.clone() {
|
||||
let tag_name_to_copy = tag_name.clone();
|
||||
menu = menu.entry(tag_name, None, move |_window, cx| {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(
|
||||
tag_name_to_copy.to_string(),
|
||||
));
|
||||
});
|
||||
}
|
||||
menu
|
||||
}),
|
||||
}
|
||||
})
|
||||
})
|
||||
.when(source == CommitContextMenuSource::GitPanel, |menu| {
|
||||
menu.entry("Show in Git Graph", None, move |window, cx| {
|
||||
window.dispatch_action(
|
||||
Box::new(crate::git_graph::OpenAtCommit {
|
||||
sha: sha.to_string(),
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
})
|
||||
.map(|mut menu| {
|
||||
menu = menu.separator().header("Custom Commands");
|
||||
|
||||
if git_tasks.is_empty() {
|
||||
return menu.item(
|
||||
ContextMenuEntry::new("Learn More")
|
||||
.icon(IconName::ArrowUpRight)
|
||||
.icon_color(Color::Muted)
|
||||
.icon_position(IconPosition::End)
|
||||
.handler(|_window, cx| {
|
||||
let docs_url =
|
||||
release_channel::docs_url(CUSTOM_GIT_COMMANDS_DOCS_SLUG, cx);
|
||||
cx.open_url(&docs_url);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
for (task_source_kind, resolved_task) in git_tasks {
|
||||
let label = resolved_task.display_label().to_string();
|
||||
let workspace = workspace.clone();
|
||||
menu = menu.entry(label, None, move |window, cx| {
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.schedule_resolved_task(
|
||||
task_source_kind.clone(),
|
||||
resolved_task.clone(),
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
}
|
||||
|
||||
menu
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn git_task_context(
|
||||
repository: &Option<WeakEntity<Repository>>,
|
||||
commit_sha: git::Oid,
|
||||
ref_name: Option<&str>,
|
||||
cx: &App,
|
||||
) -> Option<TaskContext> {
|
||||
let repository_path = repository
|
||||
.as_ref()?
|
||||
.upgrade()?
|
||||
.read(cx)
|
||||
.work_directory_abs_path
|
||||
.to_path_buf();
|
||||
let repository_name = repository_path
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.map(ToString::to_string);
|
||||
let mut task_variables = TaskVariables::from_iter([
|
||||
(VariableName::GitSha, commit_sha.to_string()),
|
||||
(VariableName::GitShaShort, commit_sha.display_short()),
|
||||
(
|
||||
VariableName::GitRepositoryPath,
|
||||
repository_path.to_string_lossy().into_owned(),
|
||||
),
|
||||
]);
|
||||
|
||||
if let Some(repository_name) = repository_name {
|
||||
task_variables.insert(VariableName::GitRepositoryName, repository_name);
|
||||
}
|
||||
if let Some(ref_name) = ref_name {
|
||||
task_variables.insert(VariableName::GitRef, ref_name.to_string());
|
||||
}
|
||||
|
||||
Some(TaskContext {
|
||||
cwd: Some(repository_path),
|
||||
task_variables,
|
||||
..TaskContext::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn git_context_menu_tasks(
|
||||
task_context: Option<TaskContext>,
|
||||
workspace: &WeakEntity<Workspace>,
|
||||
cx: &App,
|
||||
) -> Vec<(project::TaskSourceKind, task::ResolvedTask)> {
|
||||
let Some(task_context) = task_context else {
|
||||
return Vec::new();
|
||||
};
|
||||
let Some(workspace) = workspace.upgrade() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let project = workspace.read(cx).project().clone();
|
||||
let task_inventory = project.read_with(cx, |project, cx| {
|
||||
project.task_store().read(cx).task_inventory().cloned()
|
||||
});
|
||||
let Some(task_inventory) = task_inventory else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
task_inventory
|
||||
.read(cx)
|
||||
.resolve_global_tasks_with_tag(GIT_COMMAND_TASK_TAG, &task_context)
|
||||
}
|
||||
+33
-223
@@ -1,4 +1,6 @@
|
||||
pub use crate::commit_context_menu::{CopyCommitSha, CopyCommitTag, OpenCommitView};
|
||||
use crate::{
|
||||
commit_context_menu::{CommitContextMenuData, CommitContextMenuSource, commit_context_menu},
|
||||
commit_tooltip::{CommitAvatar, CommitDetails, CommitTooltip},
|
||||
commit_view::CommitView,
|
||||
git_status_icon,
|
||||
@@ -17,9 +19,9 @@ use git::{
|
||||
status::{FileStatus, StatusCode, TrackedStatus},
|
||||
};
|
||||
use gpui::{
|
||||
Action, Anchor, AnyElement, App, Bounds, ClickEvent, ClipboardItem, DefiniteLength,
|
||||
DismissEvent, DragMoveEvent, ElementId, Empty, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
Hsla, MouseButton, MouseDownEvent, PathBuilder, Pixels, Point, ScrollHandle, ScrollStrategy,
|
||||
Anchor, AnyElement, App, Bounds, ClickEvent, ClipboardItem, DefiniteLength, DismissEvent,
|
||||
DragMoveEvent, ElementId, Empty, Entity, EventEmitter, FocusHandle, Focusable, Hsla,
|
||||
MouseButton, MouseDownEvent, PathBuilder, Pixels, Point, ScrollHandle, ScrollStrategy,
|
||||
ScrollWheelEvent, SharedString, Subscription, Task, TextStyleRefinement,
|
||||
UniformListScrollHandle, WeakEntity, Window, actions, anchored, deferred, point, prelude::*,
|
||||
px, uniform_list,
|
||||
@@ -29,7 +31,7 @@ use markdown::{Markdown, MarkdownElement};
|
||||
use menu::{Cancel, SelectFirst, SelectLast, SelectNext, SelectPrevious};
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use project::{
|
||||
GIT_COMMAND_TASK_TAG, ProjectPath, TaskSourceKind,
|
||||
ProjectPath,
|
||||
git_store::{
|
||||
CommitDataState, GitGraphEvent, GitStore, GitStoreEvent, GraphDataResponse, Repository,
|
||||
RepositoryEvent, RepositoryId,
|
||||
@@ -47,12 +49,12 @@ use std::{
|
||||
sync::{Arc, OnceLock},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use task::{ResolvedTask, TaskContext, TaskVariables, VariableName};
|
||||
|
||||
use theme::AccentColors;
|
||||
use time::{OffsetDateTime, UtcOffset, format_description::BorrowedFormatItem};
|
||||
use ui::{
|
||||
Chip, ColumnWidthConfig, CommonAnimationExt as _, ContextMenu, ContextMenuEntry, DiffStat,
|
||||
Divider, HeaderResizeInfo, HighlightedLabel, IndentGuideColors, ListItem, ListItemSpacing,
|
||||
Chip, ColumnWidthConfig, CommonAnimationExt as _, ContextMenu, DiffStat, Divider,
|
||||
HeaderResizeInfo, HighlightedLabel, IndentGuideColors, ListItem, ListItemSpacing,
|
||||
RedistributableColumnsState, ScrollableHandle, Table, TableInteractionState,
|
||||
TableRenderContext, TableResizeBehavior, Tooltip, WithScrollbar, bind_redistributable_columns,
|
||||
prelude::*, redistribute_hidden_fractions, redistribute_hidden_widths,
|
||||
@@ -72,7 +74,6 @@ const LINE_WIDTH: Pixels = px(1.5);
|
||||
const RESIZE_HANDLE_WIDTH: f32 = 8.0;
|
||||
const COPIED_STATE_DURATION: Duration = Duration::from_secs(2);
|
||||
const COMMIT_TAG_LIST_WIDTH_IN_REMS: Rems = rems(10.);
|
||||
const CUSTOM_GIT_COMMANDS_DOCS_SLUG: &str = "tasks#custom-git-commands";
|
||||
const TREE_INDENT: f32 = 20.0;
|
||||
const TABLE_COLUMN_COUNT: usize = 4;
|
||||
const ROW_VERTICAL_PADDING: Pixels = px(4.0);
|
||||
@@ -580,12 +581,6 @@ actions!(
|
||||
[
|
||||
/// Opens the Git Graph Tab.
|
||||
Open,
|
||||
/// Copies the SHA of the selected commit to the clipboard.
|
||||
CopyCommitSha,
|
||||
/// Copies a tag from the selected commit to the clipboard.
|
||||
CopyCommitTag,
|
||||
/// Opens the commit view for the selected commit.
|
||||
OpenCommitView,
|
||||
/// Focuses the search field.
|
||||
FocusSearch,
|
||||
/// Focuses the next git graph tab stop.
|
||||
@@ -2467,91 +2462,6 @@ impl GitGraph {
|
||||
self.copy_commit_tag(selected_entry_index, window, cx);
|
||||
}
|
||||
|
||||
fn git_task_context(
|
||||
&self,
|
||||
commit_sha: Oid,
|
||||
ref_name: Option<&str>,
|
||||
cx: &App,
|
||||
) -> Option<TaskContext> {
|
||||
let repository_path = self
|
||||
.get_repository(cx)?
|
||||
.read(cx)
|
||||
.work_directory_abs_path
|
||||
.to_path_buf();
|
||||
|
||||
let repository_name = repository_path
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.map(ToString::to_string);
|
||||
|
||||
let mut task_variables = TaskVariables::from_iter([
|
||||
(VariableName::GitSha, commit_sha.to_string()),
|
||||
(VariableName::GitShaShort, commit_sha.display_short()),
|
||||
(
|
||||
VariableName::GitRepositoryPath,
|
||||
repository_path.to_string_lossy().into_owned(),
|
||||
),
|
||||
]);
|
||||
|
||||
if let Some(repository_name) = repository_name {
|
||||
task_variables.insert(VariableName::GitRepositoryName, repository_name);
|
||||
}
|
||||
|
||||
if let Some(ref_name) = ref_name {
|
||||
task_variables.insert(VariableName::GitRef, ref_name.to_string());
|
||||
}
|
||||
|
||||
Some(TaskContext {
|
||||
cwd: Some(repository_path),
|
||||
task_variables,
|
||||
..TaskContext::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn git_context_menu_tasks(
|
||||
&self,
|
||||
task_context: &TaskContext,
|
||||
cx: &App,
|
||||
) -> Vec<(TaskSourceKind, ResolvedTask)> {
|
||||
let Some(workspace) = self.workspace.upgrade() else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let project = workspace.read(cx).project().clone();
|
||||
|
||||
let task_inventory = project.read_with(cx, |project, cx| {
|
||||
project.task_store().read(cx).task_inventory().cloned()
|
||||
});
|
||||
|
||||
let Some(task_inventory) = task_inventory else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
task_inventory
|
||||
.read(cx)
|
||||
.resolve_global_tasks_with_tag(GIT_COMMAND_TASK_TAG, task_context)
|
||||
}
|
||||
|
||||
fn schedule_git_task(
|
||||
&mut self,
|
||||
task_source_kind: TaskSourceKind,
|
||||
resolved_task: ResolvedTask,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.schedule_resolved_task(
|
||||
task_source_kind,
|
||||
resolved_task,
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn deploy_entry_context_menu(
|
||||
&mut self,
|
||||
position: Point<Pixels>,
|
||||
@@ -2563,129 +2473,27 @@ impl GitGraph {
|
||||
let Some(commit) = self.graph_data.commits.get(index) else {
|
||||
return;
|
||||
};
|
||||
let sha = commit.data.sha;
|
||||
let sha_short = sha.display_short();
|
||||
let git_tasks = self
|
||||
.git_task_context(sha, ref_name.as_deref(), cx)
|
||||
.map(|task_context| self.git_context_menu_tasks(&task_context, cx))
|
||||
.unwrap_or_default();
|
||||
|
||||
let header = match &ref_name {
|
||||
Some(ref_name) => format!("Ref {ref_name}"),
|
||||
None => format!("Commit {sha_short}"),
|
||||
};
|
||||
|
||||
let focus_handle = self.focus_handle.clone();
|
||||
let git_graph = cx.entity();
|
||||
let context_menu = ContextMenu::build(window, cx, |context_menu, window, _| {
|
||||
context_menu
|
||||
.context(focus_handle)
|
||||
.header(header)
|
||||
.entry(
|
||||
"View Commit",
|
||||
Some(OpenCommitView.boxed_clone()),
|
||||
window.handler_for(&git_graph, move |this, window, cx| {
|
||||
this.open_commit_view(index, window, cx);
|
||||
}),
|
||||
)
|
||||
.entry(
|
||||
"Copy SHA",
|
||||
Some(CopyCommitSha.boxed_clone()),
|
||||
window.handler_for(&git_graph, move |this, _window, cx| {
|
||||
this.copy_commit_sha(index, cx);
|
||||
}),
|
||||
)
|
||||
.when_some(ref_name.clone(), |menu, ref_name| {
|
||||
menu.entry("Copy Ref Name", None, move |_window, cx| {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(ref_name.to_string()));
|
||||
})
|
||||
})
|
||||
.when(ref_name.is_none(), |menu| {
|
||||
menu.map(|menu| {
|
||||
let tag_names = commit
|
||||
.data
|
||||
.tag_names()
|
||||
.into_iter()
|
||||
.map(|tag_name| SharedString::from(tag_name.to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
let copy_tag_label = "Copy Tag";
|
||||
|
||||
match tag_names.as_slice() {
|
||||
[] => menu.item(
|
||||
ContextMenuEntry::new(copy_tag_label)
|
||||
.action(CopyCommitTag.boxed_clone())
|
||||
.disabled(true),
|
||||
),
|
||||
[tag_name] => {
|
||||
let tag_name = tag_name.clone();
|
||||
let label = format!("{copy_tag_label}: {tag_name}");
|
||||
menu.entry(
|
||||
label,
|
||||
Some(CopyCommitTag.boxed_clone()),
|
||||
move |_window, cx| {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(
|
||||
tag_name.to_string(),
|
||||
));
|
||||
},
|
||||
)
|
||||
}
|
||||
_ => menu.submenu(copy_tag_label, move |menu, _window, _cx| {
|
||||
let mut menu =
|
||||
menu.fixed_width(COMMIT_TAG_LIST_WIDTH_IN_REMS.into());
|
||||
|
||||
for tag_name in tag_names.clone() {
|
||||
let tag_name_to_copy = tag_name.clone();
|
||||
|
||||
menu = menu.entry(tag_name, None, move |_window, cx| {
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(
|
||||
tag_name_to_copy.to_string(),
|
||||
));
|
||||
});
|
||||
}
|
||||
menu
|
||||
}),
|
||||
}
|
||||
})
|
||||
})
|
||||
.map(|mut menu| {
|
||||
menu = menu.separator().header("Custom Commands");
|
||||
|
||||
if git_tasks.is_empty() {
|
||||
return menu.item(
|
||||
ContextMenuEntry::new("Learn More")
|
||||
.icon(IconName::ArrowUpRight)
|
||||
.icon_color(Color::Muted)
|
||||
.icon_position(IconPosition::End)
|
||||
.handler(|_window, cx| {
|
||||
let docs_url = release_channel::docs_url(
|
||||
CUSTOM_GIT_COMMANDS_DOCS_SLUG,
|
||||
cx,
|
||||
);
|
||||
cx.open_url(&docs_url);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
for (task_source_kind, resolved_task) in git_tasks {
|
||||
let label = resolved_task.display_label().to_string();
|
||||
|
||||
menu = menu.entry(
|
||||
label,
|
||||
None,
|
||||
window.handler_for(&git_graph, move |this, window, cx| {
|
||||
this.schedule_git_task(
|
||||
task_source_kind.clone(),
|
||||
resolved_task.clone(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
menu
|
||||
})
|
||||
});
|
||||
let repository = self
|
||||
.get_repository(cx)
|
||||
.map(|repository| repository.downgrade());
|
||||
let context_menu = commit_context_menu(
|
||||
CommitContextMenuData {
|
||||
sha: commit.data.sha,
|
||||
tag_names: commit
|
||||
.data
|
||||
.tag_names()
|
||||
.into_iter()
|
||||
.map(|tag_name| SharedString::from(tag_name.to_string()))
|
||||
.collect(),
|
||||
},
|
||||
CommitContextMenuSource::GitGraph,
|
||||
ref_name,
|
||||
self.focus_handle.clone(),
|
||||
repository,
|
||||
self.workspace.clone(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
self.set_context_menu(context_menu, position, Some(index), window, cx);
|
||||
}
|
||||
|
||||
@@ -4946,7 +4754,9 @@ mod tests {
|
||||
use git::repository::{CommitData, InitialGraphCommitData};
|
||||
use gpui::{TestAppContext, UpdateGlobal};
|
||||
use project::git_store::{GitStoreEvent, RepositoryEvent};
|
||||
use project::{Project, TaskSourceKind, task_store::TaskSettingsLocation};
|
||||
use project::{
|
||||
GIT_COMMAND_TASK_TAG, Project, TaskSourceKind, task_store::TaskSettingsLocation,
|
||||
};
|
||||
use rand::prelude::*;
|
||||
use serde_json::json;
|
||||
use settings::{SettingsStore, ThemeSettingsContent};
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::askpass_modal::AskPassModal;
|
||||
use crate::commit_context_menu::{
|
||||
CommitContextMenuData, CommitContextMenuSource, commit_context_menu,
|
||||
};
|
||||
use crate::commit_modal::CommitModal;
|
||||
use crate::commit_tooltip::{CommitAvatar, CommitTooltip};
|
||||
use crate::commit_view::CommitView;
|
||||
@@ -6187,6 +6190,37 @@ impl GitPanel {
|
||||
);
|
||||
}
|
||||
|
||||
fn deploy_history_context_menu(
|
||||
&mut self,
|
||||
position: Point<Pixels>,
|
||||
index: usize,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let Some(commit) = self.commit_history_entries().get(index).cloned() else {
|
||||
return;
|
||||
};
|
||||
let Some(repository) = self.active_repository.as_ref() else {
|
||||
return;
|
||||
};
|
||||
let context_menu = commit_context_menu(
|
||||
CommitContextMenuData {
|
||||
sha: commit.sha,
|
||||
tag_names: commit.tag_names,
|
||||
},
|
||||
CommitContextMenuSource::GitPanel,
|
||||
None,
|
||||
self.focus_handle.clone(),
|
||||
Some(repository.downgrade()),
|
||||
self.workspace.clone(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
self.focused_history_entry = Some(index);
|
||||
self.history_keyboard_nav = false;
|
||||
self.set_context_menu(context_menu, position, window, cx);
|
||||
}
|
||||
|
||||
fn activate_changes_tab(
|
||||
&mut self,
|
||||
_: &ActivateChangesTab,
|
||||
@@ -6347,6 +6381,7 @@ impl GitPanel {
|
||||
let focused_history_entry = self.focused_history_entry;
|
||||
let is_panel_focused = self.focus_handle.is_focused(window);
|
||||
let show_focus_border = self.history_keyboard_nav;
|
||||
let has_context_menu = self.context_menu.is_some();
|
||||
|
||||
let ahead_count = active_repository
|
||||
.read(cx)
|
||||
@@ -6485,13 +6520,20 @@ impl GitPanel {
|
||||
tag_names
|
||||
.iter()
|
||||
.take(MAX_HISTORY_TAG_CHIPS)
|
||||
.cloned()
|
||||
.map(|tag_name| {
|
||||
let tag_name = tag_name.clone();
|
||||
Chip::new(tag_name.clone())
|
||||
.truncate()
|
||||
.tooltip(Tooltip::text(
|
||||
tag_name,
|
||||
))
|
||||
.when(
|
||||
!has_context_menu,
|
||||
|chip| {
|
||||
chip.tooltip(
|
||||
Tooltip::text(
|
||||
tag_name,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}),
|
||||
)
|
||||
.when(hidden_tag_count > 0, |this| {
|
||||
@@ -6502,9 +6544,11 @@ impl GitPanel {
|
||||
Chip::new(format!(
|
||||
"+{hidden_tag_count}"
|
||||
))
|
||||
.tooltip(Tooltip::text(
|
||||
hidden_tag_names,
|
||||
)),
|
||||
.when(!has_context_menu, |chip| {
|
||||
chip.tooltip(Tooltip::text(
|
||||
hidden_tag_names,
|
||||
))
|
||||
}),
|
||||
)
|
||||
})
|
||||
}))
|
||||
@@ -6541,13 +6585,15 @@ impl GitPanel {
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.tooltip(move |_, cx| {
|
||||
Tooltip::with_meta(
|
||||
"View Commit",
|
||||
None,
|
||||
short_sha.clone(),
|
||||
cx,
|
||||
)
|
||||
.when(!has_context_menu, |this| {
|
||||
this.tooltip(move |_, cx| {
|
||||
Tooltip::with_meta(
|
||||
"View Commit",
|
||||
None,
|
||||
short_sha.clone(),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
})
|
||||
.on_mouse_down(gpui::MouseButton::Left, {
|
||||
let git_panel = git_panel.clone();
|
||||
@@ -6561,6 +6607,22 @@ impl GitPanel {
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.on_mouse_down(MouseButton::Right, {
|
||||
let git_panel = git_panel.clone();
|
||||
move |event, window, cx| {
|
||||
git_panel
|
||||
.update(cx, |panel, cx| {
|
||||
panel.deploy_history_context_menu(
|
||||
event.position,
|
||||
index,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
cx.stop_propagation();
|
||||
}
|
||||
})
|
||||
.on_click(move |_, window, cx| {
|
||||
CommitView::open(
|
||||
sha_for_click.clone(),
|
||||
|
||||
@@ -36,6 +36,7 @@ use crate::{
|
||||
mod askpass_modal;
|
||||
pub mod branch_diff;
|
||||
pub mod branch_picker;
|
||||
mod commit_context_menu;
|
||||
mod commit_modal;
|
||||
pub mod commit_tooltip;
|
||||
pub mod commit_view;
|
||||
|
||||
Reference in new issue
Block a user