8160 lines · 291.3 KB · Rust
RawDownload
pub mod dock;pub mod item;mod modal_layer;pub mod notifications;pub mod pane;pub mod pane_group;mod persistence;pub mod searchable;pub mod shared_screen;mod status_bar;pub mod tasks;mod theme_preview;mod toolbar;mod workspace_settings; use anyhow::{anyhow, Context as _, Result};use call::{call_settings::CallSettings, ActiveCall};use client::{    proto::{self, ErrorCode, PanelId, PeerId},    ChannelId, Client, ErrorExt, Status, TypedEnvelope, UserStore,};use collections::{hash_map, HashMap, HashSet};use derive_more::{Deref, DerefMut};use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle, RESIZE_HANDLE_SIZE};use futures::{    channel::{        mpsc::{self, UnboundedReceiver, UnboundedSender},        oneshot,    },    future::try_join_all,    Future, FutureExt, StreamExt,};use gpui::{    action_as, actions, canvas, impl_action_as, impl_actions, point, relative, size,    transparent_black, Action, AnyView, AnyWeakView, AppContext, AsyncAppContext,    AsyncWindowContext, Bounds, CursorStyle, Decorations, DragMoveEvent, Entity as _, EntityId,    EventEmitter, Flatten, FocusHandle, FocusableView, Global, Hsla, KeyContext, Keystroke,    ManagedView, Model, ModelContext, MouseButton, PathPromptOptions, Point, PromptLevel, Render,    ResizeEdge, Size, Stateful, Subscription, Task, Tiling, View, WeakView, WindowBounds,    WindowHandle, WindowId, WindowOptions,};pub use item::{    FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, PreviewTabsSettings,    ProjectItem, SerializableItem, SerializableItemHandle, WeakItemHandle,};use itertools::Itertools;use language::{LanguageRegistry, Rope};pub use modal_layer::*;use node_runtime::NodeRuntime;use notifications::{    simple_message_notification::MessageNotification, DetachAndPromptErr, NotificationHandle,};pub use pane::*;pub use pane_group::*;pub use persistence::{    model::{ItemId, LocalPaths, SerializedWorkspaceLocation},    WorkspaceDb, DB as WORKSPACE_DB,};use persistence::{    model::{SerializedSshProject, SerializedWorkspace},    SerializedWindowBounds, DB,};use postage::stream::Stream;use project::{    DirectoryLister, Project, ProjectEntryId, ProjectPath, ResolvedPath, Worktree, WorktreeId,};use remote::{ssh_session::ConnectionIdentifier, SshClientDelegate, SshConnectionOptions};use serde::Deserialize;use session::AppSession;use settings::Settings;use shared_screen::SharedScreen;use sqlez::{    bindable::{Bind, Column, StaticColumnCount},    statement::Statement,};use status_bar::StatusBar;pub use status_bar::StatusItemView;use std::{    any::TypeId,    borrow::Cow,    cell::RefCell,    cmp,    collections::hash_map::DefaultHasher,    env,    hash::{Hash, Hasher},    path::{Path, PathBuf},    rc::Rc,    sync::{atomic::AtomicUsize, Arc, LazyLock, Weak},    time::Duration,};use task::SpawnInTerminal;use theme::{ActiveTheme, SystemAppearance, ThemeSettings};pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};pub use ui;use ui::prelude::*;use util::{paths::SanitizedPath, ResultExt, TryFutureExt};use uuid::Uuid;pub use workspace_settings::{    AutosaveSetting, RestoreOnStartupBehavior, TabBarSettings, WorkspaceSettings,}; use crate::notifications::NotificationId;use crate::persistence::{    model::{DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup},    SerializedAxis,}; static ZED_WINDOW_SIZE: LazyLock<Option<Size<Pixels>>> = LazyLock::new(|| {    env::var("ZED_WINDOW_SIZE")        .ok()        .as_deref()        .and_then(parse_pixel_size_env_var)}); static ZED_WINDOW_POSITION: LazyLock<Option<Point<Pixels>>> = LazyLock::new(|| {    env::var("ZED_WINDOW_POSITION")        .ok()        .as_deref()        .and_then(parse_pixel_position_env_var)}); #[derive(Clone, PartialEq)]pub struct RemoveWorktreeFromProject(pub WorktreeId); actions!(assistant, [ShowConfiguration]); actions!(    workspace,    [        ActivateNextPane,        ActivatePreviousPane,        AddFolderToProject,        ClearAllNotifications,        CloseAllDocks,        CloseWindow,        CopyPath,        CopyRelativePath,        Feedback,        FollowNextCollaborator,        NewCenterTerminal,        NewFile,        NewFileSplitVertical,        NewFileSplitHorizontal,        NewSearch,        NewTerminal,        NewWindow,        Open,        OpenInTerminal,        ReloadActiveItem,        SaveAs,        SaveWithoutFormat,        ToggleBottomDock,        ToggleCenteredLayout,        ToggleLeftDock,        ToggleRightDock,        ToggleZoom,        Unfollow,        Welcome,    ]); #[derive(Clone, PartialEq)]pub struct OpenPaths {    pub paths: Vec<PathBuf>,} #[derive(Clone, Deserialize, PartialEq)]pub struct ActivatePane(pub usize); #[derive(Clone, Deserialize, PartialEq)]pub struct ActivatePaneInDirection(pub SplitDirection); #[derive(Clone, Deserialize, PartialEq)]pub struct SwapPaneInDirection(pub SplitDirection); #[derive(Clone, PartialEq, Debug, Deserialize)]#[serde(rename_all = "camelCase")]pub struct SaveAll {    pub save_intent: Option<SaveIntent>,} #[derive(Clone, PartialEq, Debug, Deserialize)]#[serde(rename_all = "camelCase")]pub struct Save {    pub save_intent: Option<SaveIntent>,} #[derive(Clone, PartialEq, Debug, Deserialize, Default)]#[serde(rename_all = "camelCase")]pub struct CloseAllItemsAndPanes {    pub save_intent: Option<SaveIntent>,} #[derive(Clone, PartialEq, Debug, Deserialize, Default)]#[serde(rename_all = "camelCase")]pub struct CloseInactiveTabsAndPanes {    pub save_intent: Option<SaveIntent>,} #[derive(Clone, Deserialize, PartialEq)]pub struct SendKeystrokes(pub String); #[derive(Clone, Deserialize, PartialEq, Default)]pub struct Reload {    pub binary_path: Option<PathBuf>,} action_as!(project_symbols, ToggleProjectSymbols as Toggle); #[derive(Default, PartialEq, Eq, Clone, serde::Deserialize)]pub struct ToggleFileFinder {    #[serde(default)]    pub separate_history: bool,} impl_action_as!(file_finder, ToggleFileFinder as Toggle); impl_actions!(    workspace,    [        ActivatePane,        ActivatePaneInDirection,        CloseAllItemsAndPanes,        CloseInactiveTabsAndPanes,        OpenTerminal,        Reload,        Save,        SaveAll,        SwapPaneInDirection,        SendKeystrokes,    ]); #[derive(PartialEq, Eq, Debug)]pub enum CloseIntent {    /// Quit the program entirely.    Quit,    /// Close a window.    CloseWindow,    /// Replace the workspace in an existing window.    ReplaceWindow,} #[derive(Clone)]pub struct Toast {    id: NotificationId,    msg: Cow<'static, str>,    autohide: bool,    on_click: Option<(Cow<'static, str>, Arc<dyn Fn(&mut WindowContext)>)>,} impl Toast {    pub fn new<I: Into<Cow<'static, str>>>(id: NotificationId, msg: I) -> Self {        Toast {            id,            msg: msg.into(),            on_click: None,            autohide: false,        }    }     pub fn on_click<F, M>(mut self, message: M, on_click: F) -> Self    where        M: Into<Cow<'static, str>>,        F: Fn(&mut WindowContext) + 'static,    {        self.on_click = Some((message.into(), Arc::new(on_click)));        self    }     pub fn autohide(mut self) -> Self {        self.autohide = true;        self    }} impl PartialEq for Toast {    fn eq(&self, other: &Self) -> bool {        self.id == other.id            && self.msg == other.msg            && self.on_click.is_some() == other.on_click.is_some()    }} #[derive(Debug, Default, Clone, Deserialize, PartialEq)]pub struct OpenTerminal {    pub working_directory: PathBuf,} #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]pub struct WorkspaceId(i64); impl StaticColumnCount for WorkspaceId {}impl Bind for WorkspaceId {    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {        self.0.bind(statement, start_index)    }}impl Column for WorkspaceId {    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {        i64::column(statement, start_index)            .map(|(i, next_index)| (Self(i), next_index))            .with_context(|| format!("Failed to read WorkspaceId at index {start_index}"))    }}impl From<WorkspaceId> for i64 {    fn from(val: WorkspaceId) -> Self {        val.0    }} pub fn init_settings(cx: &mut AppContext) {    WorkspaceSettings::register(cx);    ItemSettings::register(cx);    PreviewTabsSettings::register(cx);    TabBarSettings::register(cx);} pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {    init_settings(cx);    notifications::init(cx);    theme_preview::init(cx);     cx.on_action(Workspace::close_global);    cx.on_action(reload);     cx.on_action({        let app_state = Arc::downgrade(&app_state);        move |_: &Open, cx: &mut AppContext| {            let paths = cx.prompt_for_paths(PathPromptOptions {                files: true,                directories: true,                multiple: true,            });             if let Some(app_state) = app_state.upgrade() {                cx.spawn(move |cx| async move {                    match Flatten::flatten(paths.await.map_err(|e| e.into())) {                        Ok(Some(paths)) => {                            cx.update(|cx| {                                open_paths(&paths, app_state, OpenOptions::default(), cx)                                    .detach_and_log_err(cx)                            })                            .ok();                        }                        Ok(None) => {}                        Err(err) => {                            cx.update(|cx| {                                if let Some(workspace_window) = cx                                    .active_window()                                    .and_then(|window| window.downcast::<Workspace>())                                {                                    workspace_window                                        .update(cx, |workspace, cx| {                                            workspace.show_portal_error(err.to_string(), cx);                                        })                                        .ok();                                }                            })                            .ok();                        }                    };                })                .detach();            }        }    });} #[derive(Clone, Default, Deref, DerefMut)]struct ProjectItemOpeners(Vec<ProjectItemOpener>); type ProjectItemOpener = fn(    &Model<Project>,    &ProjectPath,    &mut WindowContext,)    -> Option<Task<Result<(Option<ProjectEntryId>, WorkspaceItemBuilder)>>>; type WorkspaceItemBuilder = Box<dyn FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>>; impl Global for ProjectItemOpeners {} /// Registers a [ProjectItem] for the app. When opening a file, all the registered/// items will get a chance to open the file, starting from the project item that/// was added last.pub fn register_project_item<I: ProjectItem>(cx: &mut AppContext) {    let builders = cx.default_global::<ProjectItemOpeners>();    builders.push(|project, project_path, cx| {        let project_item = <I::Item as project::ProjectItem>::try_open(project, project_path, cx)?;        let project = project.clone();        Some(cx.spawn(|cx| async move {            let project_item = project_item.await?;            let project_entry_id: Option<ProjectEntryId> =                project_item.read_with(&cx, project::ProjectItem::entry_id)?;            let build_workspace_item = Box::new(|cx: &mut ViewContext<Pane>| {                Box::new(cx.new_view(|cx| I::for_project_item(project, project_item, cx)))                    as Box<dyn ItemHandle>            }) as Box<_>;            Ok((project_entry_id, build_workspace_item))        }))    });} #[derive(Default)]pub struct FollowableViewRegistry(HashMap<TypeId, FollowableViewDescriptor>); struct FollowableViewDescriptor {    from_state_proto: fn(        View<Workspace>,        ViewId,        &mut Option<proto::view::Variant>,        &mut WindowContext,    ) -> Option<Task<Result<Box<dyn FollowableItemHandle>>>>,    to_followable_view: fn(&AnyView) -> Box<dyn FollowableItemHandle>,} impl Global for FollowableViewRegistry {} impl FollowableViewRegistry {    pub fn register<I: FollowableItem>(cx: &mut AppContext) {        cx.default_global::<Self>().0.insert(            TypeId::of::<I>(),            FollowableViewDescriptor {                from_state_proto: |workspace, id, state, cx| {                    I::from_state_proto(workspace, id, state, cx).map(|task| {                        cx.foreground_executor()                            .spawn(async move { Ok(Box::new(task.await?) as Box<_>) })                    })                },                to_followable_view: |view| Box::new(view.clone().downcast::<I>().unwrap()),            },        );    }     pub fn from_state_proto(        workspace: View<Workspace>,        view_id: ViewId,        mut state: Option<proto::view::Variant>,        cx: &mut WindowContext,    ) -> Option<Task<Result<Box<dyn FollowableItemHandle>>>> {        cx.update_default_global(|this: &mut Self, cx| {            this.0.values().find_map(|descriptor| {                (descriptor.from_state_proto)(workspace.clone(), view_id, &mut state, cx)            })        })    }     pub fn to_followable_view(        view: impl Into<AnyView>,        cx: &AppContext,    ) -> Option<Box<dyn FollowableItemHandle>> {        let this = cx.try_global::<Self>()?;        let view = view.into();        let descriptor = this.0.get(&view.entity_type())?;        Some((descriptor.to_followable_view)(&view))    }} #[derive(Copy, Clone)]struct SerializableItemDescriptor {    deserialize: fn(        Model<Project>,        WeakView<Workspace>,        WorkspaceId,        ItemId,        &mut ViewContext<Pane>,    ) -> Task<Result<Box<dyn ItemHandle>>>,    cleanup: fn(WorkspaceId, Vec<ItemId>, &mut WindowContext) -> Task<Result<()>>,    view_to_serializable_item: fn(AnyView) -> Box<dyn SerializableItemHandle>,} #[derive(Default)]struct SerializableItemRegistry {    descriptors_by_kind: HashMap<Arc<str>, SerializableItemDescriptor>,    descriptors_by_type: HashMap<TypeId, SerializableItemDescriptor>,} impl Global for SerializableItemRegistry {} impl SerializableItemRegistry {    fn deserialize(        item_kind: &str,        project: Model<Project>,        workspace: WeakView<Workspace>,        workspace_id: WorkspaceId,        item_item: ItemId,        cx: &mut ViewContext<Pane>,    ) -> Task<Result<Box<dyn ItemHandle>>> {        let Some(descriptor) = Self::descriptor(item_kind, cx) else {            return Task::ready(Err(anyhow!(                "cannot deserialize {}, descriptor not found",                item_kind            )));        };         (descriptor.deserialize)(project, workspace, workspace_id, item_item, cx)    }     fn cleanup(

Lines 1–500 of 8160.