pub mod dock;pub mod history_manager;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 toast_layer;mod toolbar;mod workspace_settings; use dap::DapRegistry;pub use toast_layer::{RunAction, ToastAction, ToastLayer, ToastView}; use anyhow::{Context as _, Result, anyhow};use call::{ActiveCall, call_settings::CallSettings};use client::{ ChannelId, Client, ErrorExt, Status, TypedEnvelope, UserStore, proto::{self, ErrorCode, PanelId, PeerId},};use collections::{HashMap, HashSet, hash_map};use derive_more::{Deref, DerefMut};pub use dock::Panel;use dock::{Dock, DockPosition, PanelButtons, PanelHandle, RESIZE_HANDLE_SIZE};use futures::{ Future, FutureExt, StreamExt, channel::{ mpsc::{self, UnboundedReceiver, UnboundedSender}, oneshot, }, future::try_join_all,};use gpui::{ Action, AnyView, AnyWeakView, App, AsyncApp, AsyncWindowContext, Bounds, Context, CursorStyle, Decorations, DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle, Focusable, Global, Hsla, KeyContext, Keystroke, ManagedView, MouseButton, PathPromptOptions, Point, PromptLevel, Render, ResizeEdge, Size, Stateful, Subscription, Task, Tiling, WeakEntity, WindowBounds, WindowHandle, WindowId, WindowOptions, action_as, actions, canvas, impl_action_as, impl_actions, point, relative, size, transparent_black,};pub use history_manager::*;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::{ DetachAndPromptErr, Notifications, simple_message_notification::MessageNotification,};pub use pane::*;pub use pane_group::*;use persistence::{ DB, SerializedWindowBounds, model::{SerializedSshProject, SerializedWorkspace},};pub use persistence::{ DB as WORKSPACE_DB, WorkspaceDb, model::{ItemId, LocalPaths, SerializedWorkspaceLocation},};use postage::stream::Stream;use project::{ DirectoryLister, Project, ProjectEntryId, ProjectPath, ResolvedPath, Worktree, WorktreeId, debugger::breakpoint_store::BreakpointStoreEvent,};use remote::{SshClientDelegate, SshConnectionOptions, ssh_session::ConnectionIdentifier};use schemars::JsonSchema;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::{Arc, LazyLock, Weak, atomic::AtomicUsize}, time::Duration,};use task::{DebugTaskDefinition, SpawnInTerminal, TaskId};use theme::{ActiveTheme, SystemAppearance, ThemeSettings};pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};pub use ui;use ui::prelude::*;use util::{ResultExt, TryFutureExt, paths::SanitizedPath, serde::default_true};use uuid::Uuid;pub use workspace_settings::{ AutosaveSetting, BottomDockLayout, RestoreOnStartupBehavior, TabBarSettings, WorkspaceSettings,}; use crate::notifications::NotificationId;use crate::persistence::{ SerializedAxis, model::{DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup},}; pub const SERIALIZATION_THROTTLE_TIME: Duration = Duration::from_millis(200); 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)}); actions!( workspace, [ ActivateNextPane, ActivatePreviousPane, ActivateNextWindow, ActivatePreviousWindow, AddFolderToProject, ClearAllNotifications, CloseAllDocks, CloseWindow, Feedback, FollowNextCollaborator, MoveFocusedPanelToNextPosition, NewCenterTerminal, NewFile, NewFileSplitVertical, NewFileSplitHorizontal, NewSearch, NewTerminal, NewWindow, Open, OpenFiles, OpenInTerminal, OpenComponentPreview, ReloadActiveItem, SaveAs, SaveWithoutFormat, ShutdownDebugAdapters, ToggleBottomDock, ToggleCenteredLayout, ToggleLeftDock, ToggleRightDock, ToggleZoom, Unfollow, Welcome, RestoreBanner, ]); #[derive(Clone, PartialEq)]pub struct OpenPaths { pub paths: Vec<PathBuf>,} #[derive(Clone, Deserialize, PartialEq, JsonSchema)]pub struct ActivatePane(pub usize); #[derive(Clone, Deserialize, PartialEq, JsonSchema)]#[serde(deny_unknown_fields)]pub struct MoveItemToPane { pub destination: usize, #[serde(default = "default_true")] pub focus: bool,} #[derive(Clone, Deserialize, PartialEq, JsonSchema)]#[serde(deny_unknown_fields)]pub struct MoveItemToPaneInDirection { pub direction: SplitDirection, #[serde(default = "default_true")] pub focus: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema)]#[serde(deny_unknown_fields)]pub struct SaveAll { pub save_intent: Option<SaveIntent>,} #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema)]#[serde(deny_unknown_fields)]pub struct Save { pub save_intent: Option<SaveIntent>,} #[derive(Clone, PartialEq, Debug, Deserialize, Default, JsonSchema)]#[serde(deny_unknown_fields)]pub struct CloseAllItemsAndPanes { pub save_intent: Option<SaveIntent>,} #[derive(Clone, PartialEq, Debug, Deserialize, Default, JsonSchema)]#[serde(deny_unknown_fields)]pub struct CloseInactiveTabsAndPanes { pub save_intent: Option<SaveIntent>,} #[derive(Clone, Deserialize, PartialEq, JsonSchema)]pub struct SendKeystrokes(pub String); #[derive(Clone, Deserialize, PartialEq, Default, JsonSchema)]#[serde(deny_unknown_fields)]pub struct Reload { pub binary_path: Option<PathBuf>,} action_as!(project_symbols, ToggleProjectSymbols as Toggle); #[derive(Default, PartialEq, Eq, Clone, Deserialize, JsonSchema)]pub struct ToggleFileFinder { #[serde(default)] pub separate_history: bool,} impl_action_as!(file_finder, ToggleFileFinder as Toggle); impl_actions!( workspace, [ ActivatePane, CloseAllItemsAndPanes, CloseInactiveTabsAndPanes, MoveItemToPane, MoveItemToPaneInDirection, OpenTerminal, Reload, Save, SaveAll, SendKeystrokes, ]); actions!( workspace, [ ActivatePaneLeft, ActivatePaneRight, ActivatePaneUp, ActivatePaneDown, SwapPaneLeft, SwapPaneRight, SwapPaneUp, SwapPaneDown, ]); #[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 Window, &mut App)>)>,} 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 Window, &mut App) + '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, JsonSchema)]#[serde(deny_unknown_fields)]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 App) { WorkspaceSettings::register(cx); ItemSettings::register(cx); PreviewTabsSettings::register(cx); TabBarSettings::register(cx);} fn prompt_and_open_paths(app_state: Arc<AppState>, options: PathPromptOptions, cx: &mut App) { let paths = cx.prompt_for_paths(options); cx.spawn( async move |cx| match paths.await.anyhow().and_then(|res| res) { Ok(Some(paths)) => { cx.update(|cx| { open_paths(&paths, app_state, OpenOptions::default(), cx).detach_and_log_err(cx) }) .ok(); } Ok(None) => {} Err(err) => { util::log_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();} pub fn init(app_state: Arc<AppState>, cx: &mut App) { init_settings(cx); component::init(); theme_preview::init(cx); toast_layer::init(cx); history_manager::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 App| { if let Some(app_state) = app_state.upgrade() { prompt_and_open_paths( app_state, PathPromptOptions { files: true, directories: true, multiple: true, }, cx, ); } } }); cx.on_action({ let app_state = Arc::downgrade(&app_state); move |_: &OpenFiles, cx: &mut App| { let directories = cx.can_select_mixed_files_and_dirs(); if let Some(app_state) = app_state.upgrade() { prompt_and_open_paths( app_state, PathPromptOptions { files: true, directories, multiple: true, }, cx, ); } } });} #[derive(Clone, Default, Deref, DerefMut)]struct ProjectItemOpeners(Vec<ProjectItemOpener>); type ProjectItemOpener = fn( &Entity<Project>, &ProjectPath, &mut Window, &mut App,) -> Option<Task<Result<(Option<ProjectEntryId>, WorkspaceItemBuilder)>>>; type WorkspaceItemBuilder = Box<dyn FnOnce(&mut Pane, &mut Window, &mut Context<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 App) { let builders = cx.default_global::<ProjectItemOpeners>(); builders.push(|project, project_path, window, cx| { let project_item = <I::Item as project::ProjectItem>::try_open(project, project_path, cx)?; let project = project.clone(); Some(window.spawn(cx, async move |cx| { 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( |pane: &mut Pane, window: &mut Window, cx: &mut Context<Pane>| { Box::new( cx.new(|cx| I::for_project_item(project, pane, project_item, window, 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( Entity<Workspace>, ViewId, &mut Option<proto::view::Variant>, &mut Window, &mut App, ) -> 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 App) { cx.default_global::<Self>().0.insert( TypeId::of::<I>(), FollowableViewDescriptor { from_state_proto: |workspace, id, state, window, cx| { I::from_state_proto(workspace, id, state, window, 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()), },Lines 1–500 of 9287. Load more
pub mod dock; pub mod history_manager; 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 toast_layer; mod toolbar; mod workspace_settings; use dap::DapRegistry; pub use toast_layer::{RunAction, ToastAction, ToastLayer, ToastView}; use anyhow::{Context as _, Result, anyhow}; use call::{ActiveCall, call_settings::CallSettings}; use client::{ ChannelId, Client, ErrorExt, Status, TypedEnvelope, UserStore, proto::{self, ErrorCode, PanelId, PeerId}, }; use collections::{HashMap, HashSet, hash_map}; use derive_more::{Deref, DerefMut}; pub use dock::Panel; use dock::{Dock, DockPosition, PanelButtons, PanelHandle, RESIZE_HANDLE_SIZE}; use futures::{ Future, FutureExt, StreamExt, channel::{ mpsc::{self, UnboundedReceiver, UnboundedSender}, oneshot, }, future::try_join_all, }; use gpui::{ Action, AnyView, AnyWeakView, App, AsyncApp, AsyncWindowContext, Bounds, Context, CursorStyle, Decorations, DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle, Focusable, Global, Hsla, KeyContext, Keystroke, ManagedView, MouseButton, PathPromptOptions, Point, PromptLevel, Render, ResizeEdge, Size, Stateful, Subscription, Task, Tiling, WeakEntity, WindowBounds, WindowHandle, WindowId, WindowOptions, action_as, actions, canvas, impl_action_as, impl_actions, point, relative, size, transparent_black, }; pub use history_manager::*; 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::{ DetachAndPromptErr, Notifications, simple_message_notification::MessageNotification, }; pub use pane::*; pub use pane_group::*; use persistence::{ DB, SerializedWindowBounds, model::{SerializedSshProject, SerializedWorkspace}, }; pub use persistence::{ DB as WORKSPACE_DB, WorkspaceDb, model::{ItemId, LocalPaths, SerializedWorkspaceLocation}, }; use postage::stream::Stream; use project::{ DirectoryLister, Project, ProjectEntryId, ProjectPath, ResolvedPath, Worktree, WorktreeId, debugger::breakpoint_store::BreakpointStoreEvent, }; use remote::{SshClientDelegate, SshConnectionOptions, ssh_session::ConnectionIdentifier}; use schemars::JsonSchema; 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::{Arc, LazyLock, Weak, atomic::AtomicUsize}, time::Duration, }; use task::{DebugTaskDefinition, SpawnInTerminal, TaskId}; use theme::{ActiveTheme, SystemAppearance, ThemeSettings}; pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView}; pub use ui; use ui::prelude::*; use util::{ResultExt, TryFutureExt, paths::SanitizedPath, serde::default_true}; use uuid::Uuid; pub use workspace_settings::{ AutosaveSetting, BottomDockLayout, RestoreOnStartupBehavior, TabBarSettings, WorkspaceSettings, }; use crate::notifications::NotificationId; use crate::persistence::{ SerializedAxis, model::{DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup}, }; pub const SERIALIZATION_THROTTLE_TIME: Duration = Duration::from_millis(200); 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) }); actions!( workspace, [ ActivateNextPane, ActivatePreviousPane, ActivateNextWindow, ActivatePreviousWindow, AddFolderToProject, ClearAllNotifications, CloseAllDocks, CloseWindow, Feedback, FollowNextCollaborator, MoveFocusedPanelToNextPosition, NewCenterTerminal, NewFile, NewFileSplitVertical, NewFileSplitHorizontal, NewSearch, NewTerminal, NewWindow, Open, OpenFiles, OpenInTerminal, OpenComponentPreview, ReloadActiveItem, SaveAs, SaveWithoutFormat, ShutdownDebugAdapters, ToggleBottomDock, ToggleCenteredLayout, ToggleLeftDock, ToggleRightDock, ToggleZoom, Unfollow, Welcome, RestoreBanner, ] ); #[derive(Clone, PartialEq)] pub struct OpenPaths { pub paths: Vec<PathBuf>, } #[derive(Clone, Deserialize, PartialEq, JsonSchema)] pub struct ActivatePane(pub usize); #[derive(Clone, Deserialize, PartialEq, JsonSchema)] #[serde(deny_unknown_fields)] pub struct MoveItemToPane { pub destination: usize, #[serde(default = "default_true")] pub focus: bool, } #[derive(Clone, Deserialize, PartialEq, JsonSchema)] #[serde(deny_unknown_fields)] pub struct MoveItemToPaneInDirection { pub direction: SplitDirection, #[serde(default = "default_true")] pub focus: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct SaveAll { pub save_intent: Option<SaveIntent>, } #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct Save { pub save_intent: Option<SaveIntent>, } #[derive(Clone, PartialEq, Debug, Deserialize, Default, JsonSchema)] #[serde(deny_unknown_fields)] pub struct CloseAllItemsAndPanes { pub save_intent: Option<SaveIntent>, } #[derive(Clone, PartialEq, Debug, Deserialize, Default, JsonSchema)] #[serde(deny_unknown_fields)] pub struct CloseInactiveTabsAndPanes { pub save_intent: Option<SaveIntent>, } #[derive(Clone, Deserialize, PartialEq, JsonSchema)] pub struct SendKeystrokes(pub String); #[derive(Clone, Deserialize, PartialEq, Default, JsonSchema)] #[serde(deny_unknown_fields)] pub struct Reload { pub binary_path: Option<PathBuf>, } action_as!(project_symbols, ToggleProjectSymbols as Toggle); #[derive(Default, PartialEq, Eq, Clone, Deserialize, JsonSchema)] pub struct ToggleFileFinder { #[serde(default)] pub separate_history: bool, } impl_action_as!(file_finder, ToggleFileFinder as Toggle); impl_actions!( workspace, [ ActivatePane, CloseAllItemsAndPanes, CloseInactiveTabsAndPanes, MoveItemToPane, MoveItemToPaneInDirection, OpenTerminal, Reload, Save, SaveAll, SendKeystrokes, ] ); actions!( workspace, [ ActivatePaneLeft, ActivatePaneRight, ActivatePaneUp, ActivatePaneDown, SwapPaneLeft, SwapPaneRight, SwapPaneUp, SwapPaneDown, ] ); #[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 Window, &mut App)>)>, } 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 Window, &mut App) + '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, JsonSchema)] #[serde(deny_unknown_fields)] 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 App) { WorkspaceSettings::register(cx); ItemSettings::register(cx); PreviewTabsSettings::register(cx); TabBarSettings::register(cx); } fn prompt_and_open_paths(app_state: Arc<AppState>, options: PathPromptOptions, cx: &mut App) { let paths = cx.prompt_for_paths(options); cx.spawn( async move |cx| match paths.await.anyhow().and_then(|res| res) { Ok(Some(paths)) => { cx.update(|cx| { open_paths(&paths, app_state, OpenOptions::default(), cx).detach_and_log_err(cx) }) .ok(); } Ok(None) => {} Err(err) => { util::log_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(); } pub fn init(app_state: Arc<AppState>, cx: &mut App) { init_settings(cx); component::init(); theme_preview::init(cx); toast_layer::init(cx); history_manager::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 App| { if let Some(app_state) = app_state.upgrade() { prompt_and_open_paths( app_state, PathPromptOptions { files: true, directories: true, multiple: true, }, cx, ); } } }); cx.on_action({ let app_state = Arc::downgrade(&app_state); move |_: &OpenFiles, cx: &mut App| { let directories = cx.can_select_mixed_files_and_dirs(); if let Some(app_state) = app_state.upgrade() { prompt_and_open_paths( app_state, PathPromptOptions { files: true, directories, multiple: true, }, cx, ); } } }); } #[derive(Clone, Default, Deref, DerefMut)] struct ProjectItemOpeners(Vec<ProjectItemOpener>); type ProjectItemOpener = fn( &Entity<Project>, &ProjectPath, &mut Window, &mut App, ) -> Option<Task<Result<(Option<ProjectEntryId>, WorkspaceItemBuilder)>>>; type WorkspaceItemBuilder = Box<dyn FnOnce(&mut Pane, &mut Window, &mut Context<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 App) { let builders = cx.default_global::<ProjectItemOpeners>(); builders.push(|project, project_path, window, cx| { let project_item = <I::Item as project::ProjectItem>::try_open(project, project_path, cx)?; let project = project.clone(); Some(window.spawn(cx, async move |cx| { 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( |pane: &mut Pane, window: &mut Window, cx: &mut Context<Pane>| { Box::new( cx.new(|cx| I::for_project_item(project, pane, project_item, window, 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( Entity<Workspace>, ViewId, &mut Option<proto::view::Variant>, &mut Window, &mut App, ) -> 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 App) { cx.default_global::<Self>().0.insert( TypeId::of::<I>(), FollowableViewDescriptor { from_state_proto: |workspace, id, state, window, cx| { I::from_state_proto(workspace, id, state, window, 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()), },