use anyhow::anyhow;use commit_modal::CommitModal;use editor::{Editor, actions::DiffClipboardWithSelectionData}; use ui::{ Color, Headline, HeadlineSize, Icon, IconName, IconSize, IntoElement, ParentElement, Render, Styled, StyledExt, div, h_flex, rems, v_flex,};use workspace::{Toast, notifications::NotificationId}; mod blame_ui;pub mod clone; use git::{ repository::{Branch, CommitDetails, Upstream, UpstreamTracking, UpstreamTrackingStatus}, status::{FileStatus, StatusCode, UnmergedStatus, UnmergedStatusCode},};use gpui::{ App, ClipboardItem, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, SharedString, Subscription, Task, TaskExt, Window,};use menu::{Cancel, Confirm};use project::git_store::Repository;use project_diff::ProjectDiff;use time::OffsetDateTime;use ui::prelude::*;use workspace::{ModalView, OpenMode, Workspace, notifications::DetachAndPromptErr};use zed_actions; use crate::{commit_view::CommitView, git_panel::GitPanel, text_diff_view::TextDiffView}; mod askpass_modal;pub mod branch_picker;mod commit_modal;pub mod commit_tooltip;pub mod commit_view;mod conflict_view;pub mod file_diff_view;pub mod git_panel;mod git_panel_settings;pub mod git_picker;mod git_runtime_diagnostics;pub mod multi_diff_view;pub mod picker_prompt;pub mod project_diff;pub(crate) mod remote_output;pub mod repository_selector;pub mod stash_picker;pub mod text_diff_view;pub mod worktree_names;pub mod worktree_picker;pub mod worktree_service; pub use conflict_view::MergeConflictIndicator; pub fn get_provider_icon(name: &str) -> IconName { match name { "Bitbucket" => IconName::Bitbucket, "Codeberg" => IconName::Codeberg, "Forgejo Self-Hosted" => IconName::Forgejo, "GitHub" => IconName::Github, "GitLab" => IconName::Gitlab, "Gitea" => IconName::Gitea, _ => IconName::Link, }} pub fn init(cx: &mut App) { editor::set_blame_renderer(blame_ui::GitBlameRenderer, cx); commit_view::init(cx); cx.observe_new(|editor: &mut Editor, _, cx| { conflict_view::register_editor(editor, editor.buffer().clone(), cx); }) .detach(); cx.observe_new(|workspace: &mut Workspace, _, cx| { ProjectDiff::register(workspace, cx); CommitModal::register(workspace); git_panel::register(workspace); repository_selector::register(workspace); git_picker::register(workspace); workspace.register_action( |workspace, action: &zed_actions::CreateWorktree, window, cx| { worktree_service::handle_create_worktree(workspace, action, window, None, cx); }, ); workspace.register_action( |workspace, action: &zed_actions::SwitchWorktree, window, cx| { worktree_service::handle_switch_worktree(workspace, action, window, None, cx); }, ); workspace.register_action(|workspace, _: &zed_actions::git::Worktree, window, cx| { let focused_dock = workspace.focused_dock_position(window, cx); let project = workspace.project().clone(); let workspace_handle = workspace.weak_handle(); workspace.toggle_modal(window, cx, |window, cx| { worktree_picker::WorktreePicker::new_modal( project, workspace_handle, focused_dock, window, cx, ) }); }); workspace.register_action( |workspace, action: &zed_actions::OpenWorktreeInNewWindow, window, cx| { let path = action.path.clone(); let is_remote = !workspace.project().read(cx).is_local(); if is_remote { let connection_options = workspace.project().read(cx).remote_connection_options(cx); let app_state = workspace.app_state().clone(); let workspace_handle = workspace.weak_handle(); cx.spawn_in(window, async move |_, cx| { if let Some(connection_options) = connection_options { crate::worktree_picker::open_remote_worktree( connection_options, vec![path], app_state, workspace_handle, cx, ) .await?; } anyhow::Ok(()) }) .detach_and_log_err(cx); } else { workspace .open_workspace_for_paths(OpenMode::NewWindow, vec![path], window, cx) .detach_and_log_err(cx); } }, ); let project = workspace.project().read(cx); if project.is_read_only(cx) { return; } if !project.is_via_collab() { workspace.register_action( |workspace, _: &zed_actions::git::CreatePullRequest, window, cx| { if let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) { panel.update(cx, |panel, cx| { panel.create_pull_request(window, cx); }); } }, ); workspace.register_action(|workspace, _: &git::Fetch, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.fetch(true, window, cx); }); }); workspace.register_action(|workspace, _: &git::FetchFrom, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.fetch(false, window, cx); }); }); workspace.register_action(|workspace, _: &git::Push, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.push(false, false, window, cx); }); }); workspace.register_action(|workspace, _: &git::PushTo, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.push(false, true, window, cx); }); }); workspace.register_action(|workspace, _: &git::ForcePush, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.push(true, false, window, cx); }); }); workspace.register_action(|workspace, _: &git::Pull, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.pull(false, window, cx); }); }); workspace.register_action(|workspace, _: &git::PullRebase, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.pull(true, window, cx); }); }); } workspace.register_action(|workspace, action: &git::StashAll, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.stash_all(action, window, cx); }); }); workspace.register_action(|workspace, action: &git::StashPop, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.stash_pop(action, window, cx); }); }); workspace.register_action(|workspace, action: &git::StashApply, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.stash_apply(action, window, cx); }); }); workspace.register_action(|workspace, action: &git::StageAll, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.stage_all(action, window, cx); }); }); workspace.register_action(|workspace, action: &git::UnstageAll, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.unstage_all(action, window, cx); }); }); workspace.register_action(|workspace, _: &git::Uncommit, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.uncommit(window, cx); }) }); workspace.register_action(|workspace, _action: &git::Init, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.git_init(window, cx); }); }); workspace.register_action(|workspace, _action: &git::Clone, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; workspace.toggle_modal(window, cx, |window, cx| { GitCloneModal::show(panel, window, cx) }); }); workspace.register_action(|workspace, _: &git::OpenModifiedFiles, window, cx| { open_modified_files(workspace, window, cx); }); workspace.register_action(|workspace, _: &git::RenameBranch, window, cx| { rename_current_branch(workspace, window, cx); }); workspace.register_action(|workspace, _: &git::CopyBranchName, _, cx| { copy_branch_name(workspace, cx); }); workspace.register_action(show_ref_picker); workspace.register_action( |workspace, action: &DiffClipboardWithSelectionData, window, cx| { if let Some(task) = TextDiffView::open(action, workspace, window, cx) { task.detach(); }; }, ); }) .detach();} fn open_modified_files( workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>,) { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; let modified_paths: Vec<_> = panel.update(cx, |panel, cx| { let Some(repo) = panel.active_repository.as_ref() else { return Vec::new(); }; let repo = repo.read(cx); repo.cached_status() .filter_map(|entry| { if entry.status.is_modified() { repo.repo_path_to_project_path(&entry.repo_path, cx) } else { None } }) .collect() }); for path in modified_paths { workspace.open_path(path, None, true, window, cx).detach(); }} pub fn git_status_icon(status: FileStatus) -> impl IntoElement { GitStatusIcon::new(status)} struct RenameBranchModal { current_branch: SharedString, editor: Entity<Editor>, repo: Entity<Repository>,} impl RenameBranchModal { fn new( current_branch: String, repo: Entity<Repository>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let editor = cx.new(|cx| { let mut editor = Editor::single_line(window, cx); editor.set_text(current_branch.clone(), window, cx); editor }); Self { current_branch: current_branch.into(), editor, repo, } } fn cancel(&mut self, _: &Cancel, _window: &mut Window, cx: &mut Context<Self>) { cx.emit(DismissEvent); } fn confirm(&mut self, _: &Confirm, window: &mut Window, cx: &mut Context<Self>) { let new_name = self.editor.read(cx).text(cx); if new_name.is_empty() || new_name == self.current_branch.as_ref() { cx.emit(DismissEvent); return; } let repo = self.repo.clone(); let current_branch = self.current_branch.to_string(); cx.spawn(async move |_, cx| { match repo .update(cx, |repo, _| { repo.rename_branch(current_branch, new_name.clone()) }) .await { Ok(Ok(_)) => Ok(()), Ok(Err(error)) => Err(error), Err(_) => Err(anyhow!("Operation was canceled")), } }) .detach_and_prompt_err("Failed to rename branch", window, cx, |_, _, _| None); cx.emit(DismissEvent); }} impl EventEmitter<DismissEvent> for RenameBranchModal {}impl ModalView for RenameBranchModal {}impl Focusable for RenameBranchModal { fn focus_handle(&self, cx: &App) -> FocusHandle { self.editor.focus_handle(cx) }} impl Render for RenameBranchModal { fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { v_flex() .key_context("RenameBranchModal") .on_action(cx.listener(Self::cancel)) .on_action(cx.listener(Self::confirm)) .elevation_2(cx) .w(rems(34.)) .child( h_flex() .px_3() .pt_2() .pb_1() .w_full() .gap_1p5() .child(Icon::new(IconName::GitBranch).size(IconSize::XSmall)) .child( Headline::new(format!("Rename Branch ({})", self.current_branch)) .size(HeadlineSize::XSmall), ), ) .child(div().px_3().pb_3().w_full().child(self.editor.clone())) }} fn rename_current_branch( workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>,) { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; let current_branch: Option<String> = panel.update(cx, |panel, cx| { let repo = panel.active_repository.as_ref()?; let repo = repo.read(cx); repo.branch.as_ref().map(|branch| branch.name().to_string()) }); let Some(current_branch_name) = current_branch else { return; }; let repo = panel.read(cx).active_repository.clone(); let Some(repo) = repo else { return; }; workspace.toggle_modal(window, cx, |window, cx| { RenameBranchModal::new(current_branch_name, repo, window, cx) });} fn copy_branch_name(workspace: &mut Workspace, cx: &mut Context<Workspace>) { let Some(panel) = workspace.panel::<GitPanel>(cx) else { return; }; let branch_name = panel.update(cx, |panel, cx| { let repo = panel.active_repository.as_ref()?; let repo = repo.read(cx); repo.branch.as_ref().map(|branch| branch.name().to_string()) }); if let Some(name) = branch_name { cx.write_to_clipboard(ClipboardItem::new_string(name)); }} struct RefPickerModal { editor: Entity<Editor>, repo: Entity<Repository>, workspace: Entity<Workspace>, commit_details: Option<CommitDetails>, lookup_task: Option<Task<()>>, _editor_subscription: Subscription,} impl RefPickerModal { fn new( repo: Entity<Repository>, workspace: Entity<Workspace>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let editor = cx.new(|cx| { let mut editor = Editor::single_line(window, cx); editor.set_placeholder_text("Enter git ref...", window, cx); editor }); let _editor_subscription = cx.subscribe_in( &editor, window, |this, _editor, event: &editor::EditorEvent, window, cx| { if let editor::EditorEvent::BufferEdited = event { this.lookup_commit_details(window, cx); } }, ); Self { editor, repo, workspace, commit_details: None, lookup_task: None, _editor_subscription, }Lines 1–500 of 1259. Load more
use anyhow::anyhow; use commit_modal::CommitModal; use editor::{Editor, actions::DiffClipboardWithSelectionData}; use ui::{ Color, Headline, HeadlineSize, Icon, IconName, IconSize, IntoElement, ParentElement, Render, Styled, StyledExt, div, h_flex, rems, v_flex, }; use workspace::{Toast, notifications::NotificationId}; mod blame_ui; pub mod clone; use git::{ repository::{Branch, CommitDetails, Upstream, UpstreamTracking, UpstreamTrackingStatus}, status::{FileStatus, StatusCode, UnmergedStatus, UnmergedStatusCode}, }; use gpui::{ App, ClipboardItem, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, SharedString, Subscription, Task, TaskExt, Window, }; use menu::{Cancel, Confirm}; use project::git_store::Repository; use project_diff::ProjectDiff; use time::OffsetDateTime; use ui::prelude::*; use workspace::{ModalView, OpenMode, Workspace, notifications::DetachAndPromptErr}; use zed_actions; use crate::{commit_view::CommitView, git_panel::GitPanel, text_diff_view::TextDiffView}; mod askpass_modal; pub mod branch_picker; mod commit_modal; pub mod commit_tooltip; pub mod commit_view; mod conflict_view; pub mod file_diff_view; pub mod git_panel; mod git_panel_settings; pub mod git_picker; mod git_runtime_diagnostics; pub mod multi_diff_view; pub mod picker_prompt; pub mod project_diff; pub(crate) mod remote_output; pub mod repository_selector; pub mod stash_picker; pub mod text_diff_view; pub mod worktree_names; pub mod worktree_picker; pub mod worktree_service; pub use conflict_view::MergeConflictIndicator; pub fn get_provider_icon(name: &str) -> IconName { match name { "Bitbucket" => IconName::Bitbucket, "Codeberg" => IconName::Codeberg, "Forgejo Self-Hosted" => IconName::Forgejo, "GitHub" => IconName::Github, "GitLab" => IconName::Gitlab, "Gitea" => IconName::Gitea, _ => IconName::Link, } } pub fn init(cx: &mut App) { editor::set_blame_renderer(blame_ui::GitBlameRenderer, cx); commit_view::init(cx); cx.observe_new(|editor: &mut Editor, _, cx| { conflict_view::register_editor(editor, editor.buffer().clone(), cx); }) .detach(); cx.observe_new(|workspace: &mut Workspace, _, cx| { ProjectDiff::register(workspace, cx); CommitModal::register(workspace); git_panel::register(workspace); repository_selector::register(workspace); git_picker::register(workspace); workspace.register_action( |workspace, action: &zed_actions::CreateWorktree, window, cx| { worktree_service::handle_create_worktree(workspace, action, window, None, cx); }, ); workspace.register_action( |workspace, action: &zed_actions::SwitchWorktree, window, cx| { worktree_service::handle_switch_worktree(workspace, action, window, None, cx); }, ); workspace.register_action(|workspace, _: &zed_actions::git::Worktree, window, cx| { let focused_dock = workspace.focused_dock_position(window, cx); let project = workspace.project().clone(); let workspace_handle = workspace.weak_handle(); workspace.toggle_modal(window, cx, |window, cx| { worktree_picker::WorktreePicker::new_modal( project, workspace_handle, focused_dock, window, cx, ) }); }); workspace.register_action( |workspace, action: &zed_actions::OpenWorktreeInNewWindow, window, cx| { let path = action.path.clone(); let is_remote = !workspace.project().read(cx).is_local(); if is_remote { let connection_options = workspace.project().read(cx).remote_connection_options(cx); let app_state = workspace.app_state().clone(); let workspace_handle = workspace.weak_handle(); cx.spawn_in(window, async move |_, cx| { if let Some(connection_options) = connection_options { crate::worktree_picker::open_remote_worktree( connection_options, vec![path], app_state, workspace_handle, cx, ) .await?; } anyhow::Ok(()) }) .detach_and_log_err(cx); } else { workspace .open_workspace_for_paths(OpenMode::NewWindow, vec![path], window, cx) .detach_and_log_err(cx); } }, ); let project = workspace.project().read(cx); if project.is_read_only(cx) { return; } if !project.is_via_collab() { workspace.register_action( |workspace, _: &zed_actions::git::CreatePullRequest, window, cx| { if let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) { panel.update(cx, |panel, cx| { panel.create_pull_request(window, cx); }); } }, ); workspace.register_action(|workspace, _: &git::Fetch, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.fetch(true, window, cx); }); }); workspace.register_action(|workspace, _: &git::FetchFrom, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.fetch(false, window, cx); }); }); workspace.register_action(|workspace, _: &git::Push, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.push(false, false, window, cx); }); }); workspace.register_action(|workspace, _: &git::PushTo, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.push(false, true, window, cx); }); }); workspace.register_action(|workspace, _: &git::ForcePush, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.push(true, false, window, cx); }); }); workspace.register_action(|workspace, _: &git::Pull, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.pull(false, window, cx); }); }); workspace.register_action(|workspace, _: &git::PullRebase, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.pull(true, window, cx); }); }); } workspace.register_action(|workspace, action: &git::StashAll, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.stash_all(action, window, cx); }); }); workspace.register_action(|workspace, action: &git::StashPop, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.stash_pop(action, window, cx); }); }); workspace.register_action(|workspace, action: &git::StashApply, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.stash_apply(action, window, cx); }); }); workspace.register_action(|workspace, action: &git::StageAll, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.stage_all(action, window, cx); }); }); workspace.register_action(|workspace, action: &git::UnstageAll, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.unstage_all(action, window, cx); }); }); workspace.register_action(|workspace, _: &git::Uncommit, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.uncommit(window, cx); }) }); workspace.register_action(|workspace, _action: &git::Init, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; panel.update(cx, |panel, cx| { panel.git_init(window, cx); }); }); workspace.register_action(|workspace, _action: &git::Clone, window, cx| { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; workspace.toggle_modal(window, cx, |window, cx| { GitCloneModal::show(panel, window, cx) }); }); workspace.register_action(|workspace, _: &git::OpenModifiedFiles, window, cx| { open_modified_files(workspace, window, cx); }); workspace.register_action(|workspace, _: &git::RenameBranch, window, cx| { rename_current_branch(workspace, window, cx); }); workspace.register_action(|workspace, _: &git::CopyBranchName, _, cx| { copy_branch_name(workspace, cx); }); workspace.register_action(show_ref_picker); workspace.register_action( |workspace, action: &DiffClipboardWithSelectionData, window, cx| { if let Some(task) = TextDiffView::open(action, workspace, window, cx) { task.detach(); }; }, ); }) .detach(); } fn open_modified_files( workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>, ) { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; let modified_paths: Vec<_> = panel.update(cx, |panel, cx| { let Some(repo) = panel.active_repository.as_ref() else { return Vec::new(); }; let repo = repo.read(cx); repo.cached_status() .filter_map(|entry| { if entry.status.is_modified() { repo.repo_path_to_project_path(&entry.repo_path, cx) } else { None } }) .collect() }); for path in modified_paths { workspace.open_path(path, None, true, window, cx).detach(); } } pub fn git_status_icon(status: FileStatus) -> impl IntoElement { GitStatusIcon::new(status) } struct RenameBranchModal { current_branch: SharedString, editor: Entity<Editor>, repo: Entity<Repository>, } impl RenameBranchModal { fn new( current_branch: String, repo: Entity<Repository>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let editor = cx.new(|cx| { let mut editor = Editor::single_line(window, cx); editor.set_text(current_branch.clone(), window, cx); editor }); Self { current_branch: current_branch.into(), editor, repo, } } fn cancel(&mut self, _: &Cancel, _window: &mut Window, cx: &mut Context<Self>) { cx.emit(DismissEvent); } fn confirm(&mut self, _: &Confirm, window: &mut Window, cx: &mut Context<Self>) { let new_name = self.editor.read(cx).text(cx); if new_name.is_empty() || new_name == self.current_branch.as_ref() { cx.emit(DismissEvent); return; } let repo = self.repo.clone(); let current_branch = self.current_branch.to_string(); cx.spawn(async move |_, cx| { match repo .update(cx, |repo, _| { repo.rename_branch(current_branch, new_name.clone()) }) .await { Ok(Ok(_)) => Ok(()), Ok(Err(error)) => Err(error), Err(_) => Err(anyhow!("Operation was canceled")), } }) .detach_and_prompt_err("Failed to rename branch", window, cx, |_, _, _| None); cx.emit(DismissEvent); } } impl EventEmitter<DismissEvent> for RenameBranchModal {} impl ModalView for RenameBranchModal {} impl Focusable for RenameBranchModal { fn focus_handle(&self, cx: &App) -> FocusHandle { self.editor.focus_handle(cx) } } impl Render for RenameBranchModal { fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { v_flex() .key_context("RenameBranchModal") .on_action(cx.listener(Self::cancel)) .on_action(cx.listener(Self::confirm)) .elevation_2(cx) .w(rems(34.)) .child( h_flex() .px_3() .pt_2() .pb_1() .w_full() .gap_1p5() .child(Icon::new(IconName::GitBranch).size(IconSize::XSmall)) .child( Headline::new(format!("Rename Branch ({})", self.current_branch)) .size(HeadlineSize::XSmall), ), ) .child(div().px_3().pb_3().w_full().child(self.editor.clone())) } } fn rename_current_branch( workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>, ) { let Some(panel) = workspace.panel::<git_panel::GitPanel>(cx) else { return; }; let current_branch: Option<String> = panel.update(cx, |panel, cx| { let repo = panel.active_repository.as_ref()?; let repo = repo.read(cx); repo.branch.as_ref().map(|branch| branch.name().to_string()) }); let Some(current_branch_name) = current_branch else { return; }; let repo = panel.read(cx).active_repository.clone(); let Some(repo) = repo else { return; }; workspace.toggle_modal(window, cx, |window, cx| { RenameBranchModal::new(current_branch_name, repo, window, cx) }); } fn copy_branch_name(workspace: &mut Workspace, cx: &mut Context<Workspace>) { let Some(panel) = workspace.panel::<GitPanel>(cx) else { return; }; let branch_name = panel.update(cx, |panel, cx| { let repo = panel.active_repository.as_ref()?; let repo = repo.read(cx); repo.branch.as_ref().map(|branch| branch.name().to_string()) }); if let Some(name) = branch_name { cx.write_to_clipboard(ClipboardItem::new_string(name)); } } struct RefPickerModal { editor: Entity<Editor>, repo: Entity<Repository>, workspace: Entity<Workspace>, commit_details: Option<CommitDetails>, lookup_task: Option<Task<()>>, _editor_subscription: Subscription, } impl RefPickerModal { fn new( repo: Entity<Repository>, workspace: Entity<Workspace>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let editor = cx.new(|cx| { let mut editor = Editor::single_line(window, cx); editor.set_placeholder_text("Enter git ref...", window, cx); editor }); let _editor_subscription = cx.subscribe_in( &editor, window, |this, _editor, event: &editor::EditorEvent, window, cx| { if let editor::EditorEvent::BufferEdited = event { this.lookup_commit_details(window, cx); } }, ); Self { editor, repo, workspace, commit_details: None, lookup_task: None, _editor_subscription, }