#![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>, 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, project: Entity, active_repository: Option>, repository_selector: Entity, commit_editor: Entity, width: Option, commit_task: Task>, 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 for QuickCommitModal {} impl ModalView for QuickCommitModal {} impl QuickCommitModal { pub fn register(workspace: &mut Workspace, _: &mut Window, cx: &mut Context) { 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, fs: Arc, window: &mut Window, commit_message_buffer: Option>, cx: &mut Context, ) -> 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) -> 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, ) -> 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) -> 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) { 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)) } }