New commit review flow in project diff view (#25229)
Closes #ISSUE Release Notes: - N/A --------- Co-authored-by: Nate Butler <iamnbutler@gmail.com>
This commit is contained in:
parent
6b9397c380
commit
4871d3c9e7
18 changed files with 982 additions and 480 deletions
|
@ -1,4 +1,5 @@
|
|||
use crate::git_panel_settings::StatusStyle;
|
||||
use crate::project_diff::Diff;
|
||||
use crate::repository_selector::RepositorySelectorPopoverMenu;
|
||||
use crate::{
|
||||
git_panel_settings::GitPanelSettings, git_status_icon, repository_selector::RepositorySelector,
|
||||
|
@ -78,16 +79,16 @@ pub fn init(cx: &mut App) {
|
|||
workspace.toggle_panel_focus::<GitPanel>(window, cx);
|
||||
});
|
||||
|
||||
workspace.register_action(|workspace, _: &Commit, window, cx| {
|
||||
workspace.open_panel::<GitPanel>(window, cx);
|
||||
if let Some(git_panel) = workspace.panel::<GitPanel>(cx) {
|
||||
git_panel
|
||||
.read(cx)
|
||||
.commit_editor
|
||||
.focus_handle(cx)
|
||||
.focus(window);
|
||||
}
|
||||
});
|
||||
// workspace.register_action(|workspace, _: &Commit, window, cx| {
|
||||
// workspace.open_panel::<GitPanel>(window, cx);
|
||||
// if let Some(git_panel) = workspace.panel::<GitPanel>(cx) {
|
||||
// git_panel
|
||||
// .read(cx)
|
||||
// .commit_editor
|
||||
// .focus_handle(cx)
|
||||
// .focus(window);
|
||||
// }
|
||||
// });
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
|
@ -174,7 +175,7 @@ struct PendingOperation {
|
|||
}
|
||||
|
||||
pub struct GitPanel {
|
||||
active_repository: Option<Entity<Repository>>,
|
||||
pub(crate) active_repository: Option<Entity<Repository>>,
|
||||
commit_editor: Entity<Editor>,
|
||||
conflicted_count: usize,
|
||||
conflicted_staged_count: usize,
|
||||
|
@ -190,7 +191,7 @@ pub struct GitPanel {
|
|||
pending: Vec<PendingOperation>,
|
||||
pending_commit: Option<Task<()>>,
|
||||
pending_serialization: Task<Option<()>>,
|
||||
project: Entity<Project>,
|
||||
pub(crate) project: Entity<Project>,
|
||||
repository_selector: Entity<RepositorySelector>,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
scrollbar_state: ScrollbarState,
|
||||
|
@ -202,17 +203,20 @@ pub struct GitPanel {
|
|||
width: Option<Pixels>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
|
||||
modal_open: bool,
|
||||
}
|
||||
|
||||
fn commit_message_editor(
|
||||
pub(crate) fn commit_message_editor(
|
||||
commit_message_buffer: Entity<Buffer>,
|
||||
project: Entity<Project>,
|
||||
in_panel: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<'_, Editor>,
|
||||
) -> Editor {
|
||||
let buffer = cx.new(|cx| MultiBuffer::singleton(commit_message_buffer, cx));
|
||||
let max_lines = if in_panel { 6 } else { 18 };
|
||||
let mut commit_editor = Editor::new(
|
||||
EditorMode::AutoHeight { max_lines: 6 },
|
||||
EditorMode::AutoHeight { max_lines },
|
||||
buffer,
|
||||
None,
|
||||
false,
|
||||
|
@ -251,8 +255,9 @@ impl GitPanel {
|
|||
// 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));
|
||||
let commit_editor =
|
||||
cx.new(|cx| commit_message_editor(temporary_buffer, project.clone(), window, cx));
|
||||
let commit_editor = cx.new(|cx| {
|
||||
commit_message_editor(temporary_buffer, project.clone(), true, window, cx)
|
||||
});
|
||||
commit_editor.update(cx, |editor, cx| {
|
||||
editor.clear(window, cx);
|
||||
});
|
||||
|
@ -309,6 +314,7 @@ impl GitPanel {
|
|||
width: Some(px(360.)),
|
||||
context_menu: None,
|
||||
workspace,
|
||||
modal_open: false,
|
||||
};
|
||||
git_panel.schedule_update(false, window, cx);
|
||||
git_panel.show_scrollbar = git_panel.should_show_scrollbar(cx);
|
||||
|
@ -351,6 +357,11 @@ impl GitPanel {
|
|||
);
|
||||
}
|
||||
|
||||
pub(crate) fn set_modal_open(&mut self, open: bool, cx: &mut Context<Self>) {
|
||||
self.modal_open = open;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn dispatch_context(&self, window: &mut Window, cx: &Context<Self>) -> KeyContext {
|
||||
let mut dispatch_context = KeyContext::new_with_defaults();
|
||||
dispatch_context.add("GitPanel");
|
||||
|
@ -592,7 +603,6 @@ impl GitPanel {
|
|||
})
|
||||
.ok()
|
||||
});
|
||||
self.focus_handle.focus(window);
|
||||
}
|
||||
|
||||
fn open_file(
|
||||
|
@ -998,6 +1008,16 @@ impl GitPanel {
|
|||
.detach();
|
||||
}
|
||||
|
||||
pub fn commit_message_buffer(&self, cx: &App) -> Entity<Buffer> {
|
||||
self.commit_editor
|
||||
.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn toggle_staged_for_selected(
|
||||
&mut self,
|
||||
_: &git::ToggleStaged,
|
||||
|
@ -1022,7 +1042,7 @@ impl GitPanel {
|
|||
self.commit_changes(window, cx)
|
||||
}
|
||||
|
||||
fn commit_changes(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
pub(crate) fn commit_changes(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(active_repository) = self.active_repository.clone() else {
|
||||
return;
|
||||
};
|
||||
|
@ -1288,7 +1308,7 @@ impl GitPanel {
|
|||
!= Some(&buffer)
|
||||
{
|
||||
git_panel.commit_editor = cx.new(|cx| {
|
||||
commit_message_editor(buffer, git_panel.project.clone(), window, cx)
|
||||
commit_message_editor(buffer, git_panel.project.clone(), true, window, cx)
|
||||
});
|
||||
}
|
||||
})
|
||||
|
@ -1476,12 +1496,18 @@ impl GitPanel {
|
|||
entry.is_staged
|
||||
}
|
||||
|
||||
fn has_staged_changes(&self) -> bool {
|
||||
pub(crate) fn has_staged_changes(&self) -> bool {
|
||||
self.tracked_staged_count > 0
|
||||
|| self.new_staged_count > 0
|
||||
|| self.conflicted_staged_count > 0
|
||||
}
|
||||
|
||||
pub(crate) fn has_unstaged_changes(&self) -> bool {
|
||||
self.tracked_count > self.tracked_staged_count
|
||||
|| self.new_count > self.new_staged_count
|
||||
|| self.conflicted_count > self.conflicted_staged_count
|
||||
}
|
||||
|
||||
fn has_conflicts(&self) -> bool {
|
||||
self.conflicted_count > 0
|
||||
}
|
||||
|
@ -1490,7 +1516,7 @@ impl GitPanel {
|
|||
self.tracked_count > 0
|
||||
}
|
||||
|
||||
fn has_unstaged_conflicts(&self) -> bool {
|
||||
pub fn has_unstaged_conflicts(&self) -> bool {
|
||||
self.conflicted_count > 0 && self.conflicted_count != self.conflicted_staged_count
|
||||
}
|
||||
|
||||
|
@ -1564,7 +1590,17 @@ impl GitPanel {
|
|||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(self.render_repository_selector(cx)),
|
||||
.child(self.render_repository_selector(cx))
|
||||
.child(div().flex_grow())
|
||||
.child(
|
||||
Button::new("diff", "+/-")
|
||||
.tooltip(Tooltip::for_action_title("Open diff", &Diff))
|
||||
.on_click(|_, _, cx| {
|
||||
cx.defer(|cx| {
|
||||
cx.dispatch_action(&Diff);
|
||||
})
|
||||
}),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
|
@ -1587,48 +1623,21 @@ impl GitPanel {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn render_commit_editor(
|
||||
&self,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let editor = self.commit_editor.clone();
|
||||
let can_commit = (self.has_staged_changes() || self.has_tracked_changes())
|
||||
&& self.pending_commit.is_none()
|
||||
&& !editor.read(cx).is_empty(cx)
|
||||
&& !self.has_unstaged_conflicts()
|
||||
&& self.has_write_access(cx);
|
||||
pub fn can_commit(&self) -> bool {
|
||||
(self.has_staged_changes() || self.has_tracked_changes()) && !self.has_unstaged_conflicts()
|
||||
}
|
||||
|
||||
// let can_commit_all =
|
||||
// !self.commit_pending && self.can_commit_all && !editor.read(cx).is_empty(cx);
|
||||
let panel_editor_style = panel_editor_style(true, window, cx);
|
||||
pub fn can_stage_all(&self) -> bool {
|
||||
self.has_unstaged_changes()
|
||||
}
|
||||
|
||||
let editor_focus_handle = editor.read(cx).focus_handle(cx).clone();
|
||||
|
||||
let focus_handle_1 = self.focus_handle(cx).clone();
|
||||
let tooltip = if self.has_staged_changes() {
|
||||
"Commit staged changes"
|
||||
} else {
|
||||
"Commit changes to tracked files"
|
||||
};
|
||||
let title = if self.has_staged_changes() {
|
||||
"Commit"
|
||||
} else {
|
||||
"Commit All"
|
||||
};
|
||||
|
||||
let commit_button = panel_filled_button(title)
|
||||
.tooltip(move |window, cx| {
|
||||
let focus_handle = focus_handle_1.clone();
|
||||
Tooltip::for_action_in(tooltip, &Commit, &focus_handle, window, cx)
|
||||
})
|
||||
.disabled(!can_commit)
|
||||
.on_click({
|
||||
cx.listener(move |this, _: &ClickEvent, window, cx| this.commit_changes(window, cx))
|
||||
});
|
||||
pub fn can_unstage_all(&self) -> bool {
|
||||
self.has_staged_changes()
|
||||
}
|
||||
|
||||
pub(crate) fn render_co_authors(&self, cx: &Context<Self>) -> Option<AnyElement> {
|
||||
let potential_co_authors = self.potential_co_authors(cx);
|
||||
let enable_coauthors = if potential_co_authors.is_empty() {
|
||||
if potential_co_authors.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
|
@ -1654,9 +1663,48 @@ impl GitPanel {
|
|||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.add_coauthors = !this.add_coauthors;
|
||||
cx.notify();
|
||||
})),
|
||||
}))
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_commit_editor(
|
||||
&self,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let editor = self.commit_editor.clone();
|
||||
let can_commit = self.can_commit()
|
||||
&& self.pending_commit.is_none()
|
||||
&& !editor.read(cx).is_empty(cx)
|
||||
&& self.has_write_access(cx);
|
||||
let panel_editor_style = panel_editor_style(true, window, cx);
|
||||
let enable_coauthors = self.render_co_authors(cx);
|
||||
|
||||
let editor_focus_handle = editor.read(cx).focus_handle(cx).clone();
|
||||
|
||||
let focus_handle_1 = self.focus_handle(cx).clone();
|
||||
let tooltip = if self.has_staged_changes() {
|
||||
"Commit staged changes"
|
||||
} else {
|
||||
"Commit changes to tracked files"
|
||||
};
|
||||
let title = if self.has_staged_changes() {
|
||||
"Commit"
|
||||
} else {
|
||||
"Commit All"
|
||||
};
|
||||
|
||||
let commit_button = panel_filled_button(title)
|
||||
.tooltip(move |window, cx| {
|
||||
let focus_handle = focus_handle_1.clone();
|
||||
Tooltip::for_action_in(tooltip, &Commit, &focus_handle, window, cx)
|
||||
})
|
||||
.disabled(!can_commit)
|
||||
.on_click({
|
||||
cx.listener(move |this, _: &ClickEvent, window, cx| this.commit_changes(window, cx))
|
||||
});
|
||||
|
||||
let branch = self
|
||||
.active_repository
|
||||
|
@ -1698,26 +1746,28 @@ impl GitPanel {
|
|||
.on_click(cx.listener(move |_, _: &ClickEvent, window, _cx| {
|
||||
window.focus(&editor_focus_handle);
|
||||
}))
|
||||
.child(EditorElement::new(&self.commit_editor, panel_editor_style))
|
||||
.child(
|
||||
h_flex()
|
||||
.absolute()
|
||||
.bottom_0()
|
||||
.left_2()
|
||||
.h(footer_size)
|
||||
.flex_none()
|
||||
.child(branch_selector),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.absolute()
|
||||
.bottom_0()
|
||||
.right_2()
|
||||
.h(footer_size)
|
||||
.flex_none()
|
||||
.children(enable_coauthors)
|
||||
.child(commit_button),
|
||||
)
|
||||
.when(!self.modal_open, |el| {
|
||||
el.child(EditorElement::new(&self.commit_editor, panel_editor_style))
|
||||
.child(
|
||||
h_flex()
|
||||
.absolute()
|
||||
.bottom_0()
|
||||
.left_2()
|
||||
.h(footer_size)
|
||||
.flex_none()
|
||||
.child(branch_selector),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.absolute()
|
||||
.bottom_0()
|
||||
.right_2()
|
||||
.h(footer_size)
|
||||
.flex_none()
|
||||
.children(enable_coauthors)
|
||||
.child(commit_button),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn render_previous_commit(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
|
||||
|
@ -1892,8 +1942,8 @@ impl GitPanel {
|
|||
Some(
|
||||
h_flex()
|
||||
.id("start-slot")
|
||||
.text_lg()
|
||||
.child(checkbox)
|
||||
.child(git_status_icon(entry.status_entry()?.status, cx))
|
||||
.on_mouse_down(MouseButton::Left, |_, _, cx| {
|
||||
// prevent the list item active state triggering when toggling checkbox
|
||||
cx.stop_propagation();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue