@@ -354,6 +354,7 @@ pub struct GitPanel {
workspace: WeakEntity<Workspace>,
context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
modal_open: bool,
+ show_placeholders: bool,
_settings_subscription: Subscription,
}
@@ -407,6 +408,16 @@ impl GitPanel {
})
.detach();
+ let mut was_sort_by_path = GitPanelSettings::get_global(cx).sort_by_path;
+ cx.observe_global::<SettingsStore>(move |this, cx| {
+ let is_sort_by_path = GitPanelSettings::get_global(cx).sort_by_path;
+ if is_sort_by_path != was_sort_by_path {
+ this.update_visible_entries(cx);
+ }
+ was_sort_by_path = is_sort_by_path
+ })
+ .detach();
+
// just to let us render a placeholder editor.
// Once the active git repo is set, this buffer will be replaced.
let temporary_buffer = cx.new(|cx| Buffer::local("", cx));
@@ -506,6 +517,7 @@ impl GitPanel {
tracked_staged_count: 0,
update_visible_entries_task: Task::ready(()),
width: None,
+ show_placeholders: false,
context_menu: None,
workspace,
modal_open: false,
@@ -598,7 +610,14 @@ impl GitPanel {
cx.notify();
}
- pub fn entry_by_path(&self, path: &RepoPath) -> Option<usize> {
+ pub fn entry_by_path(&self, path: &RepoPath, cx: &App) -> Option<usize> {
+ if GitPanelSettings::get_global(cx).sort_by_path {
+ return self
+ .entries
+ .binary_search_by(|entry| entry.status_entry().unwrap().repo_path.cmp(&path))
+ .ok();
+ }
+
if self.conflicted_count > 0 {
let conflicted_start = 1;
if let Ok(ix) = self.entries[conflicted_start..conflicted_start + self.conflicted_count]
@@ -650,7 +669,7 @@ impl GitPanel {
let Some(repo_path) = git_repo.read(cx).project_path_to_repo_path(&path, cx) else {
return;
};
- let Some(ix) = self.entry_by_path(&repo_path) else {
+ let Some(ix) = self.entry_by_path(&repo_path, cx) else {
return;
};
self.selected_entry = Some(ix);
@@ -2294,6 +2313,8 @@ impl GitPanel {
self.tracked_staged_count = 0;
self.entry_count = 0;
+ let sort_by_path = GitPanelSettings::get_global(cx).sort_by_path;
+
let mut changed_entries = Vec::new();
let mut new_entries = Vec::new();
let mut conflict_entries = Vec::new();
@@ -2353,7 +2374,9 @@ impl GitPanel {
None => max_width_item = Some((entry.repo_path.clone(), width_estimate)),
}
- if is_conflict {
+ if sort_by_path {
+ changed_entries.push(entry);
+ } else if is_conflict {
conflict_entries.push(entry);
} else if is_new {
new_entries.push(entry);
@@ -2408,9 +2431,11 @@ impl GitPanel {
}
if changed_entries.len() > 0 {
- self.entries.push(GitListEntry::Header(GitHeaderEntry {
- header: Section::Tracked,
- }));
+ if !sort_by_path {
+ self.entries.push(GitListEntry::Header(GitHeaderEntry {
+ header: Section::Tracked,
+ }));
+ }
self.entries.extend(
changed_entries
.into_iter()
@@ -2464,6 +2489,7 @@ impl GitPanel {
}
fn update_counts(&mut self, repo: &Repository) {
+ self.show_placeholders = false;
self.conflicted_count = 0;
self.conflicted_staged_count = 0;
self.new_count = 0;
@@ -2525,10 +2551,6 @@ impl GitPanel {
|| self.conflicted_count > self.conflicted_staged_count
}
- fn has_conflicts(&self) -> bool {
- self.conflicted_count > 0
- }
-
fn has_tracked_changes(&self) -> bool {
self.tracked_count > 0
}
@@ -2935,14 +2957,11 @@ impl GitPanel {
cx: &mut Context<Self>,
) -> Option<impl IntoElement> {
let active_repository = self.active_repository.clone()?;
- let (can_commit, tooltip) = self.configure_commit_button(cx);
let panel_editor_style = panel_editor_style(true, window, cx);
let enable_coauthors = self.render_co_authors(cx);
- let title = self.commit_button_title();
let editor_focus_handle = self.commit_editor.focus_handle(cx);
- let commit_tooltip_focus_handle = editor_focus_handle.clone();
let expand_tooltip_focus_handle = editor_focus_handle.clone();
let branch = active_repository.read(cx).branch.clone();
@@ -3010,183 +3029,7 @@ impl GitPanel {
h_flex()
.gap_0p5()
.children(enable_coauthors)
- .when(self.amend_pending, {
- |this| {
- this.h_flex()
- .gap_1()
- .child(
- panel_filled_button("Cancel")
- .tooltip({
- let handle =
- commit_tooltip_focus_handle.clone();
- move |window, cx| {
- Tooltip::for_action_in(
- "Cancel amend",
- &git::Cancel,
- &handle,
- window,
- cx,
- )
- }
- })
- .on_click(move |_, window, cx| {
- window.dispatch_action(
- Box::new(git::Cancel),
- cx,
- );
- }),
- )
- .child(
- panel_filled_button(title)
- .tooltip({
- let handle =
- commit_tooltip_focus_handle.clone();
- move |window, cx| {
- if can_commit {
- Tooltip::for_action_in(
- tooltip, &Amend, &handle,
- window, cx,
- )
- } else {
- Tooltip::simple(tooltip, cx)
- }
- }
- })
- .disabled(!can_commit || self.modal_open)
- .on_click({
- let git_panel = git_panel.downgrade();
- move |_, window, cx| {
- telemetry::event!(
- "Git Amended",
- source = "Git Panel"
- );
- git_panel
- .update(cx, |git_panel, cx| {
- git_panel
- .set_amend_pending(
- false, cx,
- );
- git_panel.commit_changes(
- CommitOptions {
- amend: true,
- },
- window,
- cx,
- );
- })
- .ok();
- }
- }),
- )
- }
- })
- .when(!self.amend_pending, |this| {
- this.when(has_previous_commit, |this| {
- this.child(SplitButton::new(
- ui::ButtonLike::new_rounded_left(ElementId::Name(
- format!("split-button-left-{}", title).into(),
- ))
- .layer(ui::ElevationIndex::ModalSurface)
- .size(ui::ButtonSize::Compact)
- .child(
- div()
- .child(
- Label::new(title)
- .size(LabelSize::Small),
- )
- .mr_0p5(),
- )
- .on_click({
- let git_panel = git_panel.downgrade();
- move |_, window, cx| {
- telemetry::event!(
- "Git Committed",
- source = "Git Panel"
- );
- git_panel
- .update(cx, |git_panel, cx| {
- git_panel.commit_changes(
- CommitOptions { amend: false },
- window,
- cx,
- );
- })
- .ok();
- }
- })
- .disabled(!can_commit || self.modal_open)
- .tooltip({
- let handle =
- commit_tooltip_focus_handle.clone();
- move |window, cx| {
- if can_commit {
- Tooltip::with_meta_in(
- tooltip,
- Some(&git::Commit),
- "git commit",
- &handle.clone(),
- window,
- cx,
- )
- } else {
- Tooltip::simple(tooltip, cx)
- }
- }
- }),
- self.render_git_commit_menu(
- ElementId::Name(
- format!("split-button-right-{}", title)
- .into(),
- ),
- Some(commit_tooltip_focus_handle.clone()),
- )
- .into_any_element(),
- ))
- })
- .when(
- !has_previous_commit,
- |this| {
- this.child(
- panel_filled_button(title)
- .tooltip(move |window, cx| {
- if can_commit {
- Tooltip::with_meta_in(
- tooltip,
- Some(&git::Commit),
- "git commit",
- &commit_tooltip_focus_handle,
- window,
- cx,
- )
- } else {
- Tooltip::simple(tooltip, cx)
- }
- })
- .disabled(!can_commit || self.modal_open)
- .on_click({
- let git_panel = git_panel.downgrade();
- move |_, window, cx| {
- telemetry::event!(
- "Git Committed",
- source = "Git Panel"
- );
- git_panel
- .update(cx, |git_panel, cx| {
- git_panel.commit_changes(
- CommitOptions {
- amend: false,
- },
- window,
- cx,
- );
- })
- .ok();
- }
- }),
- )
- },
- )
- }),
+ .child(self.render_commit_button(has_previous_commit, cx)),
),
)
.child(
@@ -3235,6 +3078,168 @@ impl GitPanel {
Some(footer)
}
+ fn render_commit_button(
+ &self,
+ has_previous_commit: bool,
+ cx: &mut Context<Self>,
+ ) -> impl IntoElement {
+ let (can_commit, tooltip) = self.configure_commit_button(cx);
+ let title = self.commit_button_title();
+ let commit_tooltip_focus_handle = self.commit_editor.focus_handle(cx);
+ div()
+ .id("commit-wrapper")
+ .on_hover(cx.listener(move |this, hovered, _, cx| {
+ this.show_placeholders =
+ *hovered && !this.has_staged_changes() && !this.has_unstaged_conflicts();
+ cx.notify()
+ }))
+ .when(self.amend_pending, {
+ |this| {
+ this.h_flex()
+ .gap_1()
+ .child(
+ panel_filled_button("Cancel")
+ .tooltip({
+ let handle = commit_tooltip_focus_handle.clone();
+ move |window, cx| {
+ Tooltip::for_action_in(
+ "Cancel amend",
+ &git::Cancel,
+ &handle,
+ window,
+ cx,
+ )
+ }
+ })
+ .on_click(move |_, window, cx| {
+ window.dispatch_action(Box::new(git::Cancel), cx);
+ }),
+ )
+ .child(
+ panel_filled_button(title)
+ .tooltip({
+ let handle = commit_tooltip_focus_handle.clone();
+ move |window, cx| {
+ if can_commit {
+ Tooltip::for_action_in(
+ tooltip, &Amend, &handle, window, cx,
+ )
+ } else {
+ Tooltip::simple(tooltip, cx)
+ }
+ }
+ })
+ .disabled(!can_commit || self.modal_open)
+ .on_click({
+ let git_panel = cx.weak_entity();
+ move |_, window, cx| {
+ telemetry::event!("Git Amended", source = "Git Panel");
+ git_panel
+ .update(cx, |git_panel, cx| {
+ git_panel.set_amend_pending(false, cx);
+ git_panel.commit_changes(
+ CommitOptions { amend: true },
+ window,
+ cx,
+ );
+ })
+ .ok();
+ }
+ }),
+ )
+ }
+ })
+ .when(!self.amend_pending, |this| {
+ this.when(has_previous_commit, |this| {
+ this.child(SplitButton::new(
+ ui::ButtonLike::new_rounded_left(ElementId::Name(
+ format!("split-button-left-{}", title).into(),
+ ))
+ .layer(ui::ElevationIndex::ModalSurface)
+ .size(ui::ButtonSize::Compact)
+ .child(
+ div()
+ .child(Label::new(title).size(LabelSize::Small))
+ .mr_0p5(),
+ )
+ .on_click({
+ let git_panel = cx.weak_entity();
+ move |_, window, cx| {
+ telemetry::event!("Git Committed", source = "Git Panel");
+ git_panel
+ .update(cx, |git_panel, cx| {
+ git_panel.commit_changes(
+ CommitOptions { amend: false },
+ window,
+ cx,
+ );
+ })
+ .ok();
+ }
+ })
+ .disabled(!can_commit || self.modal_open)
+ .tooltip({
+ let handle = commit_tooltip_focus_handle.clone();
+ move |window, cx| {
+ if can_commit {
+ Tooltip::with_meta_in(
+ tooltip,
+ Some(&git::Commit),
+ "git commit",
+ &handle.clone(),
+ window,
+ cx,
+ )
+ } else {
+ Tooltip::simple(tooltip, cx)
+ }
+ }
+ }),
+ self.render_git_commit_menu(
+ ElementId::Name(format!("split-button-right-{}", title).into()),
+ Some(commit_tooltip_focus_handle.clone()),
+ )
+ .into_any_element(),
+ ))
+ })
+ .when(!has_previous_commit, |this| {
+ this.child(
+ panel_filled_button(title)
+ .tooltip(move |window, cx| {
+ if can_commit {
+ Tooltip::with_meta_in(
+ tooltip,
+ Some(&git::Commit),
+ "git commit",
+ &commit_tooltip_focus_handle,
+ window,
+ cx,
+ )
+ } else {
+ Tooltip::simple(tooltip, cx)
+ }
+ })
+ .disabled(!can_commit || self.modal_open)
+ .on_click({
+ let git_panel = cx.weak_entity();
+ move |_, window, cx| {
+ telemetry::event!("Git Committed", source = "Git Panel");
+ git_panel
+ .update(cx, |git_panel, cx| {
+ git_panel.commit_changes(
+ CommitOptions { amend: false },
+ window,
+ cx,
+ );
+ })
+ .ok();
+ }
+ }),
+ )
+ })
+ })
+ }
+
fn render_pending_amend(&self, cx: &mut Context<Self>) -> impl IntoElement {
div()
.py_2()
@@ -3482,7 +3487,7 @@ impl GitPanel {
let repo = self.active_repository.as_ref()?.read(cx);
let project_path = (file.worktree_id(cx), file.path()).into();
let repo_path = repo.project_path_to_repo_path(&project_path, cx)?;
- let ix = self.entry_by_path(&repo_path)?;
+ let ix = self.entry_by_path(&repo_path, cx)?;
let entry = self.entries.get(ix)?;
let entry_staging = self.entry_staging(entry.status_entry()?);
@@ -3852,8 +3857,7 @@ impl GitPanel {
let entry_staging = self.entry_staging(entry);
let mut is_staged: ToggleState = self.entry_staging(entry).as_bool().into();
-
- if !self.has_staged_changes() && !self.has_conflicts() && !entry.status.is_created() {
+ if self.show_placeholders && !self.has_staged_changes() && !entry.status.is_created() {
is_staged = ToggleState::Selected;
}
@@ -3953,11 +3957,6 @@ impl GitPanel {
Checkbox::new(checkbox_id, is_staged)
.disabled(!has_write_access)
.fill()
- .placeholder(
- !self.has_staged_changes()
- && !self.has_conflicts()
- && !entry.status.is_created(),
- )
.elevation(ElevationIndex::Surface)
.on_click({
let entry = entry.clone();