1259 lines · 41.5 KB · Rust
RawDownload
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.