
Also prep infrastructure for pushing a commit Release Notes: - N/A --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> Co-authored-by: Conrad <conrad@zed.dev> Co-authored-by: Nate Butler <iamnbutler@gmail.com>
307 lines
10 KiB
Rust
307 lines
10 KiB
Rust
#![allow(unused, dead_code)]
|
|
|
|
use crate::repository_selector::RepositorySelector;
|
|
use anyhow::Result;
|
|
use git::{CommitAllChanges, CommitChanges};
|
|
use language::Buffer;
|
|
use panel::{panel_editor_container, panel_editor_style, panel_filled_button, panel_icon_button};
|
|
use ui::{prelude::*, Tooltip};
|
|
|
|
use editor::{Editor, EditorElement, EditorMode, MultiBuffer};
|
|
use gpui::*;
|
|
use project::git::Repository;
|
|
use project::{Fs, Project};
|
|
use std::sync::Arc;
|
|
use workspace::{ModalView, Workspace};
|
|
|
|
actions!(
|
|
git,
|
|
[QuickCommitWithMessage, QuickCommitStaged, QuickCommitAll]
|
|
);
|
|
|
|
pub fn init(cx: &mut App) {
|
|
cx.observe_new(|workspace: &mut Workspace, window, cx| {
|
|
let Some(window) = window else {
|
|
return;
|
|
};
|
|
QuickCommitModal::register(workspace, window, cx)
|
|
})
|
|
.detach();
|
|
}
|
|
|
|
fn commit_message_editor(
|
|
commit_message_buffer: Option<Entity<Buffer>>,
|
|
window: &mut Window,
|
|
cx: &mut Context<'_, Editor>,
|
|
) -> Editor {
|
|
let mut commit_editor = if let Some(commit_message_buffer) = commit_message_buffer {
|
|
let buffer = cx.new(|cx| MultiBuffer::singleton(commit_message_buffer, cx));
|
|
Editor::new(
|
|
EditorMode::AutoHeight { max_lines: 10 },
|
|
buffer,
|
|
None,
|
|
false,
|
|
window,
|
|
cx,
|
|
)
|
|
} else {
|
|
Editor::auto_height(10, window, cx)
|
|
};
|
|
commit_editor.set_use_autoclose(false);
|
|
commit_editor.set_show_gutter(false, cx);
|
|
commit_editor.set_show_wrap_guides(false, cx);
|
|
commit_editor.set_show_indent_guides(false, cx);
|
|
commit_editor.set_placeholder_text("Enter commit message", cx);
|
|
commit_editor
|
|
}
|
|
|
|
pub struct QuickCommitModal {
|
|
focus_handle: FocusHandle,
|
|
fs: Arc<dyn Fs>,
|
|
project: Entity<Project>,
|
|
active_repository: Option<Entity<Repository>>,
|
|
repository_selector: Entity<RepositorySelector>,
|
|
commit_editor: Entity<Editor>,
|
|
width: Option<Pixels>,
|
|
commit_task: Task<Result<()>>,
|
|
commit_pending: bool,
|
|
can_commit: bool,
|
|
can_commit_all: bool,
|
|
enable_auto_coauthors: bool,
|
|
}
|
|
|
|
impl Focusable for QuickCommitModal {
|
|
fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
|
|
self.focus_handle.clone()
|
|
}
|
|
}
|
|
|
|
impl EventEmitter<DismissEvent> for QuickCommitModal {}
|
|
impl ModalView for QuickCommitModal {}
|
|
|
|
impl QuickCommitModal {
|
|
pub fn register(workspace: &mut Workspace, _: &mut Window, cx: &mut Context<Workspace>) {
|
|
workspace.register_action(|workspace, _: &QuickCommitWithMessage, window, cx| {
|
|
let project = workspace.project().clone();
|
|
let fs = workspace.app_state().fs.clone();
|
|
|
|
workspace.toggle_modal(window, cx, move |window, cx| {
|
|
QuickCommitModal::new(project, fs, window, None, cx)
|
|
});
|
|
});
|
|
}
|
|
|
|
pub fn new(
|
|
project: Entity<Project>,
|
|
fs: Arc<dyn Fs>,
|
|
window: &mut Window,
|
|
commit_message_buffer: Option<Entity<Buffer>>,
|
|
cx: &mut Context<Self>,
|
|
) -> Self {
|
|
let git_store = project.read(cx).git_store().clone();
|
|
let active_repository = project.read(cx).active_repository(cx);
|
|
|
|
let focus_handle = cx.focus_handle();
|
|
|
|
let commit_editor = cx.new(|cx| commit_message_editor(commit_message_buffer, window, cx));
|
|
commit_editor.update(cx, |editor, cx| {
|
|
editor.clear(window, cx);
|
|
});
|
|
|
|
let repository_selector = cx.new(|cx| RepositorySelector::new(project.clone(), window, cx));
|
|
|
|
Self {
|
|
focus_handle,
|
|
fs,
|
|
project,
|
|
active_repository,
|
|
repository_selector,
|
|
commit_editor,
|
|
width: None,
|
|
commit_task: Task::ready(Ok(())),
|
|
commit_pending: false,
|
|
can_commit: false,
|
|
can_commit_all: false,
|
|
enable_auto_coauthors: true,
|
|
}
|
|
}
|
|
|
|
pub fn render_header(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
|
let all_repositories = self
|
|
.project
|
|
.read(cx)
|
|
.git_store()
|
|
.read(cx)
|
|
.all_repositories();
|
|
let entry_count = self
|
|
.active_repository
|
|
.as_ref()
|
|
.map_or(0, |repo| repo.read(cx).entry_count());
|
|
|
|
let changes_string = match entry_count {
|
|
0 => "No changes".to_string(),
|
|
1 => "1 change".to_string(),
|
|
n => format!("{} changes", n),
|
|
};
|
|
|
|
div().absolute().top_0().right_0().child(
|
|
panel_icon_button("open_change_list", IconName::PanelRight)
|
|
.disabled(true)
|
|
.tooltip(Tooltip::text("Changes list coming soon!")),
|
|
)
|
|
}
|
|
|
|
pub fn render_commit_editor(
|
|
&self,
|
|
name_and_email: Option<(SharedString, SharedString)>,
|
|
window: &mut Window,
|
|
cx: &mut Context<Self>,
|
|
) -> impl IntoElement {
|
|
let editor = self.commit_editor.clone();
|
|
let can_commit = !self.commit_pending && self.can_commit && !editor.read(cx).is_empty(cx);
|
|
let editor_focus_handle = editor.read(cx).focus_handle(cx).clone();
|
|
|
|
let focus_handle_1 = self.focus_handle(cx).clone();
|
|
let focus_handle_2 = self.focus_handle(cx).clone();
|
|
|
|
let panel_editor_style = panel_editor_style(true, window, cx);
|
|
|
|
let commit_staged_button = panel_filled_button("Commit")
|
|
.tooltip(move |window, cx| {
|
|
let focus_handle = focus_handle_1.clone();
|
|
Tooltip::for_action_in(
|
|
"Commit all staged changes",
|
|
&CommitChanges,
|
|
&focus_handle,
|
|
window,
|
|
cx,
|
|
)
|
|
})
|
|
.when(!can_commit, |this| {
|
|
this.disabled(true).style(ButtonStyle::Transparent)
|
|
});
|
|
// .on_click({
|
|
// let name_and_email = name_and_email.clone();
|
|
// cx.listener(move |this, _: &ClickEvent, window, cx| {
|
|
// this.commit_changes(&CommitChanges, name_and_email.clone(), window, cx)
|
|
// })
|
|
// });
|
|
|
|
let commit_all_button = panel_filled_button("Commit All")
|
|
.tooltip(move |window, cx| {
|
|
let focus_handle = focus_handle_2.clone();
|
|
Tooltip::for_action_in(
|
|
"Commit all changes, including unstaged changes",
|
|
&CommitAllChanges,
|
|
&focus_handle,
|
|
window,
|
|
cx,
|
|
)
|
|
})
|
|
.when(!can_commit, |this| {
|
|
this.disabled(true).style(ButtonStyle::Transparent)
|
|
});
|
|
// .on_click({
|
|
// let name_and_email = name_and_email.clone();
|
|
// cx.listener(move |this, _: &ClickEvent, window, cx| {
|
|
// this.commit_tracked_changes(
|
|
// &CommitAllChanges,
|
|
// name_and_email.clone(),
|
|
// window,
|
|
// cx,
|
|
// )
|
|
// })
|
|
// });
|
|
|
|
let co_author_button = panel_icon_button("add-co-author", IconName::UserGroup)
|
|
.icon_color(if self.enable_auto_coauthors {
|
|
Color::Muted
|
|
} else {
|
|
Color::Accent
|
|
})
|
|
.icon_size(IconSize::Small)
|
|
.toggle_state(self.enable_auto_coauthors)
|
|
// .on_click({
|
|
// cx.listener(move |this, _: &ClickEvent, _, cx| {
|
|
// this.toggle_auto_coauthors(cx);
|
|
// })
|
|
// })
|
|
.tooltip(move |window, cx| {
|
|
Tooltip::with_meta(
|
|
"Toggle automatic co-authors",
|
|
None,
|
|
"Automatically adds current collaborators",
|
|
window,
|
|
cx,
|
|
)
|
|
});
|
|
|
|
panel_editor_container(window, cx)
|
|
.id("commit-editor-container")
|
|
.relative()
|
|
.w_full()
|
|
.border_t_1()
|
|
.border_color(cx.theme().colors().border)
|
|
.h(px(140.))
|
|
.bg(cx.theme().colors().editor_background)
|
|
.on_click(cx.listener(move |_, _: &ClickEvent, window, _cx| {
|
|
window.focus(&editor_focus_handle);
|
|
}))
|
|
.child(EditorElement::new(&self.commit_editor, panel_editor_style))
|
|
.child(div().flex_1())
|
|
.child(
|
|
h_flex()
|
|
.items_center()
|
|
.h_8()
|
|
.justify_between()
|
|
.gap_1()
|
|
.child(co_author_button)
|
|
.child(commit_all_button)
|
|
.child(commit_staged_button),
|
|
)
|
|
}
|
|
|
|
pub fn render_footer(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
|
h_flex()
|
|
.w_full()
|
|
.justify_between()
|
|
.child(h_flex().child("cmd+esc clear message"))
|
|
.child(
|
|
h_flex()
|
|
.child(panel_filled_button("Commit"))
|
|
.child(panel_filled_button("Commit All")),
|
|
)
|
|
}
|
|
|
|
fn dismiss(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
|
|
cx.emit(DismissEvent);
|
|
}
|
|
}
|
|
|
|
impl Render for QuickCommitModal {
|
|
fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
|
|
v_flex()
|
|
.id("quick-commit-modal")
|
|
.key_context("QuickCommit")
|
|
.on_action(cx.listener(Self::dismiss))
|
|
.relative()
|
|
.bg(cx.theme().colors().elevated_surface_background)
|
|
.rounded(px(16.))
|
|
.border_1()
|
|
.border_color(cx.theme().colors().border)
|
|
.py_2()
|
|
.px_4()
|
|
.w(self.width.unwrap_or(px(640.)))
|
|
.h(px(450.))
|
|
.flex_1()
|
|
.overflow_hidden()
|
|
.child(self.render_header(window, cx))
|
|
.child(
|
|
v_flex()
|
|
.flex_1()
|
|
// TODO: pass name_and_email
|
|
.child(self.render_commit_editor(None, window, cx)),
|
|
)
|
|
.child(self.render_footer(window, cx))
|
|
}
|
|
}
|